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 decimalFixedPointToString(T value, qint32 pointPos) noexcept {
192  using UnsignedT = typename std::make_unsigned<T>::type;
193 
194  if (value == 0) {
195  // special case
196  return "0.0";
197  }
198 
199  UnsignedT valueAbs;
200  if (value < 0) {
201  valueAbs = -static_cast<UnsignedT>(value);
202  } else {
203  valueAbs = static_cast<UnsignedT>(value);
204  }
205 
206  QString str = QString::number(valueAbs);
207  if (str.length() > pointPos) {
208  // pointPos must be > 0 for this to work correctly
209  str.insert(str.length() - pointPos, '.');
210  } else {
211  for (qint32 i = pointPos - str.length(); i != 0; i--) str.insert(0, '0');
212  str.insert(0, "0.");
213  }
214 
215  while (str.endsWith('0') && !str.endsWith(".0")) str.chop(1);
216 
217  if (value < 0) str.insert(0, '-');
218 
219  return str;
220  }
221 
229  template <typename T>
230  static T decimalFixedPointFromString(const QString& str, qint32 pointPos) {
231  using UnsignedT = typename std::make_unsigned<T>::type;
232 
233  const T min = std::numeric_limits<T>::min();
234  const T max = std::numeric_limits<T>::max();
235  const UnsignedT max_u = std::numeric_limits<UnsignedT>::max();
236 
237  enum class State {
238  INVALID,
239  START,
240  AFTER_SIGN,
241  LONELY_DOT,
242  INT_PART,
243  FRAC_PART,
244  EXP,
245  EXP_AFTER_SIGN,
246  EXP_DIGITS,
247  };
248  State state = State::START;
249  UnsignedT valueAbs = 0;
250  bool sign = false;
251  qint32 expOffset = pointPos;
252 
253  const quint32 maxExp = std::numeric_limits<quint32>::max();
254  quint32 exp = 0;
255  bool expSign = false;
256 
257  for (QChar c : str) {
258  if (state == State::INVALID) {
259  // break the loop, not the switch
260  break;
261  }
262  switch (state) {
263  case State::INVALID:
264  // already checked, but needed to avoid compiler warnings
265  break;
266 
267  case State::START:
268  if (c == '-') {
269  sign = true;
270  state = State::AFTER_SIGN;
271  } else if (c == '+') {
272  state = State::AFTER_SIGN;
273  } else if (c == '.') {
274  state = State::LONELY_DOT;
275  } else if (c.isDigit()) {
276  valueAbs = static_cast<UnsignedT>(c.digitValue());
277  state = State::INT_PART;
278  } else {
279  state = State::INVALID;
280  }
281  break;
282 
283  case State::AFTER_SIGN:
284  if (c == '.') {
285  state = State::LONELY_DOT;
286  } else if (c.isDigit()) {
287  valueAbs = static_cast<UnsignedT>(c.digitValue());
288  state = State::INT_PART;
289  } else {
290  state = State::INVALID;
291  }
292  break;
293 
294  case State::LONELY_DOT:
295  if (c.isDigit()) {
296  valueAbs = static_cast<UnsignedT>(c.digitValue());
297  expOffset -= 1;
298  state = State::FRAC_PART;
299  } else {
300  state = State::INVALID;
301  }
302  break;
303 
304  case State::INT_PART:
305  if (c == '.') {
306  state = State::FRAC_PART;
307  } else if (c == 'e' || c == 'E') {
308  state = State::EXP;
309  } else if (c.isDigit()) {
310  UnsignedT digit = static_cast<UnsignedT>(c.digitValue());
311  if (valueAbs > (max_u / 10)) {
312  // Would overflow
313  state = State::INVALID;
314  break;
315  }
316  valueAbs *= 10;
317  if (valueAbs > (max_u - digit)) {
318  // Would overflow
319  state = State::INVALID;
320  break;
321  }
322  valueAbs += digit;
323  } else {
324  state = State::INVALID;
325  }
326  break;
327 
328  case State::FRAC_PART:
329  if (c == 'e' || c == 'E') {
330  state = State::EXP;
331  } else if (c.isDigit()) {
332  UnsignedT digit = static_cast<UnsignedT>(c.digitValue());
333  if (valueAbs > (max_u / 10)) {
334  // Would overflow
335  state = State::INVALID;
336  break;
337  }
338  valueAbs *= 10;
339  if (valueAbs > (max_u - digit)) {
340  // Would overflow
341  state = State::INVALID;
342  break;
343  }
344  valueAbs += digit;
345  expOffset -= 1;
346  } else {
347  state = State::INVALID;
348  }
349  break;
350 
351  case State::EXP:
352  if (c == '-') {
353  expSign = true;
354  state = State::EXP_AFTER_SIGN;
355  } else if (c == '+') {
356  state = State::EXP_AFTER_SIGN;
357  } else if (c.isDigit()) {
358  exp = static_cast<quint32>(c.digitValue());
359  state = State::EXP_DIGITS;
360  } else {
361  state = State::INVALID;
362  }
363  break;
364 
365  case State::EXP_AFTER_SIGN:
366  if (c.isDigit()) {
367  exp = static_cast<quint32>(c.digitValue());
368  state = State::EXP_DIGITS;
369  } else {
370  state = State::INVALID;
371  }
372  break;
373 
374  case State::EXP_DIGITS:
375  if (c.isDigit()) {
376  quint32 digit = static_cast<quint32>(c.digitValue());
377  if (exp > (maxExp / 10)) {
378  // Would overflow
379  state = State::INVALID;
380  break;
381  }
382  exp *= 10;
383  if (exp > (maxExp - digit)) {
384  // Would overflow
385  state = State::INVALID;
386  break;
387  }
388  exp += digit;
389  } else {
390  state = State::INVALID;
391  }
392  }
393  }
394 
395  bool ok = true;
396  switch (state) {
397  case State::INVALID:
398  case State::START:
399  case State::AFTER_SIGN:
400  case State::LONELY_DOT:
401  case State::EXP:
402  case State::EXP_AFTER_SIGN:
403  ok = false;
404  break;
405 
406  case State::INT_PART:
407  case State::FRAC_PART:
408  case State::EXP_DIGITS:
409  break;
410  }
411 
412  if (ok) {
413  quint32 expOffsetAbs;
414  if (expOffset < 0) {
415  expOffsetAbs = -static_cast<quint32>(expOffset);
416  } else {
417  expOffsetAbs = static_cast<quint32>(expOffset);
418  }
419 
420  if (expSign == (expOffset < 0)) {
421  if (exp > (maxExp - expOffsetAbs)) {
422  // would overflow
423  ok = false;
424  } else {
425  exp += expOffsetAbs;
426  }
427  } else {
428  if (exp < expOffsetAbs) {
429  // would overflow
430  ok = false;
431  } else {
432  exp -= expOffsetAbs;
433  }
434  }
435  }
436 
437  T result = 0;
438  if (ok) {
439  // No need to apply exponent or sign if valueAbs is zero
440  if (valueAbs != 0) {
441  if (expSign) {
442  for (quint32 i = 0; i < exp; i++) {
443  if ((valueAbs % 10) != 0) {
444  // more decimal digits than allowed
445  ok = false;
446  break;
447  }
448  valueAbs /= 10;
449  }
450  } else {
451  for (quint32 i = 0; i < exp; i++) {
452  if (valueAbs > (max_u / 10)) {
453  // would overflow
454  ok = false;
455  break;
456  }
457  valueAbs *= 10;
458  }
459  }
460  if (ok) {
461  if (sign) {
462  if (valueAbs > static_cast<UnsignedT>(min)) {
463  ok = false;
464  } else {
465  result = static_cast<T>(-valueAbs);
466  }
467  } else {
468  if (valueAbs > static_cast<UnsignedT>(max)) {
469  ok = false;
470  } else {
471  result = static_cast<T>(valueAbs);
472  }
473  }
474  }
475  }
476  }
477 
478  if (!ok) {
479  throw RuntimeError(
480  __FILE__, __LINE__,
481  QString(tr("Invalid fixed point number string: \"%1\"")).arg(str));
482  }
483  return result;
484  }
485 
486 private:
490  static QStringList expandRangesInString(
491  const QString& input,
492  const QVector<std::tuple<int, int, QStringList>>& replacements) noexcept;
493 };
494 
495 /*******************************************************************************
496  * End of File
497  ******************************************************************************/
498 
499 } // namespace librepcb
500 
501 #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:191
static Length 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:117
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:209
static T decimalFixedPointFromString(const QString &str, qint32 pointPos)
Convert a fixed point decimal number from a QString to an integer.
Definition: toolbox.h:230
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:128
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:58
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
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:98
Toolbox & operator=(const Toolbox &rhs)=delete
static QStringList expandRangesInString(const QString &string) noexcept
Expand ranges like "1..5" in a string to all its values.
Definition: toolbox.cpp:146
static Point arcCenter(const Point &p1, const Point &p2, const Angle &a) noexcept
Definition: toolbox.cpp:74
type_safe::constrained_type< Length, UnsignedLengthConstraint, UnsignedLengthVerifier > UnsignedLength
Definition: length.h:659