sciplot  0.3.1
A modern C++ plotting library powered by gnuplot
Utils.hpp
1 // sciplot - a modern C++ scientific plotting library powered by gnuplot
2 // https://github.com/sciplot/sciplot
3 //
4 // Licensed under the MIT License <http://opensource.org/licenses/MIT>.
5 //
6 // Copyright (c) 2018-2021 Allan Leal
7 //
8 // Permission is hereby granted, free of charge, to any person obtaining a copy
9 // of this software and associated documentation files (the "Software"), to deal
10 // in the Software without restriction, including without limitation the rights
11 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 // copies of the Software, and to permit persons to whom the Software is
13 // furnished to do so, subject to the following conditions:
14 //
15 // The above copyright notice and this permission notice shall be included in all
16 // copies or substantial portions of the Software.
17 //
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 // SOFTWARE.
25 
26 #pragma once
27 
28 // C++ includes
29 #include <algorithm>
30 #include <cctype>
31 #include <cmath>
32 #include <cstdlib>
33 #include <fstream>
34 #include <sstream>
35 #include <string>
36 #include <type_traits>
37 #include <valarray>
38 
39 // sciplot includes
40 #include <sciplot/Constants.hpp>
41 #include <sciplot/Enums.hpp>
42 #include <sciplot/Palettes.hpp>
43 
44 namespace sciplot
45 {
46 namespace internal
47 {
48 
50 template <typename T>
51 auto str(const T& val) -> std::string
52 {
53  std::stringstream ss;
54  ss << val;
55  return ss.str(); // Note: This is different than std::to_string(i). For example, it works with custom types. Also, std::to_string(2.0) may produce "2.000000", difficulting string comparison in the tests.
56 }
57 
59 inline auto str(const char* word) -> std::string
60 {
61  return word;
62 }
63 
65 inline auto str() -> std::string
66 {
67  return {};
68 }
69 
71 inline auto trimleft(std::string str, unsigned char character = ' ') -> std::string
72 {
73  str.erase(str.begin(), std::find_if(str.begin(), str.end(),
74  [&](unsigned char ch)
75  { return ch != character; }));
76  return str;
77 }
78 
80 inline auto trimright(std::string str, unsigned char character = ' ') -> std::string
81 {
82  str.erase(std::find_if(str.rbegin(), str.rend(),
83  [&](unsigned char ch)
84  { return ch != character; })
85  .base(),
86  str.end());
87  return str;
88 }
89 
91 inline auto trim(std::string str, unsigned char character = ' ') -> std::string
92 {
93  return trimleft(trimright(str, character), character);
94 }
95 
97 inline auto collapseWhitespaces(std::string s) -> std::string
98 {
99  s.erase(std::unique(std::begin(s), std::end(s),
100  [](unsigned char a, unsigned char b)
101  { return std::isspace(a) && std::isspace(b); }),
102  std::end(s));
103  return s;
104 }
105 
107 inline auto removeExtraWhitespaces(std::string s) -> std::string
108 {
109  return trim(collapseWhitespaces(s));
110 }
111 
113 template <typename VectorType>
114 auto minsize(const VectorType& v) -> std::size_t
115 {
116  return v.size();
117 }
118 
120 template <typename VectorType, typename... Args>
121 auto minsize(const VectorType& v, const Args&... args) -> std::size_t
122 {
123  return std::min<decltype(v.size())>(v.size(), minsize(args...));
124 }
125 
127 template <typename T>
128 constexpr auto isString = std::is_same_v<std::decay_t<T>, std::string>;
129 
131 template <typename V>
132 constexpr auto isStringVector = isString<decltype(std::declval<V>()[0])>;
133 
135 template <typename T>
136 auto escapeIfNeeded(const T& val)
137 {
138  if constexpr (isString<T>)
139  return "\"" + val + "\""; // Due bug #102 we escape data using double quotes
140  else
141  return std::isfinite(static_cast<double>(val)) ? internal::str(val) : MISSING_INDICATOR; // static_cast to avoid MSVC error C2668: 'fpclassify': ambiguous call to overloaded function
142 }
143 
145 template <typename VectorType>
146 auto writeline(std::ostream& out, std::size_t i, const VectorType& v) -> std::ostream&
147 {
148  out << escapeIfNeeded(v[i]) << '\n';
149  return out;
150 }
151 
153 template <typename VectorType, typename... Args>
154 auto writeline(std::ostream& out, std::size_t i, const VectorType& v, const Args&... args) -> std::ostream&
155 {
156  out << escapeIfNeeded(v[i]) << " ";
157  writeline(out, i, args...);
158  return out;
159 }
160 
162 template <typename... Args>
163 auto write(std::ostream& out, const Args&... args) -> std::ostream&
164 {
165  const auto size = minsize(args...);
166  for (std::size_t i = 0; i < size; ++i)
167  writeline(out, i, args...);
168  return out;
169 }
170 
171 } // namespace internal
172 
173 namespace gnuplot
174 {
175 
177 inline auto titlestr(std::string word) -> std::string
178 {
179  return word == "columnheader" ? word : "'" + word + "'";
180 }
181 
184 inline auto optionStr(std::string option) -> std::string
185 {
186  return option.size() ? (option + " ") : "";
187 }
188 
191 inline auto optionValueStr(std::string option, std::string value) -> std::string
192 {
193  return value.size() ? (option + " " + value + " ") : "";
194 }
195 
198 inline auto cmdValueStr(std::string cmd, std::string value) -> std::string
199 {
200  return value.size() ? (cmd + " " + value + "\n") : "";
201 }
202 
205 inline auto cmdValueEscapedStr(std::string cmd, std::string value) -> std::string
206 {
207  return value.size() ? (cmd + " '" + value + "'\n") : "";
208 }
209 
211 inline auto figureSizeStr(double sx, double sy) -> std::string
212 {
213  return internal::str(sx) + "," + internal::str(sy);
214 }
215 
217 inline auto canvasSizeStr(std::size_t width, std::size_t height, bool asinches) -> std::string
218 {
219  return asinches ? (internal::str(width * POINT_TO_INCHES) + "in," + internal::str(height * POINT_TO_INCHES) + "in") : (internal::str(width) + "," + internal::str(height));
220 }
221 
223 inline auto rgb(std::string color) -> std::string { return "rgb '" + color + "'"; }
224 
226 inline auto rgb(int hexcolor) -> std::string { return "rgb " + internal::str(hexcolor); }
227 
229 struct angle
230 {
232  static auto deg(long val) -> std::string { return internal::str(val) + "deg"; }
233 
235  static auto rad(double val) -> std::string { return internal::str(val); }
236 
238  static auto pi(double val) -> std::string { return internal::str(val) + "pi"; }
239 };
240 
242 template <typename... Args>
243 auto writedataset(std::ostream& out, std::size_t index, const Args&... args) -> std::ostream&
244 {
245  // Save the given vectors x and y in a new data set of the data file
246  out << "#==============================================================================" << std::endl;
247  out << "# DATASET #" << index << std::endl;
248  out << "#==============================================================================" << std::endl;
249  // Write the vector arguments to the ostream object
250  internal::write(out, args...);
251  // Ensure two blank lines are added here so that gnuplot understands a new data set has been added
252  out << "\n\n";
253  return out;
254 }
255 
257 inline auto palettecmd(std::ostream& out, std::string palette) -> std::ostream&
258 {
259  out << "#==============================================================================" << std::endl;
260  out << "# GNUPLOT-palette (" << palette << ")" << std::endl;
261  out << "#------------------------------------------------------------------------------" << std::endl;
262  out << "# see more at https://github.com/Gnuplotting/gnuplot-palettes" << std::endl;
263  out << "#==============================================================================" << std::endl;
264  out << palettes.at(palette) << std::endl;
265  return out;
266 }
267 
269 inline auto unsetpalettecmd(std::ostream& out) -> std::ostream&
270 {
271  out << "do for [i=1:20] { unset style line i }" << std::endl;
272  return out;
273 }
274 
276 inline auto showterminalcmd(std::ostream& out, std::string size, std::string font, std::string title) -> std::ostream&
277 {
278  out << "#==============================================================================" << std::endl;
279  out << "# TERMINAL" << std::endl;
280  out << "#==============================================================================" << std::endl;
281  // We set a terminal here to make sure we can also set a size. This is necessary, because the
282  // canvas size can ONLY be set using "set terminal <TERMINAL> size W, H".
283  // See: http://www.bersch.net/gnuplot-doc/canvas-size.html#set-term-size
284  // The GNUTERM variable contains the default terminal, which we're using for the show command.
285  // See: http://www.bersch.net/gnuplot-doc/unset.html
286  out << "set termoption enhanced" << std::endl;
287  if (font.size()) out << "set termoption " << font << std::endl;
288  out << "set terminal GNUTERM size " << size << (!title.empty() ? " title '" + title + "' " : "") << std::endl;
289  out << "set encoding utf8" << std::endl;
290  return out;
291 }
292 
294 inline auto saveterminalcmd(std::ostream& out, std::string extension, std::string size, std::string font) -> std::ostream&
295 {
296  out << "#==============================================================================" << std::endl;
297  out << "# TERMINAL" << std::endl;
298  out << "#==============================================================================" << std::endl;
299  out << "set terminal " << extension << " size " << size << " enhanced rounded " << font << std::endl;
300  out << "set encoding utf8" << std::endl;
301  return out;
302 }
303 
305 inline auto outputcmd(std::ostream& out, std::string filename) -> std::ostream&
306 {
307  out << "#==============================================================================" << std::endl;
308  out << "# OUTPUT" << std::endl;
309  out << "#==============================================================================" << std::endl;
310  out << "set output '" << filename << "'" << std::endl;
311  out << "set encoding utf8" << std::endl;
312  return out;
313 }
314 
316 inline auto multiplotcmd(std::ostream& out, std::size_t rows, std::size_t columns, std::string title) -> std::ostream&
317 {
318  out << "#==============================================================================" << std::endl;
319  out << "# MULTIPLOT" << std::endl;
320  out << "#==============================================================================" << std::endl;
321  out << "set multiplot";
322  if (rows != 0 || columns != 0)
323  {
324  out << " layout " << rows << "," << columns;
325  }
326  out << " "
327  << "rowsfirst";
328  out << " "
329  << "downwards";
330  if (!title.empty())
331  {
332  out << " title '" << title << "'";
333  }
334  out << std::endl;
335  return out;
336 }
337 
339 // persistent == true: for show commands. show the file using GNUplot until the window is closed
340 // persistent == false: for save commands. close gnuplot immediately
341 inline auto runscript(std::string scriptfilename, bool persistent) -> bool
342 {
343  std::string command = persistent ? "gnuplot -persistent " : "gnuplot ";
344  command += "\"" + scriptfilename + "\"";
345  return std::system(command.c_str()) == 0;
346 }
347 
350 inline auto cleanpath(std::string path) -> std::string
351 {
352  const std::string invalidchars = ":*?!\"<>|";
353  std::string result = path;
354  result.erase(std::remove_if(result.begin(), result.end(), [&invalidchars](char c)
355  { return (std::find(invalidchars.cbegin(), invalidchars.cend(), c) != invalidchars.cend()); }),
356  result.end());
357  return result;
358 }
359 
360 } // namespace gnuplot
361 
362 } // namespace sciplot
static auto pi(double val) -> std::string
Return the angle in radian units as a multiple of number pi.
Definition: Utils.hpp:238
The struct where static angle methods are defined.
Definition: Utils.hpp:230
static auto deg(long val) -> std::string
Return the angle in degree units.
Definition: Utils.hpp:232
static auto rad(double val) -> std::string
Return the angle in radian units.
Definition: Utils.hpp:235