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,2017,2018,2019,2020, 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.
50 #include "gromacs/fileio/gmxfio.h"
51 #include "gromacs/math/functions.h"
52 #include "gromacs/math/utilities.h"
53 #include "gromacs/utility/binaryinformation.h"
54 #include "gromacs/utility/cstringutil.h"
55 #include "gromacs/utility/exceptions.h"
56 #include "gromacs/utility/fatalerror.h"
57 #include "gromacs/utility/futil.h"
58 #include "gromacs/utility/programcontext.h"
59 #include "gromacs/utility/smalloc.h"
63 static const char mapper
[] =
64 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()-_=+{}|;:',<.>/"
66 #define NMAP static_cast<long int>(sizeof(mapper) / sizeof(mapper[0]))
68 #define MAX_XPM_LINELENGTH 4096
70 real
** mk_matrix(int nx
, int ny
, gmx_bool b1D
)
81 for (i
= 0; (i
< nx
); i
++)
85 m
[i
] = &(m
[0][i
* ny
]);
96 void done_matrix(int nx
, real
*** m
)
100 for (i
= 0; (i
< nx
); i
++)
108 static bool operator==(t_xpmelmt e1
, t_xpmelmt e2
)
110 return (e1
.c1
== e2
.c1
) && (e1
.c2
== e2
.c2
);
113 //! Return the index of the first element that matches \c c, or -1 if not found.
114 t_matelmt
searchcmap(ArrayRef
<const t_mapping
> map
, t_xpmelmt c
)
116 auto findIt
= std::find_if(map
.begin(), map
.end(), [&c
](const auto& m
) { return (m
.code
== c
); });
117 return (findIt
== map
.end()) ? -1 : (findIt
- map
.begin());
120 //! Read the mapping table from in, return number of entries
121 static std::vector
<t_mapping
> getcmap(FILE* in
, const char* fn
)
125 char code
[STRLEN
], desc
[STRLEN
];
127 std::vector
<t_mapping
> m
;
129 if (fgets2(line
, STRLEN
- 1, in
) == nullptr)
132 "Not enough lines in colormap file %s"
133 "(just wanted to read number of entries)",
136 sscanf(line
, "%d", &n
);
138 for (i
= 0; (i
< n
); i
++)
140 if (fgets2(line
, STRLEN
- 1, in
) == nullptr)
143 "Not enough lines in colormap file %s"
144 "(should be %d, found only %d)",
147 sscanf(line
, "%s%s%lf%lf%lf", code
, desc
, &r
, &g
, &b
);
148 m
[i
].code
.c1
= code
[0];
150 m
[i
].desc
= gmx_strdup(desc
);
159 std::vector
<t_mapping
> readcmap(const char* fn
)
161 FilePtr in
= openLibraryFile(fn
);
162 return getcmap(in
.get(), fn
);
165 void printcmap(FILE* out
, int n
, t_mapping map
[])
169 fprintf(out
, "%d\n", n
);
170 for (i
= 0; (i
< n
); i
++)
172 fprintf(out
, "%c%c %20s %10g %10g %10g\n", map
[i
].code
.c1
? map
[i
].code
.c1
: ' ',
173 map
[i
].code
.c2
? map
[i
].code
.c2
: ' ', map
[i
].desc
, map
[i
].rgb
.r
, map
[i
].rgb
.g
,
178 void writecmap(const char* fn
, int n
, t_mapping map
[])
182 out
= gmx_fio_fopen(fn
, "w");
183 printcmap(out
, n
, map
);
187 static char* fgetline(char** line
, int llmax
, int* llalloc
, FILE* in
)
191 if (llmax
> *llalloc
)
193 srenew(*line
, llmax
+ 1);
196 fg
= fgets(*line
, llmax
, in
);
202 static void skipstr(char* line
)
208 while ((line
[c
] != ' ') && (line
[c
] != '\0'))
213 while (line
[c
] != '\0')
215 line
[c
- i
] = line
[c
];
221 static char* line2string(char** line
)
225 if (*line
!= nullptr)
227 while (((*line
)[0] != '\"') && ((*line
)[0] != '\0'))
232 if ((*line
)[0] != '\"')
239 while (((*line
)[i
] != '\"') && ((*line
)[i
] != '\0'))
244 if ((*line
)[i
] != '\"')
257 //! If a label named \c label is found in \c line, return it. Otherwise return empty string.
258 static std::string
findLabelInLine(const std::string
& line
, const std::string
& label
)
260 std::regex
re(".*" + label
+ "\"(.*)\"");
262 if (std::regex_search(line
, match
, re
) && match
.size() > 1)
266 return std::string();
269 //! Read and return a matrix from \c in
270 static t_matrix
read_xpm_entry(FILE* in
)
272 char * line_buf
= nullptr, *line
= nullptr, *str
, buf
[256] = { 0 };
273 int i
, m
, col_len
, nch
= 0, llmax
;
275 unsigned int r
, g
, b
;
277 gmx_bool bGetOnWithIt
, bSetLine
;
284 while ((nullptr != fgetline(&line_buf
, llmax
, &llalloc
, in
))
285 && (std::strncmp(line_buf
, "static", 6) != 0))
287 std::string lineString
= line_buf
;
288 mm
.title
= findLabelInLine(lineString
, "title");
289 mm
.legend
= findLabelInLine(lineString
, "legend");
290 mm
.label_x
= findLabelInLine(lineString
, "x-label");
291 mm
.label_y
= findLabelInLine(lineString
, "y-label");
292 findLabelInLine(lineString
, "type"); // discard the returned string
295 if (!line_buf
|| strncmp(line_buf
, "static", 6) != 0)
297 gmx_input("Invalid XPixMap");
300 if (buf
[0] && (gmx_strcasecmp(buf
, "Discrete") == 0))
307 fprintf(debug
, "%s %s %s %s\n", mm
.title
.c_str(), mm
.legend
.c_str(), mm
.label_x
.c_str(),
313 bGetOnWithIt
= FALSE
;
314 while (!bGetOnWithIt
&& (nullptr != fgetline(&line_buf
, llmax
, &llalloc
, in
)))
317 while ((line
[0] != '\"') && (line
[0] != '\0'))
325 sscanf(line
, "%d %d %d %d", &(mm
.nx
), &(mm
.ny
), &nmap
, &nch
);
328 gmx_fatal(FARGS
, "Sorry can only read xpm's with at most 2 characters per pixel\n");
330 if (mm
.nx
<= 0 || mm
.ny
<= 0)
332 gmx_fatal(FARGS
, "Dimensions of xpm-file have to be larger than 0\n");
334 llmax
= std::max(STRLEN
, mm
.nx
+ 10);
340 fprintf(debug
, "mm.nx %d mm.ny %d nmap %d nch %d\n", mm
.nx
, mm
.ny
, nmap
, nch
);
344 gmx_fatal(FARGS
, "Number of characters per pixel not found in xpm\n");
350 while ((m
< nmap
) && (nullptr != fgetline(&line_buf
, llmax
, &llalloc
, in
)))
352 line
= std::strchr(line_buf
, '\"');
356 /* Read xpm color map entry */
357 mm
.map
[m
].code
.c1
= line
[0];
360 mm
.map
[m
].code
.c2
= 0;
364 mm
.map
[m
].code
.c2
= line
[1];
367 str
= std::strchr(line
, '#');
372 while (std::isxdigit(str
[col_len
]))
378 sscanf(line
, "%*s #%2x%2x%2x", &r
, &g
, &b
);
379 mm
.map
[m
].rgb
.r
= r
/ 255.0;
380 mm
.map
[m
].rgb
.g
= g
/ 255.0;
381 mm
.map
[m
].rgb
.b
= b
/ 255.0;
383 else if (col_len
== 12)
385 sscanf(line
, "%*s #%4x%4x%4x", &r
, &g
, &b
);
386 mm
.map
[m
].rgb
.r
= r
/ 65535.0;
387 mm
.map
[m
].rgb
.g
= g
/ 65535.0;
388 mm
.map
[m
].rgb
.b
= b
/ 65535.0;
392 gmx_file("Unsupported or invalid colormap in X PixMap");
397 str
= std::strchr(line
, 'c');
404 gmx_file("Unsupported or invalid colormap in X PixMap");
406 fprintf(stderr
, "Using white for color \"%s", str
);
411 line
= std::strchr(line
, '\"');
414 mm
.map
[m
].desc
= gmx_strdup(line
);
421 "Number of read colors map entries (%d) does not match the number in the header "
426 /* Read axes, if there are any */
435 GMX_RELEASE_ASSERT(line
, "Need to have valid line to parse");
436 if (strstr(line
, "x-axis"))
438 line
= std::strstr(line
, "x-axis");
441 mm
.axis_x
.reserve(mm
.nx
+ 1);
442 while (sscanf(line
, "%lf", &u
) == 1)
444 if (ssize(mm
.axis_x
) > mm
.nx
)
446 gmx_fatal(FARGS
, "Too many x-axis labels in xpm (max %d)", mm
.nx
);
448 else if (ssize(mm
.axis_x
) == mm
.nx
)
450 mm
.flags
|= MAT_SPATIAL_X
;
452 mm
.axis_x
.push_back(u
);
456 else if (std::strstr(line
, "y-axis"))
458 line
= std::strstr(line
, "y-axis");
461 mm
.axis_y
.reserve(mm
.ny
+ 1);
462 while (sscanf(line
, "%lf", &u
) == 1)
464 if (ssize(mm
.axis_y
) > mm
.ny
)
466 gmx_fatal(FARGS
, "Too many y-axis labels in xpm (max %d)", mm
.ny
);
468 else if (ssize(mm
.axis_y
) == mm
.ny
)
470 mm
.flags
|= MAT_SPATIAL_Y
;
472 mm
.axis_y
.push_back(u
);
476 } while ((line
[0] != '\"') && (nullptr != fgetline(&line_buf
, llmax
, &llalloc
, in
)));
479 mm
.matrix
.resize(mm
.nx
, mm
.ny
);
480 int rowIndex
= mm
.ny
- 1;
489 if (rowIndex
% (1 + mm
.ny
/ 100) == 0)
491 fprintf(stderr
, "%3d%%\b\b\b\b", (100 * (mm
.ny
- rowIndex
)) / mm
.ny
);
493 while ((line
[0] != '\"') && (line
[0] != '\0'))
499 gmx_fatal(FARGS
, "Not enough characters in row %d of the matrix\n", rowIndex
+ 1);
504 for (i
= 0; i
< mm
.nx
; i
++)
506 c
.c1
= line
[nch
* i
];
513 c
.c2
= line
[nch
* i
+ 1];
515 mm
.matrix(i
, rowIndex
) = searchcmap(mm
.map
, c
);
519 } while ((rowIndex
>= 0) && (nullptr != fgetline(&line_buf
, llmax
, &llalloc
, in
)));
522 gmx_incons("Not enough rows in the matrix");
529 std::vector
<t_matrix
> read_xpm_matrix(const char* fnm
)
532 char* line
= nullptr;
535 in
= gmx_fio_fopen(fnm
, "r");
537 std::vector
<t_matrix
> mat
;
538 while (nullptr != fgetline(&line
, STRLEN
, &llalloc
, in
))
540 if (std::strstr(line
, "/* XPM */"))
542 mat
.emplace_back(read_xpm_entry(in
));
549 gmx_file("Invalid XPixMap");
557 real
** matrix2real(t_matrix
* in
, real
** out
)
561 std::vector
<real
> rmap(in
->map
.size());
563 for (gmx::index i
= 0; i
!= ssize(in
->map
); ++i
)
565 if ((in
->map
[i
].desc
== nullptr) || (sscanf(in
->map
[i
].desc
, "%lf", &tmp
) != 1))
568 "Could not convert matrix to reals,\n"
569 "color map entry %zd has a non-real description: \"%s\"\n",
579 for (int i
= 0; i
< in
->nx
; i
++)
581 snew(out
[i
], in
->ny
);
584 for (int i
= 0; i
< in
->nx
; i
++)
586 for (int j
= 0; j
< in
->ny
; j
++)
588 out
[i
][j
] = rmap
[in
->matrix(i
, j
)];
592 fprintf(stderr
, "Converted a %dx%d matrix with %zu levels to reals\n", in
->nx
, in
->ny
, in
->map
.size());
597 static void write_xpm_header(FILE* out
,
598 const std::string
& title
,
599 const std::string
& legend
,
600 const std::string
& label_x
,
601 const std::string
& label_y
,
604 fprintf(out
, "/* XPM */\n");
605 fprintf(out
, "/* This file can be converted to EPS by the GROMACS program xpm2ps */\n");
606 fprintf(out
, "/* title: \"%s\" */\n", title
.c_str());
607 fprintf(out
, "/* legend: \"%s\" */\n", legend
.c_str());
608 fprintf(out
, "/* x-label: \"%s\" */\n", label_x
.c_str());
609 fprintf(out
, "/* y-label: \"%s\" */\n", label_y
.c_str());
612 fprintf(out
, "/* type: \"Discrete\" */\n");
616 fprintf(out
, "/* type: \"Continuous\" */\n");
620 static int calc_nmid(int nlevels
, real lo
, real mid
, real hi
)
622 /* Take care that we have at least 1 entry in the mid to hi range
624 return std::min(std::max(0, static_cast<int>(((mid
- lo
) / (hi
- lo
)) * (nlevels
- 1))), nlevels
- 1);
628 write_xpm_map3(FILE* out
, int n_x
, int n_y
, int* nlevels
, real lo
, real mid
, real hi
, t_rgb rlo
, t_rgb rmid
, t_rgb rhi
)
631 double r
, g
, b
, clev_lo
, clev_hi
;
633 if (*nlevels
> NMAP
* NMAP
)
635 fprintf(stderr
, "Warning, too many levels (%d) in matrix, using %d only\n", *nlevels
,
636 static_cast<int>(NMAP
* NMAP
));
637 *nlevels
= NMAP
* NMAP
;
639 else if (*nlevels
< 2)
641 fprintf(stderr
, "Warning, too few levels (%d) in matrix, using 2 instead\n", *nlevels
);
644 if (!((mid
>= lo
) && (mid
< hi
)))
646 gmx_fatal(FARGS
, "Lo: %f, Mid: %f, Hi: %f\n", lo
, mid
, hi
);
649 fprintf(out
, "static char *gromacs_xpm[] = {\n");
650 fprintf(out
, "\"%d %d %d %d\",\n", n_x
, n_y
, *nlevels
, (*nlevels
<= NMAP
) ? 1 : 2);
652 nmid
= calc_nmid(*nlevels
, lo
, mid
, hi
);
654 clev_hi
= (*nlevels
- 1 - nmid
);
655 for (i
= 0; (i
< nmid
); i
++)
657 r
= rlo
.r
+ (i
* (rmid
.r
- rlo
.r
) / clev_lo
);
658 g
= rlo
.g
+ (i
* (rmid
.g
- rlo
.g
) / clev_lo
);
659 b
= rlo
.b
+ (i
* (rmid
.b
- rlo
.b
) / clev_lo
);
660 fprintf(out
, "\"%c%c c #%02X%02X%02X \" /* \"%.3g\" */,\n", mapper
[i
% NMAP
],
661 (*nlevels
<= NMAP
) ? ' ' : mapper
[i
/ NMAP
],
662 static_cast<unsigned int>(std::round(255 * r
)),
663 static_cast<unsigned int>(std::round(255 * g
)),
664 static_cast<unsigned int>(std::round(255 * b
)), ((nmid
- i
) * lo
+ i
* mid
) / clev_lo
);
666 for (i
= 0; (i
< (*nlevels
- nmid
)); i
++)
668 r
= rmid
.r
+ (i
* (rhi
.r
- rmid
.r
) / clev_hi
);
669 g
= rmid
.g
+ (i
* (rhi
.g
- rmid
.g
) / clev_hi
);
670 b
= rmid
.b
+ (i
* (rhi
.b
- rmid
.b
) / clev_hi
);
671 fprintf(out
, "\"%c%c c #%02X%02X%02X \" /* \"%.3g\" */,\n", mapper
[(i
+ nmid
) % NMAP
],
672 (*nlevels
<= NMAP
) ? ' ' : mapper
[(i
+ nmid
) / NMAP
],
673 static_cast<unsigned int>(std::round(255 * r
)),
674 static_cast<unsigned int>(std::round(255 * g
)),
675 static_cast<unsigned int>(std::round(255 * b
)),
676 ((*nlevels
- 1 - nmid
- i
) * mid
+ i
* hi
) / clev_hi
);
680 static void pr_simple_cmap(FILE* out
, real lo
, real hi
, int nlevel
, t_rgb rlo
, t_rgb rhi
, int i0
)
685 for (i
= 0; (i
< nlevel
); i
++)
687 fac
= (i
+ 1.0) / (nlevel
);
688 r
= rlo
.r
+ fac
* (rhi
.r
- rlo
.r
);
689 g
= rlo
.g
+ fac
* (rhi
.g
- rlo
.g
);
690 b
= rlo
.b
+ fac
* (rhi
.b
- rlo
.b
);
691 fprintf(out
, "\"%c%c c #%02X%02X%02X \" /* \"%.3g\" */,\n", mapper
[(i
+ i0
) % NMAP
],
692 (nlevel
<= NMAP
) ? ' ' : mapper
[(i
+ i0
) / NMAP
],
693 static_cast<unsigned int>(std::round(255 * r
)),
694 static_cast<unsigned int>(std::round(255 * g
)),
695 static_cast<unsigned int>(std::round(255 * b
)), lo
+ fac
* (hi
- lo
));
699 static void pr_discrete_cmap(FILE* out
, int* nlevel
, int i0
)
702 { 1.0, 1.0, 1.0 }, /* white */
703 { 1.0, 0.0, 0.0 }, /* red */
704 { 1.0, 1.0, 0.0 }, /* yellow */
705 { 0.0, 0.0, 1.0 }, /* blue */
706 { 0.0, 1.0, 0.0 }, /* green */
707 { 1.0, 0.0, 1.0 }, /* purple */
708 { 1.0, 0.4, 0.0 }, /* orange */
709 { 0.0, 1.0, 1.0 }, /* cyan */
710 { 1.0, 0.4, 0.4 }, /* pink */
711 { 1.0, 1.0, 0.0 }, /* yellow */
712 { 0.4, 0.4, 1.0 }, /* lightblue */
713 { 0.4, 1.0, 0.4 }, /* lightgreen */
714 { 1.0, 0.4, 1.0 }, /* lightpurple */
715 { 1.0, 0.7, 0.4 }, /* lightorange */
716 { 0.4, 1.0, 1.0 }, /* lightcyan */
717 { 0.0, 0.0, 0.0 } /* black */
722 *nlevel
= std::min(16, *nlevel
);
724 for (i
= 0; (i
< n
); i
++)
726 fprintf(out
, "\"%c%c c #%02X%02X%02X \" /* \"%3d\" */,\n", mapper
[(i
+ i0
) % NMAP
],
727 (n
<= NMAP
) ? ' ' : mapper
[(i
+ i0
) / NMAP
],
728 static_cast<unsigned int>(round(255 * rgbd
[i
].r
)),
729 static_cast<unsigned int>(round(255 * rgbd
[i
].g
)),
730 static_cast<unsigned int>(round(255 * rgbd
[i
].b
)), i
);
735 static void write_xpm_map_split(FILE* out
,
738 const int* nlevel_top
,
743 gmx_bool bDiscreteColor
,
752 ntot
= *nlevel_top
+ *nlevel_bot
;
755 gmx_fatal(FARGS
, "Warning, too many levels (%d) in matrix", ntot
);
758 fprintf(out
, "static char *gromacs_xpm[] = {\n");
759 fprintf(out
, "\"%d %d %d %d\",\n", n_x
, n_y
, ntot
, 1);
763 pr_discrete_cmap(out
, nlevel_bot
, 0);
767 pr_simple_cmap(out
, lo_bot
, hi_bot
, *nlevel_bot
, rlo_bot
, rhi_bot
, 0);
770 pr_simple_cmap(out
, lo_top
, hi_top
, *nlevel_top
, rlo_top
, rhi_top
, *nlevel_bot
);
774 static void write_xpm_map(FILE* out
, int n_x
, int n_y
, int* nlevels
, real lo
, real hi
, t_rgb rlo
, t_rgb rhi
)
777 real invlevel
, r
, g
, b
;
779 if (*nlevels
> NMAP
* NMAP
)
781 fprintf(stderr
, "Warning, too many levels (%d) in matrix, using %d only\n", *nlevels
,
782 static_cast<int>(NMAP
* NMAP
));
783 *nlevels
= NMAP
* NMAP
;
785 else if (*nlevels
< 2)
787 fprintf(stderr
, "Warning, too few levels (%d) in matrix, using 2 instead\n", *nlevels
);
791 fprintf(out
, "static char *gromacs_xpm[] = {\n");
792 fprintf(out
, "\"%d %d %d %d\",\n", n_x
, n_y
, *nlevels
, (*nlevels
<= NMAP
) ? 1 : 2);
794 invlevel
= 1.0 / (*nlevels
- 1);
795 for (i
= 0; (i
< *nlevels
); i
++)
797 nlo
= *nlevels
- 1 - i
;
798 r
= (nlo
* rlo
.r
+ i
* rhi
.r
) * invlevel
;
799 g
= (nlo
* rlo
.g
+ i
* rhi
.g
) * invlevel
;
800 b
= (nlo
* rlo
.b
+ i
* rhi
.b
) * invlevel
;
801 fprintf(out
, "\"%c%c c #%02X%02X%02X \" /* \"%.3g\" */,\n", mapper
[i
% NMAP
],
802 (*nlevels
<= NMAP
) ? ' ' : mapper
[i
/ NMAP
],
803 static_cast<unsigned int>(std::round(255 * r
)),
804 static_cast<unsigned int>(std::round(255 * g
)),
805 static_cast<unsigned int>(std::round(255 * b
)), (nlo
* lo
+ i
* hi
) * invlevel
);
809 static void writeXpmAxis(FILE* out
, const char* axis
, ArrayRef
<const real
> label
)
815 for (gmx::index i
= 0; i
!= ssize(label
); ++i
)
821 fprintf(out
, "*/\n");
823 fprintf(out
, "/* %s-axis: ", axis
);
825 fprintf(out
, "%g ", label
[i
]);
827 fprintf(out
, "*/\n");
830 static void write_xpm_data(FILE* out
, int n_x
, int n_y
, real
** mat
, real lo
, real hi
, int nlevels
)
835 invlevel
= (nlevels
- 1) / (hi
- lo
);
836 for (j
= n_y
- 1; (j
>= 0); j
--)
838 if (j
% (1 + n_y
/ 100) == 0)
840 fprintf(stderr
, "%3d%%\b\b\b\b", (100 * (n_y
- j
)) / n_y
);
843 for (i
= 0; (i
< n_x
); i
++)
845 c
= roundToInt((mat
[i
][j
] - lo
) * invlevel
);
856 fprintf(out
, "%c", mapper
[c
]);
860 fprintf(out
, "%c%c", mapper
[c
% NMAP
], mapper
[c
/ NMAP
]);
865 fprintf(out
, "\",\n");
869 fprintf(out
, "\"\n");
874 static void write_xpm_data3(FILE* out
, int n_x
, int n_y
, real
** mat
, real lo
, real mid
, real hi
, int nlevels
)
876 int i
, j
, c
= 0, nmid
;
877 real invlev_lo
, invlev_hi
;
879 nmid
= calc_nmid(nlevels
, lo
, mid
, hi
);
880 invlev_hi
= (nlevels
- 1 - nmid
) / (hi
- mid
);
881 invlev_lo
= (nmid
) / (mid
- lo
);
883 for (j
= n_y
- 1; (j
>= 0); j
--)
885 if (j
% (1 + n_y
/ 100) == 0)
887 fprintf(stderr
, "%3d%%\b\b\b\b", (100 * (n_y
- j
)) / n_y
);
890 for (i
= 0; (i
< n_x
); i
++)
892 if (mat
[i
][j
] >= mid
)
894 c
= nmid
+ roundToInt((mat
[i
][j
] - mid
) * invlev_hi
);
896 else if (mat
[i
][j
] >= lo
)
898 c
= roundToInt((mat
[i
][j
] - lo
) * invlev_lo
);
915 fprintf(out
, "%c", mapper
[c
]);
919 fprintf(out
, "%c%c", mapper
[c
% NMAP
], mapper
[c
/ NMAP
]);
924 fprintf(out
, "\",\n");
928 fprintf(out
, "\"\n");
933 static void write_xpm_data_split(FILE* out
,
945 real invlev_top
, invlev_bot
;
947 invlev_top
= (nlevel_top
- 1) / (hi_top
- lo_top
);
948 invlev_bot
= (nlevel_bot
- 1) / (hi_bot
- lo_bot
);
950 for (j
= n_y
- 1; (j
>= 0); j
--)
952 if (j
% (1 + n_y
/ 100) == 0)
954 fprintf(stderr
, "%3d%%\b\b\b\b", (100 * (n_y
- j
)) / n_y
);
957 for (i
= 0; (i
< n_x
); i
++)
961 c
= nlevel_bot
+ roundToInt((mat
[i
][j
] - lo_top
) * invlev_top
);
962 if ((c
< nlevel_bot
) || (c
>= nlevel_bot
+ nlevel_top
))
965 "Range checking i = %d, j = %d, c = %d, bot = %d, top = %d "
967 i
, j
, c
, nlevel_bot
, nlevel_top
, mat
[i
][j
]);
972 c
= roundToInt((mat
[i
][j
] - lo_bot
) * invlev_bot
);
973 if ((c
< 0) || (c
>= nlevel_bot
+ nlevel_bot
))
976 "Range checking i = %d, j = %d, c = %d, bot = %d, top = %d "
978 i
, j
, c
, nlevel_bot
, nlevel_top
, mat
[i
][j
]);
986 fprintf(out
, "%c", mapper
[c
]);
990 fprintf(out
, "\",\n");
994 fprintf(out
, "\"\n");
999 void write_xpm_m(FILE* out
, t_matrix m
)
1004 bOneChar
= (m
.map
[0].code
.c2
== 0);
1005 write_xpm_header(out
, m
.title
, m
.legend
, m
.label_x
, m
.label_y
, m
.bDiscrete
);
1006 fprintf(out
, "static char *gromacs_xpm[] = {\n");
1007 fprintf(out
, "\"%d %d %zu %d\",\n", m
.nx
, m
.ny
, m
.map
.size(), bOneChar
? 1 : 2);
1008 for (const auto& map
: m
.map
)
1010 fprintf(out
, "\"%c%c c #%02X%02X%02X \" /* \"%s\" */,\n", map
.code
.c1
,
1011 bOneChar
? ' ' : map
.code
.c2
, static_cast<unsigned int>(round(map
.rgb
.r
* 255)),
1012 static_cast<unsigned int>(round(map
.rgb
.g
* 255)),
1013 static_cast<unsigned int>(round(map
.rgb
.b
* 255)), map
.desc
);
1015 writeXpmAxis(out
, "x", m
.axis_x
);
1016 writeXpmAxis(out
, "y", m
.axis_y
);
1017 for (int j
= m
.ny
- 1; (j
>= 0); j
--)
1019 if (j
% (1 + m
.ny
/ 100) == 0)
1021 fprintf(stderr
, "%3d%%\b\b\b\b", (100 * (m
.ny
- j
)) / m
.ny
);
1026 for (int i
= 0; (i
< m
.nx
); i
++)
1028 fprintf(out
, "%c", m
.map
[m
.matrix(i
, j
)].code
.c1
);
1033 for (int i
= 0; (i
< m
.nx
); i
++)
1035 c
= m
.map
[m
.matrix(i
, j
)].code
;
1036 fprintf(out
, "%c%c", c
.c1
, c
.c2
);
1041 fprintf(out
, "\",\n");
1045 fprintf(out
, "\"\n");
1050 void write_xpm3(FILE* out
,
1052 const std::string
& title
,
1053 const std::string
& legend
,
1054 const std::string
& label_x
,
1055 const std::string
& label_y
,
1070 * Writes a colormap varying as rlo -> rmid -> rhi.
1075 gmx_fatal(FARGS
, "hi (%g) <= lo (%g)", hi
, lo
);
1078 write_xpm_header(out
, title
, legend
, label_x
, label_y
, FALSE
);
1079 write_xpm_map3(out
, n_x
, n_y
, nlevels
, lo
, mid
, hi
, rlo
, rmid
, rhi
);
1080 writeXpmAxis(out
, "x", ArrayRef
<real
>(axis_x
, axis_x
+ n_x
+ ((flags
& MAT_SPATIAL_X
) != 0U ? 1 : 0)));
1081 writeXpmAxis(out
, "y", ArrayRef
<real
>(axis_y
, axis_y
+ n_y
+ ((flags
& MAT_SPATIAL_Y
) != 0U ? 1 : 0)));
1082 write_xpm_data3(out
, n_x
, n_y
, mat
, lo
, mid
, hi
, *nlevels
);
1085 void write_xpm_split(FILE* out
,
1087 const std::string
& title
,
1088 const std::string
& legend
,
1089 const std::string
& label_x
,
1090 const std::string
& label_y
,
1104 gmx_bool bDiscreteColor
,
1109 * Writes a colormap varying as rlo -> rmid -> rhi.
1112 if (hi_top
<= lo_top
)
1114 gmx_fatal(FARGS
, "hi_top (%g) <= lo_top (%g)", hi_top
, lo_top
);
1116 if (hi_bot
<= lo_bot
)
1118 gmx_fatal(FARGS
, "hi_bot (%g) <= lo_bot (%g)", hi_bot
, lo_bot
);
1120 if (bDiscreteColor
&& (*nlevel_bot
>= 16))
1122 gmx_impl("Can not plot more than 16 discrete colors");
1125 write_xpm_header(out
, title
, legend
, label_x
, label_y
, FALSE
);
1126 write_xpm_map_split(out
, n_x
, n_y
, nlevel_top
, lo_top
, hi_top
, rlo_top
, rhi_top
, bDiscreteColor
,
1127 nlevel_bot
, lo_bot
, hi_bot
, rlo_bot
, rhi_bot
);
1128 writeXpmAxis(out
, "x", ArrayRef
<real
>(axis_x
, axis_x
+ n_x
+ ((flags
& MAT_SPATIAL_X
) != 0U ? 1 : 0)));
1129 writeXpmAxis(out
, "y", ArrayRef
<real
>(axis_y
, axis_y
+ n_y
+ ((flags
& MAT_SPATIAL_Y
) != 0U ? 1 : 0)));
1130 write_xpm_data_split(out
, n_x
, n_y
, mat
, lo_top
, hi_top
, *nlevel_top
, lo_bot
, hi_bot
, *nlevel_bot
);
1133 void write_xpm(FILE* out
,
1135 const std::string
& title
,
1136 const std::string
& legend
,
1137 const std::string
& label_x
,
1138 const std::string
& label_y
,
1151 * title matrix title
1152 * legend label for the continuous legend
1153 * label_x label for the x-axis
1154 * label_y label for the y-axis
1155 * n_x, n_y size of the matrix
1156 * axis_x[] the x-ticklabels
1157 * axis_y[] the y-ticklables
1158 * *matrix[] element x,y is matrix[x][y]
1159 * lo output lower than lo is set to lo
1160 * hi output higher than hi is set to hi
1161 * rlo rgb value for level lo
1162 * rhi rgb value for level hi
1163 * nlevels number of color levels for the output
1168 gmx_fatal(FARGS
, "hi (%f) <= lo (%f)", hi
, lo
);
1171 write_xpm_header(out
, title
, legend
, label_x
, label_y
, FALSE
);
1172 write_xpm_map(out
, n_x
, n_y
, nlevels
, lo
, hi
, rlo
, rhi
);
1173 writeXpmAxis(out
, "x", ArrayRef
<real
>(axis_x
, axis_x
+ n_x
+ ((flags
& MAT_SPATIAL_X
) != 0U ? 1 : 0)));
1174 writeXpmAxis(out
, "y", ArrayRef
<real
>(axis_y
, axis_y
+ n_y
+ ((flags
& MAT_SPATIAL_Y
) != 0U ? 1 : 0)));
1175 write_xpm_data(out
, n_x
, n_y
, mat
, lo
, hi
, *nlevels
);