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_CORE_TOOLBOX_H
21 #define LIBREPCB_CORE_TOOLBOX_H
22 
23 /*******************************************************************************
24  * Includes
25  ******************************************************************************/
26 #include "../exceptions.h"
27 #include "../types/angle.h"
28 #include "../types/length.h"
29 #include "../types/point.h"
30 
31 #include <optional/tl/optional.hpp>
32 #include <type_traits>
33 
34 #include <QtCore>
35 #include <QtGui>
36 
37 #include <algorithm>
38 
39 /*******************************************************************************
40  * Namespace / Forward Declarations
41  ******************************************************************************/
42 namespace librepcb {
43 
44 /*******************************************************************************
45  * Class Toolbox
46  ******************************************************************************/
47 
51 class Toolbox final {
52  Q_DECLARE_TR_FUNCTIONS(Toolbox)
53 
54 public:
55  // Constructors / Destructor
56  Toolbox() = delete;
57  Toolbox(const Toolbox& other) = delete;
58  ~Toolbox() = delete;
59 
60  // Operator Overloadings
61  Toolbox& operator=(const Toolbox& rhs) = delete;
62 
63  // Static Methods
64 
77  template <typename T>
78  static inline QSet<T> toSet(const QList<T>& list) noexcept {
79 #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
80  return QSet<T>(list.begin(), list.end());
81 #else
82  return list.toSet();
83 #endif
84  }
85 
86 #if (QT_VERSION_MAJOR < 6)
87 
99  template <typename T>
100  static inline QSet<T> toSet(const QVector<T>& vector) noexcept {
101 #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
102  return QSet<T>(vector.begin(), vector.end());
103 #else
104  return vector.toList().toSet();
105 #endif
106  }
107 #endif
108 
121  template <typename T>
122  static inline QVector<T> toVector(const QSet<T>& set) noexcept {
123 #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
124  return QVector<T>(set.begin(), set.end());
125 #else
126  return set.toList().toVector();
127 #endif
128  }
129 
142  template <typename T>
143  static inline QList<T> toList(const QSet<T>& set) noexcept {
144 #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
145  return QList<T>(set.begin(), set.end());
146 #else
147  return set.toList();
148 #endif
149  }
150 
151  template <typename T>
152  static QList<T> sortedQSet(const QSet<T>& set) noexcept {
153  QList<T> list = set.values();
154  std::sort(list.begin(), list.end());
155  return list;
156  }
157 
158  template <typename T, typename Compare>
159  static QList<T> sortedQSet(const QSet<T>& set, const Compare& cmp) noexcept {
160  QList<T> list = set.values();
161  std::sort(list.begin(), list.end(), cmp);
162  return list;
163  }
164 
165  template <typename T>
166  static T sorted(const T& container) noexcept {
167  T copy(container);
168  std::sort(copy.begin(), copy.end());
169  return copy;
170  }
171 
182  template <typename T, typename Compare>
183  static void sortNumeric(
184  T& container, Compare compare,
185  Qt::CaseSensitivity caseSensitivity = Qt::CaseInsensitive,
186  bool ignorePunctuation = false) noexcept {
187  QCollator collator;
188  collator.setNumericMode(true);
189  collator.setCaseSensitivity(caseSensitivity);
190  collator.setIgnorePunctuation(ignorePunctuation);
191  std::sort(container.begin(), container.end(),
192  [&collator, &compare](const typename T::value_type& lhs,
193  const typename T::value_type& rhs) {
194  return compare(collator, lhs, rhs);
195  });
196  }
197 
205  template <typename T>
206  static void sortNumeric(
207  T& container, Qt::CaseSensitivity caseSensitivity = Qt::CaseInsensitive,
208  bool ignorePunctuation = false) noexcept {
209  return sortNumeric(
210  container,
211  [](const QCollator& collator, const typename T::value_type& lhs,
212  const typename T::value_type& rhs) { return collator(lhs, rhs); },
213  caseSensitivity, ignorePunctuation);
214  }
215 
228  static bool isTextUpsideDown(const Angle& rotation) noexcept;
229 
230  static QRectF boundingRectFromRadius(qreal radius) noexcept {
231  return QRectF(-radius, -radius, 2 * radius, 2 * radius);
232  }
233 
234  static QRectF boundingRectFromRadius(qreal rx, qreal ry) noexcept {
235  return QRectF(-rx, -ry, 2 * rx, 2 * ry);
236  }
237 
238  static QRectF adjustedBoundingRect(const QRectF& rect,
239  qreal offset) noexcept {
240  return rect.adjusted(-offset, -offset, offset, offset);
241  }
242 
243  static QPainterPath shapeFromPath(
244  const QPainterPath& path, const QPen& pen, const QBrush& brush,
245  const UnsignedLength& minWidth = UnsignedLength(0)) noexcept;
246 
247  static tl::optional<Length> arcRadius(const Point& p1, const Point& p2,
248  const Angle& angle) noexcept;
249  static tl::optional<Point> arcCenter(const Point& p1, const Point& p2,
250  const Angle& angle) noexcept;
251 
261  static Angle arcAngle(const Point& p1, const Point& p2,
262  const Point& center = Point(0, 0)) noexcept;
263 
272  static Angle angleBetweenPoints(const Point& p1, const Point& p2) noexcept;
273 
288  static Point nearestPointOnLine(const Point& p, const Point& l1,
289  const Point& l2) noexcept;
290 
303  const Point& p, const Point& l1, const Point& l2,
304  Point* nearest = nullptr) noexcept;
305 
319  static QString incrementNumberInString(QString string) noexcept;
320 
340  static QStringList expandRangesInString(const QString& string) noexcept;
341 
355  static QString cleanUserInputString(const QString& input,
356  const QRegularExpression& removeRegex,
357  bool trim = true, bool toLower = false,
358  bool toUpper = false,
359  const QString& spaceReplacement = " ",
360  int maxLength = -1) noexcept;
361 
375  static QString prettyPrintLocale(const QString& code) noexcept;
376 
383  template <typename T>
384  static QString floatToString(T value, int decimals,
385  const QLocale& locale) noexcept {
386  QString s = locale.toString(value, 'f', decimals);
387  for (int i = 1; (i < decimals) && s.endsWith(locale.zeroDigit()); ++i) {
388  s.chop(1);
389  }
390  if (qAbs(value) >= 1000) {
391  s.remove(locale.groupSeparator());
392  }
393  return s;
394  }
395 
402  template <typename T>
403  static QString decimalFixedPointToString(T value, qint32 pointPos) noexcept {
404  using UnsignedT = typename std::make_unsigned<T>::type;
405 
406  if (value == 0) {
407  // special case
408  return "0.0";
409  }
410 
411  UnsignedT valueAbs;
412  if (value < 0) {
413  valueAbs = -static_cast<UnsignedT>(value);
414  } else {
415  valueAbs = static_cast<UnsignedT>(value);
416  }
417 
418  QString str = QString::number(valueAbs);
419  if (str.length() > pointPos) {
420  // pointPos must be > 0 for this to work correctly
421  str.insert(str.length() - pointPos, '.');
422  } else {
423  for (qint32 i = pointPos - str.length(); i != 0; i--) str.insert(0, '0');
424  str.insert(0, "0.");
425  }
426 
427  while (str.endsWith('0') && !str.endsWith(".0")) str.chop(1);
428 
429  if (value < 0) str.insert(0, '-');
430 
431  return str;
432  }
433 
441  template <typename T>
442  static T decimalFixedPointFromString(const QString& str, qint32 pointPos) {
443  using UnsignedT = typename std::make_unsigned<T>::type;
444 
445  const T min = std::numeric_limits<T>::min();
446  const T max = std::numeric_limits<T>::max();
447  const UnsignedT max_u = std::numeric_limits<UnsignedT>::max();
448 
449  enum class State {
450  INVALID,
451  START,
452  AFTER_SIGN,
453  LONELY_DOT,
454  INT_PART,
455  FRAC_PART,
456  EXP,
457  EXP_AFTER_SIGN,
458  EXP_DIGITS,
459  };
460  State state = State::START;
461  UnsignedT valueAbs = 0;
462  bool sign = false;
463  qint32 expOffset = pointPos;
464 
465  const quint32 maxExp = std::numeric_limits<quint32>::max();
466  quint32 exp = 0;
467  bool expSign = false;
468 
469  for (QChar c : str) {
470  if (state == State::INVALID) {
471  // break the loop, not the switch
472  break;
473  }
474  switch (state) {
475  case State::INVALID:
476  // already checked, but needed to avoid compiler warnings
477  break;
478 
479  case State::START:
480  if (c == '-') {
481  sign = true;
482  state = State::AFTER_SIGN;
483  } else if (c == '+') {
484  state = State::AFTER_SIGN;
485  } else if (c == '.') {
486  state = State::LONELY_DOT;
487  } else if (c.isDigit()) {
488  valueAbs = static_cast<UnsignedT>(c.digitValue());
489  state = State::INT_PART;
490  } else {
491  state = State::INVALID;
492  }
493  break;
494 
495  case State::AFTER_SIGN:
496  if (c == '.') {
497  state = State::LONELY_DOT;
498  } else if (c.isDigit()) {
499  valueAbs = static_cast<UnsignedT>(c.digitValue());
500  state = State::INT_PART;
501  } else {
502  state = State::INVALID;
503  }
504  break;
505 
506  case State::LONELY_DOT:
507  if (c.isDigit()) {
508  valueAbs = static_cast<UnsignedT>(c.digitValue());
509  expOffset -= 1;
510  state = State::FRAC_PART;
511  } else {
512  state = State::INVALID;
513  }
514  break;
515 
516  case State::INT_PART:
517  if (c == '.') {
518  state = State::FRAC_PART;
519  } else if (c == 'e' || c == 'E') {
520  state = State::EXP;
521  } else if (c.isDigit()) {
522  UnsignedT digit = static_cast<UnsignedT>(c.digitValue());
523  if (valueAbs > (max_u / 10)) {
524  // Would overflow
525  state = State::INVALID;
526  break;
527  }
528  valueAbs *= 10;
529  if (valueAbs > (max_u - digit)) {
530  // Would overflow
531  state = State::INVALID;
532  break;
533  }
534  valueAbs += digit;
535  } else {
536  state = State::INVALID;
537  }
538  break;
539 
540  case State::FRAC_PART:
541  if (c == 'e' || c == 'E') {
542  state = State::EXP;
543  } else if (c.isDigit()) {
544  UnsignedT digit = static_cast<UnsignedT>(c.digitValue());
545  if (valueAbs > (max_u / 10)) {
546  // Would overflow
547  state = State::INVALID;
548  break;
549  }
550  valueAbs *= 10;
551  if (valueAbs > (max_u - digit)) {
552  // Would overflow
553  state = State::INVALID;
554  break;
555  }
556  valueAbs += digit;
557  expOffset -= 1;
558  } else {
559  state = State::INVALID;
560  }
561  break;
562 
563  case State::EXP:
564  if (c == '-') {
565  expSign = true;
566  state = State::EXP_AFTER_SIGN;
567  } else if (c == '+') {
568  state = State::EXP_AFTER_SIGN;
569  } else if (c.isDigit()) {
570  exp = static_cast<quint32>(c.digitValue());
571  state = State::EXP_DIGITS;
572  } else {
573  state = State::INVALID;
574  }
575  break;
576 
577  case State::EXP_AFTER_SIGN:
578  if (c.isDigit()) {
579  exp = static_cast<quint32>(c.digitValue());
580  state = State::EXP_DIGITS;
581  } else {
582  state = State::INVALID;
583  }
584  break;
585 
586  case State::EXP_DIGITS:
587  if (c.isDigit()) {
588  quint32 digit = static_cast<quint32>(c.digitValue());
589  if (exp > (maxExp / 10)) {
590  // Would overflow
591  state = State::INVALID;
592  break;
593  }
594  exp *= 10;
595  if (exp > (maxExp - digit)) {
596  // Would overflow
597  state = State::INVALID;
598  break;
599  }
600  exp += digit;
601  } else {
602  state = State::INVALID;
603  }
604  }
605  }
606 
607  bool ok = true;
608  switch (state) {
609  case State::INVALID:
610  case State::START:
611  case State::AFTER_SIGN:
612  case State::LONELY_DOT:
613  case State::EXP:
614  case State::EXP_AFTER_SIGN:
615  ok = false;
616  break;
617 
618  case State::INT_PART:
619  case State::FRAC_PART:
620  case State::EXP_DIGITS:
621  break;
622  }
623 
624  if (ok) {
625  quint32 expOffsetAbs;
626  if (expOffset < 0) {
627  expOffsetAbs = -static_cast<quint32>(expOffset);
628  } else {
629  expOffsetAbs = static_cast<quint32>(expOffset);
630  }
631 
632  if (expSign == (expOffset < 0)) {
633  if (exp > (maxExp - expOffsetAbs)) {
634  // would overflow
635  ok = false;
636  } else {
637  exp += expOffsetAbs;
638  }
639  } else {
640  if (exp < expOffsetAbs) {
641  // would overflow
642  ok = false;
643  } else {
644  exp -= expOffsetAbs;
645  }
646  }
647  }
648 
649  T result = 0;
650  if (ok) {
651  // No need to apply exponent or sign if valueAbs is zero
652  if (valueAbs != 0) {
653  if (expSign) {
654  for (quint32 i = 0; i < exp; i++) {
655  if ((valueAbs % 10) != 0) {
656  // more decimal digits than allowed
657  ok = false;
658  break;
659  }
660  valueAbs /= 10;
661  }
662  } else {
663  for (quint32 i = 0; i < exp; i++) {
664  if (valueAbs > (max_u / 10)) {
665  // would overflow
666  ok = false;
667  break;
668  }
669  valueAbs *= 10;
670  }
671  }
672  if (ok) {
673  if (sign) {
674  if (valueAbs > static_cast<UnsignedT>(min)) {
675  ok = false;
676  } else {
677  result = static_cast<T>(-valueAbs);
678  }
679  } else {
680  if (valueAbs > static_cast<UnsignedT>(max)) {
681  ok = false;
682  } else {
683  result = static_cast<T>(valueAbs);
684  }
685  }
686  }
687  }
688  }
689 
690  if (!ok) {
691  throw RuntimeError(
692  __FILE__, __LINE__,
693  tr("Invalid fixed point number string: \"%1\"").arg(str));
694  }
695  return result;
696  }
697 
698 private:
702  static QStringList expandRangesInString(
703  const QString& input,
704  const QVector<std::tuple<int, int, QStringList>>& replacements) noexcept;
705 };
706 
707 /*******************************************************************************
708  * End of File
709  ******************************************************************************/
710 
711 } // namespace librepcb
712 
713 #endif
The Toolbox class provides some useful general purpose methods.
Definition: toolbox.h:51
static QList< T > sortedQSet(const QSet< T > &set) noexcept
Definition: toolbox.h:152
static QSet< T > toSet(const QList< T > &list) noexcept
Helper method to convert a QList<T> to a QSet<T>
Definition: toolbox.h:78
Definition: occmodel.cpp:77
static QString decimalFixedPointToString(T value, qint32 pointPos) noexcept
Convert a fixed point decimal number from an integer to a QString.
Definition: toolbox.h:403
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:242
static T decimalFixedPointFromString(const QString &str, qint32 pointPos)
Convert a fixed point decimal number from a QString to an integer.
Definition: toolbox.h:442
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:265
static void sortNumeric(T &container, Qt::CaseSensitivity caseSensitivity=Qt::CaseInsensitive, bool ignorePunctuation=false) noexcept
Sort a container of strings using QCollators numeric mode.
Definition: toolbox.h:206
The Point class is used to represent a point/coordinate/vector, for example (1.2mm; 5...
Definition: point.h:79
static QString incrementNumberInString(QString string) noexcept
Copy a string while incrementing its contained number.
Definition: toolbox.cpp:161
static QRectF boundingRectFromRadius(qreal radius) noexcept
Definition: toolbox.h:230
static QRectF boundingRectFromRadius(qreal rx, qreal ry) noexcept
Definition: toolbox.h:234
static QSet< T > toSet(const QVector< T > &vector) noexcept
Helper method to convert a QVector<T> to a QSet<T>
Definition: toolbox.h:100
static tl::optional< Point > arcCenter(const Point &p1, const Point &p2, const Angle &angle) noexcept
Definition: toolbox.cpp:84
static QList< T > sortedQSet(const QSet< T > &set, const Compare &cmp) noexcept
Definition: toolbox.h:159
The RuntimeError class.
Definition: exceptions.h:216
static tl::optional< Length > arcRadius(const Point &p1, const Point &p2, const Angle &angle) noexcept
Definition: toolbox.cpp:64
static bool isTextUpsideDown(const Angle &rotation) noexcept
Check if a text with a given rotation is considered as upside down.
Definition: toolbox.cpp:36
static T sorted(const T &container) noexcept
Definition: toolbox.h:166
static QPainterPath shapeFromPath(const QPainterPath &path, const QPen &pen, const QBrush &brush, const UnsignedLength &minWidth=UnsignedLength(0)) noexcept
Definition: toolbox.cpp:41
static QVector< T > toVector(const QSet< T > &set) noexcept
Helper method to convert a QSet<T> to a QVector<T>
Definition: toolbox.h:122
static QList< T > toList(const QSet< T > &set) noexcept
Helper method to convert a QSet<T> to a QList<T>
Definition: toolbox.h:143
static QString floatToString(T value, int decimals, const QLocale &locale) noexcept
Convert a float or double to a localized string.
Definition: toolbox.h:384
static void sortNumeric(T &container, Compare compare, Qt::CaseSensitivity caseSensitivity=Qt::CaseInsensitive, bool ignorePunctuation=false) noexcept
Sort a container of arbitrary objects using QCollators numeric mode.
Definition: toolbox.h:183
static Angle arcAngle(const Point &p1, const Point &p2, const Point &center=Point(0, 0)) noexcept
Calculate the angle between two given points.
Definition: toolbox.cpp:112
static QRectF adjustedBoundingRect(const QRectF &rect, qreal offset) noexcept
Definition: toolbox.h:238
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:133
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:152
static Angle angleBetweenPoints(const Point &p1, const Point &p2) noexcept
Calculate the angle between two points.
Definition: toolbox.cpp:124
static QStringList expandRangesInString(const QString &string) noexcept
Expand ranges like "1..5" in a string to all its values.
Definition: toolbox.cpp:179
type_safe::constrained_type< Length, UnsignedLengthConstraint, UnsignedLengthVerifier > UnsignedLength
Definition: length.h:696