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 #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  template <typename T>
61  static QList<T> sortedQSet(const QSet<T>& set) noexcept {
62  QList<T> list = set.toList();
63  std::sort(list.begin(), list.end());
64  return list;
65  }
66 
67  template <typename T>
68  static T sorted(const T& container) noexcept {
69  T copy(container);
70  std::sort(copy.begin(), copy.end());
71  return copy;
72  }
73 
74  static QRectF boundingRectFromRadius(qreal radius) noexcept {
75  return QRectF(-radius, -radius, 2 * radius, 2 * radius);
76  }
77 
78  static QRectF boundingRectFromRadius(qreal rx, qreal ry) noexcept {
79  return QRectF(-rx, -ry, 2 * rx, 2 * ry);
80  }
81 
82  static QRectF adjustedBoundingRect(const QRectF& rect,
83  qreal offset) noexcept {
84  return rect.adjusted(-offset, -offset, offset, offset);
85  }
86 
87  static QPainterPath shapeFromPath(
88  const QPainterPath& path, const QPen& pen, const QBrush& brush,
89  const UnsignedLength& minWidth = UnsignedLength(0)) noexcept;
90 
91  static Length arcRadius(const Point& p1, const Point& p2,
92  const Angle& a) noexcept;
93  static Point arcCenter(const Point& p1, const Point& p2,
94  const Angle& a) noexcept;
95 
110  static Point nearestPointOnLine(const Point& p, const Point& l1,
111  const Point& l2) noexcept;
112 
125  const Point& p, const Point& l1, const Point& l2,
126  Point* nearest = nullptr) noexcept;
127 
141  static QString incrementNumberInString(QString string) noexcept;
142 
162  static QStringList expandRangesInString(const QString& string) noexcept;
163 
177  static QString cleanUserInputString(const QString& input,
178  const QRegularExpression& removeRegex,
179  bool trim = true, bool toLower = false,
180  bool toUpper = false,
181  const QString& spaceReplacement = " ",
182  int maxLength = -1) noexcept;
183 
190  template <typename T>
191  static QString floatToString(T value, int decimals,
192  const QLocale& locale) noexcept {
193  QString s = locale.toString(value, 'f', decimals);
194  for (int i = 1; (i < decimals) && s.endsWith(locale.zeroDigit()); ++i) {
195  s.chop(1);
196  }
197  if (qAbs(value) >= 1000) {
198  s.remove(locale.groupSeparator());
199  }
200  return s;
201  }
202 
209  template <typename T>
210  static QString decimalFixedPointToString(T value, qint32 pointPos) noexcept {
211  using UnsignedT = typename std::make_unsigned<T>::type;
212 
213  if (value == 0) {
214  // special case
215  return "0.0";
216  }
217 
218  UnsignedT valueAbs;
219  if (value < 0) {
220  valueAbs = -static_cast<UnsignedT>(value);
221  } else {
222  valueAbs = static_cast<UnsignedT>(value);
223  }
224 
225  QString str = QString::number(valueAbs);
226  if (str.length() > pointPos) {
227  // pointPos must be > 0 for this to work correctly
228  str.insert(str.length() - pointPos, '.');
229  } else {
230  for (qint32 i = pointPos - str.length(); i != 0; i--) str.insert(0, '0');
231  str.insert(0, "0.");
232  }
233 
234  while (str.endsWith('0') && !str.endsWith(".0")) str.chop(1);
235 
236  if (value < 0) str.insert(0, '-');
237 
238  return str;
239  }
240 
248  template <typename T>
249  static T decimalFixedPointFromString(const QString& str, qint32 pointPos) {
250  using UnsignedT = typename std::make_unsigned<T>::type;
251 
252  const T min = std::numeric_limits<T>::min();
253  const T max = std::numeric_limits<T>::max();
254  const UnsignedT max_u = std::numeric_limits<UnsignedT>::max();
255 
256  enum class State {
257  INVALID,
258  START,
259  AFTER_SIGN,
260  LONELY_DOT,
261  INT_PART,
262  FRAC_PART,
263  EXP,
264  EXP_AFTER_SIGN,
265  EXP_DIGITS,
266  };
267  State state = State::START;
268  UnsignedT valueAbs = 0;
269  bool sign = false;
270  qint32 expOffset = pointPos;
271 
272  const quint32 maxExp = std::numeric_limits<quint32>::max();
273  quint32 exp = 0;
274  bool expSign = false;
275 
276  for (QChar c : str) {
277  if (state == State::INVALID) {
278  // break the loop, not the switch
279  break;
280  }
281  switch (state) {
282  case State::INVALID:
283  // already checked, but needed to avoid compiler warnings
284  break;
285 
286  case State::START:
287  if (c == '-') {
288  sign = true;
289  state = State::AFTER_SIGN;
290  } else if (c == '+') {
291  state = State::AFTER_SIGN;
292  } else if (c == '.') {
293  state = State::LONELY_DOT;
294  } else if (c.isDigit()) {
295  valueAbs = static_cast<UnsignedT>(c.digitValue());
296  state = State::INT_PART;
297  } else {
298  state = State::INVALID;
299  }
300  break;
301 
302  case State::AFTER_SIGN:
303  if (c == '.') {
304  state = State::LONELY_DOT;
305  } else if (c.isDigit()) {
306  valueAbs = static_cast<UnsignedT>(c.digitValue());
307  state = State::INT_PART;
308  } else {
309  state = State::INVALID;
310  }
311  break;
312 
313  case State::LONELY_DOT:
314  if (c.isDigit()) {
315  valueAbs = static_cast<UnsignedT>(c.digitValue());
316  expOffset -= 1;
317  state = State::FRAC_PART;
318  } else {
319  state = State::INVALID;
320  }
321  break;
322 
323  case State::INT_PART:
324  if (c == '.') {
325  state = State::FRAC_PART;
326  } else if (c == 'e' || c == 'E') {
327  state = State::EXP;
328  } else if (c.isDigit()) {
329  UnsignedT digit = static_cast<UnsignedT>(c.digitValue());
330  if (valueAbs > (max_u / 10)) {
331  // Would overflow
332  state = State::INVALID;
333  break;
334  }
335  valueAbs *= 10;
336  if (valueAbs > (max_u - digit)) {
337  // Would overflow
338  state = State::INVALID;
339  break;
340  }
341  valueAbs += digit;
342  } else {
343  state = State::INVALID;
344  }
345  break;
346 
347  case State::FRAC_PART:
348  if (c == 'e' || c == 'E') {
349  state = State::EXP;
350  } else if (c.isDigit()) {
351  UnsignedT digit = static_cast<UnsignedT>(c.digitValue());
352  if (valueAbs > (max_u / 10)) {
353  // Would overflow
354  state = State::INVALID;
355  break;
356  }
357  valueAbs *= 10;
358  if (valueAbs > (max_u - digit)) {
359  // Would overflow
360  state = State::INVALID;
361  break;
362  }
363  valueAbs += digit;
364  expOffset -= 1;
365  } else {
366  state = State::INVALID;
367  }
368  break;
369 
370  case State::EXP:
371  if (c == '-') {
372  expSign = true;
373  state = State::EXP_AFTER_SIGN;
374  } else if (c == '+') {
375  state = State::EXP_AFTER_SIGN;
376  } else if (c.isDigit()) {
377  exp = static_cast<quint32>(c.digitValue());
378  state = State::EXP_DIGITS;
379  } else {
380  state = State::INVALID;
381  }
382  break;
383 
384  case State::EXP_AFTER_SIGN:
385  if (c.isDigit()) {
386  exp = static_cast<quint32>(c.digitValue());
387  state = State::EXP_DIGITS;
388  } else {
389  state = State::INVALID;
390  }
391  break;
392 
393  case State::EXP_DIGITS:
394  if (c.isDigit()) {
395  quint32 digit = static_cast<quint32>(c.digitValue());
396  if (exp > (maxExp / 10)) {
397  // Would overflow
398  state = State::INVALID;
399  break;
400  }
401  exp *= 10;
402  if (exp > (maxExp - digit)) {
403  // Would overflow
404  state = State::INVALID;
405  break;
406  }
407  exp += digit;
408  } else {
409  state = State::INVALID;
410  }
411  }
412  }
413 
414  bool ok = true;
415  switch (state) {
416  case State::INVALID:
417  case State::START:
418  case State::AFTER_SIGN:
419  case State::LONELY_DOT:
420  case State::EXP:
421  case State::EXP_AFTER_SIGN:
422  ok = false;
423  break;
424 
425  case State::INT_PART:
426  case State::FRAC_PART:
427  case State::EXP_DIGITS:
428  break;
429  }
430 
431  if (ok) {
432  quint32 expOffsetAbs;
433  if (expOffset < 0) {
434  expOffsetAbs = -static_cast<quint32>(expOffset);
435  } else {
436  expOffsetAbs = static_cast<quint32>(expOffset);
437  }
438 
439  if (expSign == (expOffset < 0)) {
440  if (exp > (maxExp - expOffsetAbs)) {
441  // would overflow
442  ok = false;
443  } else {
444  exp += expOffsetAbs;
445  }
446  } else {
447  if (exp < expOffsetAbs) {
448  // would overflow
449  ok = false;
450  } else {
451  exp -= expOffsetAbs;
452  }
453  }
454  }
455 
456  T result = 0;
457  if (ok) {
458  // No need to apply exponent or sign if valueAbs is zero
459  if (valueAbs != 0) {
460  if (expSign) {
461  for (quint32 i = 0; i < exp; i++) {
462  if ((valueAbs % 10) != 0) {
463  // more decimal digits than allowed
464  ok = false;
465  break;
466  }
467  valueAbs /= 10;
468  }
469  } else {
470  for (quint32 i = 0; i < exp; i++) {
471  if (valueAbs > (max_u / 10)) {
472  // would overflow
473  ok = false;
474  break;
475  }
476  valueAbs *= 10;
477  }
478  }
479  if (ok) {
480  if (sign) {
481  if (valueAbs > static_cast<UnsignedT>(min)) {
482  ok = false;
483  } else {
484  result = static_cast<T>(-valueAbs);
485  }
486  } else {
487  if (valueAbs > static_cast<UnsignedT>(max)) {
488  ok = false;
489  } else {
490  result = static_cast<T>(valueAbs);
491  }
492  }
493  }
494  }
495  }
496 
497  if (!ok) {
498  throw RuntimeError(
499  __FILE__, __LINE__,
500  QString(tr("Invalid fixed point number string: \"%1\"")).arg(str));
501  }
502  return result;
503  }
504 
505 private:
509  static QStringList expandRangesInString(
510  const QString& input,
511  const QVector<std::tuple<int, int, QStringList>>& replacements) noexcept;
512 };
513 
514 /*******************************************************************************
515  * End of File
516  ******************************************************************************/
517 
518 } // namespace librepcb
519 
520 #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:61
static QString decimalFixedPointToString(T value, qint32 pointPos) noexcept
Convert a fixed point decimal number from an integer to a QString.
Definition: toolbox.h:210
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:249
The Angle class is used to represent an angle (for example 12.75 degrees)
Definition: angle.h:78
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:74
static QRectF boundingRectFromRadius(qreal rx, qreal ry) noexcept
Definition: toolbox.h:78
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:68
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:191
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:82
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