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