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
add_object(t_manager * man,eObject eO,int ai,int aj)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
add_bonds(t_manager * man,const t_functype func[],t_ilist * b,bool bB[])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
add_bpl(t_manager * man,t_idef * idef,bool bB[])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
which_atom(t_manager * man,int x,int y)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
do_label(t_x11 * x11,t_manager * man,int x,int y,bool bSet)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
show_label(t_x11 * x11,t_manager * man,int x,int y)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
hide_label(t_x11 * x11,t_manager * man,int x,int y)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
set_file(t_x11 * x11,t_manager * man,const char * trajectory,const char * status)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
step_message(t_x11 * x11,t_manager * man)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
reset_mols(t_block * mols,matrix box,rvec x[])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
step_man(t_manager * man,int * nat)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
HandleClient(t_x11 * x11,t_manager * man,const long data[])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
TitleCallBack(t_x11 * x11,XEvent * event,Window,void * data)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
ManCallBack(t_x11 * x11,XEvent * event,Window,void * data)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
no_labels(t_x11 * x11,t_manager * man)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
move_man(t_x11 * x11,t_manager * man,int width,int height)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
map_man(t_x11 * x11,t_manager * man)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
toggle_animate(t_x11 * x11,t_manager * man)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
toggle_pbc(t_manager * man)609 bool toggle_pbc(t_manager* man)
610 {
611 man->bPbc = !man->bPbc;
612
613 return man->bPbc;
614 }
615
616
init_man(t_x11 * x11,Window Parent,int x,int y,int width,int height,unsigned long fg,unsigned long bg,PbcType pbcType,matrix box,gmx_output_env_t * oenv)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
done_man(t_x11 * x11,t_manager * man)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
do_filter(t_x11 * x11,t_manager * man,t_filter * filter)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 }
705