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 <type_traits>
32 
33 #include <QtCore>
34 #include <QtGui>
35 
36 #include <algorithm>
37 
38 /*******************************************************************************
39  * Namespace / Forward Declarations
40  ******************************************************************************/
41 namespace librepcb {
42 
43 /*******************************************************************************
44  * Class Toolbox
45  ******************************************************************************/
46 
50 class Toolbox final {
51  Q_DECLARE_TR_FUNCTIONS(Toolbox)
52 
53 public:
54  // Constructors / Destructor
55  Toolbox() = delete;
56  Toolbox(const Toolbox& other) = delete;
57  ~Toolbox() = delete;
58 
59  // Operator Overloadings
60  Toolbox& operator=(const Toolbox& rhs) = delete;
61 
62  // Static Methods
63 
76  template <typename T>
77  static QSet<T> toSet(const QList<T>& list) noexcept {
78 #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
79  return QSet<T>(list.begin(), list.end());
80 #else
81  return list.toSet();
82 #endif
83  }
84 
85  template <typename T>
86  static QList<T> sortedQSet(const QSet<T>& set) noexcept {
87  QList<T> list = set.values();
88  std::sort(list.begin(), list.end());
89  return list;
90  }
91 
92  template <typename T, typename Compare>
93  static QList<T> sortedQSet(const QSet<T>& set, const Compare& cmp) noexcept {
94  QList<T> list = set.values();
95  std::sort(list.begin(), list.end(), cmp);
96  return list;
97  }
98 
99  template <typename T>
100  static T sorted(const T& container) noexcept {
101  T copy(container);
102  std::sort(copy.begin(), copy.end());
103  return copy;
104  }
105 
116  template <typename T, typename Compare>
117  static void sortNumeric(
118  T& container, Compare compare,
119  Qt::CaseSensitivity caseSensitivity = Qt::CaseInsensitive,
120  bool ignorePunctuation = false) noexcept {
121  QCollator collator;
122  collator.setNumericMode(true);
123  collator.setCaseSensitivity(caseSensitivity);
124  collator.setIgnorePunctuation(ignorePunctuation);
125  std::sort(container.begin(), container.end(),
126  [&collator, &compare](const typename T::value_type& lhs,
127  const typename T::value_type& rhs) {
128  return compare(collator, lhs, rhs);
129  });
130  }
131 
139  template <typename T>
140  static void sortNumeric(
141  T& container, Qt::CaseSensitivity caseSensitivity = Qt::CaseInsensitive,
142  bool ignorePunctuation = false) noexcept {
143  return sortNumeric(
144  container,
145  [](const QCollator& collator, const typename T::value_type& lhs,
146  const typename T::value_type& rhs) { return collator(lhs, rhs); },
147  caseSensitivity, ignorePunctuation);
148  }
149 
162  static bool isTextUpsideDown(const Angle& rotation) noexcept;
163 
164  static QRectF boundingRectFromRadius(qreal radius) noexcept {
165  return QRectF(-radius, -radius, 2 * radius, 2 * radius);
166  }
167 
168  static QRectF boundingRectFromRadius(qreal rx, qreal ry) noexcept {
169  return QRectF(-rx, -ry, 2 * rx, 2 * ry);
170  }
171 
172  static QRectF adjustedBoundingRect(const QRectF& rect,
173  qreal offset) noexcept {
174  return rect.adjusted(-offset, -offset, offset, offset);
175  }
176 
177  static QPainterPath shapeFromPath(
178  const QPainterPath& path, const QPen& pen, const QBrush& brush,
179  const UnsignedLength& minWidth = UnsignedLength(0)) noexcept;
180 
181  static Length arcRadius(const Point& p1, const Point& p2,
182  const Angle& a) noexcept;
183  static Point arcCenter(const Point& p1, const Point& p2,
184  const Angle& a) noexcept;
185 
195  static Angle arcAngle(const Point& p1, const Point& p2,
196  const Point& center = Point(0, 0)) noexcept;
197 
206  static Angle angleBetweenPoints(const Point& p1, const Point& p2) noexcept;
207 
222  static Point nearestPointOnLine(const Point& p, const Point& l1,
223  const Point& l2) noexcept;
224 
237  const Point& p, const Point& l1, const Point& l2,
238  Point* nearest = nullptr) noexcept;
239 
253  static QString incrementNumberInString(QString string) noexcept;
254 
274  static QStringList expandRangesInString(const QString& string) noexcept;
275 
289  static QString cleanUserInputString(const QString& input,
290  const QRegularExpression& removeRegex,
291  bool trim = true, bool toLower = false,
292  bool toUpper = false,
293  const QString& spaceReplacement = " ",
294  int maxLength = -1) noexcept;
295 
309  static QString prettyPrintLocale(const QString& code) noexcept;
310 
317  template <typename T>
318  static QString floatToString(T value, int decimals,
319  const QLocale& locale) noexcept {
320  QString s = locale.toString(value, 'f', decimals);
321  for (int i = 1; (i < decimals) && s.endsWith(locale.zeroDigit()); ++i) {
322  s.chop(1);
323  }
324  if (qAbs(value) >= 1000) {
325  s.remove(locale.groupSeparator());
326  }
327  return s;
328  }
329 
336  template <typename T>
337  static QString decimalFixedPointToString(T value, qint32 pointPos) noexcept {
338  using UnsignedT = typename std::make_unsigned<T>::type;
339 
340  if (value == 0) {
341  // special case
342  return "0.0";
343  }
344 
345  UnsignedT valueAbs;
346  if (value < 0) {
347  valueAbs = -static_cast<UnsignedT>(value);
348  } else {
349  valueAbs = static_cast<UnsignedT>(value);
350  }
351 
352  QString str = QString::number(valueAbs);
353  if (str.length() > pointPos) {
354  // pointPos must be > 0 for this to work correctly
355  str.insert(str.length() - pointPos, '.');
356  } else {
357  for (qint32 i = pointPos - str.length(); i != 0; i--) str.insert(0, '0');
358  str.insert(0, "0.");
359  }
360 
361  while (str.endsWith('0') && !str.endsWith(".0")) str.chop(1);
362 
363  if (value < 0) str.insert(0, '-');
364 
365  return str;
366  }
367 
375  template <typename T>
376  static T decimalFixedPointFromString(const QString& str, qint32 pointPos) {
377  using UnsignedT = typename std::make_unsigned<T>::type;
378 
379  const T min = std::numeric_limits<T>::min();
380  const T max = std::numeric_limits<T>::max();
381  const UnsignedT max_u = std::numeric_limits<UnsignedT>::max();
382 
383  enum class State {
384  INVALID,
385  START,
386  AFTER_SIGN,
387  LONELY_DOT,
388  INT_PART,
389  FRAC_PART,
390  EXP,
391  EXP_AFTER_SIGN,
392  EXP_DIGITS,
393  };
394  State state = State::START;
395  UnsignedT valueAbs = 0;
396  bool sign = false;
397  qint32 expOffset = pointPos;
398 
399  const quint32 maxExp = std::numeric_limits<quint32>::max();
400  quint32 exp = 0;
401  bool expSign = false;
402 
403  for (QChar c : str) {
404  if (state == State::INVALID) {
405  // break the loop, not the switch
406  break;
407  }
408  switch (state) {
409  case State::INVALID:
410  // already checked, but needed to avoid compiler warnings
411  break;
412 
413  case State::START:
414  if (c == '-') {
415  sign = true;
416  state = State::AFTER_SIGN;
417  } else if (c == '+') {
418  state = State::AFTER_SIGN;
419  } else if (c == '.') {
420  state = State::LONELY_DOT;
421  } else if (c.isDigit()) {
422  valueAbs = static_cast<UnsignedT>(c.digitValue());
423  state = State::INT_PART;
424  } else {
425  state = State::INVALID;
426  }
427  break;
428 
429  case State::AFTER_SIGN:
430  if (c == '.') {
431  state = State::LONELY_DOT;
432  } else if (c.isDigit()) {
433  valueAbs = static_cast<UnsignedT>(c.digitValue());
434  state = State::INT_PART;
435  } else {
436  state = State::INVALID;
437  }
438  break;
439 
440  case State::LONELY_DOT:
441  if (c.isDigit()) {
442  valueAbs = static_cast<UnsignedT>(c.digitValue());
443  expOffset -= 1;
444  state = State::FRAC_PART;
445  } else {
446  state = State::INVALID;
447  }
448  break;
449 
450  case State::INT_PART:
451  if (c == '.') {
452  state = State::FRAC_PART;
453  } else if (c == 'e' || c == 'E') {
454  state = State::EXP;
455  } else if (c.isDigit()) {
456  UnsignedT digit = static_cast<UnsignedT>(c.digitValue());
457  if (valueAbs > (max_u / 10)) {
458  // Would overflow
459  state = State::INVALID;
460  break;
461  }
462  valueAbs *= 10;
463  if (valueAbs > (max_u - digit)) {
464  // Would overflow
465  state = State::INVALID;
466  break;
467  }
468  valueAbs += digit;
469  } else {
470  state = State::INVALID;
471  }
472  break;
473 
474  case State::FRAC_PART:
475  if (c == 'e' || c == 'E') {
476  state = State::EXP;
477  } else if (c.isDigit()) {
478  UnsignedT digit = static_cast<UnsignedT>(c.digitValue());
479  if (valueAbs > (max_u / 10)) {
480  // Would overflow
481  state = State::INVALID;
482  break;
483  }
484  valueAbs *= 10;
485  if (valueAbs > (max_u - digit)) {
486  // Would overflow
487  state = State::INVALID;
488  break;
489  }
490  valueAbs += digit;
491  expOffset -= 1;
492  } else {
493  state = State::INVALID;
494  }
495  break;
496 
497  case State::EXP:
498  if (c == '-') {
499  expSign = true;
500  state = State::EXP_AFTER_SIGN;
501  } else if (c == '+') {
502  state = State::EXP_AFTER_SIGN;
503  } else if (c.isDigit()) {
504  exp = static_cast<quint32>(c.digitValue());
505  state = State::EXP_DIGITS;
506  } else {
507  state = State::INVALID;
508  }
509  break;
510 
511  case State::EXP_AFTER_SIGN:
512  if (c.isDigit()) {
513  exp = static_cast<quint32>(c.digitValue());
514  state = State::EXP_DIGITS;
515  } else {
516  state = State::INVALID;
517  }
518  break;
519 
520  case State::EXP_DIGITS:
521  if (c.isDigit()) {
522  quint32 digit = static_cast<quint32>(c.digitValue());
523  if (exp > (maxExp / 10)) {
524  // Would overflow
525  state = State::INVALID;
526  break;
527  }
528  exp *= 10;
529  if (exp > (maxExp - digit)) {
530  // Would overflow
531  state = State::INVALID;
532  break;
533  }
534  exp += digit;
535  } else {
536  state = State::INVALID;
537  }
538  }
539  }
540 
541  bool ok = true;
542  switch (state) {
543  case State::INVALID:
544  case State::START:
545  case State::AFTER_SIGN:
546  case State::LONELY_DOT:
547  case State::EXP:
548  case State::EXP_AFTER_SIGN:
549  ok = false;
550  break;
551 
552  case State::INT_PART:
553  case State::FRAC_PART:
554  case State::EXP_DIGITS:
555  break;
556  }
557 
558  if (ok) {
559  quint32 expOffsetAbs;
560  if (expOffset < 0) {
561  expOffsetAbs = -static_cast<quint32>(expOffset);
562  } else {
563  expOffsetAbs = static_cast<quint32>(expOffset);
564  }
565 
566  if (expSign == (expOffset < 0)) {
567  if (exp > (maxExp - expOffsetAbs)) {
568  // would overflow
569  ok = false;
570  } else {
571  exp += expOffsetAbs;
572  }
573  } else {
574  if (exp < expOffsetAbs) {
575  // would overflow
576  ok = false;
577  } else {
578  exp -= expOffsetAbs;
579  }
580  }
581  }
582 
583  T result = 0;
584  if (ok) {
585  // No need to apply exponent or sign if valueAbs is zero
586  if (valueAbs != 0) {
587  if (expSign) {
588  for (quint32 i = 0; i < exp; i++) {
589  if ((valueAbs % 10) != 0) {
590  // more decimal digits than allowed
591  ok = false;
592  break;
593  }
594  valueAbs /= 10;
595  }
596  } else {
597  for (quint32 i = 0; i < exp; i++) {
598  if (valueAbs > (max_u / 10)) {
599  // would overflow
600  ok = false;
601  break;
602  }
603  valueAbs *= 10;
604  }
605  }
606  if (ok) {
607  if (sign) {
608  if (valueAbs > static_cast<UnsignedT>(min)) {
609  ok = false;
610  } else {
611  result = static_cast<T>(-valueAbs);
612  }
613  } else {
614  if (valueAbs > static_cast<UnsignedT>(max)) {
615  ok = false;
616  } else {
617  result = static_cast<T>(valueAbs);
618  }
619  }
620  }
621  }
622  }
623 
624  if (!ok) {
625  throw RuntimeError(
626  __FILE__, __LINE__,
627  tr("Invalid fixed point number string: \"%1\"").arg(str));
628  }
629  return result;
630  }
631 
632 private:
636  static QStringList expandRangesInString(
637  const QString& input,
638  const QVector<std::tuple<int, int, QStringList>>& replacements) noexcept;
639 };
640 
641 /*******************************************************************************
642  * End of File
643  ******************************************************************************/
644 
645 } // namespace librepcb
646 
647 #endif
The Toolbox class provides some useful general purpose methods.
Definition: toolbox.h:50
static QList< T > sortedQSet(const QSet< T > &set) noexcept
Definition: toolbox.h:86
static QSet< T > toSet(const QList< T > &list) noexcept
Helper method to convert a QList<T> to a QSet<T>
Definition: toolbox.h:77
Definition: occmodel.cpp:76
static QString decimalFixedPointToString(T value, qint32 pointPos) noexcept
Convert a fixed point decimal number from an integer to a QString.
Definition: toolbox.h:337
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:235
static T decimalFixedPointFromString(const QString &str, qint32 pointPos)
Convert a fixed point decimal number from a QString to an integer.
Definition: toolbox.h:376
The Angle class is used to represent an angle (for example 12.75 degrees)
Definition: angle.h:76
static QString prettyPrintLocale(const QString &code) noexcept
Pretty print the name of a QLocale.
Definition: toolbox.cpp:258
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:140
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:154
static QRectF boundingRectFromRadius(qreal radius) noexcept
Definition: toolbox.h:164
static QRectF boundingRectFromRadius(qreal rx, qreal ry) noexcept
Definition: toolbox.h:168
static QList< T > sortedQSet(const QSet< T > &set, const Compare &cmp) noexcept
Definition: toolbox.h:93
The RuntimeError class.
Definition: exceptions.h:216
static Length arcRadius(const Point &p1, const Point &p2, const Angle &a) 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:100
static QPainterPath shapeFromPath(const QPainterPath &path, const QPen &pen, const QBrush &brush, const UnsignedLength &minWidth=UnsignedLength(0)) noexcept
Definition: toolbox.cpp:41
static QString floatToString(T value, int decimals, const QLocale &locale) noexcept
Convert a float or double to a localized string.
Definition: toolbox.h:318
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:117
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:105
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:172
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:126
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:145
static Angle angleBetweenPoints(const Point &p1, const Point &p2) noexcept
Calculate the angle between two points.
Definition: toolbox.cpp:117
static QStringList expandRangesInString(const QString &string) noexcept
Expand ranges like "1..5" in a string to all its values.
Definition: toolbox.cpp:172
static Point arcCenter(const Point &p1, const Point &p2, const Angle &a) noexcept
Definition: toolbox.cpp:80
type_safe::constrained_type< Length, UnsignedLengthConstraint, UnsignedLengthVerifier > UnsignedLength
Definition: length.h:670