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, led by
7 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
8 * and including many others, as listed in the AUTHORS file in the
9 * top-level source directory and at http://www.gromacs.org.
11 * GROMACS is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public License
13 * as published by the Free Software Foundation; either version 2.1
14 * of the License, or (at your option) any later version.
16 * GROMACS is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with GROMACS; if not, see
23 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
24 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 * If you want to redistribute modifications to GROMACS, please
27 * consider that scientific software is very special. Version
28 * control is crucial - bugs must be traceable. We will be happy to
29 * consider code for inclusion in the official distribution, but
30 * derived work must not be called official GROMACS. Details are found
31 * in the README & COPYING files - if they are missing, get the
32 * official version at http://www.gromacs.org.
34 * To help us fund GROMACS development, we humbly ask that you cite
35 * the research papers on the package. Check out http://www.gromacs.org.
48 #include "gromacs/commandline/pargs.h"
49 #include "gromacs/commandline/viewit.h"
50 #include "gromacs/fileio/matio.h"
51 #include "gromacs/fileio/readinp.h"
52 #include "gromacs/fileio/trxio.h"
53 #include "gromacs/fileio/warninp.h"
54 #include "gromacs/fileio/writeps.h"
55 #include "gromacs/gmxana/gmx_ana.h"
56 #include "gromacs/utility/arraysize.h"
57 #include "gromacs/utility/cstringutil.h"
58 #include "gromacs/utility/exceptions.h"
59 #include "gromacs/utility/fatalerror.h"
60 #include "gromacs/utility/filestream.h"
61 #include "gromacs/utility/futil.h"
62 #include "gromacs/utility/gmxassert.h"
63 #include "gromacs/utility/smalloc.h"
64 #include "gromacs/utility/stringutil.h"
81 char tickfont[STRLEN];
96 char leglabel[STRLEN];
97 char leg2label[STRLEN];
107 /* MUST correspond to char *legend[] in main() */
109 elSel, elBoth, elFirst, elSecond, elNone, elNR
112 /* MUST correspond to char *combine[] in main() */
114 ecSel, ecHalves, ecAdd, ecSub, ecMult, ecDiv, ecNR
117 void get_params(const char *mpin, const char *mpout, t_psrec *psr)
119 static const char *gmx_bools[BOOL_NR+1] = { "no", "yes", nullptr };
120 /* this must correspond to t_rgb *linecolors[] below */
121 static const char *colors[] = { "none", "black", "white", nullptr };
127 wi = init_warning(FALSE, 0);
131 gmx::TextInputFile stream(mpin);
132 inp = read_inpfile(&stream, mpin, &ninp, wi);
138 ETYPE("black&white", psr->bw, gmx_bools);
139 RTYPE("linewidth", psr->linewidth, 1.0);
140 STYPE("titlefont", psr->titfont, "Helvetica");
141 RTYPE("titlefontsize", psr->titfontsize, 20.0);
142 ETYPE("legend", psr->legend, gmx_bools);
143 STYPE("legendfont", psr->legfont, psr->titfont);
144 STYPE("legendlabel", psr->leglabel, "");
145 STYPE("legend2label", psr->leg2label, psr->leglabel);
146 RTYPE("legendfontsize", psr->legfontsize, 14.0);
147 RTYPE("xbox", psr->xboxsize, 0.0);
148 RTYPE("ybox", psr->yboxsize, 0.0);
149 RTYPE("matrixspacing", psr->boxspacing, 20.0);
150 RTYPE("xoffset", psr->xoffs, 0.0);
151 RTYPE("yoffset", psr->yoffs, psr->xoffs);
152 RTYPE("boxlinewidth", psr->boxlinewidth, psr->linewidth);
153 RTYPE("ticklinewidth", psr->ticklinewidth, psr->linewidth);
154 RTYPE("zerolinewidth", psr->zerolinewidth, psr->ticklinewidth);
155 ETYPE("x-lineat0value", psr->X.lineatzero, colors);
156 RTYPE("x-major", psr->X.major, 1);
157 RTYPE("x-minor", psr->X.minor, 1);
158 RTYPE("x-firstmajor", psr->X.offset, 0.0);
159 ETYPE("x-majorat0", psr->X.first, gmx_bools);
160 RTYPE("x-majorticklen", psr->X.majorticklen, 8.0);
161 RTYPE("x-minorticklen", psr->X.minorticklen, 4.0);
162 STYPE("x-label", psr->X.label, "");
163 RTYPE("x-fontsize", psr->X.fontsize, 16.0);
164 STYPE("x-font", psr->X.font, psr->titfont);
165 RTYPE("x-tickfontsize", psr->X.tickfontsize, 10.0);
166 STYPE("x-tickfont", psr->X.tickfont, psr->X.font);
167 ETYPE("y-lineat0value", psr->Y.lineatzero, colors);
168 RTYPE("y-major", psr->Y.major, psr->X.major);
169 RTYPE("y-minor", psr->Y.minor, psr->X.minor);
170 RTYPE("y-firstmajor", psr->Y.offset, psr->X.offset);
171 ETYPE("y-majorat0", psr->Y.first, gmx_bools);
172 RTYPE("y-majorticklen", psr->Y.majorticklen, psr->X.majorticklen);
173 RTYPE("y-minorticklen", psr->Y.minorticklen, psr->X.minorticklen);
174 STYPE("y-label", psr->Y.label, psr->X.label);
175 RTYPE("y-fontsize", psr->Y.fontsize, psr->X.fontsize);
176 STYPE("y-font", psr->Y.font, psr->X.font);
177 RTYPE("y-tickfontsize", psr->Y.tickfontsize, psr->X.tickfontsize);
178 STYPE("y-tickfont", psr->Y.tickfont, psr->Y.font);
180 check_warning_error(wi, FARGS);
182 if (mpout != nullptr)
184 gmx::TextOutputFile stream(mpout);
185 write_inpfile(&stream, mpout, ninp, inp, TRUE, WriteMdpHeader::yes, wi);
189 done_warning(wi, FARGS);
192 t_rgb black = { 0, 0, 0 };
193 t_rgb white = { 1, 1, 1 };
194 t_rgb red = { 1, 0, 0 };
195 t_rgb blue = { 0, 0, 1 };
196 #define BLACK (&black)
197 /* this must correspond to *colors[] in get_params */
198 t_rgb *linecolors[] = { nullptr, &black, &white, nullptr };
200 gmx_bool diff_maps(int nmap1, t_mapping *map1, int nmap2, t_mapping *map2)
203 gmx_bool bDiff, bColDiff = FALSE;
212 for (i = 0; i < nmap1; i++)
214 if (!matelmt_cmp(map1[i].code, map2[i].code))
218 if (std::strcmp(map1[i].desc, map2[i].desc) != 0)
222 if ((map1[i].rgb.r != map2[i].rgb.r) ||
223 (map1[i].rgb.g != map2[i].rgb.g) ||
224 (map1[i].rgb.b != map2[i].rgb.b))
229 if (!bDiff && bColDiff)
231 fprintf(stderr, "Warning: two colormaps differ only in RGB value, using one colormap.\n");
238 void leg_discrete(t_psdata ps, real x0, real y0, char *label,
239 real fontsize, char *font, int nmap, t_mapping map[])
245 boxhh = fontsize+DDD;
248 ps_strfont(ps, font, fontsize);
249 yhh = y0+fontsize+3*DDD;
250 if (std::strlen(label) > 0)
252 ps_ctext(ps, x0, yhh, label, eXLeft);
254 ps_moveto(ps, x0, y0);
255 for (i = 0; (i < nmap); i++)
258 ps_rgb(ps, &(map[i].rgb));
259 ps_fillbox(ps, DDD, DDD, DDD+fontsize, boxhh-DDD);
261 ps_box(ps, DDD, DDD, DDD+fontsize, boxhh-DDD);
262 ps_ctext(ps, boxhh+2*DDD, fontsize/3, map[i].desc, eXLeft);
264 ps_moverel(ps, DDD, -fontsize/3);
268 void leg_continuous(t_psdata ps, real x0, real x, real y0, char *label,
269 real fontsize, char *font,
270 int nmap, t_mapping map[],
275 real yhh, boxxh, boxyh;
282 boxxh = x/(nmap-mapoffset);
283 if (boxxh > fontsize)
288 GMX_RELEASE_ASSERT(map != nullptr, "NULL map array provided to leg_continuous()");
291 xx0 = x0-((nmap-mapoffset)*boxxh)/2.0;
293 for (i = 0; (i < nmap-mapoffset); i++)
295 ps_rgb(ps, &(map[i+mapoffset].rgb));
296 ps_fillbox(ps, xx0+i*boxxh, y0, xx0+(i+1)*boxxh, y0+boxyh);
298 ps_strfont(ps, font, fontsize);
300 ps_box(ps, xx0, y0, xx0+(nmap-mapoffset)*boxxh, y0+boxyh);
302 yhh = y0+boxyh+3*DDD;
303 ps_ctext(ps, xx0+boxxh/2, yhh, map[0].desc, eXCenter);
304 if (std::strlen(label) > 0)
306 ps_ctext(ps, x0, yhh, label, eXCenter);
308 ps_ctext(ps, xx0+((nmap-mapoffset)*boxxh)
309 - boxxh/2, yhh, map[nmap-1].desc, eXCenter);
312 void leg_bicontinuous(t_psdata ps, real x0, real x, real y0, char *label1,
313 char *label2, real fontsize, char *font,
314 int nmap1, t_mapping map1[], int nmap2, t_mapping map2[])
316 real xx1, xx2, x1, x2;
318 x1 = x/(nmap1+nmap2)*nmap1; /* width of legend 1 */
319 x2 = x/(nmap1+nmap2)*nmap2; /* width of legend 2 */
320 xx1 = x0-(x2/2.0)-fontsize; /* center of legend 1 */
321 xx2 = x0+(x1/2.0)+fontsize; /* center of legend 2 */
322 x1 -= fontsize/2; /* adjust width */
323 x2 -= fontsize/2; /* adjust width */
324 leg_continuous(ps, xx1, x1, y0, label1, fontsize, font, nmap1, map1, 0);
325 leg_continuous(ps, xx2, x2, y0, label2, fontsize, font, nmap2, map2, 0);
328 static real box_height(t_matrix *mat, t_psrec *psr)
330 return mat->ny*psr->yboxsize;
333 static real box_dh(t_psrec *psr)
335 return psr->boxspacing;
338 #define IS_ONCE (i == nmat-1)
339 static real box_dh_top(gmx_bool bOnce, t_psrec *psr)
343 if (psr->bTitle || (psr->bTitleOnce && bOnce) )
345 dh = 2*psr->titfontsize;
355 static gmx_bool box_do_all_x_maj_ticks(t_psrec *psr)
357 return (psr->boxspacing > (1.5*psr->X.majorticklen));
360 static gmx_bool box_do_all_x_min_ticks(t_psrec *psr)
362 return (psr->boxspacing > (1.5*psr->X.minorticklen));
365 static void draw_boxes(t_psdata ps, real x0, real y0, real w,
366 int nmat, t_matrix mat[], t_psrec *psr)
371 char **xtick, **ytick;
372 real xx, yy, dy, xx00, yy00, offset_x, offset_y;
373 int i, j, x, y, ntx, nty;
376 /* Only necessary when there will be no y-labels */
381 ps_linewidth(ps, static_cast<int>(psr->boxlinewidth));
383 for (i = 0; (i < nmat); i++)
385 dy = box_height(&(mat[i]), psr);
386 ps_box(ps, x0-1, yy00-1, x0+w+1, yy00+dy+1);
387 yy00 += dy+box_dh(psr)+box_dh_top(IS_ONCE, psr);
390 /* Draw the ticks on the axes */
391 ps_linewidth(ps, static_cast<int>(psr->ticklinewidth));
394 for (i = 0; (i < nmat); i++)
396 if (mat[i].flags & MAT_SPATIAL_X)
406 if (mat[i].flags & MAT_SPATIAL_Y)
417 for (j = 0; (j < ntx); j++)
419 sprintf(buf, "%g", mat[i].axis_x[j]);
420 xtick[j] = gmx_strdup(buf);
422 ps_strfont(ps, psr->X.tickfont, psr->X.tickfontsize);
423 for (x = 0; (x < ntx); x++)
425 xx = xx00 + (x + offset_x)*psr->xboxsize;
426 if ( ( bRmod(mat[i].axis_x[x], psr->X.offset, psr->X.major) ||
427 (psr->X.first && (x == 0))) &&
428 ( (i == 0) || box_do_all_x_maj_ticks(psr) ) )
430 /* Longer tick marks */
431 ps_line (ps, xx, yy00, xx, yy00-psr->X.majorticklen);
432 /* Plot label on lowest graph only */
436 yy00-DDD-psr->X.majorticklen-psr->X.tickfontsize*0.8,
440 else if (bRmod(mat[i].axis_x[x], psr->X.offset, psr->X.minor) &&
441 ( (i == 0) || box_do_all_x_min_ticks(psr) ) )
443 /* Shorter tick marks */
444 ps_line(ps, xx, yy00, xx, yy00-psr->X.minorticklen);
446 else if (bRmod(mat[i].axis_x[x], psr->X.offset, psr->X.major) )
448 /* Even shorter marks, only each X.major */
449 ps_line(ps, xx, yy00, xx, yy00-(psr->boxspacing/2));
452 ps_strfont(ps, psr->Y.tickfont, psr->Y.tickfontsize);
454 for (j = 0; (j < nty); j++)
456 sprintf(buf, "%g", mat[i].axis_y[j]);
457 ytick[j] = gmx_strdup(buf);
460 for (y = 0; (y < nty); y++)
462 yy = yy00 + (y + offset_y)*psr->yboxsize;
463 if (bRmod(mat[i].axis_y[y], psr->Y.offset, psr->Y.major) ||
464 (psr->Y.first && (y == 0)))
467 strlength = std::max(strlength, std::strlen(ytick[y]));
468 ps_line (ps, xx00, yy, xx00-psr->Y.majorticklen, yy);
469 ps_ctext(ps, xx00-psr->Y.majorticklen-DDD,
470 yy-psr->Y.tickfontsize/3.0, ytick[y], eXRight);
472 else if (bRmod(mat[i].axis_y[y], psr->Y.offset, psr->Y.minor) )
475 ps_line(ps, xx00, yy, xx00-psr->Y.minorticklen, yy);
481 /* Label on Y-axis */
482 if (!psr->bYonce || i == nmat/2)
484 if (strlen(psr->Y.label) > 0)
486 mylab = psr->Y.label;
490 mylab = mat[i].label_y;
492 if (strlen(mylab) > 0)
494 ps_strfont(ps, psr->Y.font, psr->Y.fontsize);
496 xxx = x0-psr->X.majorticklen-psr->X.tickfontsize*strlength-DDD;
497 ps_ctext(ps, yy00+box_height(&mat[i], psr)/2.0, 612.5-xxx,
503 yy00 += box_height(&(mat[i]), psr)+box_dh(psr)+box_dh_top(IS_ONCE, psr);
505 /* Label on X-axis */
506 if (strlen(psr->X.label) > 0)
508 mylab = psr->X.label;
512 mylab = mat[0].label_x;
514 if (strlen(mylab) > 0)
516 ps_strfont(ps, psr->X.font, psr->X.fontsize);
517 ps_ctext(ps, x0+w/2, y0-DDD-psr->X.majorticklen-psr->X.tickfontsize*FUDGE-
518 psr->X.fontsize, mylab, eXCenter);
522 static void draw_zerolines(t_psdata out, real x0, real y0, real w,
523 int nmat, t_matrix mat[], t_psrec *psr)
525 real xx, yy, dy, xx00, yy00;
530 ps_linewidth(out, static_cast<int>(psr->zerolinewidth));
531 for (i = 0; (i < nmat); i++)
533 dy = box_height(&(mat[i]), psr);
534 /* mat[i].axis_x and _y were already set by draw_boxes */
535 if (psr->X.lineatzero)
537 ps_rgb(out, linecolors[psr->X.lineatzero]);
538 for (x = 0; (x < mat[i].nx); x++)
540 xx = xx00+(x+0.7)*psr->xboxsize;
541 /* draw lines whenever tick label almost zero (e.g. next trajectory) */
542 if (x != 0 && x < mat[i].nx-1 &&
543 std::abs(mat[i].axis_x[x]) <
544 0.1*std::abs(mat[i].axis_x[x+1]-mat[i].axis_x[x]) )
546 ps_line (out, xx, yy00, xx, yy00+dy+2);
550 if (psr->Y.lineatzero)
552 ps_rgb(out, linecolors[psr->Y.lineatzero]);
553 for (y = 0; (y < mat[i].ny); y++)
555 yy = yy00+(y+0.7)*psr->yboxsize;
556 /* draw lines whenever tick label almost zero (e.g. next trajectory) */
557 if (y != 0 && y < mat[i].ny-1 &&
558 std::abs(mat[i].axis_y[y]) <
559 0.1*std::abs(mat[i].axis_y[y+1]-mat[i].axis_y[y]) )
561 ps_line (out, xx00, yy, xx00+w+2, yy);
565 yy00 += box_height(&(mat[i]), psr)+box_dh(psr)+box_dh_top(IS_ONCE, psr);
569 static void box_dim(int nmat, t_matrix mat[], t_matrix *mat2, t_psrec *psr,
570 int elegend, gmx_bool bFrame,
571 real *w, real *h, real *dw, real *dh)
574 real ww, hh, dww, dhh;
580 for (i = 0; (i < nmat); i++)
582 ww = std::max(ww, mat[i].nx*psr->xboxsize);
583 hh += box_height(&(mat[i]), psr);
584 maxytick = std::max(maxytick, mat[i].nx);
588 if (mat[0].label_y[0])
590 dww += 2.0*(psr->Y.fontsize+DDD);
592 if (psr->Y.major > 0)
594 dww += psr->Y.majorticklen + DDD +
595 psr->Y.tickfontsize*(std::log(static_cast<real>(maxytick))/std::log(10.0));
597 else if (psr->Y.minor > 0)
599 dww += psr->Y.minorticklen;
602 if (mat[0].label_x[0])
604 dhh += psr->X.fontsize+2*DDD;
606 if ( /* fool emacs auto-indent */
607 (elegend == elBoth && (mat[0].legend[0] || (mat2 && mat2[0].legend[0]))) ||
608 (elegend == elFirst && mat[0].legend[0]) ||
609 (elegend == elSecond && (mat2 && mat2[0].legend[0])) )
611 dhh += 2*(psr->legfontsize*FUDGE+2*DDD);
615 dhh += psr->legfontsize*FUDGE+2*DDD;
617 if (psr->X.major > 0)
619 dhh += psr->X.tickfontsize*FUDGE+2*DDD+psr->X.majorticklen;
621 else if (psr->X.minor > 0)
623 dhh += psr->X.minorticklen;
626 hh += (nmat-1)*box_dh(psr);
627 hh += box_dh_top(TRUE, psr);
630 hh += (nmat-1)*box_dh_top(FALSE, psr);
639 int add_maps(t_mapping **newmap,
640 int nmap1, t_mapping map1[], int nmap2, t_mapping map2[])
642 static char mapper[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()-_=+{}|;:',<.>/?";
647 nsymbols = std::strlen(mapper);
649 if (nmap > nsymbols*nsymbols)
651 gmx_fatal(FARGS, "Not enough symbols to merge the two colormaps\n");
653 printf("Combining colormaps of %d and %d elements into one of %d elements\n",
656 for (j = 0; j < nmap1; j++)
658 map[j].code.c1 = mapper[j % nsymbols];
661 map[j].code.c2 = mapper[j/nsymbols];
663 map[j].rgb.r = map1[j].rgb.r;
664 map[j].rgb.g = map1[j].rgb.g;
665 map[j].rgb.b = map1[j].rgb.b;
666 map[j].desc = map1[j].desc;
668 for (j = 0; j < nmap2; j++)
671 map[k].code.c1 = mapper[k % nsymbols];
674 map[k].code.c2 = mapper[k/nsymbols];
676 map[k].rgb.r = map2[j].rgb.r;
677 map[k].rgb.g = map2[j].rgb.g;
678 map[k].rgb.b = map2[j].rgb.b;
679 map[k].desc = map2[j].desc;
686 void xpm_mat(const char *outf, int nmat, t_matrix *mat, t_matrix *mat2,
687 gmx_bool bDiag, gmx_bool bFirstDiag)
692 t_mapping *map = nullptr;
694 out = gmx_ffopen(outf, "w");
696 for (i = 0; i < nmat; i++)
698 if (!mat2 || !diff_maps(mat[i].nmap, mat[i].map, mat2[i].nmap, mat2[i].map))
700 write_xpm_m(out, mat[0]);
704 nmap = add_maps(&map, mat[i].nmap, mat[i].map, mat2[i].nmap, mat2[i].map);
705 for (x = 0; (x < mat[i].nx); x++)
707 for (y = 0; (y < mat[i].nx); y++)
709 if ((x < y) || ((x == y) && bFirstDiag)) /* upper left -> map1 */
711 col = mat[i].matrix[x][y];
713 else /* lower right -> map2 */
715 col = mat[i].nmap+mat[i].matrix[x][y];
717 if ((bDiag) || (x != y))
719 mat[i].matrix[x][y] = col;
723 mat[i].matrix[x][y] = 0;
730 if (std::strcmp(mat[i].title, mat2[i].title) != 0)
732 sprintf(mat[i].title+strlen(mat[i].title), " / %s", mat2[i].title);
734 if (std::strcmp(mat[i].legend, mat2[i].legend) != 0)
736 sprintf(mat[i].legend+strlen(mat[i].legend), " / %s", mat2[i].legend);
738 write_xpm_m(out, mat[i]);
744 static void tick_spacing(int n, real axis[], real offset, char axisnm,
745 real *major, real *minor)
749 int i, j, t, f = 0, ten;
751 real major_fact[NFACT] = {5, 4, 2, 1};
752 real minor_fact[NFACT] = {5, 4, 4, 5};
754 /* start with interval between 10 matrix points: */
755 space = std::max(10*axis[1]-axis[0], axis[std::min(10, n-1)]-axis[0]);
756 /* get power of 10 */
757 ten = static_cast<int>(std::ceil(std::log(space)/std::log(10.0))-1);
759 for (t = ten+2; t > ten-3 && bTryAgain; t--)
761 for (f = 0; f < NFACT && bTryAgain; f++)
763 space = std::pow(static_cast<real>(10.0), static_cast<real>(t)) * major_fact[f];
764 /* count how many ticks we would get: */
766 for (j = 0; j < n; j++)
768 if (bRmod(axis[j], offset, space) )
773 /* do we have a reasonable number of ticks ? */
774 bTryAgain = (i > std::min(10, n-1)) || (i < 5);
779 space = std::max(10*axis[1]-axis[0], axis[std::min(10, n-1)]-axis[0]);
780 fprintf(stderr, "Auto tick spacing failed for %c-axis, guessing %g\n",
784 *minor = space / minor_fact[(f > 0) ? f-1 : 0];
785 fprintf(stderr, "Auto tick spacing for %c-axis: major %g, minor %g\n",
786 axisnm, *major, *minor);
789 void ps_mat(const char *outf, int nmat, t_matrix mat[], t_matrix mat2[],
790 gmx_bool bFrame, gmx_bool bDiag, gmx_bool bFirstDiag,
791 gmx_bool bTitle, gmx_bool bTitleOnce, gmx_bool bYonce, int elegend,
792 real size, real boxx, real boxy, const char *m2p, const char *m2pout,
800 int i, x, y, col, leg = 0;
803 int nmap1 = 0, nmap2 = 0, leg_nmap;
804 t_mapping *map1 = nullptr, *map2 = nullptr, *leg_map;
805 gmx_bool bMap1, bNextMap1, bDiscrete;
808 libm2p = m2p ? gmxlibfn(m2p) : m2p;
811 get_params(libm2p, m2pout, &psrec);
813 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
817 if (psr->X.major <= 0)
819 tick_spacing((mat[0].flags & MAT_SPATIAL_X) ? mat[0].nx + 1 : mat[0].nx,
820 mat[0].axis_x, psr->X.offset, 'X',
821 &(psr->X.major), &(psr->X.minor) );
823 if (psr->X.minor <= 0)
825 psr->X.minor = psr->X.major / 2;
827 if (psr->Y.major <= 0)
829 tick_spacing((mat[0].flags & MAT_SPATIAL_Y) ? mat[0].ny + 1 : mat[0].ny,
830 mat[0].axis_y, psr->Y.offset, 'Y',
831 &(psr->Y.major), &(psr->Y.minor) );
833 if (psr->Y.minor <= 0)
835 psr->Y.minor = psr->Y.major / 2;
840 psr->xboxsize = boxx;
841 psr->yboxsize = boxx;
845 psr->yboxsize = boxy;
848 if (psr->xboxsize == 0)
850 psr->xboxsize = size/mat[0].nx;
851 printf("Set the x-size of the box to %.3f\n", psr->xboxsize);
853 if (psr->yboxsize == 0)
855 psr->yboxsize = size/mat[0].nx;
856 printf("Set the y-size of the box to %.3f\n", psr->yboxsize);
860 for (i = 0; (i < nmat); i++)
862 if (mat[i].nmap > nmap1)
871 printf("Selected legend of matrix # %d for display\n", leg);
876 for (i = 0; (i < nmat); i++)
878 if (mat2[i].nmap > nmap2)
880 nmap2 = mat2[i].nmap;
887 printf("Selected legend of matrix # %d for second display\n", leg);
890 if ( (mat[0].legend[0] == 0) && psr->legend)
892 std::strcpy(mat[0].legend, psr->leglabel);
895 bTitle = bTitle && mat[nmat-1].title[0];
896 bTitleOnce = bTitleOnce && mat[nmat-1].title[0];
897 psr->bTitle = bTitle;
898 psr->bTitleOnce = bTitleOnce;
899 psr->bYonce = bYonce;
901 /* Set up size of box for nice colors */
902 box_dim(nmat, mat, mat2, psr, elegend, bFrame, &w, &h, &dw, &dh);
904 /* Set up bounding box */
905 W = static_cast<int>(w+dw);
906 H = static_cast<int>(h+dh);
911 x = static_cast<int>(W+psr->xoffs);
912 y = static_cast<int>(H+psr->yoffs);
918 out = ps_open(outf, 0, 0, x, y);
919 ps_linewidth(out, static_cast<int>(psr->linewidth));
920 ps_init_rgb_box(out, psr->xboxsize, psr->yboxsize);
921 ps_init_rgb_nbox(out, psr->xboxsize, psr->yboxsize);
922 ps_translate(out, psr->xoffs, psr->yoffs);
926 ps_comment(out, "Here starts the BOX drawing");
927 draw_boxes(out, x0, y0, w, nmat, mat, psr);
930 for (i = 0; (i < nmat); i++)
932 if (bTitle || (bTitleOnce && i == nmat-1) )
934 /* Print title, if any */
936 ps_strfont(out, psr->titfont, psr->titfontsize);
938 if (!mat2 || (std::strcmp(mat[i].title, mat2[i].title) == 0))
944 buf = gmx::formatString("%s / %s", mat[i].title, mat2[i].title);
946 ps_ctext(out, x0+w/2, y0+box_height(&(mat[i]), psr)+psr->titfontsize,
947 buf.c_str(), eXCenter);
949 ps_comment(out, gmx::formatString("Here starts the filling of box #%d", i).c_str());
950 for (x = 0; (x < mat[i].nx); x++)
955 xx = x0+x*psr->xboxsize;
956 ps_moveto(out, xx, y0);
958 bMap1 = (!mat2 || (x < y || (x == y && bFirstDiag)));
959 if ((bDiag) || (x != y))
961 col = mat[i].matrix[x][y];
967 for (nexty = 1; (nexty <= mat[i].ny); nexty++)
969 bNextMap1 = (!mat2 || (x < nexty || (x == nexty && bFirstDiag)));
970 /* TRUE: upper left -> map1 */
971 /* FALSE: lower right -> map2 */
972 if ((nexty == mat[i].ny) || (!bDiag && (x == nexty)))
978 nextcol = mat[i].matrix[x][nexty];
980 if ( (nexty == mat[i].ny) || (col != nextcol) || (bMap1 != bNextMap1) )
986 ps_rgb_nbox(out, &(mat[i].map[col].rgb), nexty-y);
991 ps_rgb_nbox(out, &(mat2[i].map[col].rgb), nexty-y);
996 ps_moverel(out, 0, psr->yboxsize);
1004 y0 += box_height(&(mat[i]), psr)+box_dh(psr)+box_dh_top(IS_ONCE, psr);
1007 if (psr->X.lineatzero || psr->Y.lineatzero)
1009 /* reset y0 for first box */
1011 ps_comment(out, "Here starts the zero lines drawing");
1012 draw_zerolines(out, x0, y0, w, nmat, mat, psr);
1015 if (elegend != elNone)
1017 ps_comment(out, "Now it's legend time!");
1018 ps_linewidth(out, static_cast<int>(psr->linewidth));
1019 if (mat2 == nullptr || elegend != elSecond)
1021 bDiscrete = mat[0].bDiscrete;
1022 legend = mat[0].legend;
1028 bDiscrete = mat2[0].bDiscrete;
1029 legend = mat2[0].legend;
1035 leg_discrete(out, psr->legfontsize, DDD, legend,
1036 psr->legfontsize, psr->legfont, leg_nmap, leg_map);
1040 if (elegend != elBoth)
1042 leg_continuous(out, x0+w/2, w/2, DDD, legend,
1043 psr->legfontsize, psr->legfont, leg_nmap, leg_map,
1049 leg_bicontinuous(out, x0+w/2, w, DDD, mat[0].legend, mat2[0].legend,
1050 psr->legfontsize, psr->legfont, nmap1, map1, nmap2, map2);
1053 ps_comment(out, "Were there, dude");
1059 void make_axis_labels(int nmat, t_matrix *mat)
1063 for (i = 0; (i < nmat); i++)
1065 /* Make labels for x axis */
1066 if (mat[i].axis_x == nullptr)
1068 snew(mat[i].axis_x, mat[i].nx);
1069 for (j = 0; (j < mat[i].nx); j++)
1071 mat[i].axis_x[j] = j;
1074 /* Make labels for y axis */
1075 if (mat[i].axis_y == nullptr)
1077 snew(mat[i].axis_y, mat[i].ny);
1078 for (j = 0; (j < mat[i].ny); j++)
1080 mat[i].axis_y[j] = j;
1086 void prune_mat(int nmat, t_matrix *mat, t_matrix *mat2, int skip)
1088 int i, x, y, xs, ys;
1090 for (i = 0; i < nmat; i++)
1092 fprintf(stderr, "converting %dx%d matrix to %dx%d\n",
1093 mat[i].nx, mat[i].ny,
1094 (mat[i].nx+skip-1)/skip, (mat[i].ny+skip-1)/skip);
1095 /* walk through matrix */
1097 for (x = 0; (x < mat[i].nx); x++)
1101 mat[i].axis_x[xs] = mat[i].axis_x[x];
1104 mat2[i].axis_x[xs] = mat2[i].axis_x[x];
1107 for (y = 0; (y < mat[i].ny); y++)
1111 mat[i].axis_y[ys] = mat[i].axis_y[y];
1114 mat2[i].axis_y[ys] = mat2[i].axis_y[y];
1119 mat[i].matrix[xs][ys] = mat[i].matrix[x][y];
1122 mat2[i].matrix[xs][ys] = mat2[i].matrix[x][y];
1130 /* adjust parameters */
1131 mat[i].nx = (mat[i].nx+skip-1)/skip;
1132 mat[i].ny = (mat[i].ny+skip-1)/skip;
1135 mat2[i].nx = (mat2[i].nx+skip-1)/skip;
1136 mat2[i].ny = (mat2[i].ny+skip-1)/skip;
1141 void zero_lines(int nmat, t_matrix *mat, t_matrix *mat2)
1146 for (i = 0; i < nmat; i++)
1148 for (m = 0; m < (mat2 ? 2 : 1); m++)
1158 for (x = 0; x < mats[i].nx-1; x++)
1160 if (std::abs(mats[i].axis_x[x+1]) < 1e-5)
1162 for (y = 0; y < mats[i].ny; y++)
1164 mats[i].matrix[x][y] = 0;
1168 for (y = 0; y < mats[i].ny-1; y++)
1170 if (std::abs(mats[i].axis_y[y+1]) < 1e-5)
1172 for (x = 0; x < mats[i].nx; x++)
1174 mats[i].matrix[x][y] = 0;
1182 void write_combined_matrix(int ecombine, const char *fn,
1183 int nmat, t_matrix *mat1, t_matrix *mat2,
1184 real *cmin, real *cmax)
1186 int i, j, k, nlevels;
1188 real **rmat1, **rmat2;
1191 out = gmx_ffopen(fn, "w");
1192 for (k = 0; k < nmat; k++)
1194 if (mat2[k].nx != mat1[k].nx || mat2[k].ny != mat1[k].ny)
1196 gmx_fatal(FARGS, "Size of frame %d in 1st (%dx%d) and 2nd matrix (%dx%d) do"
1197 " not match.\n", k, mat1[k].nx, mat1[k].ny, mat2[k].nx, mat2[k].ny);
1199 printf("Combining two %dx%d matrices\n", mat1[k].nx, mat1[k].ny);
1200 rmat1 = matrix2real(&mat1[k], nullptr);
1201 rmat2 = matrix2real(&mat2[k], nullptr);
1202 if (nullptr == rmat1 || nullptr == rmat2)
1204 gmx_fatal(FARGS, "Could not extract real data from %s xpm matrices. Note that, e.g.,\n"
1205 "g_rms and g_mdmat provide such data, but not do_dssp.\n",
1206 (nullptr == rmat1 && nullptr == rmat2) ? "both" : "one of the" );
1210 for (j = 0; j < mat1[k].ny; j++)
1212 for (i = 0; i < mat1[k].nx; i++)
1216 case ecAdd: rmat1[i][j] += rmat2[i][j]; break;
1217 case ecSub: rmat1[i][j] -= rmat2[i][j]; break;
1218 case ecMult: rmat1[i][j] *= rmat2[i][j]; break;
1219 case ecDiv: rmat1[i][j] /= rmat2[i][j]; break;
1221 gmx_fatal(FARGS, "No such combination rule %d for matrices", ecombine);
1223 rlo = std::min(rlo, rmat1[i][j]);
1224 rhi = std::max(rhi, rmat1[i][j]);
1235 nlevels = std::max(mat1[k].nmap, mat2[k].nmap);
1239 "combination results in uniform matrix (%g), no output\n", rhi);
1242 else if (rlo>=0 || rhi<=0)
1243 write_xpm(out, mat1[k].flags, mat1[k].title, mat1[k].legend,
1244 mat1[k].label_x, mat1[k].label_y,
1245 mat1[k].nx, mat1[k].ny, mat1[k].axis_x, mat1[k].axis_y,
1246 rmat1, rlo, rhi, rhi<=0?red:white, rhi<=0?white:blue,
1249 write_xpm3(out, mat2[k].flags, mat1[k].title, mat1[k].legend,
1250 mat1[k].label_x, mat1[k].label_y,
1251 mat1[k].nx, mat1[k].ny, mat1[k].axis_x, mat1[k].axis_y,
1252 rmat1, rlo, 0, rhi, red, white, blue, &nlevels);
1256 write_xpm(out, mat1[k].flags, mat1[k].title, mat1[k].legend,
1257 mat1[k].label_x, mat1[k].label_y,
1258 mat1[k].nx, mat1[k].ny, mat1[k].axis_x, mat1[k].axis_y,
1259 rmat1, rlo, rhi, white, black, &nlevels);
1265 void do_mat(int nmat, t_matrix *mat, t_matrix *mat2,
1266 gmx_bool bFrame, gmx_bool bZeroLine, gmx_bool bDiag, gmx_bool bFirstDiag, gmx_bool bTitle,
1267 gmx_bool bTitleOnce, gmx_bool bYonce, int elegend,
1268 real size, real boxx, real boxy,
1269 const char *epsfile, const char *xpmfile, const char *m2p,
1270 const char *m2pout, int skip, int mapoffset)
1276 for (k = 0; (k < nmat); k++)
1278 if ((mat2[k].nx != mat[k].nx) || (mat2[k].ny != mat[k].ny))
1280 gmx_fatal(FARGS, "WAKE UP!! Size of frame %d in 2nd matrix file (%dx%d) does not match size of 1st matrix (%dx%d) or the other way around.\n",
1281 k, mat2[k].nx, mat2[k].ny, mat[k].nx, mat[k].ny);
1283 for (j = 0; (j < mat[k].ny); j++)
1285 for (i = bFirstDiag ? j+1 : j; (i < mat[k].nx); i++)
1287 mat[k].matrix[i][j] = mat2[k].matrix[i][j];
1292 for (i = 0; (i < nmat); i++)
1294 fprintf(stderr, "Matrix %d is %d x %d\n", i, mat[i].nx, mat[i].ny);
1297 make_axis_labels(nmat, mat);
1301 prune_mat(nmat, mat, mat2, skip);
1306 zero_lines(nmat, mat, mat);
1309 if (epsfile != nullptr)
1311 ps_mat(epsfile, nmat, mat, mat2, bFrame, bDiag, bFirstDiag,
1312 bTitle, bTitleOnce, bYonce, elegend,
1313 size, boxx, boxy, m2p, m2pout, mapoffset);
1315 if (xpmfile != nullptr)
1317 xpm_mat(xpmfile, nmat, mat, mat2, bDiag, bFirstDiag);
1321 void gradient_map(rvec grad, int nmap, t_mapping map[])
1326 for (i = 0; i < nmap; i++)
1329 map[i].rgb.r = 1-c*(1-grad[XX]);
1330 map[i].rgb.g = 1-c*(1-grad[YY]);
1331 map[i].rgb.b = 1-c*(1-grad[ZZ]);
1335 void gradient_mat(rvec grad, int nmat, t_matrix mat[])
1339 for (m = 0; m < nmat; m++)
1341 gradient_map(grad, mat[m].nmap, mat[m].map);
1345 void rainbow_map(gmx_bool bBlue, int nmap, t_mapping map[])
1350 for (i = 0; i < nmap; i++)
1352 c = (map[i].rgb.r + map[i].rgb.g + map[i].rgb.b)/3;
1361 if (c <= 0.25) /* 0-0.25 */
1364 g = std::pow(4.0*c, 2.0/3.0);
1367 else if (c <= 0.5) /* 0.25-0.5 */
1371 b = std::pow(2.0-4.0*c, 2.0/3.0);
1373 else if (c <= 0.75) /* 0.5-0.75 */
1375 r = std::pow(4.0*c-2.0, 2.0/3.0);
1382 g = std::pow(4.0-4.0*c, 2.0/3.0);
1391 void rainbow_mat(gmx_bool bBlue, int nmat, t_matrix mat[])
1395 for (m = 0; m < nmat; m++)
1397 rainbow_map(bBlue, mat[m].nmap, mat[m].map);
1401 int gmx_xpm2ps(int argc, char *argv[])
1403 const char *desc[] = {
1404 "[THISMODULE] makes a beautiful color plot of an XPixelMap file.",
1405 "Labels and axis can be displayed, when they are supplied",
1406 "in the correct matrix format.",
1407 "Matrix data may be generated by programs such as [gmx-do_dssp], [gmx-rms] or",
1408 "[gmx-mdmat].[PAR]",
1409 "Parameters are set in the [TT].m2p[tt] file optionally supplied with",
1410 "[TT]-di[tt]. Reasonable defaults are provided. Settings for the [IT]y[it]-axis",
1411 "default to those for the [IT]x[it]-axis. Font names have a defaulting hierarchy:",
1412 "titlefont -> legendfont; titlefont -> (xfont -> yfont -> ytickfont)",
1413 "-> xtickfont, e.g. setting titlefont sets all fonts, setting xfont",
1414 "sets yfont, ytickfont and xtickfont.[PAR]",
1415 "When no [TT].m2p[tt] file is supplied, many settings are taken from",
1416 "command line options. The most important option is [TT]-size[tt],",
1417 "which sets the size of the whole matrix in postscript units.",
1418 "This option can be overridden with the [TT]-bx[tt] and [TT]-by[tt]",
1419 "options (and the corresponding parameters in the [TT].m2p[tt] file),",
1420 "which set the size of a single matrix element.[PAR]",
1421 "With [TT]-f2[tt] a second matrix file can be supplied. Both matrix",
1422 "files will be read simultaneously and the upper left half of the",
1423 "first one ([TT]-f[tt]) is plotted together with the lower right",
1424 "half of the second one ([TT]-f2[tt]). The diagonal will contain",
1425 "values from the matrix file selected with [TT]-diag[tt].",
1426 "Plotting of the diagonal values can be suppressed altogether by",
1427 "setting [TT]-diag[tt] to [TT]none[tt].",
1428 "In this case, a new color map will be generated with",
1429 "a red gradient for negative numbers and a blue for positive.",
1430 "If the color coding and legend labels of both matrices are identical,",
1431 "only one legend will be displayed, else two separate legends are",
1433 "With [TT]-combine[tt], an alternative operation can be selected",
1434 "to combine the matrices. The output range is automatically set",
1435 "to the actual range of the combined matrix. This can be overridden",
1436 "with [TT]-cmin[tt] and [TT]-cmax[tt].[PAR]",
1437 "[TT]-title[tt] can be set to [TT]none[tt] to suppress the title, or to",
1438 "[TT]ylabel[tt] to show the title in the Y-label position (alongside",
1439 "the [IT]y[it]-axis).[PAR]",
1440 "With the [TT]-rainbow[tt] option, dull grayscale matrices can be turned",
1441 "into attractive color pictures.[PAR]",
1442 "Merged or rainbowed matrices can be written to an XPixelMap file with",
1443 "the [TT]-xpm[tt] option."
1446 gmx_output_env_t *oenv;
1447 const char *fn, *epsfile = nullptr, *xpmfile = nullptr;
1448 int i, nmat, nmat2, etitle, elegend, ediag, erainbow, ecombine;
1449 t_matrix *mat = nullptr, *mat2 = nullptr;
1450 gmx_bool bTitle, bTitleOnce, bDiag, bFirstDiag, bGrad;
1451 static gmx_bool bFrame = TRUE, bZeroLine = FALSE, bYonce = FALSE;
1452 static real size = 400, boxx = 0, boxy = 0, cmin = 0, cmax = 0;
1453 static rvec grad = {0, 0, 0};
1455 etSel, etTop, etOnce, etYlabel, etNone, etNR
1457 const char *title[] = { nullptr, "top", "once", "ylabel", "none", nullptr };
1458 /* MUST correspond to enum elXxx as defined at top of file */
1459 const char *legend[] = { nullptr, "both", "first", "second", "none", nullptr };
1461 edSel, edFirst, edSecond, edNone, edNR
1463 const char *diag[] = { nullptr, "first", "second", "none", nullptr };
1465 erSel, erNo, erBlue, erRed, erNR
1467 const char *rainbow[] = { nullptr, "no", "blue", "red", nullptr };
1468 /* MUST correspond to enum ecXxx as defined at top of file */
1469 const char *combine[] = {
1470 nullptr, "halves", "add", "sub", "mult", "div", nullptr
1472 static int skip = 1, mapoffset = 0;
1474 { "-frame", FALSE, etBOOL, {&bFrame},
1475 "Display frame, ticks, labels, title and legend" },
1476 { "-title", FALSE, etENUM, {title}, "Show title at" },
1477 { "-yonce", FALSE, etBOOL, {&bYonce}, "Show y-label only once" },
1478 { "-legend", FALSE, etENUM, {legend}, "Show legend" },
1479 { "-diag", FALSE, etENUM, {diag}, "Diagonal" },
1480 { "-size", FALSE, etREAL, {&size},
1481 "Horizontal size of the matrix in ps units" },
1482 { "-bx", FALSE, etREAL, {&boxx},
1483 "Element x-size, overrides [TT]-size[tt] (also y-size when [TT]-by[tt] is not set)" },
1484 { "-by", FALSE, etREAL, {&boxy}, "Element y-size" },
1485 { "-rainbow", FALSE, etENUM, {rainbow},
1486 "Rainbow colors, convert white to" },
1487 { "-gradient", FALSE, etRVEC, {grad},
1488 "Re-scale colormap to a smooth gradient from white {1,1,1} to {r,g,b}" },
1489 { "-skip", FALSE, etINT, {&skip},
1490 "only write out every nr-th row and column" },
1491 { "-zeroline", FALSE, etBOOL, {&bZeroLine},
1492 "insert line in [REF].xpm[ref] matrix where axis label is zero"},
1493 { "-legoffset", FALSE, etINT, {&mapoffset},
1494 "Skip first N colors from [REF].xpm[ref] file for the legend" },
1495 { "-combine", FALSE, etENUM, {combine}, "Combine two matrices" },
1496 { "-cmin", FALSE, etREAL, {&cmin}, "Minimum for combination output" },
1497 { "-cmax", FALSE, etREAL, {&cmax}, "Maximum for combination output" }
1499 #define NPA asize(pa)
1501 { efXPM, "-f", nullptr, ffREAD },
1502 { efXPM, "-f2", "root2", ffOPTRD },
1503 { efM2P, "-di", nullptr, ffLIBOPTRD },
1504 { efM2P, "-do", "out", ffOPTWR },
1505 { efEPS, "-o", nullptr, ffOPTWR },
1506 { efXPM, "-xpm", nullptr, ffOPTWR }
1508 #define NFILE asize(fnm)
1510 if (!parse_common_args(&argc, argv, PCA_CAN_VIEW,
1511 NFILE, fnm, NPA, pa,
1512 asize(desc), desc, 0, nullptr, &oenv))
1517 etitle = nenum(title);
1518 elegend = nenum(legend);
1519 ediag = nenum(diag);
1520 erainbow = nenum(rainbow);
1521 ecombine = nenum(combine);
1522 bGrad = opt2parg_bSet("-gradient", NPA, pa);
1523 for (i = 0; i < DIM; i++)
1525 if (grad[i] < 0 || grad[i] > 1)
1527 gmx_fatal(FARGS, "RGB value %g out of range (0.0-1.0)", grad[i]);
1536 epsfile = ftp2fn_null(efEPS, NFILE, fnm);
1537 xpmfile = opt2fn_null("-xpm", NFILE, fnm);
1538 if (epsfile == nullptr && xpmfile == nullptr)
1540 if (ecombine != ecHalves)
1542 xpmfile = opt2fn("-xpm", NFILE, fnm);
1546 epsfile = ftp2fn(efEPS, NFILE, fnm);
1549 if (ecombine != ecHalves && epsfile)
1552 "WARNING: can only write result of arithmetic combination "
1553 "of two matrices to .xpm file\n"
1554 " file %s will not be written\n", epsfile);
1558 bDiag = ediag != edNone;
1559 bFirstDiag = ediag != edSecond;
1561 fn = opt2fn("-f", NFILE, fnm);
1562 nmat = read_xpm_matrix(fn, &mat);
1563 fprintf(stderr, "There %s %d matri%s in %s\n", (nmat > 1) ? "are" : "is", nmat, (nmat > 1) ? "ces" : "x", fn);
1564 fn = opt2fn_null("-f2", NFILE, fnm);
1567 nmat2 = read_xpm_matrix(fn, &mat2);
1568 fprintf(stderr, "There %s %d matri%s in %s\n", (nmat2 > 1) ? "are" : "is", nmat2, (nmat2 > 1) ? "ces" : "x", fn);
1571 fprintf(stderr, "Different number of matrices, using the smallest number.\n");
1572 nmat = nmat2 = std::min(nmat, nmat2);
1577 if (ecombine != ecHalves)
1580 "WARNING: arithmetic matrix combination selected (-combine), "
1581 "but no second matrix (-f2) supplied\n"
1582 " no matrix combination will be performed\n");
1587 bTitle = etitle == etTop;
1588 bTitleOnce = etitle == etOnce;
1589 if (etitle == etYlabel)
1591 for (i = 0; (i < nmat); i++)
1593 std::strcpy(mat[i].label_y, mat[i].title);
1596 std::strcpy(mat2[i].label_y, mat2[i].title);
1602 gradient_mat(grad, nmat, mat);
1605 gradient_mat(grad, nmat2, mat2);
1608 if (erainbow != erNo)
1610 rainbow_mat(erainbow == erBlue, nmat, mat);
1613 rainbow_mat(erainbow == erBlue, nmat2, mat2);
1617 if ((mat2 == nullptr) && (elegend != elNone))
1622 if (ecombine && ecombine != ecHalves)
1624 write_combined_matrix(ecombine, xpmfile, nmat, mat, mat2,
1625 opt2parg_bSet("-cmin", NPA, pa) ? &cmin : nullptr,
1626 opt2parg_bSet("-cmax", NPA, pa) ? &cmax : nullptr);
1630 do_mat(nmat, mat, mat2, bFrame, bZeroLine, bDiag, bFirstDiag,
1631 bTitle, bTitleOnce, bYonce,
1632 elegend, size, boxx, boxy, epsfile, xpmfile,
1633 opt2fn_null("-di", NFILE, fnm), opt2fn_null("-do", NFILE, fnm), skip,
1637 view_all(oenv, NFILE, fnm);