Apply re-formatting to C++ in src/ 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 by the GROMACS development team.
7  * Copyright (c) 2019,2020, by the GROMACS development team, led by
8  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
9  * and including many others, as listed in the AUTHORS file in the
10  * top-level source directory and at http://www.gromacs.org.
11  *
12  * GROMACS is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU Lesser General Public License
14  * as published by the Free Software Foundation; either version 2.1
15  * of the License, or (at your option) any later version.
16  *
17  * GROMACS is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  * Lesser General Public License for more details.
21  *
22  * You should have received a copy of the GNU Lesser General Public
23  * License along with GROMACS; if not, see
24  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
26  *
27  * If you want to redistribute modifications to GROMACS, please
28  * consider that scientific software is very special. Version
29  * control is crucial - bugs must be traceable. We will be happy to
30  * consider code for inclusion in the official distribution, but
31  * derived work must not be called official GROMACS. Details are found
32  * in the README & COPYING files - if they are missing, get the
33  * official version at http://www.gromacs.org.
34  *
35  * To help us fund GROMACS development, we humbly ask that you cite
36  * the research papers on the package. Check out http://www.gromacs.org.
37  */
38 #include "gmxpre.h"
39
40 #include "manager.h"
41
42 #include "config.h"
43
44 #include <cctype>
45 #include <cstdio>
46 #include <cstdlib>
47 #include <cstring>
48
49 #include <string>
50
51 #ifdef HAVE_UNISTD_H
52 #    include <unistd.h> // for usleep()
53 #endif
54
55 #include "gromacs/fileio/tpxio.h"
56 #include "gromacs/math/utilities.h"
57 #include "gromacs/math/vec.h"
58 #include "gromacs/mdtypes/md_enums.h"
59 #include "gromacs/pbcutil/pbc.h"
60 #include "gromacs/topology/atomprop.h"
61 #include "gromacs/topology/ifunc.h"
62 #include "gromacs/utility/coolstuff.h"
63 #include "gromacs/utility/cstringutil.h"
64 #include "gromacs/utility/fatalerror.h"
65 #include "gromacs/utility/futil.h"
66 #include "gromacs/utility/smalloc.h"
67 #include "gromacs/utility/stringutil.h"
68
69 #include "3dview.h"
70 #include "nmol.h"
71
72 static void add_object(t_manager* man, eObject eO, int ai, int aj)
73 {
74     srenew(man->obj, ++man->nobj);
75     man->obj[man->nobj - 1].eO    = eO;
76     man->obj[man->nobj - 1].eV    = eVNormal;
77     man->obj[man->nobj - 1].color = WHITE;
78     man->obj[man->nobj - 1].ai    = ai;
79     man->obj[man->nobj - 1].aj    = aj;
80     man->obj[man->nobj - 1].z     = 0.0;
81 }
82
83 static void add_bonds(t_manager* man, const t_functype func[], 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(
190                 x11->disp, man->molw->wd.self, x11->gc, x + 2, y - 2, man->szLab[ai], 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, const char* status)
206 {
207     t_atoms* at;
208     bool*    bB;
209     int      i;
210
211     TpxFileHeader sh = readTpxHeader(status, true);
212     snew(man->ix, sh.natoms);
213     snew(man->zz, sh.natoms);
214     snew(man->col, sh.natoms);
215     snew(man->size, sh.natoms);
216     snew(man->vdw, sh.natoms);
217     snew(man->bLabel, sh.natoms);
218     snew(man->bVis, sh.natoms);
219     for (i = 0; (i < sh.natoms); i++)
220     {
221         man->bVis[i] = false;
222     }
223
224     man->bPbc = false;
225
226     snew(man->szLab, sh.natoms);
227     snew(man->bHydro, sh.natoms);
228     snew(bB, sh.natoms);
229     read_tpx_top(status, nullptr, man->box, &man->natom, nullptr, nullptr, &man->top);
230     man->gpbc = gmx_rmpbc_init(&man->top.idef, PbcType::Unset, man->natom);
231
232     man->natom = read_first_x(man->oenv, &man->status, trajectory, &(man->time), &(man->x), man->box);
233     man->trajfile = gmx_strdup(trajectory);
234     if (man->natom > man->top.atoms.nr)
235     {
236         gmx_fatal(FARGS,
237                   "Topology %s (%d atoms) and trajectory %s (%d atoms) "
238                   "do not match",
239                   status,
240                   man->top.atoms.nr,
241                   trajectory,
242                   man->natom);
243     }
244
245     man->title.text =
246             gmx_strdup(gmx::formatString("%s: %s", *man->top.name, gmx::getCoolQuote().c_str()).c_str());
247     man->view = init_view(man->box);
248     at        = &(man->top.atoms);
249     AtomProperties aps;
250     for (i = 0; (i < man->natom); i++)
251     {
252         char*      aname = *(at->atomname[i]);
253         t_resinfo* ri    = &at->resinfo[at->atom[i].resind];
254
255         man->col[i] = Type2Color(aname);
256         snew(man->szLab[i], 20);
257         if (ri->ic != ' ')
258         {
259             std::sprintf(man->szLab[i], "%s%d%c, %s", *ri->name, ri->nr, ri->ic, aname);
260         }
261         else
262         {
263             std::sprintf(man->szLab[i], "%s%d, %s", *ri->name, ri->nr, aname);
264         }
265         man->bHydro[i] = (toupper(aname[0]) == 'H');
266         if (man->bHydro[i])
267         {
268             man->vdw[i] = 0;
269         }
270         else if (!aps.setAtomProperty(epropVDW, *ri->name, aname, &(man->vdw[i])))
271         {
272             man->vdw[i] = 0;
273         }
274     }
275     add_bpl(man, &(man->top.idef), bB);
276     for (i = 0; (i < man->natom); i++)
277     {
278         if (!bB[i])
279         {
280             add_object(man, eOSingle, i, 0);
281         }
282     }
283     sfree(bB);
284
285     ExposeWin(x11->disp, man->molw->wd.self);
286 }
287
288 void step_message(t_x11* x11, t_manager* man)
289 {
290     XEvent letter;
291
292     letter.type                 = ClientMessage;
293     letter.xclient.display      = x11->disp;
294     letter.xclient.window       = man->wd.self;
295     letter.xclient.message_type = 0;
296     letter.xclient.format       = 32;
297     letter.xclient.data.l[0]    = IDSTEP;
298     letter.xclient.data.l[1]    = Button1;
299     XSendEvent(x11->disp, letter.xclient.window, True, 0, &letter);
300 }
301
302 static void reset_mols(t_block* mols, matrix box, rvec x[])
303 {
304     int  i, m0, m1, j, m;
305     rvec xcm, icm;
306     real ix, iy, iz;
307
308     for (i = 0; (i < mols->nr); i++)
309     {
310         m0 = mols->index[i];
311         m1 = mols->index[i + 1];
312
313         clear_rvec(xcm);
314         clear_rvec(icm);
315
316         for (j = m0; (j < m1); j++)
317         {
318             rvec_inc(xcm, x[j]);
319         }
320         for (m = 0; (m < DIM); m++)
321         {
322             xcm[m] /= (m1 - m0);
323         }
324         for (m = 0; (m < DIM); m++)
325         {
326             if (xcm[m] < 0)
327             {
328                 icm[m] = box[m][m];
329             }
330             else if (xcm[m] >= box[m][m])
331             {
332                 icm[m] = -box[m][m];
333             }
334         }
335         ix = icm[XX], iy = icm[YY], iz = icm[ZZ];
336
337         if ((ix != 0) || (iy != 0) || (iz != 0))
338         {
339             for (j = m0; (j < m1); j++)
340             {
341                 x[j][XX] += ix;
342                 x[j][YY] += iy;
343                 x[j][ZZ] += iz;
344             }
345         }
346     }
347 }
348
349 static bool step_man(t_manager* man, int* nat)
350 {
351     static int ncount = 0;
352     bool       bEof;
353
354     if (!man->natom)
355     {
356         std::fprintf(stderr, "Not initiated yet!");
357         std::exit(1);
358     }
359     bEof = read_next_x(man->oenv, man->status, &man->time, man->x, man->box);
360     *nat = man->natom;
361     if (ncount == man->nSkip)
362     {
363         auto atomsArrayRef = gmx::arrayRefFromArray(reinterpret_cast<gmx::RVec*>(man->x), man->natom);
364         switch (man->molw->boxtype)
365         {
366             case esbTri:
367                 put_atoms_in_triclinic_unitcell(ecenterDEF, man->box, atomsArrayRef);
368                 break;
369             case esbTrunc:
370                 put_atoms_in_compact_unitcell(man->molw->pbcType, ecenterDEF, man->box, atomsArrayRef);
371                 break;
372             case esbRect:
373             case esbNone:
374             default: break;
375         }
376         if (man->bPbc)
377         {
378             gmx_rmpbc(man->gpbc, man->natom, man->box, man->x);
379             reset_mols(&(man->top.mols), man->box, man->x);
380         }
381         ncount = 0;
382     }
383     else
384     {
385         if (man->nSkip > 0)
386         {
387             ncount++;
388             return step_man(man, nat);
389         }
390     }
391
392     return bEof;
393 }
394
395 static void HandleClient(t_x11* x11, t_manager* man, const long data[])
396 {
397     int  ID, button, x, y;
398     bool bPos;
399     real fac;
400
401     ID     = data[0];
402     button = data[1];
403     x      = data[2];
404     y      = data[3];
405     bPos   = (button == Button1);
406     switch (ID)
407     {
408         case IDROTX:
409         case IDROTY:
410         case IDROTZ:
411             rotate_3d(man->view, ID - IDROTX, bPos);
412             draw_mol(x11, man);
413             break;
414         case IDZOOM:
415             if (bPos)
416             {
417                 fac = 0.8; /* Reduce distance between eye and origin */
418             }
419             else
420             {
421                 fac = 1.25;
422             }
423
424             /*  zoom changed to scale by Berk Hess 3-7-96
425                if (zoom_3d(man->view,fac))
426                draw_mol(x11,man); */
427             man->view->sc_x /= fac;
428             man->view->sc_y /= fac;
429             draw_mol(x11, man);
430             break;
431         case IDTRANSX:
432         case IDTRANSY:
433         case IDTRANSZ:
434             translate_view(man->view, ID - IDTRANSX, bPos);
435             draw_mol(x11, man);
436             break;
437         case IDREWIND:
438             if (man->status)
439             {
440                 rewind_trj(man->status);
441                 read_next_x(man->oenv, man->status, &(man->time), man->x, man->box);
442                 man->bEof = false;
443                 draw_mol(x11, man);
444             }
445             break;
446         case IDSTEP:
447         {
448             int nat;
449
450             nat = 0;
451             if (!step_man(man, &nat))
452             {
453                 man->bEof  = true;
454                 man->bStop = true;
455             }
456             else
457             {
458                 if (nat > 0)
459                 {
460                     draw_mol(x11, man);
461                     usleep(man->nWait * 1000);
462                 }
463             }
464             break;
465         }
466         case IDFF: man->bStop = false; break;
467         case IDSTOP_ANI: man->bStop = true; break;
468         case IDDRAWMOL: draw_mol(x11, man); break;
469         case IDLABEL:
470             switch (button)
471             {
472                 case Button1:
473                 case Button2: show_label(x11, man, x, y); break;
474                 case Button3: hide_label(x11, man, x, y); break;
475             }
476             break;
477         default: break;
478     }
479     if (man->bAnimate && !man->bEof && !man->bStop)
480     {
481         step_message(x11, man);
482     }
483 }
484
485 static bool TitleCallBack(t_x11* x11, XEvent* event, Window /*w*/, void* data)
486 {
487     t_windata* wd;
488
489     wd = static_cast<t_windata*>(data);
490     switch (event->type)
491     {
492         case Expose:
493             if (wd->text && (wd->width > 10))
494             {
495                 XSetForeground(x11->disp, x11->gc, WHITE);
496                 TextInWin(x11, wd, wd->text, eXCenter, eYCenter);
497                 XDrawLine(x11->disp, wd->self, x11->gc, 0, wd->height, wd->width, wd->height);
498             }
499             break;
500         case ConfigureNotify:
501             wd->width  = event->xconfigure.width;
502             wd->height = event->xconfigure.height;
503             break;
504     }
505     return false;
506 }
507
508 static bool ManCallBack(t_x11* x11, XEvent* event, Window /*w*/, void* data)
509 {
510     t_manager* man;
511     int        width, height;
512
513     man = static_cast<t_manager*>(data);
514     switch (event->type)
515     {
516         case ConfigureNotify:
517             width  = event->xconfigure.width;
518             height = event->xconfigure.height;
519             if ((width != man->wd.width) || (height != man->wd.height))
520             {
521                 move_man(x11, man, width, height);
522             }
523             break;
524         case ClientMessage: HandleClient(x11, man, event->xclient.data.l); break;
525         default: break;
526     }
527     return false;
528 }
529
530 void no_labels(t_x11* x11, t_manager* man)
531 {
532     int i;
533
534     for (i = 0; (i < man->natom); i++)
535     {
536         man->bLabel[i] = false;
537     }
538     draw_mol(x11, man);
539 }
540
541 void move_man(t_x11* x11, t_manager* man, int width, int height)
542 {
543     int x0, y0, mw, mh, hb;
544     int th;
545
546 #ifdef DEBUG
547     std::fprintf(stderr, "Move manager %dx%d\n", width, height);
548 #endif
549     man->wd.width  = width;
550     man->wd.height = height;
551
552     /* Move all subwindows, resize only Mol window */
553     x0 = width - EWIDTH - AIR - 4 * BORDER; /* Starting of ewin etc. */
554     y0 = AIR;
555
556     /* Mol Window */
557     mw = x0 - 2 * AIR - 4 * BORDER;
558     mh = height - y0 - AIR - 2 * BORDER;
559     XMoveResizeWindow(x11->disp, man->molw->wd.self, AIR, y0, mw, mh);
560
561     /* Title Window */
562     th = XTextHeight(x11->font);
563     XMoveResizeWindow(x11->disp, man->title.self, 0, 0, mw, th + AIR);
564
565     /* Legend Window */
566     XMoveResizeWindow(x11->disp, man->legw->wd.self, x0, y0, EWIDTH, LEGHEIGHT);
567     y0 += LEGHEIGHT + AIR + 2 * BORDER;
568
569     if (y0 > height)
570     {
571         std::printf("Error: Windows falling out of main window!\n");
572     }
573
574     /* Button Box */
575     hb = height - y0 - AIR - 2 * BORDER;
576     XMoveResizeWindow(x11->disp, man->bbox->wd.self, x0, y0, EWIDTH, hb);
577
578     /* Video Box */
579     x0 = (mw - man->vbox->wd.width) / 2;
580     y0 = (mh - 2 - AIR - man->vbox->wd.height);
581     XMoveWindow(x11->disp, man->vbox->wd.self, x0, y0);
582 }
583
584 void map_man(t_x11* x11, t_manager* man)
585 {
586     XMapWindow(x11->disp, man->wd.self);
587     map_mw(x11, man->molw);
588     XMapWindow(x11->disp, man->title.self);
589     map_legw(x11, man->legw);
590     show_but(x11, man->bbox);
591 }
592
593 bool toggle_animate(t_x11* x11, t_manager* man)
594 {
595     if (man->status)
596     {
597         man->bAnimate = !man->bAnimate;
598         man->bStop    = true;
599         man->bEof     = false;
600         if (man->bAnimate)
601         {
602             show_but(x11, man->vbox);
603         }
604         else
605         {
606             hide_but(x11, man->vbox);
607         }
608     }
609     return man->bAnimate;
610 }
611
612 bool toggle_pbc(t_manager* man)
613 {
614     man->bPbc = !man->bPbc;
615
616     return man->bPbc;
617 }
618
619
620 t_manager* init_man(t_x11*            x11,
621                     Window            Parent,
622                     int               x,
623                     int               y,
624                     int               width,
625                     int               height,
626                     unsigned long     fg,
627                     unsigned long     bg,
628                     PbcType           pbcType,
629                     matrix            box,
630                     gmx_output_env_t* oenv)
631 {
632     t_manager* man;
633
634     snew(man, 1);
635     man->status = nullptr;
636     man->bPlus  = true;
637     man->bSort  = true;
638     man->oenv   = oenv;
639     InitWin(&(man->wd), x, y, width, height, 0, "Manager");
640     man->wd.self = XCreateSimpleWindow(
641             x11->disp, Parent, man->wd.x, man->wd.y, man->wd.width, man->wd.height, man->wd.bwidth, fg, bg);
642     x11->RegisterCallback(x11, man->wd.self, Parent, ManCallBack, man);
643     x11->SetInputMask(x11, man->wd.self, StructureNotifyMask | ExposureMask | ButtonPressMask);
644
645     /* The order of creating windows is important for the stacking order */
646     /* Mol Window */
647     man->molw = init_mw(x11, man->wd.self, 0, 0, 1, 1, WHITE, BLUE, pbcType, box);
648
649     /* Title Window */
650     InitWin(&(man->title), 0, 0, 1, 1, 0, nullptr);
651     man->title.self = XCreateSimpleWindow(x11->disp,
652                                           man->molw->wd.self,
653                                           man->title.x,
654                                           man->title.y,
655                                           man->title.width,
656                                           man->title.height,
657                                           man->title.bwidth,
658                                           WHITE,
659                                           BLUE);
660     x11->RegisterCallback(x11, man->title.self, man->molw->wd.self, TitleCallBack, &(man->title));
661     x11->SetInputMask(x11, man->title.self, ExposureMask | StructureNotifyMask);
662
663     /* Button box */
664     man->bbox = init_bbox(x11, man->wd.self, man->wd.self, 1, WHITE, BLUE);
665
666     /* Legend Window */
667     man->legw = init_legw(x11, man->wd.self, 0, 0, EWIDTH, LEGHEIGHT, WHITE, BLUE);
668
669     /* Video Box */
670     man->vbox = init_vbox(x11, man->molw->wd.self, man->wd.self, WHITE, BLUE);
671
672     return man;
673 }
674
675 void done_man(t_x11* x11, t_manager* man)
676 {
677     done_bbox(x11, man->vbox);
678     done_bbox(x11, man->bbox);
679     done_mw(x11, man->molw);
680     done_legw(x11, man->legw);
681     x11->UnRegisterCallback(x11, man->title.self);
682     x11->UnRegisterCallback(x11, man->wd.self);
683     sfree(man->x);
684     sfree(man->obj);
685     sfree(man->bHydro);
686     sfree(man->bLabel);
687     sfree(man->szLab);
688     sfree(man->col);
689     sfree(man);
690 }
691
692 void do_filter(t_x11* x11, t_manager* man, t_filter* filter)
693 {
694     int i;
695     int j;
696
697     for (i = 0; (i < man->natom); i++)
698     {
699         man->bVis[i] = false;
700     }
701     for (i = 0; (i < filter->grps->nr); i++)
702     {
703         if (filter->bShow[i])
704         {
705             for (j = filter->grps->index[i]; (j < filter->grps->index[i + 1]); j++)
706             {
707                 man->bVis[filter->grps->a[j]] = true;
708             }
709         }
710     }
711
712     ExposeWin(x11->disp, man->wd.self);
713 }