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>
93  static T sorted(const T& container) noexcept {
94  T copy(container);
95  std::sort(copy.begin(), copy.end());
96  return copy;
97  }
98 
109  template <typename T, typename Compare>
110  static void sortNumeric(
111  T& container, Compare compare,
112  Qt::CaseSensitivity caseSensitivity = Qt::CaseInsensitive,
113  bool ignorePunctuation = false) noexcept {
114  QCollator collator;
115  collator.setNumericMode(true);
116  collator.setCaseSensitivity(caseSensitivity);
117  collator.setIgnorePunctuation(ignorePunctuation);
118  std::sort(container.begin(), container.end(),
119  [&collator, &compare](const typename T::value_type& lhs,
120  const typename T::value_type& rhs) {
121  return compare(collator, lhs, rhs);
122  });
123  }
124 
132  template <typename T>
133  static void sortNumeric(
134  T& container, Qt::CaseSensitivity caseSensitivity = Qt::CaseInsensitive,
135  bool ignorePunctuation = false) noexcept {
136  return sortNumeric(
137  container,
138  [](const QCollator& collator, const typename T::value_type& lhs,
139  const typename T::value_type& rhs) { return collator(lhs, rhs); },
140  caseSensitivity, ignorePunctuation);
141  }
142 
155  static bool isTextUpsideDown(const Angle& rotation) noexcept;
156 
157  static QRectF boundingRectFromRadius(qreal radius) noexcept {
158  return QRectF(-radius, -radius, 2 * radius, 2 * radius);
159  }
160 
161  static QRectF boundingRectFromRadius(qreal rx, qreal ry) noexcept {
162  return QRectF(-rx, -ry, 2 * rx, 2 * ry);
163  }
164 
165  static QRectF adjustedBoundingRect(const QRectF& rect,
166  qreal offset) noexcept {
167  return rect.adjusted(-offset, -offset, offset, offset);
168  }
169 
170  static QPainterPath shapeFromPath(
171  const QPainterPath& path, const QPen& pen, const QBrush& brush,
172  const UnsignedLength& minWidth = UnsignedLength(0)) noexcept;
173 
174  static Length arcRadius(const Point& p1, const Point& p2,
175  const Angle& a) noexcept;
176  static Point arcCenter(const Point& p1, const Point& p2,
177  const Angle& a) noexcept;
178 
188  static Angle arcAngle(const Point& p1, const Point& p2,
189  const Point& center = Point(0, 0)) noexcept;
190 
199  static Angle angleBetweenPoints(const Point& p1, const Point& p2) noexcept;
200 
215  static Point nearestPointOnLine(const Point& p, const Point& l1,
216  const Point& l2) noexcept;
217 
230  const Point& p, const Point& l1, const Point& l2,
231  Point* nearest = nullptr) noexcept;
232 
246  static QString incrementNumberInString(QString string) noexcept;
247 
267  static QStringList expandRangesInString(const QString& string) noexcept;
268 
282  static QString cleanUserInputString(const QString& input,
283  const QRegularExpression& removeRegex,
284  bool trim = true, bool toLower = false,
285  bool toUpper = false,
286  const QString& spaceReplacement = " ",
287  int maxLength = -1) noexcept;
288 
302  static QString prettyPrintLocale(const QString& code) noexcept;
303 
310  template <typename T>
311  static QString floatToString(T value, int decimals,
312  const QLocale& locale) noexcept {
313  QString s = locale.toString(value, 'f', decimals);
314  for (int i = 1; (i < decimals) && s.endsWith(locale.zeroDigit()); ++i) {
315  s.chop(1);
316  }
317  if (qAbs(value) >= 1000) {
318  s.remove(locale.groupSeparator());
319  }
320  return s;
321  }
322 
329  template <typename T>
330  static QString decimalFixedPointToString(T value, qint32 pointPos) noexcept {
331  using UnsignedT = typename std::make_unsigned<T>::type;
332 
333  if (value == 0) {
334  // special case
335  return "0.0";
336  }
337 
338  UnsignedT valueAbs;
339  if (value < 0) {
340  valueAbs = -static_cast<UnsignedT>(value);
341  } else {
342  valueAbs = static_cast<UnsignedT>(value);
343  }
344 
345  QString str = QString::number(valueAbs);
346  if (str.length() > pointPos) {
347  // pointPos must be > 0 for this to work correctly
348  str.insert(str.length() - pointPos, '.');
349  } else {
350  for (qint32 i = pointPos - str.length(); i != 0; i--) str.insert(0, '0');
351  str.insert(0, "0.");
352  }
353 
354  while (str.endsWith('0') && !str.endsWith(".0")) str.chop(1);
355 
356  if (value < 0) str.insert(0, '-');
357 
358  return str;
359  }
360 
368  template <typename T>
369  static T decimalFixedPointFromString(const QString& str, qint32 pointPos) {
370  using UnsignedT = typename std::make_unsigned<T>::type;
371 
372  const T min = std::numeric_limits<T>::min();
373  const T max = std::numeric_limits<T>::max();
374  const UnsignedT max_u = std::numeric_limits<UnsignedT>::max();
375 
376  enum class State {
377  INVALID,
378  START,
379  AFTER_SIGN,
380  LONELY_DOT,
381  INT_PART,
382  FRAC_PART,
383  EXP,
384  EXP_AFTER_SIGN,
385  EXP_DIGITS,
386  };
387  State state = State::START;
388  UnsignedT valueAbs = 0;
389  bool sign = false;
390  qint32 expOffset = pointPos;
391 
392  const quint32 maxExp = std::numeric_limits<quint32>::max();
393  quint32 exp = 0;
394  bool expSign = false;
395 
396  for (QChar c : str) {
397  if (state == State::INVALID) {
398  // break the loop, not the switch
399  break;
400  }
401  switch (state) {
402  case State::INVALID:
403  // already checked, but needed to avoid compiler warnings
404  break;
405 
406  case State::START:
407  if (c == '-') {
408  sign = true;
409  state = State::AFTER_SIGN;
410  } else if (c == '+') {
411  state = State::AFTER_SIGN;
412  } else if (c == '.') {
413  state = State::LONELY_DOT;
414  } else if (c.isDigit()) {
415  valueAbs = static_cast<UnsignedT>(c.digitValue());
416  state = State::INT_PART;
417  } else {
418  state = State::INVALID;
419  }
420  break;
421 
422  case State::AFTER_SIGN:
423  if (c == '.') {
424  state = State::LONELY_DOT;
425  } else if (c.isDigit()) {
426  valueAbs = static_cast<UnsignedT>(c.digitValue());
427  state = State::INT_PART;
428  } else {
429  state = State::INVALID;
430  }
431  break;
432 
433  case State::LONELY_DOT:
434  if (c.isDigit()) {
435  valueAbs = static_cast<UnsignedT>(c.digitValue());
436  expOffset -= 1;
437  state = State::FRAC_PART;
438  } else {
439  state = State::INVALID;
440  }
441  break;
442 
443  case State::INT_PART:
444  if (c == '.') {
445  state = State::FRAC_PART;
446  } else if (c == 'e' || c == 'E') {
447  state = State::EXP;
448  } else if (c.isDigit()) {
449  UnsignedT digit = static_cast<UnsignedT>(c.digitValue());
450  if (valueAbs > (max_u / 10)) {
451  // Would overflow
452  state = State::INVALID;
453  break;
454  }
455  valueAbs *= 10;
456  if (valueAbs > (max_u - digit)) {
457  // Would overflow
458  state = State::INVALID;
459  break;
460  }
461  valueAbs += digit;
462  } else {
463  state = State::INVALID;
464  }
465  break;
466 
467  case State::FRAC_PART:
468  if (c == 'e' || c == 'E') {
469  state = State::EXP;
470  } else if (c.isDigit()) {
471  UnsignedT digit = static_cast<UnsignedT>(c.digitValue());
472  if (valueAbs > (max_u / 10)) {
473  // Would overflow
474  state = State::INVALID;
475  break;
476  }
477  valueAbs *= 10;
478  if (valueAbs > (max_u - digit)) {
479  // Would overflow
480  state = State::INVALID;
481  break;
482  }
483  valueAbs += digit;
484  expOffset -= 1;
485  } else {
486  state = State::INVALID;
487  }
488  break;
489 
490  case State::EXP:
491  if (c == '-') {
492  expSign = true;
493  state = State::EXP_AFTER_SIGN;
494  } else if (c == '+') {
495  state = State::EXP_AFTER_SIGN;
496  } else if (c.isDigit()) {
497  exp = static_cast<quint32>(c.digitValue());
498  state = State::EXP_DIGITS;
499  } else {
500  state = State::INVALID;
501  }
502  break;
503 
504  case State::EXP_AFTER_SIGN:
505  if (c.isDigit()) {
506  exp = static_cast<quint32>(c.digitValue());
507  state = State::EXP_DIGITS;
508  } else {
509  state = State::INVALID;
510  }
511  break;
512 
513  case State::EXP_DIGITS:
514  if (c.isDigit()) {
515  quint32 digit = static_cast<quint32>(c.digitValue());
516  if (exp > (maxExp / 10)) {
517  // Would overflow
518  state = State::INVALID;
519  break;
520  }
521  exp *= 10;
522  if (exp > (maxExp - digit)) {
523  // Would overflow
524  state = State::INVALID;
525  break;
526  }
527  exp += digit;
528  } else {
529  state = State::INVALID;
530  }
531  }
532  }
533 
534  bool ok = true;
535  switch (state) {
536  case State::INVALID:
537  case State::START:
538  case State::AFTER_SIGN:
539  case State::LONELY_DOT:
540  case State::EXP:
541  case State::EXP_AFTER_SIGN:
542  ok = false;
543  break;
544 
545  case State::INT_PART:
546  case State::FRAC_PART:
547  case State::EXP_DIGITS:
548  break;
549  }
550 
551  if (ok) {
552  quint32 expOffsetAbs;
553  if (expOffset < 0) {
554  expOffsetAbs = -static_cast<quint32>(expOffset);
555  } else {
556  expOffsetAbs = static_cast<quint32>(expOffset);
557  }
558 
559  if (expSign == (expOffset < 0)) {
560  if (exp > (maxExp - expOffsetAbs)) {
561  // would overflow
562  ok = false;
563  } else {
564  exp += expOffsetAbs;
565  }
566  } else {
567  if (exp < expOffsetAbs) {
568  // would overflow
569  ok = false;
570  } else {
571  exp -= expOffsetAbs;
572  }
573  }
574  }
575 
576  T result = 0;
577  if (ok) {
578  // No need to apply exponent or sign if valueAbs is zero
579  if (valueAbs != 0) {
580  if (expSign) {
581  for (quint32 i = 0; i < exp; i++) {
582  if ((valueAbs % 10) != 0) {
583  // more decimal digits than allowed
584  ok = false;
585  break;
586  }
587  valueAbs /= 10;
588  }
589  } else {
590  for (quint32 i = 0; i < exp; i++) {
591  if (valueAbs > (max_u / 10)) {
592  // would overflow
593  ok = false;
594  break;
595  }
596  valueAbs *= 10;
597  }
598  }
599  if (ok) {
600  if (sign) {
601  if (valueAbs > static_cast<UnsignedT>(min)) {
602  ok = false;
603  } else {
604  result = static_cast<T>(-valueAbs);
605  }
606  } else {
607  if (valueAbs > static_cast<UnsignedT>(max)) {
608  ok = false;
609  } else {
610  result = static_cast<T>(valueAbs);
611  }
612  }
613  }
614  }
615  }
616 
617  if (!ok) {
618  throw RuntimeError(
619  __FILE__, __LINE__,
620  tr("Invalid fixed point number string: \"%1\"").arg(str));
621  }
622  return result;
623  }
624 
625 private:
629  static QStringList expandRangesInString(
630  const QString& input,
631  const QVector<std::tuple<int, int, QStringList>>& replacements) noexcept;
632 };
633 
634 /*******************************************************************************
635  * End of File
636  ******************************************************************************/
637 
638 } // namespace librepcb
639 
640 #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:330
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:369
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:133
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:157
static QRectF boundingRectFromRadius(qreal rx, qreal ry) noexcept
Definition: toolbox.h:161
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:93
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:311
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:110
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:165
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