LibrePCB Developers Documentation
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
toolbox.h
Go to the documentation of this file.
1 /*
2  * LibrePCB - Professional EDA for everyone!
3  * Copyright (C) 2013 LibrePCB Developers, see AUTHORS.md for contributors.
4  * https://librepcb.org/
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #ifndef LIBREPCB_TOOLBOX_H
21 #define LIBREPCB_TOOLBOX_H
22 
23 /*******************************************************************************
24  * Includes
25  ******************************************************************************/
26 #include "units/all_length_units.h"
27 
28 #include <type_traits>
29 
30 #include <QtCore>
31 #include <QtWidgets>
32 
33 /*******************************************************************************
34  * Namespace / Forward Declarations
35  ******************************************************************************/
36 namespace librepcb {
37 
38 /*******************************************************************************
39  * Class Toolbox
40  ******************************************************************************/
41 
48 class Toolbox final {
49  Q_DECLARE_TR_FUNCTIONS(Toolbox)
50 
51 public:
52  // Constructors / Destructor
53  Toolbox() = delete;
54  Toolbox(const Toolbox& other) = delete;
55  ~Toolbox() = delete;
56 
57  // Operator Overloadings
58  Toolbox& operator=(const Toolbox& rhs) = delete;
59 
60  // Static Methods
61  template <typename T>
62  static QList<T> sortedQSet(const QSet<T>& set) noexcept {
63  QList<T> list = set.toList();
64  qSort(list);
65  return list;
66  }
67 
68  static QRectF boundingRectFromRadius(qreal radius) noexcept {
69  return QRectF(-radius, -radius, 2 * radius, 2 * radius);
70  }
71 
72  static QRectF boundingRectFromRadius(qreal rx, qreal ry) noexcept {
73  return QRectF(-rx, -ry, 2 * rx, 2 * ry);
74  }
75 
76  static QRectF adjustedBoundingRect(const QRectF& rect,
77  qreal offset) noexcept {
78  return rect.adjusted(-offset, -offset, offset, offset);
79  }
80 
81  static QPainterPath shapeFromPath(
82  const QPainterPath& path, const QPen& pen, const QBrush& brush,
83  const UnsignedLength& minWidth = UnsignedLength(0)) noexcept;
84 
85  static Length arcRadius(const Point& p1, const Point& p2,
86  const Angle& a) noexcept;
87  static Point arcCenter(const Point& p1, const Point& p2,
88  const Angle& a) noexcept;
89 
104  static Point nearestPointOnLine(const Point& p, const Point& l1,
105  const Point& l2) noexcept;
106 
119  const Point& p, const Point& l1, const Point& l2,
120  Point* nearest = nullptr) noexcept;
121 
129  static QVariant stringOrNumberToQVariant(const QString& string) noexcept;
130 
144  static QString cleanUserInputString(const QString& input,
145  const QRegularExpression& removeRegex,
146  bool trim = true, bool toLower = false,
147  bool toUpper = false,
148  const QString& spaceReplacement = " ",
149  int maxLength = -1) noexcept;
150 
157  template <typename T>
158  static QString decimalFixedPointToString(T value, qint32 pointPos) noexcept {
159  using UnsignedT = typename std::make_unsigned<T>::type;
160 
161  if (value == 0) {
162  // special case
163  return "0.0";
164  }
165 
166  UnsignedT valueAbs;
167  if (value < 0) {
168  valueAbs = -static_cast<UnsignedT>(value);
169  } else {
170  valueAbs = static_cast<UnsignedT>(value);
171  }
172 
173  QString str = QString::number(valueAbs);
174  if (str.length() > pointPos) {
175  // pointPos must be > 0 for this to work correctly
176  str.insert(str.length() - pointPos, '.');
177  } else {
178  for (qint32 i = pointPos - str.length(); i != 0; i--) str.insert(0, '0');
179  str.insert(0, "0.");
180  }
181 
182  while (str.endsWith('0') && !str.endsWith(".0")) str.chop(1);
183 
184  if (value < 0) str.insert(0, '-');
185 
186  return str;
187  }
188 
196  template <typename T>
197  static T decimalFixedPointFromString(const QString& str, qint32 pointPos) {
198  using UnsignedT = typename std::make_unsigned<T>::type;
199 
200  const T min = std::numeric_limits<T>::min();
201  const T max = std::numeric_limits<T>::max();
202  const UnsignedT max_u = std::numeric_limits<UnsignedT>::max();
203 
204  enum class State {
205  INVALID,
206  START,
207  AFTER_SIGN,
208  LONELY_DOT,
209  INT_PART,
210  FRAC_PART,
211  EXP,
212  EXP_AFTER_SIGN,
213  EXP_DIGITS,
214  };
215  State state = State::START;
216  UnsignedT valueAbs = 0;
217  bool sign = false;
218  qint32 expOffset = pointPos;
219 
220  const quint32 maxExp = std::numeric_limits<quint32>::max();
221  quint32 exp = 0;
222  bool expSign = false;
223 
224  for (QChar c : str) {
225  if (state == State::INVALID) {
226  // break the loop, not the switch
227  break;
228  }
229  switch (state) {
230  case State::INVALID:
231  // already checked, but needed to avoid compiler warnings
232  break;
233 
234  case State::START:
235  if (c == '-') {
236  sign = true;
237  state = State::AFTER_SIGN;
238  } else if (c == '+') {
239  state = State::AFTER_SIGN;
240  } else if (c == '.') {
241  state = State::LONELY_DOT;
242  } else if (c.isDigit()) {
243  valueAbs = static_cast<UnsignedT>(c.digitValue());
244  state = State::INT_PART;
245  } else {
246  state = State::INVALID;
247  }
248  break;
249 
250  case State::AFTER_SIGN:
251  if (c == '.') {
252  state = State::LONELY_DOT;
253  } else if (c.isDigit()) {
254  valueAbs = static_cast<UnsignedT>(c.digitValue());
255  state = State::INT_PART;
256  } else {
257  state = State::INVALID;
258  }
259  break;
260 
261  case State::LONELY_DOT:
262  if (c.isDigit()) {
263  valueAbs = static_cast<UnsignedT>(c.digitValue());
264  expOffset -= 1;
265  state = State::FRAC_PART;
266  } else {
267  state = State::INVALID;
268  }
269  break;
270 
271  case State::INT_PART:
272  if (c == '.') {
273  state = State::FRAC_PART;
274  } else if (c == 'e' || c == 'E') {
275  state = State::EXP;
276  } else if (c.isDigit()) {
277  UnsignedT digit = static_cast<UnsignedT>(c.digitValue());
278  if (valueAbs > (max_u / 10)) {
279  // Would overflow
280  state = State::INVALID;
281  break;
282  }
283  valueAbs *= 10;
284  if (valueAbs > (max_u - digit)) {
285  // Would overflow
286  state = State::INVALID;
287  break;
288  }
289  valueAbs += digit;
290  } else {
291  state = State::INVALID;
292  }
293  break;
294 
295  case State::FRAC_PART:
296  if (c == 'e' || c == 'E') {
297  state = State::EXP;
298  } else if (c.isDigit()) {
299  UnsignedT digit = static_cast<UnsignedT>(c.digitValue());
300  if (valueAbs > (max_u / 10)) {
301  // Would overflow
302  state = State::INVALID;
303  break;
304  }
305  valueAbs *= 10;
306  if (valueAbs > (max_u - digit)) {
307  // Would overflow
308  state = State::INVALID;
309  break;
310  }
311  valueAbs += digit;
312  expOffset -= 1;
313  } else {
314  state = State::INVALID;
315  }
316  break;
317 
318  case State::EXP:
319  if (c == '-') {
320  expSign = true;
321  state = State::EXP_AFTER_SIGN;
322  } else if (c == '+') {
323  state = State::EXP_AFTER_SIGN;
324  } else if (c.isDigit()) {
325  exp = static_cast<quint32>(c.digitValue());
326  state = State::EXP_DIGITS;
327  } else {
328  state = State::INVALID;
329  }
330  break;
331 
332  case State::EXP_AFTER_SIGN:
333  if (c.isDigit()) {
334  exp = static_cast<quint32>(c.digitValue());
335  state = State::EXP_DIGITS;
336  } else {
337  state = State::INVALID;
338  }
339  break;
340 
341  case State::EXP_DIGITS:
342  if (c.isDigit()) {
343  quint32 digit = static_cast<quint32>(c.digitValue());
344  if (exp > (maxExp / 10)) {
345  // Would overflow
346  state = State::INVALID;
347  break;
348  }
349  exp *= 10;
350  if (exp > (maxExp - digit)) {
351  // Would overflow
352  state = State::INVALID;
353  break;
354  }
355  exp += digit;
356  } else {
357  state = State::INVALID;
358  }
359  }
360  }
361 
362  bool ok = true;
363  switch (state) {
364  case State::INVALID:
365  case State::START:
366  case State::AFTER_SIGN:
367  case State::LONELY_DOT:
368  case State::EXP:
369  case State::EXP_AFTER_SIGN:
370  ok = false;
371  break;
372 
373  case State::INT_PART:
374  case State::FRAC_PART:
375  case State::EXP_DIGITS:
376  break;
377  }
378 
379  if (ok) {
380  quint32 expOffsetAbs;
381  if (expOffset < 0) {
382  expOffsetAbs = -static_cast<quint32>(expOffset);
383  } else {
384  expOffsetAbs = static_cast<quint32>(expOffset);
385  }
386 
387  if (expSign == (expOffset < 0)) {
388  if (exp > (maxExp - expOffsetAbs)) {
389  // would overflow
390  ok = false;
391  } else {
392  exp += expOffsetAbs;
393  }
394  } else {
395  if (exp < expOffsetAbs) {
396  // would overflow
397  ok = false;
398  } else {
399  exp -= expOffsetAbs;
400  }
401  }
402  }
403 
404  T result = 0;
405  if (ok) {
406  // No need to apply exponent or sign if valueAbs is zero
407  if (valueAbs != 0) {
408  if (expSign) {
409  for (quint32 i = 0; i < exp; i++) {
410  if ((valueAbs % 10) != 0) {
411  // more decimal digits than allowed
412  ok = false;
413  break;
414  }
415  valueAbs /= 10;
416  }
417  } else {
418  for (quint32 i = 0; i < exp; i++) {
419  if (valueAbs > (max_u / 10)) {
420  // would overflow
421  ok = false;
422  break;
423  }
424  valueAbs *= 10;
425  }
426  }
427  if (ok) {
428  if (sign) {
429  if (valueAbs > static_cast<UnsignedT>(min)) {
430  ok = false;
431  } else {
432  result = static_cast<T>(-valueAbs);
433  }
434  } else {
435  if (valueAbs > static_cast<UnsignedT>(max)) {
436  ok = false;
437  } else {
438  result = static_cast<T>(valueAbs);
439  }
440  }
441  }
442  }
443  }
444 
445  if (!ok) {
446  throw RuntimeError(
447  __FILE__, __LINE__,
448  QString(tr("Invalid fixed point number string: \"%1\"")).arg(str));
449  }
450  return result;
451  }
452 };
453 
454 /*******************************************************************************
455  * End of File
456  ******************************************************************************/
457 
458 } // namespace librepcb
459 
460 #endif // LIBREPCB_TOOLBOX_H
The Toolbox class provides some useful general purpose methods.
Definition: toolbox.h:48
static QList< T > sortedQSet(const QSet< T > &set) noexcept
Definition: toolbox.h:62
static QVariant stringOrNumberToQVariant(const QString &string) noexcept
Convert a numeric or non-numeric string to the corresponding QVariant.
Definition: toolbox.cpp:128
static QString decimalFixedPointToString(T value, qint32 pointPos) noexcept
Convert a fixed point decimal number from an integer to a QString.
Definition: toolbox.h:158
static Length shortestDistanceBetweenPointAndLine(const Point &p, const Point &l1, const Point &l2, Point *nearest=nullptr) noexcept
Calculate the shortest distance between a given point and a given line.
Definition: toolbox.cpp:117
static QString cleanUserInputString(const QString &input, const QRegularExpression &removeRegex, bool trim=true, bool toLower=false, bool toUpper=false, const QString &spaceReplacement=" ", int maxLength=-1) noexcept
Clean a user input string.
Definition: toolbox.cpp:137
static T decimalFixedPointFromString(const QString &str, qint32 pointPos)
Convert a fixed point decimal number from a QString to an integer.
Definition: toolbox.h:197
The Angle class is used to represent an angle (for example 12.75 degrees)
Definition: angle.h:81
The Point class is used to represent a point/coordinate/vector, for example (1.2mm; 5...
Definition: point.h:81
static QRectF boundingRectFromRadius(qreal radius) noexcept
Definition: toolbox.h:68
static QRectF boundingRectFromRadius(qreal rx, qreal ry) noexcept
Definition: toolbox.h:72
The RuntimeError class.
Definition: exceptions.h:219
static Length arcRadius(const Point &p1, const Point &p2, const Angle &a) noexcept
Definition: toolbox.cpp:58
static QPainterPath shapeFromPath(const QPainterPath &path, const QPen &pen, const QBrush &brush, const UnsignedLength &minWidth=UnsignedLength(0)) noexcept
Definition: toolbox.cpp:36
The Length class is used to represent a length (for example 12.75 millimeters)
Definition: length.h:85
static QRectF adjustedBoundingRect(const QRectF &rect, qreal offset) noexcept
Definition: toolbox.h:76
static Point nearestPointOnLine(const Point &p, const Point &l1, const Point &l2) noexcept
Calculate the point on a given line which is nearest to a given point.
Definition: toolbox.cpp:98
Toolbox & operator=(const Toolbox &rhs)=delete
static Point arcCenter(const Point &p1, const Point &p2, const Angle &a) noexcept
Definition: toolbox.cpp:74
type_safe::constrained_type< Length, UnsignedLengthConstraint, UnsignedLengthVerifier > UnsignedLength
Definition: length.h:662