Update instructions in containers.rst
[gromacs.git] / src / gromacs / gmxana / gmx_xpm2ps.cpp
blobe0e24fea33722508c4cb7b1cb05da1f7d95b2085
1 /*
2 * This file is part of the GROMACS molecular simulation package.
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) 2018,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.
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.
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.
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.
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.
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.
38 #include "gmxpre.h"
40 #include <cassert>
41 #include <cmath>
42 #include <cstdio>
43 #include <cstdlib>
44 #include <cstring>
46 #include <algorithm>
47 #include <numeric>
48 #include <string>
50 #include "gromacs/commandline/pargs.h"
51 #include "gromacs/commandline/viewit.h"
52 #include "gromacs/fileio/matio.h"
53 #include "gromacs/fileio/readinp.h"
54 #include "gromacs/fileio/trxio.h"
55 #include "gromacs/fileio/warninp.h"
56 #include "gromacs/fileio/writeps.h"
57 #include "gromacs/gmxana/gmx_ana.h"
58 #include "gromacs/utility/arrayref.h"
59 #include "gromacs/utility/arraysize.h"
60 #include "gromacs/utility/cstringutil.h"
61 #include "gromacs/utility/exceptions.h"
62 #include "gromacs/utility/fatalerror.h"
63 #include "gromacs/utility/filestream.h"
64 #include "gromacs/utility/futil.h"
65 #include "gromacs/utility/gmxassert.h"
66 #include "gromacs/utility/smalloc.h"
67 #include "gromacs/utility/stringutil.h"
69 #define FUDGE 1.2
70 #define DDD 2
72 typedef struct
74 real major;
75 real minor;
76 real offset;
77 gmx_bool first;
78 int lineatzero;
79 real majorticklen;
80 real minorticklen;
81 char label[STRLEN];
82 real fontsize;
83 char font[STRLEN];
84 real tickfontsize;
85 char tickfont[STRLEN];
86 } t_axisdef;
88 typedef struct
90 int bw;
91 real linewidth;
92 real xoffs, yoffs;
93 gmx_bool bTitle;
94 gmx_bool bTitleOnce;
95 gmx_bool bYonce;
96 real titfontsize;
97 char titfont[STRLEN];
98 gmx_bool legend;
99 real legfontsize;
100 char legfont[STRLEN];
101 char leglabel[STRLEN];
102 char leg2label[STRLEN];
103 real xboxsize;
104 real yboxsize;
105 real boxspacing;
106 real boxlinewidth;
107 real ticklinewidth;
108 real zerolinewidth;
109 t_axisdef X, Y;
110 } t_psrec;
112 /* MUST correspond to char *legend[] in main() */
113 enum
115 elSel,
116 elBoth,
117 elFirst,
118 elSecond,
119 elNone,
120 elNR
123 /* MUST correspond to char *combine[] in main() */
124 enum
126 ecSel,
127 ecHalves,
128 ecAdd,
129 ecSub,
130 ecMult,
131 ecDiv,
132 ecNR
135 static void get_params(const char* mpin, const char* mpout, t_psrec* psr)
137 static const char* gmx_bools[BOOL_NR + 1] = { "no", "yes", nullptr };
138 /* this must correspond to t_rgb *linecolors[] below */
139 static const char* colors[] = { "none", "black", "white", nullptr };
140 warninp_t wi;
141 std::vector<t_inpfile> inp;
143 wi = init_warning(FALSE, 0);
145 if (mpin != nullptr)
147 std::string libmpin = gmx::findLibraryFile(mpin);
148 gmx::TextInputFile stream(libmpin);
149 inp = read_inpfile(&stream, libmpin.c_str(), wi);
151 else
153 inp.clear();
156 psr->bw = get_eenum(&inp, "black&white", gmx_bools);
157 psr->linewidth = get_ereal(&inp, "linewidth", 1.0, wi);
158 setStringEntry(&inp, "titlefont", psr->titfont, "Helvetica");
159 psr->titfontsize = get_ereal(&inp, "titlefontsize", 20.0, wi);
160 psr->legend = (get_eenum(&inp, "legend", gmx_bools) != 0);
161 setStringEntry(&inp, "legendfont", psr->legfont, psr->titfont);
162 setStringEntry(&inp, "legendlabel", psr->leglabel, "");
163 setStringEntry(&inp, "legend2label", psr->leg2label, psr->leglabel);
164 psr->legfontsize = get_ereal(&inp, "legendfontsize", 14.0, wi);
165 psr->xboxsize = get_ereal(&inp, "xbox", 0.0, wi);
166 psr->yboxsize = get_ereal(&inp, "ybox", 0.0, wi);
167 psr->boxspacing = get_ereal(&inp, "matrixspacing", 20.0, wi);
168 psr->xoffs = get_ereal(&inp, "xoffset", 0.0, wi);
169 psr->yoffs = get_ereal(&inp, "yoffset", psr->xoffs, wi);
170 psr->boxlinewidth = get_ereal(&inp, "boxlinewidth", psr->linewidth, wi);
171 psr->ticklinewidth = get_ereal(&inp, "ticklinewidth", psr->linewidth, wi);
172 psr->zerolinewidth = get_ereal(&inp, "zerolinewidth", psr->ticklinewidth, wi);
173 psr->X.lineatzero = get_eenum(&inp, "x-lineat0value", colors);
174 psr->X.major = get_ereal(&inp, "x-major", 1, wi);
175 psr->X.minor = get_ereal(&inp, "x-minor", 1, wi);
176 psr->X.offset = get_ereal(&inp, "x-firstmajor", 0.0, wi);
177 psr->X.first = (get_eenum(&inp, "x-majorat0", gmx_bools) != 0);
178 psr->X.majorticklen = get_ereal(&inp, "x-majorticklen", 8.0, wi);
179 psr->X.minorticklen = get_ereal(&inp, "x-minorticklen", 4.0, wi);
180 setStringEntry(&inp, "x-label", psr->X.label, "");
181 psr->X.fontsize = get_ereal(&inp, "x-fontsize", 16.0, wi);
182 setStringEntry(&inp, "x-font", psr->X.font, psr->titfont);
183 psr->X.tickfontsize = get_ereal(&inp, "x-tickfontsize", 10.0, wi);
184 setStringEntry(&inp, "x-tickfont", psr->X.tickfont, psr->X.font);
185 psr->Y.lineatzero = get_eenum(&inp, "y-lineat0value", colors);
186 psr->Y.major = get_ereal(&inp, "y-major", psr->X.major, wi);
187 psr->Y.minor = get_ereal(&inp, "y-minor", psr->X.minor, wi);
188 psr->Y.offset = get_ereal(&inp, "y-firstmajor", psr->X.offset, wi);
189 psr->Y.first = (get_eenum(&inp, "y-majorat0", gmx_bools) != 0);
190 psr->Y.majorticklen = get_ereal(&inp, "y-majorticklen", psr->X.majorticklen, wi);
191 psr->Y.minorticklen = get_ereal(&inp, "y-minorticklen", psr->X.minorticklen, wi);
192 setStringEntry(&inp, "y-label", psr->Y.label, psr->X.label);
193 psr->Y.fontsize = get_ereal(&inp, "y-fontsize", psr->X.fontsize, wi);
194 setStringEntry(&inp, "y-font", psr->Y.font, psr->X.font);
195 psr->Y.tickfontsize = get_ereal(&inp, "y-tickfontsize", psr->X.tickfontsize, wi);
196 setStringEntry(&inp, "y-tickfont", psr->Y.tickfont, psr->Y.font);
198 check_warning_error(wi, FARGS);
200 if (mpout != nullptr)
202 gmx::TextOutputFile stream(mpout);
203 write_inpfile(&stream, mpout, &inp, TRUE, WriteMdpHeader::yes, wi);
204 stream.close();
207 done_warning(wi, FARGS);
210 static t_rgb black = { 0, 0, 0 };
211 static t_rgb white = { 1, 1, 1 };
212 #define BLACK (&black)
213 /* this must correspond to *colors[] in get_params */
214 static t_rgb* linecolors[] = { nullptr, &black, &white, nullptr };
216 static void leg_discrete(t_psdata* ps,
217 real x0,
218 real y0,
219 const std::string& label,
220 real fontsize,
221 char* font,
222 gmx::ArrayRef<const t_mapping> map)
224 real yhh;
225 real boxhh;
227 boxhh = fontsize + DDD;
228 /* LANDSCAPE */
229 ps_rgb(ps, BLACK);
230 ps_strfont(ps, font, fontsize);
231 yhh = y0 + fontsize + 3 * DDD;
232 if (!label.empty())
234 ps_ctext(ps, x0, yhh, label, eXLeft);
236 ps_moveto(ps, x0, y0);
237 for (const auto& m : map)
239 ps_setorigin(ps);
240 ps_rgb(ps, &m.rgb);
241 ps_fillbox(ps, DDD, DDD, DDD + fontsize, boxhh - DDD);
242 ps_rgb(ps, BLACK);
243 ps_box(ps, DDD, DDD, DDD + fontsize, boxhh - DDD);
244 ps_ctext(ps, boxhh + 2 * DDD, fontsize / 3, m.desc, eXLeft);
245 ps_unsetorigin(ps);
246 ps_moverel(ps, DDD, -fontsize / 3);
250 static void leg_continuous(t_psdata* ps,
251 real x0,
252 real x,
253 real y0,
254 const std::string& label,
255 real fontsize,
256 char* font,
257 gmx::ArrayRef<const t_mapping> map,
258 int mapoffset)
260 real xx0;
261 real yhh, boxxh, boxyh;
262 gmx::index mapIndex = gmx::ssize(map) - mapoffset;
264 boxyh = fontsize;
265 if (x < 8 * fontsize)
267 x = 8 * fontsize;
269 boxxh = x / mapIndex;
270 if (boxxh > fontsize)
272 boxxh = fontsize;
275 GMX_RELEASE_ASSERT(!map.empty(), "NULL map array provided to leg_continuous()");
277 /* LANDSCAPE */
278 xx0 = x0 - (mapIndex * boxxh) / 2.0;
280 for (gmx::index i = 0; (i < mapIndex); i++)
282 ps_rgb(ps, &(map[i + mapoffset].rgb));
283 ps_fillbox(ps, xx0 + i * boxxh, y0, xx0 + (i + 1) * boxxh, y0 + boxyh);
285 ps_strfont(ps, font, fontsize);
286 ps_rgb(ps, BLACK);
287 ps_box(ps, xx0, y0, xx0 + mapIndex * boxxh, y0 + boxyh);
289 yhh = y0 + boxyh + 3 * DDD;
290 ps_ctext(ps, xx0 + boxxh / 2, yhh, map[0].desc, eXCenter);
291 if (!label.empty())
293 ps_ctext(ps, x0, yhh, label, eXCenter);
295 ps_ctext(ps, xx0 + (mapIndex * boxxh) - boxxh / 2, yhh, map[map.size() - 1].desc, eXCenter);
298 static void leg_bicontinuous(t_psdata* ps,
299 real x0,
300 real x,
301 real y0,
302 const std::string& label1,
303 const std::string& label2,
304 real fontsize,
305 char* font,
306 gmx::ArrayRef<const t_mapping> map1,
307 gmx::ArrayRef<const t_mapping> map2)
309 real xx1, xx2, x1, x2;
311 x1 = x / (map1.size() + map2.size()) * map1.size(); /* width of legend 1 */
312 x2 = x / (map1.size() + map2.size()) * map2.size(); /* width of legend 2 */
313 xx1 = x0 - (x2 / 2.0) - fontsize; /* center of legend 1 */
314 xx2 = x0 + (x1 / 2.0) + fontsize; /* center of legend 2 */
315 x1 -= fontsize / 2; /* adjust width */
316 x2 -= fontsize / 2; /* adjust width */
317 leg_continuous(ps, xx1, x1, y0, label1, fontsize, font, map1, 0);
318 leg_continuous(ps, xx2, x2, y0, label2, fontsize, font, map2, 0);
321 static real box_height(const t_matrix& mat, t_psrec* psr)
323 return mat.ny * psr->yboxsize;
326 static real box_dh(t_psrec* psr)
328 return psr->boxspacing;
331 static real box_dh_top(gmx_bool bOnce, t_psrec* psr)
333 real dh;
335 if (psr->bTitle || (psr->bTitleOnce && bOnce))
337 dh = 2 * psr->titfontsize;
339 else
341 dh = 0;
344 return dh;
347 static gmx_bool box_do_all_x_maj_ticks(t_psrec* psr)
349 return (psr->boxspacing > (1.5 * psr->X.majorticklen));
352 static gmx_bool box_do_all_x_min_ticks(t_psrec* psr)
354 return (psr->boxspacing > (1.5 * psr->X.minorticklen));
357 static void draw_boxes(t_psdata* ps, real x0, real y0, real w, gmx::ArrayRef<t_matrix> mat, t_psrec* psr)
359 char buf[128];
360 real xxx;
361 char **xtick, **ytick;
362 real xx, yy, dy, xx00, yy00, offset_x, offset_y;
363 int x, ntx, nty;
364 size_t strlength;
366 /* Only necessary when there will be no y-labels */
367 strlength = 0;
369 /* Draw the box */
370 ps_rgb(ps, BLACK);
371 ps_linewidth(ps, static_cast<int>(psr->boxlinewidth));
372 yy00 = y0;
373 for (auto m = mat.begin(); m != mat.end(); ++m)
375 dy = box_height(*m, psr);
376 ps_box(ps, x0 - 1, yy00 - 1, x0 + w + 1, yy00 + dy + 1);
377 yy00 += dy + box_dh(psr) + box_dh_top(m + 1 == mat.end(), psr);
380 /* Draw the ticks on the axes */
381 ps_linewidth(ps, static_cast<int>(psr->ticklinewidth));
382 xx00 = x0 - 1;
383 yy00 = y0 - 1;
384 auto halfway = mat.begin() + (mat.size() / 2);
385 for (auto m = mat.begin(); m != mat.end(); ++m)
387 if (m->flags & MAT_SPATIAL_X)
389 ntx = m->nx + 1;
390 offset_x = 0.1;
392 else
394 ntx = m->nx;
395 offset_x = 0.6;
397 if (m->flags & MAT_SPATIAL_Y)
399 nty = m->ny + 1;
400 offset_y = 0.1;
402 else
404 nty = m->ny;
405 offset_y = 0.6;
407 snew(xtick, ntx);
408 for (int j = 0; (j < ntx); j++)
410 sprintf(buf, "%g", m->axis_x[j]);
411 xtick[j] = gmx_strdup(buf);
413 ps_strfont(ps, psr->X.tickfont, psr->X.tickfontsize);
414 for (x = 0; (x < ntx); x++)
416 xx = xx00 + (x + offset_x) * psr->xboxsize;
417 if ((bRmod(m->axis_x[x], psr->X.offset, psr->X.major) || (psr->X.first && (x == 0)))
418 && (m == mat.begin() || box_do_all_x_maj_ticks(psr)))
420 /* Longer tick marks */
421 ps_line(ps, xx, yy00, xx, yy00 - psr->X.majorticklen);
422 /* Plot label on lowest graph only */
423 if (m == mat.begin())
425 ps_ctext(ps, xx, yy00 - DDD - psr->X.majorticklen - psr->X.tickfontsize * 0.8,
426 xtick[x], eXCenter);
429 else if (bRmod(m->axis_x[x], psr->X.offset, psr->X.minor)
430 && ((m == mat.begin()) || box_do_all_x_min_ticks(psr)))
432 /* Shorter tick marks */
433 ps_line(ps, xx, yy00, xx, yy00 - psr->X.minorticklen);
435 else if (bRmod(m->axis_x[x], psr->X.offset, psr->X.major))
437 /* Even shorter marks, only each X.major */
438 ps_line(ps, xx, yy00, xx, yy00 - (psr->boxspacing / 2));
441 ps_strfont(ps, psr->Y.tickfont, psr->Y.tickfontsize);
442 snew(ytick, nty);
443 for (int j = 0; (j < nty); j++)
445 sprintf(buf, "%g", m->axis_y[j]);
446 ytick[j] = gmx_strdup(buf);
449 for (int y = 0; (y < nty); y++)
451 yy = yy00 + (y + offset_y) * psr->yboxsize;
452 if (bRmod(m->axis_y[y], psr->Y.offset, psr->Y.major) || (psr->Y.first && (y == 0)))
454 /* Major ticks */
455 strlength = std::max(strlength, std::strlen(ytick[y]));
456 ps_line(ps, xx00, yy, xx00 - psr->Y.majorticklen, yy);
457 ps_ctext(ps, xx00 - psr->Y.majorticklen - DDD, yy - psr->Y.tickfontsize / 3.0,
458 ytick[y], eXRight);
460 else if (bRmod(m->axis_y[y], psr->Y.offset, psr->Y.minor))
462 /* Minor ticks */
463 ps_line(ps, xx00, yy, xx00 - psr->Y.minorticklen, yy);
466 sfree(xtick);
467 sfree(ytick);
469 /* Label on Y-axis */
470 if (!psr->bYonce || m == halfway)
472 std::string mylab;
473 if (strlen(psr->Y.label) > 0)
475 mylab = psr->Y.label;
477 else
479 mylab = m->label_y;
481 if (!mylab.empty())
483 ps_strfont(ps, psr->Y.font, psr->Y.fontsize);
484 ps_flip(ps, TRUE);
485 xxx = x0 - psr->X.majorticklen - psr->X.tickfontsize * strlength - DDD;
486 ps_ctext(ps, yy00 + box_height(*m, psr) / 2.0, 612.5 - xxx, mylab, eXCenter);
487 ps_flip(ps, FALSE);
491 yy00 += box_height(*m, psr) + box_dh(psr) + box_dh_top(m + 1 == mat.end(), psr);
493 /* Label on X-axis */
494 std::string mylab;
495 if (strlen(psr->X.label) > 0)
497 mylab = psr->X.label;
499 else
501 mylab = mat[0].label_x;
503 if (!mylab.empty())
505 ps_strfont(ps, psr->X.font, psr->X.fontsize);
506 ps_ctext(ps, x0 + w / 2,
507 y0 - DDD - psr->X.majorticklen - psr->X.tickfontsize * FUDGE - psr->X.fontsize,
508 mylab, eXCenter);
512 static void draw_zerolines(t_psdata* out, real x0, real y0, real w, gmx::ArrayRef<t_matrix> mat, t_psrec* psr)
514 real xx, yy, dy, xx00, yy00;
515 int x, y;
517 xx00 = x0 - 1.5;
518 yy00 = y0 - 1.5;
519 ps_linewidth(out, static_cast<int>(psr->zerolinewidth));
520 for (auto m = mat.begin(); m != mat.end(); ++m)
522 dy = box_height(*m, psr);
523 /* m->axis_x and _y were already set by draw_boxes */
524 if (psr->X.lineatzero)
526 ps_rgb(out, linecolors[psr->X.lineatzero]);
527 for (x = 0; (x < m->nx); x++)
529 xx = xx00 + (x + 0.7) * psr->xboxsize;
530 /* draw lines whenever tick label almost zero (e.g. next trajectory) */
531 if (x != 0 && x < m->nx - 1
532 && std::abs(m->axis_x[x]) < 0.1 * std::abs(m->axis_x[x + 1] - m->axis_x[x]))
534 ps_line(out, xx, yy00, xx, yy00 + dy + 2);
538 if (psr->Y.lineatzero)
540 ps_rgb(out, linecolors[psr->Y.lineatzero]);
541 for (y = 0; (y < m->ny); y++)
543 yy = yy00 + (y + 0.7) * psr->yboxsize;
544 /* draw lines whenever tick label almost zero (e.g. next trajectory) */
545 if (y != 0 && y < m->ny - 1
546 && std::abs(m->axis_y[y]) < 0.1 * std::abs(m->axis_y[y + 1] - m->axis_y[y]))
548 ps_line(out, xx00, yy, xx00 + w + 2, yy);
552 yy00 += box_height(*m, psr) + box_dh(psr) + box_dh_top(m + 1 == mat.end(), psr);
556 static void box_dim(gmx::ArrayRef<t_matrix> mat,
557 gmx::ArrayRef<t_matrix> mat2,
558 t_psrec* psr,
559 int elegend,
560 gmx_bool bFrame,
561 real* w,
562 real* h,
563 real* dw,
564 real* dh)
566 int maxytick;
567 real ww, hh, dww, dhh;
569 hh = dww = dhh = 0;
570 maxytick = 0;
572 ww = 0;
573 for (const auto& m : mat)
575 ww = std::max(ww, m.nx * psr->xboxsize);
576 hh += box_height(m, psr);
577 maxytick = std::max(maxytick, m.nx);
579 if (bFrame)
581 if (mat[0].label_y[0])
583 dww += 2.0 * (psr->Y.fontsize + DDD);
585 if (psr->Y.major > 0)
587 dww += psr->Y.majorticklen + DDD
588 + psr->Y.tickfontsize * (std::log(static_cast<real>(maxytick)) / std::log(10.0));
590 else if (psr->Y.minor > 0)
592 dww += psr->Y.minorticklen;
595 if (mat[0].label_x[0])
597 dhh += psr->X.fontsize + 2 * DDD;
599 if (/* fool emacs auto-indent */
600 (elegend == elBoth && (mat[0].legend[0] || (!mat2.empty() && mat2[0].legend[0])))
601 || (elegend == elFirst && mat[0].legend[0])
602 || (elegend == elSecond && (!mat2.empty() && mat2[0].legend[0])))
604 dhh += 2 * (psr->legfontsize * FUDGE + 2 * DDD);
606 else
608 dhh += psr->legfontsize * FUDGE + 2 * DDD;
610 if (psr->X.major > 0)
612 dhh += psr->X.tickfontsize * FUDGE + 2 * DDD + psr->X.majorticklen;
614 else if (psr->X.minor > 0)
616 dhh += psr->X.minorticklen;
619 hh += (mat.size() - 1) * box_dh(psr);
620 hh += box_dh_top(TRUE, psr);
621 if (mat.size() > 1)
623 hh += (mat.size() - 1) * box_dh_top(FALSE, psr);
626 *w = ww;
627 *h = hh;
628 *dw = dww;
629 *dh = dhh;
632 static std::vector<t_mapping> add_maps(gmx::ArrayRef<t_mapping> map1, gmx::ArrayRef<t_mapping> map2)
634 static char mapper[] =
635 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()-_=+{}|;:',<"
636 ".>/?";
637 std::vector<t_mapping> map(map1.size() + map2.size());
639 size_t nsymbols = std::strlen(mapper);
640 if (map.size() > nsymbols * nsymbols)
642 gmx_fatal(FARGS, "Not enough symbols to merge the two colormaps\n");
644 printf("Combining colormaps of %zu and %zu elements into one of %zu elements\n", map1.size(),
645 map2.size(), map.size());
646 gmx::index k = 0;
647 for (gmx::index j = 0; j < gmx::ssize(map1) && k < gmx::ssize(map); ++j, ++k)
649 map[k].code.c1 = mapper[k % nsymbols];
650 if (map.size() > nsymbols)
652 map[k].code.c2 = mapper[k / nsymbols];
654 map[k].rgb.r = map1[j].rgb.r;
655 map[k].rgb.g = map1[j].rgb.g;
656 map[k].rgb.b = map1[j].rgb.b;
657 map[k].desc = map1[j].desc;
659 for (gmx::index j = 0; j < gmx::ssize(map2) && k < gmx::ssize(map); ++j, ++k)
661 map[k].code.c1 = mapper[k % nsymbols];
662 if (map.size() > nsymbols)
664 map[k].code.c2 = mapper[k / nsymbols];
666 map[k].rgb.r = map2[j].rgb.r;
667 map[k].rgb.g = map2[j].rgb.g;
668 map[k].rgb.b = map2[j].rgb.b;
669 map[k].desc = map2[j].desc;
672 return map;
675 static bool operator==(const t_mapping& lhs, const t_mapping& rhs)
677 return (lhs.rgb.r == rhs.rgb.r && lhs.rgb.g == rhs.rgb.g && lhs.rgb.b == rhs.rgb.b);
680 static void xpm_mat(const char* outf,
681 gmx::ArrayRef<t_matrix> mat,
682 gmx::ArrayRef<t_matrix> mat2,
683 gmx_bool bDiag,
684 gmx_bool bFirstDiag)
686 FILE* out;
687 int x, y, col;
689 out = gmx_ffopen(outf, "w");
691 GMX_RELEASE_ASSERT(mat.size() == mat2.size(),
692 "Combined matrix write requires matrices of the same size");
693 for (gmx::index i = 0; i != gmx::ssize(mat); ++i)
695 // Color maps that differ only in RGB value are considered different
696 if (mat2.empty() || std::equal(mat[i].map.begin(), mat[i].map.end(), mat2[i].map.begin()))
698 write_xpm_m(out, mat[0]);
700 else
702 auto map = add_maps(mat[i].map, mat2[i].map);
703 for (x = 0; (x < mat[i].nx); x++)
705 for (y = 0; (y < mat[i].nx); y++)
707 if ((x < y) || ((x == y) && bFirstDiag)) /* upper left -> map1 */
709 col = mat[i].matrix(x, y);
711 else /* lower right -> map2 */
713 col = mat[i].map.size() + mat[i].matrix(x, y);
715 if ((bDiag) || (x != y))
717 mat[i].matrix(x, y) = col;
719 else
721 mat[i].matrix(x, y) = 0;
725 mat[i].map = map;
726 if (mat[i].title != mat2[i].title)
728 mat[i].title += " / " + mat2[i].title;
730 if (mat[i].legend != mat2[i].legend)
732 mat[i].legend += " / " + mat2[i].legend;
734 write_xpm_m(out, mat[i]);
737 gmx_ffclose(out);
740 static void tick_spacing(int n, real axis[], real offset, char axisnm, real* major, real* minor)
742 real space;
743 gmx_bool bTryAgain;
744 int i, j, t, f = 0, ten;
745 #define NFACT 4
746 real major_fact[NFACT] = { 5, 4, 2, 1 };
747 real minor_fact[NFACT] = { 5, 4, 4, 5 };
749 /* start with interval between 10 matrix points: */
750 space = std::max(10 * axis[1] - axis[0], axis[std::min(10, n - 1)] - axis[0]);
751 /* get power of 10 */
752 ten = static_cast<int>(std::ceil(std::log(space) / std::log(10.0)) - 1);
753 bTryAgain = TRUE;
754 for (t = ten + 2; t > ten - 3 && bTryAgain; t--)
756 for (f = 0; f < NFACT && bTryAgain; f++)
758 space = std::pow(10.0_real, static_cast<real>(t)) * major_fact[f];
759 /* count how many ticks we would get: */
760 i = 0;
761 for (j = 0; j < n; j++)
763 if (bRmod(axis[j], offset, space))
765 i++;
768 /* do we have a reasonable number of ticks ? */
769 bTryAgain = (i > std::min(10, n - 1)) || (i < 5);
772 if (bTryAgain)
774 space = std::max(10 * axis[1] - axis[0], axis[std::min(10, n - 1)] - axis[0]);
775 fprintf(stderr, "Auto tick spacing failed for %c-axis, guessing %g\n", axisnm, space);
777 *major = space;
778 *minor = space / minor_fact[(f > 0) ? f - 1 : 0];
779 fprintf(stderr, "Auto tick spacing for %c-axis: major %g, minor %g\n", axisnm, *major, *minor);
782 static void ps_mat(const char* outf,
783 gmx::ArrayRef<t_matrix> mat,
784 gmx::ArrayRef<t_matrix> mat2,
785 gmx_bool bFrame,
786 gmx_bool bDiag,
787 gmx_bool bFirstDiag,
788 gmx_bool bTitle,
789 gmx_bool bTitleOnce,
790 gmx_bool bYonce,
791 int elegend,
792 real size,
793 real boxx,
794 real boxy,
795 const char* m2p,
796 const char* m2pout,
797 int mapoffset)
799 t_psrec psrec, *psr;
800 int W, H;
801 int x, y, col;
802 real x0, y0, xx;
803 real w, h, dw, dh;
804 gmx_bool bMap1, bNextMap1, bDiscrete;
808 get_params(m2p, m2pout, &psrec);
810 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
812 psr = &psrec;
814 if (psr->X.major <= 0)
816 tick_spacing((mat[0].flags & MAT_SPATIAL_X) ? mat[0].nx + 1 : mat[0].nx,
817 mat[0].axis_x.data(), psr->X.offset, 'X', &(psr->X.major), &(psr->X.minor));
819 if (psr->X.minor <= 0)
821 psr->X.minor = psr->X.major / 2;
823 if (psr->Y.major <= 0)
825 tick_spacing((mat[0].flags & MAT_SPATIAL_Y) ? mat[0].ny + 1 : mat[0].ny,
826 mat[0].axis_y.data(), psr->Y.offset, 'Y', &(psr->Y.major), &(psr->Y.minor));
828 if (psr->Y.minor <= 0)
830 psr->Y.minor = psr->Y.major / 2;
833 if (boxx > 0)
835 psr->xboxsize = boxx;
836 psr->yboxsize = boxx;
838 if (boxy > 0)
840 psr->yboxsize = boxy;
843 if (psr->xboxsize == 0)
845 psr->xboxsize = size / mat[0].nx;
846 printf("Set the x-size of the box to %.3f\n", psr->xboxsize);
848 if (psr->yboxsize == 0)
850 psr->yboxsize = size / mat[0].nx;
851 printf("Set the y-size of the box to %.3f\n", psr->yboxsize);
854 gmx::ArrayRef<const t_mapping> map1;
855 int legendIndex = 0;
856 for (const auto& m : mat)
858 if (m.map.size() > map1.size())
860 if (map1.empty())
862 printf("Selected legend of matrix # %d for display\n", legendIndex);
864 map1 = m.map;
866 ++legendIndex;
868 gmx::ArrayRef<const t_mapping> map2;
869 if (!mat2.empty())
871 for (const auto& m : mat2)
873 if (m.map.size() > map2.size())
875 if (map2.empty())
877 printf("Selected legend of matrix # %d for second display\n", legendIndex);
879 map2 = m.map;
881 ++legendIndex;
884 if (mat[0].legend.empty() && psr->legend)
886 mat[0].legend = psr->leglabel;
889 bTitle = bTitle && !mat.back().title.empty();
890 bTitleOnce = bTitleOnce && !mat.back().title.empty();
891 psr->bTitle = bTitle;
892 psr->bTitleOnce = bTitleOnce;
893 psr->bYonce = bYonce;
895 /* Set up size of box for nice colors */
896 box_dim(mat, mat2, psr, elegend, bFrame, &w, &h, &dw, &dh);
898 /* Set up bounding box */
899 W = static_cast<int>(w + dw);
900 H = static_cast<int>(h + dh);
902 /* Start box at */
903 x0 = dw;
904 y0 = dh;
905 x = static_cast<int>(W + psr->xoffs);
906 y = static_cast<int>(H + psr->yoffs);
907 if (bFrame)
909 x += 5 * DDD;
910 y += 4 * DDD;
912 t_psdata out = ps_open(outf, 0, 0, x, y);
913 ps_linewidth(&out, static_cast<int>(psr->linewidth));
914 ps_init_rgb_box(&out, psr->xboxsize, psr->yboxsize);
915 ps_init_rgb_nbox(&out, psr->xboxsize, psr->yboxsize);
916 ps_translate(&out, psr->xoffs, psr->yoffs);
918 if (bFrame)
920 ps_comment(&out, "Here starts the BOX drawing");
921 draw_boxes(&out, x0, y0, w, mat, psr);
924 for (gmx::index i = 0; i != gmx::ssize(mat); ++i)
926 if (bTitle || (bTitleOnce && i == gmx::ssize(mat) - 1))
928 /* Print title, if any */
929 ps_rgb(&out, BLACK);
930 ps_strfont(&out, psr->titfont, psr->titfontsize);
931 std::string buf;
932 if (!mat2.empty() || mat[i].title == mat2[i].title)
934 buf = mat[i].title;
936 else
938 buf = mat[i].title + " / " + mat2[i].title;
940 ps_ctext(&out, x0 + w / 2, y0 + box_height(mat[i], psr) + psr->titfontsize, buf, eXCenter);
942 ps_comment(&out, gmx::formatString("Here starts the filling of box #%zd", i).c_str());
943 for (x = 0; (x < mat[i].nx); x++)
945 int nexty;
946 int nextcol;
948 xx = x0 + x * psr->xboxsize;
949 ps_moveto(&out, xx, y0);
950 y = 0;
951 bMap1 = (mat2.empty() || (x < y || (x == y && bFirstDiag)));
952 if ((bDiag) || (x != y))
954 col = mat[i].matrix(x, y);
956 else
958 col = -1;
960 for (nexty = 1; (nexty <= mat[i].ny); nexty++)
962 bNextMap1 = (mat2.empty() || (x < nexty || (x == nexty && bFirstDiag)));
963 /* TRUE: upper left -> map1 */
964 /* FALSE: lower right -> map2 */
965 if ((nexty == mat[i].ny) || (!bDiag && (x == nexty)))
967 nextcol = -1;
969 else
971 nextcol = mat[i].matrix(x, nexty);
973 if ((nexty == mat[i].ny) || (col != nextcol) || (bMap1 != bNextMap1))
975 if (col >= 0)
977 if (bMap1)
979 ps_rgb_nbox(&out, &(mat[i].map[col].rgb), nexty - y);
981 else
983 assert(!mat2.empty());
984 ps_rgb_nbox(&out, &(mat2[i].map[col].rgb), nexty - y);
987 else
989 ps_moverel(&out, 0, psr->yboxsize);
991 y = nexty;
992 bMap1 = bNextMap1;
993 col = nextcol;
997 y0 += box_height(mat[i], psr) + box_dh(psr) + box_dh_top(i + 1 == gmx::ssize(mat), psr);
1000 if (psr->X.lineatzero || psr->Y.lineatzero)
1002 /* reset y0 for first box */
1003 y0 = dh;
1004 ps_comment(&out, "Here starts the zero lines drawing");
1005 draw_zerolines(&out, x0, y0, w, mat, psr);
1008 if (elegend != elNone)
1010 std::string legend;
1011 gmx::ArrayRef<const t_mapping> leg_map;
1012 ps_comment(&out, "Now it's legend time!");
1013 ps_linewidth(&out, static_cast<int>(psr->linewidth));
1014 if (mat2.empty() || elegend != elSecond)
1016 bDiscrete = mat[0].bDiscrete;
1017 legend = mat[0].legend;
1018 leg_map = map1;
1020 else
1022 bDiscrete = mat2[0].bDiscrete;
1023 legend = mat2[0].legend;
1024 leg_map = map2;
1026 if (bDiscrete)
1028 leg_discrete(&out, psr->legfontsize, DDD, legend, psr->legfontsize, psr->legfont, leg_map);
1030 else
1032 if (elegend != elBoth)
1034 leg_continuous(&out, x0 + w / 2, w / 2, DDD, legend, psr->legfontsize, psr->legfont,
1035 leg_map, mapoffset);
1037 else
1039 assert(!mat2.empty());
1040 leg_bicontinuous(&out, x0 + w / 2, w, DDD, mat[0].legend, mat2[0].legend,
1041 psr->legfontsize, psr->legfont, map1, map2);
1044 ps_comment(&out, "Done processing");
1047 ps_close(&out);
1050 static void make_axis_labels(gmx::ArrayRef<t_matrix> mat1)
1052 for (auto& m : mat1)
1054 /* Make labels for x axis */
1055 if (m.axis_x.empty())
1057 m.axis_x.resize(m.nx);
1058 std::iota(m.axis_x.begin(), m.axis_x.end(), 0);
1060 /* Make labels for y axis */
1061 if (m.axis_y.empty())
1063 m.axis_y.resize(m.ny);
1064 std::iota(m.axis_y.begin(), m.axis_y.end(), 0);
1069 static void prune_mat(gmx::ArrayRef<t_matrix> mat, gmx::ArrayRef<t_matrix> mat2, int skip)
1071 GMX_RELEASE_ASSERT(mat.size() == mat2.size() || mat2.empty(),
1072 "Matrix pruning requires matrices of the same size");
1073 for (gmx::index i = 0; i != gmx::ssize(mat); ++i)
1075 fprintf(stderr, "converting %dx%d matrix to %dx%d\n", mat[i].nx, mat[i].ny,
1076 (mat[i].nx + skip - 1) / skip, (mat[i].ny + skip - 1) / skip);
1077 /* walk through matrix */
1078 int xs = 0;
1079 for (int x = 0; (x < mat[i].nx); x++)
1081 if (x % skip == 0)
1083 mat[i].axis_x[xs] = mat[i].axis_x[x];
1084 if (!mat2.empty())
1086 mat2[i].axis_x[xs] = mat2[i].axis_x[x];
1088 int ys = 0;
1089 for (int y = 0; (y < mat[i].ny); y++)
1091 if (x == 0)
1093 mat[i].axis_y[ys] = mat[i].axis_y[y];
1094 if (!mat2.empty())
1096 mat2[i].axis_y[ys] = mat2[i].axis_y[y];
1099 if (y % skip == 0)
1101 mat[i].matrix(xs, ys) = mat[i].matrix(x, y);
1102 if (!mat2.empty())
1104 mat2[i].matrix(xs, ys) = mat2[i].matrix(x, y);
1106 ys++;
1109 xs++;
1112 /* adjust parameters */
1113 mat[i].nx = (mat[i].nx + skip - 1) / skip;
1114 mat[i].ny = (mat[i].ny + skip - 1) / skip;
1115 if (!mat2.empty())
1117 mat2[i].nx = (mat2[i].nx + skip - 1) / skip;
1118 mat2[i].ny = (mat2[i].ny + skip - 1) / skip;
1123 static void zero_lines(gmx::ArrayRef<t_matrix> mat, gmx::ArrayRef<t_matrix> mat2)
1125 GMX_RELEASE_ASSERT(mat.size() == mat2.size(), "zero_lines requires matrices of the same size");
1126 for (gmx::index i = 0; i != gmx::ssize(mat); ++i)
1128 for (int m = 0; m < (!mat2.empty() ? 2 : 1); m++)
1130 gmx::ArrayRef<t_matrix> mats;
1131 if (m == 0)
1133 mats = mat;
1135 else
1137 mats = mat2;
1139 for (int x = 0; x < mats[i].nx - 1; x++)
1141 if (std::abs(mats[i].axis_x[x + 1]) < 1e-5)
1143 for (int y = 0; y < mats[i].ny; y++)
1145 mats[i].matrix(x, y) = 0;
1149 for (int y = 0; y < mats[i].ny - 1; y++)
1151 if (std::abs(mats[i].axis_y[y + 1]) < 1e-5)
1153 for (int x = 0; x < mats[i].nx; x++)
1155 mats[i].matrix(x, y) = 0;
1163 static void write_combined_matrix(int ecombine,
1164 const char* fn,
1165 gmx::ArrayRef<t_matrix> mat1,
1166 gmx::ArrayRef<t_matrix> mat2,
1167 const real* cmin,
1168 const real* cmax)
1170 FILE* out;
1171 real **rmat1, **rmat2;
1172 real rhi, rlo;
1174 out = gmx_ffopen(fn, "w");
1175 GMX_RELEASE_ASSERT(mat1.size() == mat2.size(),
1176 "Combined matrix write requires matrices of the same size");
1177 for (gmx::index k = 0; k != gmx::ssize(mat1); k++)
1179 if (mat2[k].nx != mat1[k].nx || mat2[k].ny != mat1[k].ny)
1181 gmx_fatal(FARGS,
1182 "Size of frame %zd in 1st (%dx%d) and 2nd matrix (%dx%d) do"
1183 " not match.\n",
1184 k, mat1[k].nx, mat1[k].ny, mat2[k].nx, mat2[k].ny);
1186 printf("Combining two %dx%d matrices\n", mat1[k].nx, mat1[k].ny);
1187 rmat1 = matrix2real(&mat1[k], nullptr);
1188 rmat2 = matrix2real(&mat2[k], nullptr);
1189 if (nullptr == rmat1 || nullptr == rmat2)
1191 gmx_fatal(FARGS,
1192 "Could not extract real data from %s xpm matrices. Note that, e.g.,\n"
1193 "g_rms and g_mdmat provide such data, but not do_dssp.\n",
1194 (nullptr == rmat1 && nullptr == rmat2) ? "both" : "one of the");
1196 rlo = 1e38;
1197 rhi = -1e38;
1198 for (int j = 0; j < mat1[k].ny; j++)
1200 for (int i = 0; i < mat1[k].nx; i++)
1202 switch (ecombine)
1204 case ecAdd: rmat1[i][j] += rmat2[i][j]; break;
1205 case ecSub: rmat1[i][j] -= rmat2[i][j]; break;
1206 case ecMult: rmat1[i][j] *= rmat2[i][j]; break;
1207 case ecDiv: rmat1[i][j] /= rmat2[i][j]; break;
1208 default: gmx_fatal(FARGS, "No such combination rule %d for matrices", ecombine);
1210 rlo = std::min(rlo, rmat1[i][j]);
1211 rhi = std::max(rhi, rmat1[i][j]);
1214 if (cmin)
1216 rlo = *cmin;
1218 if (cmax)
1220 rhi = *cmax;
1222 int nlevels = static_cast<int>(std::max(mat1[k].map.size(), mat2[k].map.size()));
1223 if (rhi == rlo)
1225 fprintf(stderr, "combination results in uniform matrix (%g), no output\n", rhi);
1227 else
1229 write_xpm(out, mat1[k].flags, mat1[k].title, mat1[k].legend, mat1[k].label_x,
1230 mat1[k].label_y, mat1[k].nx, mat1[k].ny, mat1[k].axis_x.data(),
1231 mat1[k].axis_y.data(), rmat1, rlo, rhi, white, black, &nlevels);
1234 gmx_ffclose(out);
1237 static void do_mat(gmx::ArrayRef<t_matrix> mat,
1238 gmx::ArrayRef<t_matrix> mat2,
1239 gmx_bool bFrame,
1240 gmx_bool bZeroLine,
1241 gmx_bool bDiag,
1242 gmx_bool bFirstDiag,
1243 gmx_bool bTitle,
1244 gmx_bool bTitleOnce,
1245 gmx_bool bYonce,
1246 int elegend,
1247 real size,
1248 real boxx,
1249 real boxy,
1250 const char* epsfile,
1251 const char* xpmfile,
1252 const char* m2p,
1253 const char* m2pout,
1254 int skip,
1255 int mapoffset)
1257 GMX_RELEASE_ASSERT(mat.size() == mat2.size() || mat2.empty(),
1258 "Combined matrix write requires matrices of the same size");
1259 if (!mat2.empty())
1261 for (gmx::index k = 0; k != gmx::ssize(mat); k++)
1263 if ((mat2[k].nx != mat[k].nx) || (mat2[k].ny != mat[k].ny))
1265 gmx_fatal(FARGS,
1266 "WAKE UP!! Size of frame %zd in 2nd matrix file (%dx%d) does not match "
1267 "size of 1st matrix (%dx%d) or the other way around.\n",
1268 k, mat2[k].nx, mat2[k].ny, mat[k].nx, mat[k].ny);
1270 for (int j = 0; (j < mat[k].ny); j++)
1272 for (int i = bFirstDiag ? j + 1 : j; (i < mat[k].nx); i++)
1274 mat[k].matrix(i, j) = mat2[k].matrix(i, j);
1279 for (gmx::index i = 0; i != gmx::ssize(mat); i++)
1281 fprintf(stderr, "Matrix %zd is %d x %d\n", i, mat[i].nx, mat[i].ny);
1284 make_axis_labels(mat);
1286 if (skip > 1)
1288 prune_mat(mat, mat2, skip);
1291 if (bZeroLine)
1293 zero_lines(mat, mat);
1296 if (epsfile != nullptr)
1298 ps_mat(epsfile, mat, mat2, bFrame, bDiag, bFirstDiag, bTitle, bTitleOnce, bYonce, elegend,
1299 size, boxx, boxy, m2p, m2pout, mapoffset);
1301 if (xpmfile != nullptr)
1303 xpm_mat(xpmfile, mat, mat2, bDiag, bFirstDiag);
1307 static void gradient_map(const rvec grad, gmx::ArrayRef<t_mapping> map)
1309 int i = 0;
1310 real fraction = 1.0 / (map.size() - 1.0);
1311 for (auto& m : map)
1313 real c = i * fraction;
1314 m.rgb.r = 1 - c * (1 - grad[XX]);
1315 m.rgb.g = 1 - c * (1 - grad[YY]);
1316 m.rgb.b = 1 - c * (1 - grad[ZZ]);
1317 ++i;
1321 static void gradient_mat(rvec grad, gmx::ArrayRef<t_matrix> mat)
1323 for (auto& m : mat)
1325 gradient_map(grad, m.map);
1329 static void rainbow_map(gmx_bool bBlue, gmx::ArrayRef<t_mapping> map)
1331 for (auto& m : map)
1333 real c = (m.rgb.r + m.rgb.g + m.rgb.b) / 3;
1334 real r, g, b;
1335 if (c > 1)
1337 c = 1;
1339 if (bBlue)
1341 c = 1 - c;
1343 if (c <= 0.25) /* 0-0.25 */
1345 r = 0;
1346 g = std::pow(4.0 * c, 2.0 / 3.0);
1347 b = 1;
1349 else if (c <= 0.5) /* 0.25-0.5 */
1351 r = 0;
1352 g = 1;
1353 b = std::pow(2.0 - 4.0 * c, 2.0 / 3.0);
1355 else if (c <= 0.75) /* 0.5-0.75 */
1357 r = std::pow(4.0 * c - 2.0, 2.0 / 3.0);
1358 g = 1;
1359 b = 0;
1361 else /* 0.75-1 */
1363 r = 1;
1364 g = std::pow(4.0 - 4.0 * c, 2.0 / 3.0);
1365 b = 0;
1367 m.rgb.r = r;
1368 m.rgb.g = g;
1369 m.rgb.b = b;
1373 static void rainbow_mat(gmx_bool bBlue, gmx::ArrayRef<t_matrix> mat)
1375 for (auto& m : mat)
1377 rainbow_map(bBlue, m.map);
1381 int gmx_xpm2ps(int argc, char* argv[])
1383 const char* desc[] = {
1384 "[THISMODULE] makes a beautiful color plot of an XPixelMap file.",
1385 "Labels and axis can be displayed, when they are supplied",
1386 "in the correct matrix format.",
1387 "Matrix data may be generated by programs such as [gmx-do_dssp], [gmx-rms] or",
1388 "[gmx-mdmat].[PAR]",
1389 "Parameters are set in the [TT].m2p[tt] file optionally supplied with",
1390 "[TT]-di[tt]. Reasonable defaults are provided. Settings for the [IT]y[it]-axis",
1391 "default to those for the [IT]x[it]-axis. Font names have a defaulting hierarchy:",
1392 "titlefont -> legendfont; titlefont -> (xfont -> yfont -> ytickfont)",
1393 "-> xtickfont, e.g. setting titlefont sets all fonts, setting xfont",
1394 "sets yfont, ytickfont and xtickfont.[PAR]",
1395 "When no [TT].m2p[tt] file is supplied, many settings are taken from",
1396 "command line options. The most important option is [TT]-size[tt],",
1397 "which sets the size of the whole matrix in postscript units.",
1398 "This option can be overridden with the [TT]-bx[tt] and [TT]-by[tt]",
1399 "options (and the corresponding parameters in the [TT].m2p[tt] file),",
1400 "which set the size of a single matrix element.[PAR]",
1401 "With [TT]-f2[tt] a second matrix file can be supplied. Both matrix",
1402 "files will be read simultaneously and the upper left half of the",
1403 "first one ([TT]-f[tt]) is plotted together with the lower right",
1404 "half of the second one ([TT]-f2[tt]). The diagonal will contain",
1405 "values from the matrix file selected with [TT]-diag[tt].",
1406 "Plotting of the diagonal values can be suppressed altogether by",
1407 "setting [TT]-diag[tt] to [TT]none[tt].",
1408 "In this case, a new color map will be generated with",
1409 "a red gradient for negative numbers and a blue for positive.",
1410 "If the color coding and legend labels of both matrices are identical,",
1411 "only one legend will be displayed, else two separate legends are",
1412 "displayed.",
1413 "With [TT]-combine[tt], an alternative operation can be selected",
1414 "to combine the matrices. The output range is automatically set",
1415 "to the actual range of the combined matrix. This can be overridden",
1416 "with [TT]-cmin[tt] and [TT]-cmax[tt].[PAR]",
1417 "[TT]-title[tt] can be set to [TT]none[tt] to suppress the title, or to",
1418 "[TT]ylabel[tt] to show the title in the Y-label position (alongside",
1419 "the [IT]y[it]-axis).[PAR]",
1420 "With the [TT]-rainbow[tt] option, dull grayscale matrices can be turned",
1421 "into attractive color pictures.[PAR]",
1422 "Merged or rainbowed matrices can be written to an XPixelMap file with",
1423 "the [TT]-xpm[tt] option."
1426 gmx_output_env_t* oenv;
1427 const char * fn, *epsfile = nullptr, *xpmfile = nullptr;
1428 int i, etitle, elegend, ediag, erainbow, ecombine;
1429 gmx_bool bTitle, bTitleOnce, bDiag, bFirstDiag, bGrad;
1430 static gmx_bool bFrame = TRUE, bZeroLine = FALSE, bYonce = FALSE;
1431 static real size = 400, boxx = 0, boxy = 0, cmin = 0, cmax = 0;
1432 static rvec grad = { 0, 0, 0 };
1433 enum
1435 etSel,
1436 etTop,
1437 etOnce,
1438 etYlabel,
1439 etNone,
1440 etNR
1442 const char* title[] = { nullptr, "top", "once", "ylabel", "none", nullptr };
1443 /* MUST correspond to enum elXxx as defined at top of file */
1444 const char* legend[] = { nullptr, "both", "first", "second", "none", nullptr };
1445 enum
1447 edSel,
1448 edFirst,
1449 edSecond,
1450 edNone,
1451 edNR
1453 const char* diag[] = { nullptr, "first", "second", "none", nullptr };
1454 enum
1456 erSel,
1457 erNo,
1458 erBlue,
1459 erRed,
1460 erNR
1462 const char* rainbow[] = { nullptr, "no", "blue", "red", nullptr };
1463 /* MUST correspond to enum ecXxx as defined at top of file */
1464 const char* combine[] = { nullptr, "halves", "add", "sub", "mult", "div", nullptr };
1465 static int skip = 1, mapoffset = 0;
1466 t_pargs pa[] = {
1467 { "-frame", FALSE, etBOOL, { &bFrame }, "Display frame, ticks, labels, title and legend" },
1468 { "-title", FALSE, etENUM, { title }, "Show title at" },
1469 { "-yonce", FALSE, etBOOL, { &bYonce }, "Show y-label only once" },
1470 { "-legend", FALSE, etENUM, { legend }, "Show legend" },
1471 { "-diag", FALSE, etENUM, { diag }, "Diagonal" },
1472 { "-size", FALSE, etREAL, { &size }, "Horizontal size of the matrix in ps units" },
1473 { "-bx",
1474 FALSE,
1475 etREAL,
1476 { &boxx },
1477 "Element x-size, overrides [TT]-size[tt] (also y-size when [TT]-by[tt] is not set)" },
1478 { "-by", FALSE, etREAL, { &boxy }, "Element y-size" },
1479 { "-rainbow", FALSE, etENUM, { rainbow }, "Rainbow colors, convert white to" },
1480 { "-gradient",
1481 FALSE,
1482 etRVEC,
1483 { grad },
1484 "Re-scale colormap to a smooth gradient from white {1,1,1} to {r,g,b}" },
1485 { "-skip", FALSE, etINT, { &skip }, "only write out every nr-th row and column" },
1486 { "-zeroline",
1487 FALSE,
1488 etBOOL,
1489 { &bZeroLine },
1490 "insert line in [REF].xpm[ref] matrix where axis label is zero" },
1491 { "-legoffset",
1492 FALSE,
1493 etINT,
1494 { &mapoffset },
1495 "Skip first N colors from [REF].xpm[ref] file for the legend" },
1496 { "-combine", FALSE, etENUM, { combine }, "Combine two matrices" },
1497 { "-cmin", FALSE, etREAL, { &cmin }, "Minimum for combination output" },
1498 { "-cmax", FALSE, etREAL, { &cmax }, "Maximum for combination output" }
1500 #define NPA asize(pa)
1501 t_filenm fnm[] = { { efXPM, "-f", nullptr, ffREAD }, { efXPM, "-f2", "root2", ffOPTRD },
1502 { efM2P, "-di", nullptr, ffLIBOPTRD }, { efM2P, "-do", "out", ffOPTWR },
1503 { efEPS, "-o", nullptr, ffOPTWR }, { efXPM, "-xpm", nullptr, ffOPTWR } };
1504 #define NFILE asize(fnm)
1506 if (!parse_common_args(&argc, argv, PCA_CAN_VIEW, NFILE, fnm, NPA, pa, asize(desc), desc, 0,
1507 nullptr, &oenv))
1509 return 0;
1512 etitle = nenum(title);
1513 elegend = nenum(legend);
1514 ediag = nenum(diag);
1515 erainbow = nenum(rainbow);
1516 ecombine = nenum(combine);
1517 bGrad = opt2parg_bSet("-gradient", NPA, pa);
1518 for (i = 0; i < DIM; i++)
1520 if (grad[i] < 0 || grad[i] > 1)
1522 gmx_fatal(FARGS, "RGB value %g out of range (0.0-1.0)", grad[i]);
1525 if (!bFrame)
1527 etitle = etNone;
1528 elegend = elNone;
1531 epsfile = ftp2fn_null(efEPS, NFILE, fnm);
1532 xpmfile = opt2fn_null("-xpm", NFILE, fnm);
1533 if (epsfile == nullptr && xpmfile == nullptr)
1535 if (ecombine != ecHalves)
1537 xpmfile = opt2fn("-xpm", NFILE, fnm);
1539 else
1541 epsfile = ftp2fn(efEPS, NFILE, fnm);
1544 if (ecombine != ecHalves && epsfile)
1546 fprintf(stderr,
1547 "WARNING: can only write result of arithmetic combination "
1548 "of two matrices to .xpm file\n"
1549 " file %s will not be written\n",
1550 epsfile);
1551 epsfile = nullptr;
1554 bDiag = ediag != edNone;
1555 bFirstDiag = ediag != edSecond;
1557 fn = opt2fn("-f", NFILE, fnm);
1558 std::vector<t_matrix> mat, mat2;
1559 mat = read_xpm_matrix(fn);
1560 fprintf(stderr, "There %s %zu matri%s in %s\n", (mat.size() > 1) ? "are" : "is", mat.size(),
1561 (mat.size() > 1) ? "ces" : "x", fn);
1562 fn = opt2fn_null("-f2", NFILE, fnm);
1563 if (fn)
1565 mat2 = read_xpm_matrix(fn);
1566 fprintf(stderr, "There %s %zu matri%s in %s\n", (mat2.size() > 1) ? "are" : "is",
1567 mat2.size(), (mat2.size() > 1) ? "ces" : "x", fn);
1568 if (mat.size() != mat2.size())
1570 fprintf(stderr, "Different number of matrices, using the smallest number.\n");
1571 if (mat.size() > mat2.size())
1573 mat.resize(mat2.size());
1575 else
1577 mat2.resize(mat.size());
1581 else
1583 if (ecombine != ecHalves)
1585 fprintf(stderr,
1586 "WARNING: arithmetic matrix combination selected (-combine), "
1587 "but no second matrix (-f2) supplied\n"
1588 " no matrix combination will be performed\n");
1590 ecombine = 0;
1592 bTitle = etitle == etTop;
1593 bTitleOnce = etitle == etOnce;
1594 if (etitle == etYlabel)
1596 for (auto& m : mat)
1598 m.label_y = m.title;
1600 for (auto& m : mat2)
1602 m.label_y = m.title;
1605 if (bGrad)
1607 gradient_mat(grad, mat);
1608 if (!mat2.empty())
1610 gradient_mat(grad, mat2);
1613 if (erainbow != erNo)
1615 rainbow_mat(erainbow == erBlue, mat);
1616 if (!mat2.empty())
1618 rainbow_mat(erainbow == erBlue, mat2);
1622 if (mat2.empty() && (elegend != elNone))
1624 elegend = elFirst;
1627 if (ecombine && ecombine != ecHalves)
1629 write_combined_matrix(ecombine, xpmfile, mat, mat2, opt2parg_bSet("-cmin", NPA, pa) ? &cmin : nullptr,
1630 opt2parg_bSet("-cmax", NPA, pa) ? &cmax : nullptr);
1632 else
1634 do_mat(mat, mat2, bFrame, bZeroLine, bDiag, bFirstDiag, bTitle, bTitleOnce, bYonce, elegend,
1635 size, boxx, boxy, epsfile, xpmfile, opt2fn_null("-di", NFILE, fnm),
1636 opt2fn_null("-do", NFILE, fnm), skip, mapoffset);
1639 view_all(oenv, NFILE, fnm);
1641 return 0;