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.
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"
85 char tickfont
[STRLEN
];
100 char legfont
[STRLEN
];
101 char leglabel
[STRLEN
];
102 char leg2label
[STRLEN
];
112 /* MUST correspond to char *legend[] in main() */
123 /* MUST correspond to char *combine[] in main() */
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 };
141 std::vector
<t_inpfile
> inp
;
143 wi
= init_warning(FALSE
, 0);
147 std::string libmpin
= gmx::findLibraryFile(mpin
);
148 gmx::TextInputFile
stream(libmpin
);
149 inp
= read_inpfile(&stream
, libmpin
.c_str(), wi
);
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
);
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
,
219 const std::string
& label
,
222 gmx::ArrayRef
<const t_mapping
> map
)
227 boxhh
= fontsize
+ DDD
;
230 ps_strfont(ps
, font
, fontsize
);
231 yhh
= y0
+ fontsize
+ 3 * DDD
;
234 ps_ctext(ps
, x0
, yhh
, label
, eXLeft
);
236 ps_moveto(ps
, x0
, y0
);
237 for (const auto& m
: map
)
241 ps_fillbox(ps
, DDD
, DDD
, DDD
+ fontsize
, boxhh
- DDD
);
243 ps_box(ps
, DDD
, DDD
, DDD
+ fontsize
, boxhh
- DDD
);
244 ps_ctext(ps
, boxhh
+ 2 * DDD
, fontsize
/ 3, m
.desc
, eXLeft
);
246 ps_moverel(ps
, DDD
, -fontsize
/ 3);
250 static void leg_continuous(t_psdata
* ps
,
254 const std::string
& label
,
257 gmx::ArrayRef
<const t_mapping
> map
,
261 real yhh
, boxxh
, boxyh
;
262 gmx::index mapIndex
= gmx::ssize(map
) - mapoffset
;
265 if (x
< 8 * fontsize
)
269 boxxh
= x
/ mapIndex
;
270 if (boxxh
> fontsize
)
275 GMX_RELEASE_ASSERT(!map
.empty(), "NULL map array provided to leg_continuous()");
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
);
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
);
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
,
302 const std::string
& label1
,
303 const std::string
& label2
,
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
)
335 if (psr
->bTitle
|| (psr
->bTitleOnce
&& bOnce
))
337 dh
= 2 * psr
->titfontsize
;
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
)
361 char **xtick
, **ytick
;
362 real xx
, yy
, dy
, xx00
, yy00
, offset_x
, offset_y
;
366 /* Only necessary when there will be no y-labels */
371 ps_linewidth(ps
, static_cast<int>(psr
->boxlinewidth
));
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
));
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
)
397 if (m
->flags
& MAT_SPATIAL_Y
)
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,
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
);
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)))
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,
460 else if (bRmod(m
->axis_y
[y
], psr
->Y
.offset
, psr
->Y
.minor
))
463 ps_line(ps
, xx00
, yy
, xx00
- psr
->Y
.minorticklen
, yy
);
469 /* Label on Y-axis */
470 if (!psr
->bYonce
|| m
== halfway
)
473 if (strlen(psr
->Y
.label
) > 0)
475 mylab
= psr
->Y
.label
;
483 ps_strfont(ps
, psr
->Y
.font
, psr
->Y
.fontsize
);
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
);
491 yy00
+= box_height(*m
, psr
) + box_dh(psr
) + box_dh_top(m
+ 1 == mat
.end(), psr
);
493 /* Label on X-axis */
495 if (strlen(psr
->X
.label
) > 0)
497 mylab
= psr
->X
.label
;
501 mylab
= mat
[0].label_x
;
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
,
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
;
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
,
567 real ww
, hh
, dww
, dhh
;
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
);
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
);
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
);
623 hh
+= (mat
.size() - 1) * box_dh_top(FALSE
, psr
);
632 static std::vector
<t_mapping
> add_maps(gmx::ArrayRef
<t_mapping
> map1
, gmx::ArrayRef
<t_mapping
> map2
)
634 static char mapper
[] =
635 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()-_=+{}|;:',<"
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());
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
;
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
,
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]);
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
;
721 mat
[i
].matrix(x
, y
) = 0;
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
]);
740 static void tick_spacing(int n
, real axis
[], real offset
, char axisnm
, real
* major
, real
* minor
)
744 int i
, j
, t
, f
= 0, ten
;
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);
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: */
761 for (j
= 0; j
< n
; j
++)
763 if (bRmod(axis
[j
], offset
, space
))
768 /* do we have a reasonable number of ticks ? */
769 bTryAgain
= (i
> std::min(10, n
- 1)) || (i
< 5);
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
);
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
,
804 gmx_bool bMap1
, bNextMap1
, bDiscrete
;
808 get_params(m2p
, m2pout
, &psrec
);
810 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
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;
835 psr
->xboxsize
= boxx
;
836 psr
->yboxsize
= boxx
;
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
;
856 for (const auto& m
: mat
)
858 if (m
.map
.size() > map1
.size())
862 printf("Selected legend of matrix # %d for display\n", legendIndex
);
868 gmx::ArrayRef
<const t_mapping
> map2
;
871 for (const auto& m
: mat2
)
873 if (m
.map
.size() > map2
.size())
877 printf("Selected legend of matrix # %d for second display\n", 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
);
905 x
= static_cast<int>(W
+ psr
->xoffs
);
906 y
= static_cast<int>(H
+ psr
->yoffs
);
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
);
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 */
930 ps_strfont(&out
, psr
->titfont
, psr
->titfontsize
);
932 if (!mat2
.empty() || mat
[i
].title
== mat2
[i
].title
)
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
++)
948 xx
= x0
+ x
* psr
->xboxsize
;
949 ps_moveto(&out
, xx
, y0
);
951 bMap1
= (mat2
.empty() || (x
< y
|| (x
== y
&& bFirstDiag
)));
952 if ((bDiag
) || (x
!= y
))
954 col
= mat
[i
].matrix(x
, y
);
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
)))
971 nextcol
= mat
[i
].matrix(x
, nexty
);
973 if ((nexty
== mat
[i
].ny
) || (col
!= nextcol
) || (bMap1
!= bNextMap1
))
979 ps_rgb_nbox(&out
, &(mat
[i
].map
[col
].rgb
), nexty
- y
);
983 assert(!mat2
.empty());
984 ps_rgb_nbox(&out
, &(mat2
[i
].map
[col
].rgb
), nexty
- y
);
989 ps_moverel(&out
, 0, psr
->yboxsize
);
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 */
1004 ps_comment(&out
, "Here starts the zero lines drawing");
1005 draw_zerolines(&out
, x0
, y0
, w
, mat
, psr
);
1008 if (elegend
!= elNone
)
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
;
1022 bDiscrete
= mat2
[0].bDiscrete
;
1023 legend
= mat2
[0].legend
;
1028 leg_discrete(&out
, psr
->legfontsize
, DDD
, legend
, psr
->legfontsize
, psr
->legfont
, leg_map
);
1032 if (elegend
!= elBoth
)
1034 leg_continuous(&out
, x0
+ w
/ 2, w
/ 2, DDD
, legend
, psr
->legfontsize
, psr
->legfont
,
1035 leg_map
, mapoffset
);
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");
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 */
1079 for (int x
= 0; (x
< mat
[i
].nx
); x
++)
1083 mat
[i
].axis_x
[xs
] = mat
[i
].axis_x
[x
];
1086 mat2
[i
].axis_x
[xs
] = mat2
[i
].axis_x
[x
];
1089 for (int y
= 0; (y
< mat
[i
].ny
); y
++)
1093 mat
[i
].axis_y
[ys
] = mat
[i
].axis_y
[y
];
1096 mat2
[i
].axis_y
[ys
] = mat2
[i
].axis_y
[y
];
1101 mat
[i
].matrix(xs
, ys
) = mat
[i
].matrix(x
, y
);
1104 mat2
[i
].matrix(xs
, ys
) = mat2
[i
].matrix(x
, y
);
1112 /* adjust parameters */
1113 mat
[i
].nx
= (mat
[i
].nx
+ skip
- 1) / skip
;
1114 mat
[i
].ny
= (mat
[i
].ny
+ skip
- 1) / skip
;
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
;
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
,
1165 gmx::ArrayRef
<t_matrix
> mat1
,
1166 gmx::ArrayRef
<t_matrix
> mat2
,
1171 real
**rmat1
, **rmat2
;
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
)
1182 "Size of frame %zd in 1st (%dx%d) and 2nd matrix (%dx%d) do"
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
)
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");
1198 for (int j
= 0; j
< mat1
[k
].ny
; j
++)
1200 for (int i
= 0; i
< mat1
[k
].nx
; i
++)
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
]);
1222 int nlevels
= static_cast<int>(std::max(mat1
[k
].map
.size(), mat2
[k
].map
.size()));
1225 fprintf(stderr
, "combination results in uniform matrix (%g), no output\n", rhi
);
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
);
1237 static void do_mat(gmx::ArrayRef
<t_matrix
> mat
,
1238 gmx::ArrayRef
<t_matrix
> mat2
,
1242 gmx_bool bFirstDiag
,
1244 gmx_bool bTitleOnce
,
1250 const char* epsfile
,
1251 const char* xpmfile
,
1257 GMX_RELEASE_ASSERT(mat
.size() == mat2
.size() || mat2
.empty(),
1258 "Combined matrix write requires matrices of the same size");
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
))
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
);
1288 prune_mat(mat
, mat2
, skip
);
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
)
1310 real fraction
= 1.0 / (map
.size() - 1.0);
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
]);
1321 static void gradient_mat(rvec grad
, gmx::ArrayRef
<t_matrix
> mat
)
1325 gradient_map(grad
, m
.map
);
1329 static void rainbow_map(gmx_bool bBlue
, gmx::ArrayRef
<t_mapping
> map
)
1333 real c
= (m
.rgb
.r
+ m
.rgb
.g
+ m
.rgb
.b
) / 3;
1343 if (c
<= 0.25) /* 0-0.25 */
1346 g
= std::pow(4.0 * c
, 2.0 / 3.0);
1349 else if (c
<= 0.5) /* 0.25-0.5 */
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);
1364 g
= std::pow(4.0 - 4.0 * c
, 2.0 / 3.0);
1373 static void rainbow_mat(gmx_bool bBlue
, gmx::ArrayRef
<t_matrix
> 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",
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 };
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 };
1453 const char* diag
[] = { nullptr, "first", "second", "none", nullptr };
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;
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" },
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" },
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" },
1490 "insert line in [REF].xpm[ref] matrix where axis label is zero" },
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,
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
]);
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
);
1541 epsfile
= ftp2fn(efEPS
, NFILE
, fnm
);
1544 if (ecombine
!= ecHalves
&& epsfile
)
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",
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
);
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());
1577 mat2
.resize(mat
.size());
1583 if (ecombine
!= ecHalves
)
1586 "WARNING: arithmetic matrix combination selected (-combine), "
1587 "but no second matrix (-f2) supplied\n"
1588 " no matrix combination will be performed\n");
1592 bTitle
= etitle
== etTop
;
1593 bTitleOnce
= etitle
== etOnce
;
1594 if (etitle
== etYlabel
)
1598 m
.label_y
= m
.title
;
1600 for (auto& m
: mat2
)
1602 m
.label_y
= m
.title
;
1607 gradient_mat(grad
, mat
);
1610 gradient_mat(grad
, mat2
);
1613 if (erainbow
!= erNo
)
1615 rainbow_mat(erainbow
== erBlue
, mat
);
1618 rainbow_mat(erainbow
== erBlue
, mat2
);
1622 if (mat2
.empty() && (elegend
!= elNone
))
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);
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
);