d5fbd842f365399b8f8365728134d6093229a1e5
[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(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, 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, man->top.atoms.nr, trajectory, man->natom);
240     }
241
242     man->title.text =
243             gmx_strdup(gmx::formatString("%s: %s", *man->top.name, gmx::getCoolQuote().c_str()).c_str());
244     man->view = init_view(man->box);
245     at        = &(man->top.atoms);
246     AtomProperties aps;
247     for (i = 0; (i < man->natom); i++)
248     {
249         char*      aname = *(at->atomname[i]);
250         t_resinfo* ri    = &at->resinfo[at->atom[i].resind];
251
252         man->col[i] = Type2Color(aname);
253         snew(man->szLab[i], 20);
254         if (ri->ic != ' ')
255         {
256             std::sprintf(man->szLab[i], "%s%d%c, %s", *ri->name, ri->nr, ri->ic, aname);
257         }
258         else
259         {
260             std::sprintf(man->szLab[i], "%s%d, %s", *ri->name, ri->nr, aname);
261         }
262         man->bHydro[i] = (toupper(aname[0]) == 'H');
263         if (man->bHydro[i])
264         {
265             man->vdw[i] = 0;
266         }
267         else if (!aps.setAtomProperty(epropVDW, *ri->name, aname, &(man->vdw[i])))
268         {
269             man->vdw[i] = 0;
270         }
271     }
272     add_bpl(man, &(man->top.idef), bB);
273     for (i = 0; (i < man->natom); i++)
274     {
275         if (!bB[i])
276         {
277             add_object(man, eOSingle, i, 0);
278         }
279     }
280     sfree(bB);
281
282     ExposeWin(x11->disp, man->molw->wd.self);
283 }
284
285 void step_message(t_x11* x11, t_manager* man)
286 {
287     XEvent letter;
288
289     letter.type                 = ClientMessage;
290     letter.xclient.display      = x11->disp;
291     letter.xclient.window       = man->wd.self;
292     letter.xclient.message_type = 0;
293     letter.xclient.format       = 32;
294     letter.xclient.data.l[0]    = IDSTEP;
295     letter.xclient.data.l[1]    = Button1;
296     XSendEvent(x11->disp, letter.xclient.window, True, 0, &letter);
297 }
298
299 static void reset_mols(t_block* mols, matrix box, rvec x[])
300 {
301     int  i, m0, m1, j, m;
302     rvec xcm, icm;
303     real ix, iy, iz;
304
305     for (i = 0; (i < mols->nr); i++)
306     {
307         m0 = mols->index[i];
308         m1 = mols->index[i + 1];
309
310         clear_rvec(xcm);
311         clear_rvec(icm);
312
313         for (j = m0; (j < m1); j++)
314         {
315             rvec_inc(xcm, x[j]);
316         }
317         for (m = 0; (m < DIM); m++)
318         {
319             xcm[m] /= (m1 - m0);
320         }
321         for (m = 0; (m < DIM); m++)
322         {
323             if (xcm[m] < 0)
324             {
325                 icm[m] = box[m][m];
326             }
327             else if (xcm[m] >= box[m][m])
328             {
329                 icm[m] = -box[m][m];
330             }
331         }
332         ix = icm[XX], iy = icm[YY], iz = icm[ZZ];
333
334         if ((ix != 0) || (iy != 0) || (iz != 0))
335         {
336             for (j = m0; (j < m1); j++)
337             {
338                 x[j][XX] += ix;
339                 x[j][YY] += iy;
340                 x[j][ZZ] += iz;
341             }
342         }
343     }
344 }
345
346 static bool step_man(t_manager* man, int* nat)
347 {
348     static int ncount = 0;
349     bool       bEof;
350
351     if (!man->natom)
352     {
353         std::fprintf(stderr, "Not initiated yet!");
354         std::exit(1);
355     }
356     bEof = read_next_x(man->oenv, man->status, &man->time, man->x, man->box);
357     *nat = man->natom;
358     if (ncount == man->nSkip)
359     {
360         auto atomsArrayRef = gmx::arrayRefFromArray(reinterpret_cast<gmx::RVec*>(man->x), man->natom);
361         switch (man->molw->boxtype)
362         {
363             case esbTri:
364                 put_atoms_in_triclinic_unitcell(ecenterDEF, man->box, atomsArrayRef);
365                 break;
366             case esbTrunc:
367                 put_atoms_in_compact_unitcell(man->molw->pbcType, ecenterDEF, man->box, atomsArrayRef);
368                 break;
369             case esbRect:
370             case esbNone:
371             default: break;
372         }
373         if (man->bPbc)
374         {
375             gmx_rmpbc(man->gpbc, man->natom, man->box, man->x);
376             reset_mols(&(man->top.mols), man->box, man->x);
377         }
378         ncount = 0;
379     }
380     else
381     {
382         if (man->nSkip > 0)
383         {
384             ncount++;
385             return step_man(man, nat);
386         }
387     }
388
389     return bEof;
390 }
391
392 static void HandleClient(t_x11* x11, t_manager* man, const long data[])
393 {
394     int  ID, button, x, y;
395     bool bPos;
396     real fac;
397
398     ID     = data[0];
399     button = data[1];
400     x      = data[2];
401     y      = data[3];
402     bPos   = (button == Button1);
403     switch (ID)
404     {
405         case IDROTX:
406         case IDROTY:
407         case IDROTZ:
408             rotate_3d(man->view, ID - IDROTX, bPos);
409             draw_mol(x11, man);
410             break;
411         case IDZOOM:
412             if (bPos)
413             {
414                 fac = 0.8; /* Reduce distance between eye and origin */
415             }
416             else
417             {
418                 fac = 1.25;
419             }
420
421             /*  zoom changed to scale by Berk Hess 3-7-96
422                if (zoom_3d(man->view,fac))
423                draw_mol(x11,man); */
424             man->view->sc_x /= fac;
425             man->view->sc_y /= fac;
426             draw_mol(x11, man);
427             break;
428         case IDTRANSX:
429         case IDTRANSY:
430         case IDTRANSZ:
431             translate_view(man->view, ID - IDTRANSX, bPos);
432             draw_mol(x11, man);
433             break;
434         case IDREWIND:
435             if (man->status)
436             {
437                 rewind_trj(man->status);
438                 read_next_x(man->oenv, man->status, &(man->time), man->x, man->box);
439                 man->bEof = false;
440                 draw_mol(x11, man);
441             }
442             break;
443         case IDSTEP:
444         {
445             int nat;
446
447             nat = 0;
448             if (!step_man(man, &nat))
449             {
450                 man->bEof  = true;
451                 man->bStop = true;
452             }
453             else
454             {
455                 if (nat > 0)
456                 {
457                     draw_mol(x11, man);
458                     usleep(man->nWait * 1000);
459                 }
460             }
461             break;
462         }
463         case IDFF: man->bStop = false; break;
464         case IDSTOP_ANI: man->bStop = true; break;
465         case IDDRAWMOL: draw_mol(x11, man); break;
466         case IDLABEL:
467             switch (button)
468             {
469                 case Button1:
470                 case Button2: show_label(x11, man, x, y); break;
471                 case Button3: hide_label(x11, man, x, y); break;
472             }
473             break;
474         default: break;
475     }
476     if (man->bAnimate && !man->bEof && !man->bStop)
477     {
478         step_message(x11, man);
479     }
480 }
481
482 static bool TitleCallBack(t_x11* x11, XEvent* event, Window /*w*/, void* data)
483 {
484     t_windata* wd;
485
486     wd = static_cast<t_windata*>(data);
487     switch (event->type)
488     {
489         case Expose:
490             if (wd->text && (wd->width > 10))
491             {
492                 XSetForeground(x11->disp, x11->gc, WHITE);
493                 TextInWin(x11, wd, wd->text, eXCenter, eYCenter);
494                 XDrawLine(x11->disp, wd->self, x11->gc, 0, wd->height, wd->width, wd->height);
495             }
496             break;
497         case ConfigureNotify:
498             wd->width  = event->xconfigure.width;
499             wd->height = event->xconfigure.height;
500             break;
501     }
502     return false;
503 }
504
505 static bool ManCallBack(t_x11* x11, XEvent* event, Window /*w*/, void* data)
506 {
507     t_manager* man;
508     int        width, height;
509
510     man = static_cast<t_manager*>(data);
511     switch (event->type)
512     {
513         case ConfigureNotify:
514             width  = event->xconfigure.width;
515             height = event->xconfigure.height;
516             if ((width != man->wd.width) || (height != man->wd.height))
517             {
518                 move_man(x11, man, width, height);
519             }
520             break;
521         case ClientMessage: HandleClient(x11, man, event->xclient.data.l); break;
522         default: break;
523     }
524     return false;
525 }
526
527 void no_labels(t_x11* x11, t_manager* man)
528 {
529     int i;
530
531     for (i = 0; (i < man->natom); i++)
532     {
533         man->bLabel[i] = false;
534     }
535     draw_mol(x11, man);
536 }
537
538 void move_man(t_x11* x11, t_manager* man, int width, int height)
539 {
540     int x0, y0, mw, mh, hb;
541     int th;
542
543 #ifdef DEBUG
544     std::fprintf(stderr, "Move manager %dx%d\n", width, height);
545 #endif
546     man->wd.width  = width;
547     man->wd.height = height;
548
549     /* Move all subwindows, resize only Mol window */
550     x0 = width - EWIDTH - AIR - 4 * BORDER; /* Starting of ewin etc. */
551     y0 = AIR;
552
553     /* Mol Window */
554     mw = x0 - 2 * AIR - 4 * BORDER;
555     mh = height - y0 - AIR - 2 * BORDER;
556     XMoveResizeWindow(x11->disp, man->molw->wd.self, AIR, y0, mw, mh);
557
558     /* Title Window */
559     th = XTextHeight(x11->font);
560     XMoveResizeWindow(x11->disp, man->title.self, 0, 0, mw, th + AIR);
561
562     /* Legend Window */
563     XMoveResizeWindow(x11->disp, man->legw->wd.self, x0, y0, EWIDTH, LEGHEIGHT);
564     y0 += LEGHEIGHT + AIR + 2 * BORDER;
565
566     if (y0 > height)
567     {
568         std::printf("Error: Windows falling out of main window!\n");
569     }
570
571     /* Button Box */
572     hb = height - y0 - AIR - 2 * BORDER;
573     XMoveResizeWindow(x11->disp, man->bbox->wd.self, x0, y0, EWIDTH, hb);
574
575     /* Video Box */
576     x0 = (mw - man->vbox->wd.width) / 2;
577     y0 = (mh - 2 - AIR - man->vbox->wd.height);
578     XMoveWindow(x11->disp, man->vbox->wd.self, x0, y0);
579 }
580
581 void map_man(t_x11* x11, t_manager* man)
582 {
583     XMapWindow(x11->disp, man->wd.self);
584     map_mw(x11, man->molw);
585     XMapWindow(x11->disp, man->title.self);
586     map_legw(x11, man->legw);
587     show_but(x11, man->bbox);
588 }
589
590 bool toggle_animate(t_x11* x11, t_manager* man)
591 {
592     if (man->status)
593     {
594         man->bAnimate = !man->bAnimate;
595         man->bStop    = true;
596         man->bEof     = false;
597         if (man->bAnimate)
598         {
599             show_but(x11, man->vbox);
600         }
601         else
602         {
603             hide_but(x11, man->vbox);
604         }
605     }
606     return man->bAnimate;
607 }
608
609 bool toggle_pbc(t_manager* man)
610 {
611     man->bPbc = !man->bPbc;
612
613     return man->bPbc;
614 }
615
616
617 t_manager* init_man(t_x11*            x11,
618                     Window            Parent,
619                     int               x,
620                     int               y,
621                     int               width,
622                     int               height,
623                     unsigned long     fg,
624                     unsigned long     bg,
625                     PbcType           pbcType,
626                     matrix            box,
627                     gmx_output_env_t* oenv)
628 {
629     t_manager* man;
630
631     snew(man, 1);
632     man->status = nullptr;
633     man->bPlus  = true;
634     man->bSort  = true;
635     man->oenv   = oenv;
636     InitWin(&(man->wd), x, y, width, height, 0, "Manager");
637     man->wd.self = XCreateSimpleWindow(x11->disp, Parent, man->wd.x, man->wd.y, man->wd.width,
638                                        man->wd.height, man->wd.bwidth, fg, bg);
639     x11->RegisterCallback(x11, man->wd.self, Parent, ManCallBack, man);
640     x11->SetInputMask(x11, man->wd.self, StructureNotifyMask | ExposureMask | ButtonPressMask);
641
642     /* The order of creating windows is important for the stacking order */
643     /* Mol Window */
644     man->molw = init_mw(x11, man->wd.self, 0, 0, 1, 1, WHITE, BLUE, pbcType, box);
645
646     /* Title Window */
647     InitWin(&(man->title), 0, 0, 1, 1, 0, nullptr);
648     man->title.self =
649             XCreateSimpleWindow(x11->disp, man->molw->wd.self, man->title.x, man->title.y,
650                                 man->title.width, man->title.height, man->title.bwidth, WHITE, BLUE);
651     x11->RegisterCallback(x11, man->title.self, man->molw->wd.self, TitleCallBack, &(man->title));
652     x11->SetInputMask(x11, man->title.self, ExposureMask | StructureNotifyMask);
653
654     /* Button box */
655     man->bbox = init_bbox(x11, man->wd.self, man->wd.self, 1, WHITE, BLUE);
656
657     /* Legend Window */
658     man->legw = init_legw(x11, man->wd.self, 0, 0, EWIDTH, LEGHEIGHT, WHITE, BLUE);
659
660     /* Video Box */
661     man->vbox = init_vbox(x11, man->molw->wd.self, man->wd.self, WHITE, BLUE);
662
663     return man;
664 }
665
666 void done_man(t_x11* x11, t_manager* man)
667 {
668     done_bbox(x11, man->vbox);
669     done_bbox(x11, man->bbox);
670     done_mw(x11, man->molw);
671     done_legw(x11, man->legw);
672     x11->UnRegisterCallback(x11, man->title.self);
673     x11->UnRegisterCallback(x11, man->wd.self);
674     sfree(man->x);
675     sfree(man->obj);
676     sfree(man->bHydro);
677     sfree(man->bLabel);
678     sfree(man->szLab);
679     sfree(man->col);
680     sfree(man);
681 }
682
683 void do_filter(t_x11* x11, t_manager* man, t_filter* filter)
684 {
685     int i;
686     int j;
687
688     for (i = 0; (i < man->natom); i++)
689     {
690         man->bVis[i] = false;
691     }
692     for (i = 0; (i < filter->grps->nr); i++)
693     {
694         if (filter->bShow[i])
695         {
696             for (j = filter->grps->index[i]; (j < filter->grps->index[i + 1]); j++)
697             {
698                 man->bVis[filter->grps->a[j]] = true;
699             }
700         }
701     }
702
703     ExposeWin(x11->disp, man->wd.self);
704 }