SPH
Utils.cpp
Go to the documentation of this file.
1 #include "gui/Utils.h"
2 #include "common/Assert.h"
3 #include "io/FileSystem.h"
5 #include <iomanip>
6 #include <wx/dcmemory.h>
7 #include <wx/filedlg.h>
8 
10 
11 static std::string getDesc(ArrayView<const FileFormat> formats, const bool doAll) {
12  std::string desc;
13  bool isFirst = true;
14  if (doAll && formats.size() > 1) {
15  desc += "All supported formats|";
16  for (const FileFormat& format : formats) {
17  desc += "*." + format.extension + ";";
18  }
19  isFirst = false;
20  }
21  for (const FileFormat& format : formats) {
22  if (!isFirst) {
23  desc += "|";
24  }
25  isFirst = false;
26  desc += format.description + " (*." + format.extension + ")|*." + format.extension;
27  }
28  return desc;
29 }
30 
31 static Optional<std::pair<Path, int>> doFileDialog(const std::string& title,
32  const std::string& fileMask,
33  std::string& defaultDir,
34  const int flags) {
35  wxFileDialog dialog(nullptr, title, "", defaultDir, fileMask, flags);
36  if (dialog.ShowModal() == wxID_CANCEL) {
37  return NOTHING;
38  }
39  std::string s(dialog.GetPath());
40  Path path(std::move(s));
41  defaultDir = path.parentPath().native();
42  return std::make_pair(path, !fileMask.empty() ? dialog.GetFilterIndex() : -1);
43 }
44 
45 Optional<Path> doOpenFileDialog(const std::string& title, Array<FileFormat>&& formats) {
46  static std::string defaultDir = "";
47  Optional<std::pair<Path, int>> pathAndIndex =
48  doFileDialog(title, getDesc(formats, true), defaultDir, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
49  if (pathAndIndex) {
50  return pathAndIndex->first;
51  } else {
52  return NOTHING;
53  }
54 }
55 
56 Optional<Path> doSaveFileDialog(const std::string& title, Array<FileFormat>&& formats) {
57  static std::string defaultDir = "";
58  Optional<std::pair<Path, int>> pathAndIndex =
59  doFileDialog(title, getDesc(formats, false), defaultDir, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
60  if (pathAndIndex) {
61  const int index = pathAndIndex->second;
62  if (index >= 0) {
63  const std::string ext = formats[index].extension;
64  pathAndIndex->first.replaceExtension(ext);
65  }
66  return pathAndIndex->first;
67  } else {
68  return NOTHING;
69  }
70 }
71 
72 static Size getSubscriptSize(const std::wstring& text) {
73  Size size = 0;
74  if (!text.empty() && text[0] == L'-') {
75  size++;
76  }
77  for (; size < text.size(); ++size) {
78  const char c = text[size];
79  if ((c < L'0' || c > L'9') && (c < L'a' || c > L'z') && (c < L'A' || c > L'Z')) {
80  return size;
81  }
82  }
83  return text.size();
84 }
85 
86 void drawTextWithSubscripts(wxDC& dc, const std::wstring& text, const wxPoint point) {
87  std::size_t n;
88  std::size_t m = 0;
89  wxPoint actPoint = point;
90  const wxFont font = dc.GetFont();
91  const wxFont subcriptFont = font.Smaller();
92 
93  while ((n = text.find_first_of(L"_^", m)) != std::string::npos) {
94  const bool isSubscript = text[n] == '_';
95  std::wstring part = text.substr(m, n - m);
96  wxSize extent = dc.GetTextExtent(part);
97  // draw part up to subscript using current font
98  dc.DrawText(part, actPoint);
99 
100  actPoint.x += extent.x;
101  const Size subscriptSize = getSubscriptSize(text.substr(n + 1));
102  const std::wstring subscript = text.substr(n + 1, subscriptSize);
103  dc.SetFont(subcriptFont);
104  wxPoint subscriptPoint = isSubscript ? wxPoint(actPoint.x + 2, actPoint.y + extent.y / 3)
105  : wxPoint(actPoint.x + 2, actPoint.y - extent.y / 4);
106 
107  dc.DrawText(subscript, subscriptPoint);
108  actPoint.x = subscriptPoint.x + dc.GetTextExtent(subscript).x;
109 
110  dc.SetFont(font);
111  m = n + 1 + subscriptSize; // skip _ and the subscript character
112  }
113  // draw last part of the text
114  dc.DrawText(text.substr(m), actPoint);
115 }
116 
117 void drawTextWithSubscripts(wxDC& dc, const std::string& text, const wxPoint point) {
118  drawTextWithSubscripts(dc, std::wstring(text.begin(), text.end()), point);
119 }
120 
121 std::wstring toPrintableString(const Float value, const Size precision, const Float decimalThreshold) {
122  const Float absValue = abs(value);
123  std::stringstream ss;
124  if (absValue == 0._f || (absValue >= 1._f / decimalThreshold && absValue <= decimalThreshold)) {
125  ss << value;
126  } else {
127  ss << std::setprecision(precision) << std::scientific << value;
128  }
129  std::string s = ss.str();
130 
131  std::wstring printable;
132  if (value > 0) {
133  printable += ' ';
134  }
135  bool exponent = false;
136  for (Size i = 0; i < s.size(); ++i) {
137  // replace unary pluses with spaces (to keep alignment of numbers
138  if (s[i] == '+') {
139  continue;
140  }
141  // replace 'e' with 'x10^'
142  if (s[i] == 'e') {
143  exponent = true;
144  printable += L"\u00D710^";
145  continue;
146  }
147  // get rid of leading zeros in exponent
148  if (exponent) {
149  if (s[i] == '-') {
150  printable += '-';
151  continue;
152  }
153  if (s[i] == '0') {
154  continue;
155  }
156  }
157  printable += s[i];
158  exponent = false;
159  }
160  return printable;
161 }
162 
163 static Pixel getOriginOffset(wxDC& dc, Flags<TextAlign> align, const std::wstring& text) {
164  wxSize extent = dc.GetTextExtent(text);
165  if (text.find(L"^") != std::string::npos) {
166  // number with superscript is actually a bit shorter, shrink it
168  extent.x -= 6;
169  }
170  Pixel offset(0, 0);
171  if (align.has(TextAlign::LEFT)) {
172  offset.x -= extent.x;
173  }
174  if (align.has(TextAlign::HORIZONTAL_CENTER)) {
175  offset.x -= extent.x / 2;
176  }
177  if (align.has(TextAlign::TOP)) {
178  offset.y -= extent.y;
179  }
180  if (align.has(TextAlign::VERTICAL_CENTER)) {
181  offset.y -= extent.y / 2;
182  }
183  return offset;
184 }
185 
187  wxFont font = dc.GetFont();
188  for (const IRenderOutput::Label& label : labels) {
189  dc.SetTextForeground(wxColour(label.color));
190  font.SetPointSize(label.fontSize);
191  dc.SetFont(font);
192  const wxPoint origin(label.position + getOriginOffset(dc, label.align, label.text));
193  drawTextWithSubscripts(dc, label.text, origin);
194  }
195 }
196 
Custom assertions.
NAMESPACE_SPH_BEGIN
Definition: BarnesHut.cpp:13
uint32_t Size
Integral type used to index arrays (by default).
Definition: Globals.h:16
double Float
Precision used withing the code. Use Float instead of float or double where precision is important.
Definition: Globals.h:13
@ HORIZONTAL_CENTER
INLINE auto abs(const T &f)
Definition: MathUtils.h:276
#define NAMESPACE_SPH_END
Definition: Object.h:12
const NothingType NOTHING
Definition: Optional.h:16
void printLabels(wxDC &dc, ArrayView< const IRenderOutput::Label > labels)
Definition: Utils.cpp:186
void drawTextWithSubscripts(wxDC &dc, const std::wstring &text, const wxPoint point)
Definition: Utils.cpp:86
std::wstring toPrintableString(const Float value, const Size precision, const Float decimalThreshold)
Converts the value to a printable string.
Definition: Utils.cpp:121
Optional< Path > doSaveFileDialog(const std::string &title, Array< FileFormat > &&formats)
Definition: Utils.cpp:56
Optional< Path > doOpenFileDialog(const std::string &title, Array< FileFormat > &&formats)
Definition: Utils.cpp:45
Random utility functions for drawing stuff to DC.
Object providing safe access to continuous memory of data.
Definition: ArrayView.h:17
INLINE TCounter size() const
Definition: ArrayView.h:101
Generic dynamically allocated resizable storage.
Definition: Array.h:43
constexpr INLINE bool has(const TEnum flag) const
Checks if the object has a given flag.
Definition: Flags.h:77
Wrapper of type value of which may or may not be present.
Definition: Optional.h:23
Object representing a path on a filesystem.
Definition: Path.h:17
Definition: Point.h:101