LibrePCB Developers Documentation
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 #include <algorithm>
34 
35 /*******************************************************************************
36  * Namespace / Forward Declarations
37  ******************************************************************************/
38 namespace librepcb {
39 
40 /*******************************************************************************
41  * Class Toolbox
42  ******************************************************************************/
43 
47 class Toolbox final {
48  Q_DECLARE_TR_FUNCTIONS(Toolbox)
49 
50 public:
51  // Constructors / Destructor
52  Toolbox() = delete;
53  Toolbox(const Toolbox& other) = delete;
54  ~Toolbox() = delete;
55 
56  // Operator Overloadings
57  Toolbox& operator=(const Toolbox& rhs) = delete;
58 
59  // Static Methods
60 
73  template <typename T>
74  static QSet<T> toSet(const QList<T>& list) noexcept {
75 #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
76  return QSet<T>(list.begin(), list.end());
77 #else
78  return list.toSet();
79 #endif
80  }
81 
82  template <typename T>
83  static QList<T> sortedQSet(const QSet<T>& set) noexcept {
84  QList<T> list = set.values();
85  std::sort(list.begin(), list.end());
86  return list;
87  }
88 
89  template <typename T>
90  static T sorted(const T& container) noexcept {
91  T copy(container);
92  std::sort(copy.begin(), copy.end());
93  return copy;
94  }
95 
96  static QRectF boundingRectFromRadius(qreal radius) noexcept {
97  return QRectF(-radius, -radius, 2 * radius, 2 * radius);
98  }
99 
100  static QRectF boundingRectFromRadius(qreal rx, qreal ry) noexcept {
101  return QRectF(-rx, -ry, 2 * rx, 2 * ry);
102  }
103 
104  static QRectF adjustedBoundingRect(const QRectF& rect,
105  qreal offset) noexcept {
106  return rect.adjusted(-offset, -offset, offset, offset);
107  }
108 
109  static QPainterPath shapeFromPath(
110  const QPainterPath& path, const QPen& pen, const QBrush& brush,
111  const UnsignedLength& minWidth = UnsignedLength(0)) noexcept;
112 
113  static Length arcRadius(const Point& p1, const Point& p2,
114  const Angle& a) noexcept;
115  static Point arcCenter(const Point& p1, const Point& p2,
116  const Angle& a) noexcept;
117 
132  static Point nearestPointOnLine(const Point& p, const Point& l1,
133  const Point& l2) noexcept;
134 
147  const Point& p, const Point& l1, const Point& l2,
148  Point* nearest = nullptr) noexcept;
149 
163  static QString incrementNumberInString(QString string) noexcept;
164 
184  static QStringList expandRangesInString(const QString& string) noexcept;
185 
199  static QString cleanUserInputString(const QString& input,
200  const QRegularExpression& removeRegex,
201  bool trim = true, bool toLower = false,
202  bool toUpper = false,
203  const QString& spaceReplacement = " ",
204  int maxLength = -1) noexcept;
205 
219  static QString prettyPrintLocale(const QString& code) noexcept;
220 
227  template <typename T>
228  static QString floatToString(T value, int decimals,
229  const QLocale& locale) noexcept {
230  QString s = locale.toString(value, 'f', decimals);
231  for (int i = 1; (i < decimals) && s.endsWith(locale.zeroDigit()); ++i) {
232  s.chop(1);
233  }
234  if (qAbs(value) >= 1000) {
235  s.remove(locale.groupSeparator());
236  }
237  return s;
238  }
239 
246  template <typename T>
247  static QString decimalFixedPointToString(T value, qint32 pointPos) noexcept {
248  using UnsignedT = typename std::make_unsigned<T>::type;
249 
250  if (value == 0) {
251  // special case
252  return "0.0";
253  }
254 
255  UnsignedT valueAbs;
256  if (value < 0) {
257  valueAbs = -static_cast<UnsignedT>(value);
258  } else {
259  valueAbs = static_cast<UnsignedT>(value);
260  }
261 
262  QString str = QString::number(valueAbs);
263  if (str.length() > pointPos) {
264  // pointPos must be > 0 for this to work correctly
265  str.insert(str.length() - pointPos, '.');
266  } else {
267  for (qint32 i = pointPos - str.length(); i != 0; i--) str.insert(0, '0');
268  str.insert(0, "0.");
269  }
270 
271  while (str.endsWith('0') && !str.endsWith(".0")) str.chop(1);
272 
273  if (value < 0) str.insert(0, '-');
274 
275  return str;
276  }
277 
285  template <typename T>
286  static T decimalFixedPointFromString(const QString& str, qint32 pointPos) {
287  using UnsignedT = typename std::make_unsigned<T>::type;
288 
289  const T min = std::numeric_limits<T>::min();
290  const T max = std::numeric_limits<T>::max();
291  const UnsignedT max_u = std::numeric_limits<UnsignedT>::max();
292 
293  enum class State {
294  INVALID,
295  START,
296  AFTER_SIGN,
297  LONELY_DOT,
298  INT_PART,
299  FRAC_PART,
300  EXP,
301  EXP_AFTER_SIGN,
302  EXP_DIGITS,
303  };
304  State state = State::START;
305  UnsignedT valueAbs = 0;
306  bool sign = false;
307  qint32 expOffset = pointPos;
308 
309  const quint32 maxExp = std::numeric_limits<quint32>::max();
310  quint32 exp = 0;
311  bool expSign = false;
312 
313  for (QChar c : str) {
314  if (state == State::INVALID) {
315  // break the loop, not the switch
316  break;
317  }
318  switch (state) {
319  case State::INVALID:
320  // already checked, but needed to avoid compiler warnings
321  break;
322 
323  case State::START:
324  if (c == '-') {
325  sign = true;
326  state = State::AFTER_SIGN;
327  } else if (c == '+') {
328  state = State::AFTER_SIGN;
329  } else if (c == '.') {
330  state = State::LONELY_DOT;
331  } else if (c.isDigit()) {
332  valueAbs = static_cast<UnsignedT>(c.digitValue());
333  state = State::INT_PART;
334  } else {
335  state = State::INVALID;
336  }
337  break;
338 
339  case State::AFTER_SIGN:
340  if (c == '.') {
341  state = State::LONELY_DOT;
342  } else if (c.isDigit()) {
343  valueAbs = static_cast<UnsignedT>(c.digitValue());
344  state = State::INT_PART;
345  } else {
346  state = State::INVALID;
347  }
348  break;
349 
350  case State::LONELY_DOT:
351  if (c.isDigit()) {
352  valueAbs = static_cast<UnsignedT>(c.digitValue());
353  expOffset -= 1;
354  state = State::FRAC_PART;
355  } else {
356  state = State::INVALID;
357  }
358  break;
359 
360  case State::INT_PART:
361  if (c == '.') {
362  state = State::FRAC_PART;
363  } else if (c == 'e' || c == 'E') {
364  state = State::EXP;
365  } else if (c.isDigit()) {
366  UnsignedT digit = static_cast<UnsignedT>(c.digitValue());
367  if (valueAbs > (max_u / 10)) {
368  // Would overflow
369  state = State::INVALID;
370  break;
371  }
372  valueAbs *= 10;
373  if (valueAbs > (max_u - digit)) {
374  // Would overflow
375  state = State::INVALID;
376  break;
377  }
378  valueAbs += digit;
379  } else {
380  state = State::INVALID;
381  }
382  break;
383 
384  case State::FRAC_PART:
385  if (c == 'e' || c == 'E') {
386  state = State::EXP;
387  } else if (c.isDigit()) {
388  UnsignedT digit = static_cast<UnsignedT>(c.digitValue());
389  if (valueAbs > (max_u / 10)) {
390  // Would overflow
391  state = State::INVALID;
392  break;
393  }
394  valueAbs *= 10;
395  if (valueAbs > (max_u - digit)) {
396  // Would overflow
397  state = State::INVALID;
398  break;
399  }
400  valueAbs += digit;
401  expOffset -= 1;
402  } else {
403  state = State::INVALID;
404  }
405  break;
406 
407  case State::EXP:
408  if (c == '-') {
409  expSign = true;
410  state = State::EXP_AFTER_SIGN;
411  } else if (c == '+') {
412  state = State::EXP_AFTER_SIGN;
413  } else if (c.isDigit()) {
414  exp = static_cast<quint32>(c.digitValue());
415  state = State::EXP_DIGITS;
416  } else {
417  state = State::INVALID;
418  }
419  break;
420 
421  case State::EXP_AFTER_SIGN:
422  if (c.isDigit()) {
423  exp = static_cast<quint32>(c.digitValue());
424  state = State::EXP_DIGITS;
425  } else {
426  state = State::INVALID;
427  }
428  break;
429 
430  case State::EXP_DIGITS:
431  if (c.isDigit()) {
432  quint32 digit = static_cast<quint32>(c.digitValue());
433  if (exp > (maxExp / 10)) {
434  // Would overflow
435  state = State::INVALID;
436  break;
437  }
438  exp *= 10;
439  if (exp > (maxExp - digit)) {
440  // Would overflow
441  state = State::INVALID;
442  break;
443  }
444  exp += digit;
445  } else {
446  state = State::INVALID;
447  }
448  }
449  }
450 
451  bool ok = true;
452  switch (state) {
453  case State::INVALID:
454  case State::START:
455  case State::AFTER_SIGN:
456  case State::LONELY_DOT:
457  case State::EXP:
458  case State::EXP_AFTER_SIGN:
459  ok = false;
460  break;
461 
462  case State::INT_PART:
463  case State::FRAC_PART:
464  case State::EXP_DIGITS:
465  break;
466  }
467 
468  if (ok) {
469  quint32 expOffsetAbs;
470  if (expOffset < 0) {
471  expOffsetAbs = -static_cast<quint32>(expOffset);
472  } else {
473  expOffsetAbs = static_cast<quint32>(expOffset);
474  }
475 
476  if (expSign == (expOffset < 0)) {
477  if (exp > (maxExp - expOffsetAbs)) {
478  // would overflow
479  ok = false;
480  } else {
481  exp += expOffsetAbs;
482  }
483  } else {
484  if (exp < expOffsetAbs) {
485  // would overflow
486  ok = false;
487  } else {
488  exp -= expOffsetAbs;
489  }
490  }
491  }
492 
493  T result = 0;
494  if (ok) {
495  // No need to apply exponent or sign if valueAbs is zero
496  if (valueAbs != 0) {
497  if (expSign) {
498  for (quint32 i = 0; i < exp; i++) {
499  if ((valueAbs % 10) != 0) {
500  // more decimal digits than allowed
501  ok = false;
502  break;
503  }
504  valueAbs /= 10;
505  }
506  } else {
507  for (quint32 i = 0; i < exp; i++) {
508  if (valueAbs > (max_u / 10)) {
509  // would overflow
510  ok = false;
511  break;
512  }
513  valueAbs *= 10;
514  }
515  }
516  if (ok) {
517  if (sign) {
518  if (valueAbs > static_cast<UnsignedT>(min)) {
519  ok = false;
520  } else {
521  result = static_cast<T>(-valueAbs);
522  }
523  } else {
524  if (valueAbs > static_cast<UnsignedT>(max)) {
525  ok = false;
526  } else {
527  result = static_cast<T>(valueAbs);
528  }
529  }
530  }
531  }
532  }
533 
534  if (!ok) {
535  throw RuntimeError(
536  __FILE__, __LINE__,
537  QString(tr("Invalid fixed point number string: \"%1\"")).arg(str));
538  }
539  return result;
540  }
541 
542 private:
546  static QStringList expandRangesInString(
547  const QString& input,
548  const QVector<std::tuple<int, int, QStringList>>& replacements) noexcept;
549 };
550 
551 /*******************************************************************************
552  * End of File
553  ******************************************************************************/
554 
555 } // namespace librepcb
556 
557 #endif // LIBREPCB_TOOLBOX_H
The Toolbox class provides some useful general purpose methods.
Definition: toolbox.h:47
static QList< T > sortedQSet(const QSet< T > &set) noexcept
Definition: toolbox.h:83
static QSet< T > toSet(const QList< T > &list) noexcept
Helper method to convert a QList<T> to a QSet<T>
Definition: toolbox.h:74
Definition: alignment.cpp:30
static QString decimalFixedPointToString(T value, qint32 pointPos) noexcept
Convert a fixed point decimal number from an integer to a QString.
Definition: toolbox.h:247
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:208
static T decimalFixedPointFromString(const QString &str, qint32 pointPos)
Convert a fixed point decimal number from a QString to an integer.
Definition: toolbox.h:286
The Angle class is used to represent an angle (for example 12.75 degrees)
Definition: angle.h:78
static QString prettyPrintLocale(const QString &code) noexcept
Pretty print the name of a QLocale.
Definition: toolbox.cpp:231
The Point class is used to represent a point/coordinate/vector, for example (1.2mm; 5...
Definition: point.h:78
static QString incrementNumberInString(QString string) noexcept
Copy a string while incrementing its contained number.
Definition: toolbox.cpp:127
static QRectF boundingRectFromRadius(qreal radius) noexcept
Definition: toolbox.h:96
static QRectF boundingRectFromRadius(qreal rx, qreal ry) noexcept
Definition: toolbox.h:100
The RuntimeError class.
Definition: exceptions.h:219
static Length arcRadius(const Point &p1, const Point &p2, const Angle &a) noexcept
Definition: toolbox.cpp:59
static T sorted(const T &container) noexcept
Definition: toolbox.h:90
static QPainterPath shapeFromPath(const QPainterPath &path, const QPen &pen, const QBrush &brush, const UnsignedLength &minWidth=UnsignedLength(0)) noexcept
Definition: toolbox.cpp:36
static QString floatToString(T value, int decimals, const QLocale &locale) noexcept
Convert a float or double to a localized string.
Definition: toolbox.h:228
The Length class is used to represent a length (for example 12.75 millimeters)
Definition: length.h:82
static QRectF adjustedBoundingRect(const QRectF &rect, qreal offset) noexcept
Definition: toolbox.h:104
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:99
Toolbox & operator=(const Toolbox &rhs)=delete
static UnsignedLength 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:118
static QStringList expandRangesInString(const QString &string) noexcept
Expand ranges like "1..5" in a string to all its values.
Definition: toolbox.cpp:145
static Point arcCenter(const Point &p1, const Point &p2, const Angle &a) noexcept
Definition: toolbox.cpp:75
type_safe::constrained_type< Length, UnsignedLengthConstraint, UnsignedLengthVerifier > UnsignedLength
Definition: length.h:659