13766c0fcab2ef8b1ea35c5bb608b3eaf596d748
[alexxy/gromacs.git] / src / programs / view / manager.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-2004, The GROMACS development team.
6  * Copyright (c) 2013,2014,2015,2016,2017,2019, 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 "manager.h"
40
41 #include "config.h"
42
43 #include <cctype>
44 #include <cstdio>
45 #include <cstdlib>
46 #include <cstring>
47
48 #include <string>
49
50 #ifdef HAVE_UNISTD_H
51 #include <unistd.h> // for usleep()
52 #endif
53
54 #include "gromacs/fileio/tpxio.h"
55 #include "gromacs/math/utilities.h"
56 #include "gromacs/math/vec.h"
57 #include "gromacs/mdtypes/md_enums.h"
58 #include "gromacs/pbcutil/pbc.h"
59 #include "gromacs/topology/atomprop.h"
60 #include "gromacs/topology/ifunc.h"
61 #include "gromacs/utility/coolstuff.h"
62 #include "gromacs/utility/cstringutil.h"
63 #include "gromacs/utility/fatalerror.h"
64 #include "gromacs/utility/futil.h"
65 #include "gromacs/utility/smalloc.h"
66 #include "gromacs/utility/stringutil.h"
67
68 #include "3dview.h"
69 #include "nmol.h"
70
71 static void add_object(t_manager *man, eObject eO, int ai, int aj)
72 {
73     srenew(man->obj, ++man->nobj);
74     man->obj[man->nobj-1].eO    = eO;
75     man->obj[man->nobj-1].eV    = eVNormal;
76     man->obj[man->nobj-1].color = WHITE;
77     man->obj[man->nobj-1].ai    = ai;
78     man->obj[man->nobj-1].aj    = aj;
79     man->obj[man->nobj-1].z     = 0.0;
80 }
81
82 static void add_bonds(t_manager *man, const t_functype func[],
83                       t_ilist *b, bool bB[])
84 {
85     bool        *bH = man->bHydro;
86     t_iatom     *ia;
87     t_iatom      type, ai, aj, ak;
88     int          i, delta, ftype;
89
90 #ifdef DEBUG
91     std::fprintf(stderr, "Going to make bonds from an ilist with %d entries\n", b->nr);
92 #endif
93     ia = b->iatoms;
94     for (i = 0; (i < b->nr); )
95     {
96         type  = ia[0];
97         ai    = ia[1];
98         ftype = func[type];
99         delta = interaction_function[ftype].nratoms;
100
101         if (ftype == F_SETTLE)
102         {
103             aj     = ia[2];
104             ak     = ia[3];
105             bB[ai] = bB[aj] = bB[ak] = true;
106             add_object(man, eOHBond, ai, aj);
107             add_object(man, eOHBond, ai, ak);
108         }
109         else if (IS_CHEMBOND(ftype))
110         {
111             aj = ia[2];
112 #ifdef DEBUG
113             std::fprintf(stderr, "Adding bond from %d to %d\n", ai, aj);
114 #endif
115             bB[ai] = bB[aj] = true;
116             if (!(bH[ai] == bH[aj]))
117             {
118                 add_object(man, eOHBond, ai, aj);
119             }
120             else if (!bH[ai] && !bH[aj])
121             {
122                 add_object(man, eOBond, ai, aj);
123             }
124         }
125 #ifdef DEBUG
126         std::fprintf(stderr, "Type: %5d, delta: %5d\n", type, delta);
127 #endif
128         ia += delta+1;
129         i  += delta+1;
130     }
131 }
132
133 static void add_bpl(t_manager *man, t_idef *idef, bool bB[])
134 {
135     int ftype;
136
137     for (ftype = 0; ftype < F_NRE; ftype++)
138     {
139         if (IS_CHEMBOND(ftype) || ftype == F_SETTLE)
140         {
141             add_bonds(man, idef->functype, &idef->il[ftype], bB);
142         }
143     }
144 }
145
146 static int which_atom(t_manager *man, int x, int y)
147 {
148 #define DELTA 5
149     int  i;
150     iv2 *ix = man->ix;
151
152     for (i = 0; (i < man->natom); i++)
153     {
154         if ((std::abs(ix[i][XX]-x) < DELTA) && (std::abs(ix[i][YY]-y) < DELTA))
155         {
156             if (man->bVis[i])
157             {
158                 return i;
159             }
160         }
161     }
162     return -1;
163 }
164
165 static void do_label(t_x11 *x11, t_manager *man, int x, int y, bool bSet)
166 {
167     int             ai;
168     unsigned long   col;
169
170     if ((ai = which_atom(man, x, y)) != -1)
171     {
172         x = man->ix[ai][XX];
173         y = man->ix[ai][YY];
174         if (bSet && !man->bLabel[ai])
175         {
176             col             = WHITE;
177             man->bLabel[ai] = true;
178         }
179         else if (!bSet && man->bLabel[ai])
180         {
181             col             = BLUE;
182             man->bLabel[ai] = false;
183         }
184         else
185         {
186             return;
187         }
188         XSetForeground(x11->disp, x11->gc, col);
189         XDrawString(x11->disp, man->molw->wd.self, x11->gc, x+2, y-2, man->szLab[ai],
190                     std::strlen(man->szLab[ai]));
191         XSetForeground(x11->disp, x11->gc, x11->fg);
192     }
193 }
194
195 static void show_label(t_x11 *x11, t_manager *man, int x, int y)
196 {
197     do_label(x11, man, x, y, true);
198 }
199
200 static void hide_label(t_x11 *x11, t_manager *man, int x, int y)
201 {
202     do_label(x11, man, x, y, false);
203 }
204
205 void set_file(t_x11 *x11, t_manager *man, const char *trajectory,
206               const char *status)
207 {
208     t_tpxheader       sh;
209     t_atoms          *at;
210     bool             *bB;
211     int               i;
212
213     read_tpxheader(status, &sh, true);
214     snew(man->ix, sh.natoms);
215     snew(man->zz, sh.natoms);
216     snew(man->col, sh.natoms);
217     snew(man->size, sh.natoms);
218     snew(man->vdw, sh.natoms);
219     snew(man->bLabel, sh.natoms);
220     snew(man->bVis, sh.natoms);
221     for (i = 0; (i < sh.natoms); i++)
222     {
223         man->bVis[i] = false;
224     }
225
226     man->bPbc = false;
227
228     snew(man->szLab, sh.natoms);
229     snew(man->bHydro, sh.natoms);
230     snew(bB, sh.natoms);
231     read_tpx_top(status, nullptr, man->box, &man->natom, nullptr, nullptr, &man->top);
232     man->gpbc = gmx_rmpbc_init(&man->top.idef, -1, man->natom);
233
234     man->natom =
235         read_first_x(man->oenv, &man->status, trajectory, &(man->time), &(man->x),
236                      man->box);
237     man->trajfile = gmx_strdup(trajectory);
238     if (man->natom > man->top.atoms.nr)
239     {
240         gmx_fatal(FARGS, "Topology %s (%d atoms) and trajectory %s (%d atoms) "
241                   "do not match", status, man->top.atoms.nr,
242                   trajectory, man->natom);
243     }
244
245     man->title.text = gmx_strdup(gmx::formatString("%s: %s", *man->top.name, gmx::getCoolQuote().c_str()).c_str());
246     man->view       = init_view(man->box);
247     at              = &(man->top.atoms);
248     AtomProperties aps;
249     for (i = 0; (i < man->natom); i++)
250     {
251         char      *aname = *(at->atomname[i]);
252         t_resinfo *ri    = &at->resinfo[at->atom[i].resind];
253
254         man->col[i] = Type2Color(aname);
255         snew(man->szLab[i], 20);
256         if (ri->ic != ' ')
257         {
258             std::sprintf(man->szLab[i], "%s%d%c, %s", *ri->name, ri->nr, ri->ic, aname);
259         }
260         else
261         {
262             std::sprintf(man->szLab[i], "%s%d, %s", *ri->name, ri->nr, aname);
263         }
264         man->bHydro[i] = (toupper(aname[0]) == 'H');
265         if (man->bHydro[i])
266         {
267             man->vdw[i] = 0;
268         }
269         else if (!aps.setAtomProperty(epropVDW, *ri->name, aname, &(man->vdw[i])))
270         {
271             man->vdw[i] = 0;
272         }
273     }
274     add_bpl(man, &(man->top.idef), bB);
275     for (i = 0; (i < man->natom); i++)
276     {
277         if (!bB[i])
278         {
279             add_object(man, eOSingle, i, 0);
280         }
281     }
282     sfree(bB);
283
284     ExposeWin(x11->disp, man->molw->wd.self);
285 }
286
287 void step_message(t_x11 *x11, t_manager *man)
288 {
289     XEvent letter;
290
291     letter.type                 = ClientMessage;
292     letter.xclient.display      = x11->disp;
293     letter.xclient.window       = man->wd.self;
294     letter.xclient.message_type = 0;
295     letter.xclient.format       = 32;
296     letter.xclient.data.l[0]    = IDSTEP;
297     letter.xclient.data.l[1]    = Button1;
298     XSendEvent(x11->disp, letter.xclient.window, True, 0, &letter);
299 }
300
301 static void reset_mols(t_block *mols, matrix box, rvec x[])
302 {
303     int  i, m0, m1, j, m;
304     rvec xcm, icm;
305     real ix, iy, iz;
306
307     for (i = 0; (i < mols->nr); i++)
308     {
309         m0 = mols->index[i];
310         m1 = mols->index[i+1];
311
312         clear_rvec(xcm);
313         clear_rvec(icm);
314
315         for (j = m0; (j < m1); j++)
316         {
317             rvec_inc(xcm, x[j]);
318         }
319         for (m = 0; (m < DIM); m++)
320         {
321             xcm[m] /= (m1-m0);
322         }
323         for (m = 0; (m < DIM); m++)
324         {
325             if (xcm[m] < 0)
326             {
327                 icm[m] = box[m][m];
328             }
329             else if (xcm[m] >= box[m][m])
330             {
331                 icm[m] = -box[m][m];
332             }
333         }
334         ix = icm[XX], iy = icm[YY], iz = icm[ZZ];
335
336         if ((ix != 0) || (iy != 0) || (iz != 0))
337         {
338             for (j = m0; (j < m1); j++)
339             {
340                 x[j][XX] += ix;
341                 x[j][YY] += iy;
342                 x[j][ZZ] += iz;
343             }
344         }
345     }
346 }
347
348 static bool step_man(t_manager *man, int *nat)
349 {
350     static int      ncount = 0;
351     bool            bEof;
352
353     if (!man->natom)
354     {
355         std::fprintf(stderr, "Not initiated yet!");
356         std::exit(1);
357     }
358     bEof = read_next_x(man->oenv, man->status, &man->time, man->x, man->box);
359     *nat = man->natom;
360     if (ncount == man->nSkip)
361     {
362         auto atomsArrayRef = gmx::arrayRefFromArray(reinterpret_cast<gmx::RVec *>(man->x), man->natom);
363         switch (man->molw->boxtype)
364         {
365             case esbTri:
366                 put_atoms_in_triclinic_unitcell(ecenterDEF, man->box, atomsArrayRef);
367                 break;
368             case esbTrunc:
369                 put_atoms_in_compact_unitcell(man->molw->ePBC, ecenterDEF, man->box,
370                                               atomsArrayRef);
371                 break;
372             case esbRect:
373             case esbNone:
374             default:
375                 break;
376         }
377         if (man->bPbc)
378         {
379             gmx_rmpbc(man->gpbc, man->natom, man->box, man->x);
380             reset_mols(&(man->top.mols), man->box, man->x);
381         }
382         ncount = 0;
383     }
384     else
385     {
386         if (man->nSkip > 0)
387         {
388             ncount++;
389             return step_man(man, nat);
390         }
391     }
392
393     return bEof;
394 }
395
396 static void HandleClient(t_x11 *x11, t_manager *man, const long data[])
397 {
398     int  ID, button, x, y;
399     bool bPos;
400     real fac;
401
402     ID     = data[0];
403     button = data[1];
404     x      = data[2];
405     y      = data[3];
406     bPos   = (button == Button1);
407     switch (ID)
408     {
409         case IDROTX:
410         case IDROTY:
411         case IDROTZ:
412             rotate_3d(man->view, ID-IDROTX, bPos);
413             draw_mol(x11, man);
414             break;
415         case IDZOOM:
416             if (bPos)
417             {
418                 fac = 0.8; /* Reduce distance between eye and origin */
419             }
420             else
421             {
422                 fac = 1.25;
423             }
424
425             /*  zoom changed to scale by Berk Hess 3-7-96
426                if (zoom_3d(man->view,fac))
427                draw_mol(x11,man); */
428             man->view->sc_x /= fac;
429             man->view->sc_y /= fac;
430             draw_mol(x11, man);
431             break;
432         case IDTRANSX:
433         case IDTRANSY:
434         case IDTRANSZ:
435             translate_view(man->view, ID-IDTRANSX, bPos);
436             draw_mol(x11, man);
437             break;
438         case IDREWIND:
439             if (man->status)
440             {
441                 rewind_trj(man->status);
442                 read_next_x(man->oenv, man->status, &(man->time), man->x,
443                             man->box);
444                 man->bEof = false;
445                 draw_mol(x11, man);
446             }
447             break;
448         case IDSTEP:
449         {
450             int      nat;
451
452             nat = 0;
453             if (!step_man(man, &nat))
454             {
455                 man->bEof  = true;
456                 man->bStop = true;
457             }
458             else
459             {
460                 if (nat > 0)
461                 {
462                     draw_mol(x11, man);
463                     usleep(man->nWait*1000);
464                 }
465             }
466             break;
467         }
468         case IDFF:
469             man->bStop = false;
470             break;
471         case IDSTOP_ANI:
472             man->bStop = true;
473             break;
474         case IDDRAWMOL:
475             draw_mol(x11, man);
476             break;
477         case IDLABEL:
478             switch (button)
479             {
480                 case Button1:
481                 case Button2:
482                     show_label(x11, man, x, y);
483                     break;
484                 case Button3:
485                     hide_label(x11, man, x, y);
486                     break;
487             }
488             break;
489         default:
490             break;
491     }
492     if (man->bAnimate && !man->bEof && !man->bStop)
493     {
494         step_message(x11, man);
495     }
496 }
497
498 static bool TitleCallBack(t_x11 *x11, XEvent *event, Window /*w*/, void *data)
499 {
500     t_windata *wd;
501
502     wd = static_cast<t_windata *>(data);
503     switch (event->type)
504     {
505         case Expose:
506             if (wd->text && (wd->width > 10))
507             {
508                 XSetForeground(x11->disp, x11->gc, WHITE);
509                 TextInWin(x11, wd, wd->text, eXCenter, eYCenter);
510                 XDrawLine(x11->disp, wd->self, x11->gc, 0, wd->height,
511                           wd->width, wd->height);
512             }
513             break;
514         case ConfigureNotify:
515             wd->width  = event->xconfigure.width;
516             wd->height = event->xconfigure.height;
517             break;
518     }
519     return false;
520 }
521
522 static bool ManCallBack(t_x11 *x11, XEvent *event, Window /*w*/, void *data)
523 {
524     t_manager *man;
525     int        width, height;
526
527     man = static_cast<t_manager *>(data);
528     switch (event->type)
529     {
530         case ConfigureNotify:
531             width  = event->xconfigure.width;
532             height = event->xconfigure.height;
533             if ((width != man->wd.width) || (height != man->wd.height))
534             {
535                 move_man(x11, man, width, height);
536             }
537             break;
538         case ClientMessage:
539             HandleClient(x11, man, event->xclient.data.l);
540             break;
541         default:
542             break;
543     }
544     return false;
545 }
546
547 void no_labels(t_x11 *x11, t_manager *man)
548 {
549     int i;
550
551     for (i = 0; (i < man->natom); i++)
552     {
553         man->bLabel[i] = false;
554     }
555     draw_mol(x11, man);
556 }
557
558 void move_man(t_x11 *x11, t_manager *man, int width, int height)
559 {
560     int x0, y0, mw, mh, hb;
561     int th;
562
563 #ifdef DEBUG
564     std::fprintf(stderr, "Move manager %dx%d\n", width, height);
565 #endif
566     man->wd.width  = width;
567     man->wd.height = height;
568
569     /* Move all subwindows, resize only Mol window */
570     x0 = width-EWIDTH-AIR-4*BORDER;           /* Starting of ewin etc. */
571     y0 = AIR;
572
573     /* Mol Window */
574     mw = x0-2*AIR-4*BORDER;
575     mh = height-y0-AIR-2*BORDER;
576     XMoveResizeWindow(x11->disp, man->molw->wd.self, AIR, y0, mw, mh);
577
578     /* Title Window */
579     th = XTextHeight(x11->font);
580     XMoveResizeWindow(x11->disp, man->title.self, 0, 0, mw, th+AIR);
581
582     /* Legend Window */
583     XMoveResizeWindow(x11->disp, man->legw->wd.self, x0, y0, EWIDTH, LEGHEIGHT);
584     y0 += LEGHEIGHT+AIR+2*BORDER;
585
586     if (y0 > height)
587     {
588         std::printf("Error: Windows falling out of main window!\n");
589     }
590
591     /* Button Box */
592     hb = height-y0-AIR-2*BORDER;
593     XMoveResizeWindow(x11->disp, man->bbox->wd.self, x0, y0, EWIDTH, hb);
594
595     /* Video Box */
596     x0 = (mw-man->vbox->wd.width)/2;
597     y0 = (mh-2-AIR-man->vbox->wd.height);
598     XMoveWindow(x11->disp, man->vbox->wd.self, x0, y0);
599 }
600
601 void map_man(t_x11 *x11, t_manager *man)
602 {
603     XMapWindow(x11->disp, man->wd.self);
604     map_mw(x11, man->molw);
605     XMapWindow(x11->disp, man->title.self);
606     map_legw(x11, man->legw);
607     show_but(x11, man->bbox);
608 }
609
610 bool toggle_animate (t_x11 *x11, t_manager *man)
611 {
612     if (man->status)
613     {
614         man->bAnimate = !man->bAnimate;
615         man->bStop    = true;
616         man->bEof     = false;
617         if (man->bAnimate)
618         {
619             show_but(x11, man->vbox);
620         }
621         else
622         {
623             hide_but(x11, man->vbox);
624         }
625     }
626     return man->bAnimate;
627 }
628
629 bool toggle_pbc (t_manager *man)
630 {
631     man->bPbc = !man->bPbc;
632
633     return man->bPbc;
634 }
635
636
637 t_manager *init_man(t_x11 *x11, Window Parent,
638                     int x, int y, int width, int height,
639                     unsigned long fg, unsigned long bg,
640                     int ePBC, matrix box,
641                     gmx_output_env_t *oenv)
642 {
643     t_manager *man;
644
645     snew(man, 1);
646     man->status = nullptr;
647     man->bPlus  = true;
648     man->bSort  = true;
649     man->oenv   = oenv;
650     InitWin(&(man->wd), x, y, width, height, 0, "Manager");
651     man->wd.self = XCreateSimpleWindow(x11->disp, Parent, man->wd.x, man->wd.y,
652                                        man->wd.width, man->wd.height,
653                                        man->wd.bwidth, fg, bg);
654     x11->RegisterCallback(x11, man->wd.self, Parent, ManCallBack, man);
655     x11->SetInputMask(x11, man->wd.self, StructureNotifyMask |
656                       ExposureMask | ButtonPressMask);
657
658     /* The order of creating windows is important for the stacking order */
659     /* Mol Window */
660     man->molw = init_mw(x11, man->wd.self, 0, 0, 1, 1, WHITE, BLUE, ePBC, box);
661
662     /* Title Window */
663     InitWin(&(man->title), 0, 0, 1, 1, 0, nullptr);
664     man->title.self = XCreateSimpleWindow(x11->disp, man->molw->wd.self,
665                                           man->title.x, man->title.y,
666                                           man->title.width, man->title.height,
667                                           man->title.bwidth, WHITE, BLUE);
668     x11->RegisterCallback(x11, man->title.self, man->molw->wd.self,
669                           TitleCallBack, &(man->title));
670     x11->SetInputMask(x11, man->title.self, ExposureMask | StructureNotifyMask);
671
672     /* Button box */
673     man->bbox = init_bbox(x11, man->wd.self, man->wd.self, 1, WHITE, BLUE);
674
675     /* Legend Window */
676     man->legw = init_legw(x11, man->wd.self, 0, 0, EWIDTH, LEGHEIGHT, WHITE, BLUE);
677
678     /* Video Box */
679     man->vbox = init_vbox(x11, man->molw->wd.self, man->wd.self, WHITE, BLUE);
680
681     return man;
682 }
683
684 void done_man(t_x11 *x11, t_manager *man)
685 {
686     done_bbox(x11, man->vbox);
687     done_bbox(x11, man->bbox);
688     done_mw(x11, man->molw);
689     done_legw(x11, man->legw);
690     x11->UnRegisterCallback(x11, man->title.self);
691     x11->UnRegisterCallback(x11, man->wd.self);
692     sfree(man->x);
693     sfree(man->obj);
694     sfree(man->bHydro);
695     sfree(man->bLabel);
696     sfree(man->szLab);
697     sfree(man->col);
698     sfree(man);
699 }
700
701 void do_filter(t_x11 *x11, t_manager *man, t_filter *filter)
702 {
703     int      i;
704     int      j;
705
706     for (i = 0; (i < man->natom); i++)
707     {
708         man->bVis[i] = false;
709     }
710     for (i = 0; (i < filter->grps->nr); i++)
711     {
712         if (filter->bShow[i])
713         {
714             for (j = filter->grps->index[i]; (j < filter->grps->index[i+1]); j++)
715             {
716                 man->bVis[filter->grps->a[j]] = true;
717             }
718         }
719     }
720
721     ExposeWin(x11->disp, man->wd.self);
722 }