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 /*******************************************************************************
34  * Namespace / Forward Declarations
35  ******************************************************************************/
36 namespace librepcb {
37 
38 /*******************************************************************************
39  * Class Toolbox
40  ******************************************************************************/
41 
45 class Toolbox final {
46  Q_DECLARE_TR_FUNCTIONS(Toolbox)
47 
48 public:
49  // Constructors / Destructor
50  Toolbox() = delete;
51  Toolbox(const Toolbox& other) = delete;
52  ~Toolbox() = delete;
53 
54  // Operator Overloadings
55  Toolbox& operator=(const Toolbox& rhs) = delete;
56 
57  // Static Methods
58  template <typename T>
59  static QList<T> sortedQSet(const QSet<T>& set) noexcept {
60  QList<T> list = set.toList();
61  qSort(list);
62  return list;
63  }
64 
65  template <typename T>
66  static T sorted(const T& container) noexcept {
67  T copy(container);
68  qSort(copy);
69  return copy;
70  }
71 
72  static QRectF boundingRectFromRadius(qreal radius) noexcept {
73  return QRectF(-radius, -radius, 2 * radius, 2 * radius);
74  }
75 
76  static QRectF boundingRectFromRadius(qreal rx, qreal ry) noexcept {
77  return QRectF(-rx, -ry, 2 * rx, 2 * ry);
78  }
79 
80  static QRectF adjustedBoundingRect(const QRectF& rect,
81  qreal offset) noexcept {
82  return rect.adjusted(-offset, -offset, offset, offset);
83  }
84 
85  static QPainterPath shapeFromPath(
86  const QPainterPath& path, const QPen& pen, const QBrush& brush,
87  const UnsignedLength& minWidth = UnsignedLength(0)) noexcept;
88 
89  static Length arcRadius(const Point& p1, const Point& p2,
90  const Angle& a) noexcept;
91  static Point arcCenter(const Point& p1, const Point& p2,
92  const Angle& a) noexcept;
93 
108  static Point nearestPointOnLine(const Point& p, const Point& l1,
109  const Point& l2) noexcept;
110 
123  const Point& p, const Point& l1, const Point& l2,
124  Point* nearest = nullptr) noexcept;
125 
139  static QString incrementNumberInString(QString string) noexcept;
140 
160  static QStringList expandRangesInString(const QString& string) noexcept;
161 
175  static QString cleanUserInputString(const QString& input,
176  const QRegularExpression& removeRegex,
177  bool trim = true, bool toLower = false,
178  bool toUpper = false,
179  const QString& spaceReplacement = " ",
180  int maxLength = -1) noexcept;
181 
188  template <typename T>
189  static QString decimalFixedPointToString(T value, qint32 pointPos) noexcept {
190  using UnsignedT = typename std::make_unsigned<T>::type;
191 
192  if (value == 0) {
193  // special case
194  return "0.0";
195  }
196 
197  UnsignedT valueAbs;
198  if (value < 0) {
199  valueAbs = -static_cast<UnsignedT>(value);
200  } else {
201  valueAbs = static_cast<UnsignedT>(value);
202  }
203 
204  QString str = QString::number(valueAbs);
205  if (str.length() > pointPos) {
206  // pointPos must be > 0 for this to work correctly
207  str.insert(str.length() - pointPos, '.');
208  } else {
209  for (qint32 i = pointPos - str.length(); i != 0; i--) str.insert(0, '0');
210  str.insert(0, "0.");
211  }
212 
213  while (str.endsWith('0') && !str.endsWith(".0")) str.chop(1);
214 
215  if (value < 0) str.insert(0, '-');
216 
217  return str;
218  }
219 
227  template <typename T>
228  static T decimalFixedPointFromString(const QString& str, qint32 pointPos) {
229  using UnsignedT = typename std::make_unsigned<T>::type;
230 
231  const T min = std::numeric_limits<T>::min();
232  const T max = std::numeric_limits<T>::max();
233  const UnsignedT max_u = std::numeric_limits<UnsignedT>::max();
234 
235  enum class State {
236  INVALID,
237  START,
238  AFTER_SIGN,
239  LONELY_DOT,
240  INT_PART,
241  FRAC_PART,
242  EXP,
243  EXP_AFTER_SIGN,
244  EXP_DIGITS,
245  };
246  State state = State::START;
247  UnsignedT valueAbs = 0;
248  bool sign = false;
249  qint32 expOffset = pointPos;
250 
251  const quint32 maxExp = std::numeric_limits<quint32>::max();
252  quint32 exp = 0;
253  bool expSign = false;
254 
255  for (QChar c : str) {
256  if (state == State::INVALID) {
257  // break the loop, not the switch
258  break;
259  }
260  switch (state) {
261  case State::INVALID:
262  // already checked, but needed to avoid compiler warnings
263  break;
264 
265  case State::START:
266  if (c == '-') {
267  sign = true;
268  state = State::AFTER_SIGN;
269  } else if (c == '+') {
270  state = State::AFTER_SIGN;
271  } else if (c == '.') {
272  state = State::LONELY_DOT;
273  } else if (c.isDigit()) {
274  valueAbs = static_cast<UnsignedT>(c.digitValue());
275  state = State::INT_PART;
276  } else {
277  state = State::INVALID;
278  }
279  break;
280 
281  case State::AFTER_SIGN:
282  if (c == '.') {
283  state = State::LONELY_DOT;
284  } else if (c.isDigit()) {
285  valueAbs = static_cast<UnsignedT>(c.digitValue());
286  state = State::INT_PART;
287  } else {
288  state = State::INVALID;
289  }
290  break;
291 
292  case State::LONELY_DOT:
293  if (c.isDigit()) {
294  valueAbs = static_cast<UnsignedT>(c.digitValue());
295  expOffset -= 1;
296  state = State::FRAC_PART;
297  } else {
298  state = State::INVALID;
299  }
300  break;
301 
302  case State::INT_PART:
303  if (c == '.') {
304  state = State::FRAC_PART;
305  } else if (c == 'e' || c == 'E') {
306  state = State::EXP;
307  } else if (c.isDigit()) {
308  UnsignedT digit = static_cast<UnsignedT>(c.digitValue());
309  if (valueAbs > (max_u / 10)) {
310  // Would overflow
311  state = State::INVALID;
312  break;
313  }
314  valueAbs *= 10;
315  if (valueAbs > (max_u - digit)) {
316  // Would overflow
317  state = State::INVALID;
318  break;
319  }
320  valueAbs += digit;
321  } else {
322  state = State::INVALID;
323  }
324  break;
325 
326  case State::FRAC_PART:
327  if (c == 'e' || c == 'E') {
328  state = State::EXP;
329  } else if (c.isDigit()) {
330  UnsignedT digit = static_cast<UnsignedT>(c.digitValue());
331  if (valueAbs > (max_u / 10)) {
332  // Would overflow
333  state = State::INVALID;
334  break;
335  }
336  valueAbs *= 10;
337  if (valueAbs > (max_u - digit)) {
338  // Would overflow
339  state = State::INVALID;
340  break;
341  }
342  valueAbs += digit;
343  expOffset -= 1;
344  } else {
345  state = State::INVALID;
346  }
347  break;
348 
349  case State::EXP:
350  if (c == '-') {
351  expSign = true;
352  state = State::EXP_AFTER_SIGN;
353  } else if (c == '+') {
354  state = State::EXP_AFTER_SIGN;
355  } else if (c.isDigit()) {
356  exp = static_cast<quint32>(c.digitValue());
357  state = State::EXP_DIGITS;
358  } else {
359  state = State::INVALID;
360  }
361  break;
362 
363  case State::EXP_AFTER_SIGN:
364  if (c.isDigit()) {
365  exp = static_cast<quint32>(c.digitValue());
366  state = State::EXP_DIGITS;
367  } else {
368  state = State::INVALID;
369  }
370  break;
371 
372  case State::EXP_DIGITS:
373  if (c.isDigit()) {
374  quint32 digit = static_cast<quint32>(c.digitValue());
375  if (exp > (maxExp / 10)) {
376  // Would overflow
377  state = State::INVALID;
378  break;
379  }
380  exp *= 10;
381  if (exp > (maxExp - digit)) {
382  // Would overflow
383  state = State::INVALID;
384  break;
385  }
386  exp += digit;
387  } else {
388  state = State::INVALID;
389  }
390  }
391  }
392 
393  bool ok = true;
394  switch (state) {
395  case State::INVALID:
396  case State::START:
397  case State::AFTER_SIGN:
398  case State::LONELY_DOT:
399  case State::EXP:
400  case State::EXP_AFTER_SIGN:
401  ok = false;
402  break;
403 
404  case State::INT_PART:
405  case State::FRAC_PART:
406  case State::EXP_DIGITS:
407  break;
408  }
409 
410  if (ok) {
411  quint32 expOffsetAbs;
412  if (expOffset < 0) {
413  expOffsetAbs = -static_cast<quint32>(expOffset);
414  } else {
415  expOffsetAbs = static_cast<quint32>(expOffset);
416  }
417 
418  if (expSign == (expOffset < 0)) {
419  if (exp > (maxExp - expOffsetAbs)) {
420  // would overflow
421  ok = false;
422  } else {
423  exp += expOffsetAbs;
424  }
425  } else {
426  if (exp < expOffsetAbs) {
427  // would overflow
428  ok = false;
429  } else {
430  exp -= expOffsetAbs;
431  }
432  }
433  }
434 
435  T result = 0;
436  if (ok) {
437  // No need to apply exponent or sign if valueAbs is zero
438  if (valueAbs != 0) {
439  if (expSign) {
440  for (quint32 i = 0; i < exp; i++) {
441  if ((valueAbs % 10) != 0) {
442  // more decimal digits than allowed
443  ok = false;
444  break;
445  }
446  valueAbs /= 10;
447  }
448  } else {
449  for (quint32 i = 0; i < exp; i++) {
450  if (valueAbs > (max_u / 10)) {
451  // would overflow
452  ok = false;
453  break;
454  }
455  valueAbs *= 10;
456  }
457  }
458  if (ok) {
459  if (sign) {
460  if (valueAbs > static_cast<UnsignedT>(min)) {
461  ok = false;
462  } else {
463  result = static_cast<T>(-valueAbs);
464  }
465  } else {
466  if (valueAbs > static_cast<UnsignedT>(max)) {
467  ok = false;
468  } else {
469  result = static_cast<T>(valueAbs);
470  }
471  }
472  }
473  }
474  }
475 
476  if (!ok) {
477  throw RuntimeError(
478  __FILE__, __LINE__,
479  QString(tr("Invalid fixed point number string: \"%1\"")).arg(str));
480  }
481  return result;
482  }
483 
484 private:
488  static QStringList expandRangesInString(
489  const QString& input,
490  const QVector<std::tuple<int, int, QStringList>>& replacements) noexcept;
491 };
492 
493 /*******************************************************************************
494  * End of File
495  ******************************************************************************/
496 
497 } // namespace librepcb
498 
499 #endif // LIBREPCB_TOOLBOX_H
The Toolbox class provides some useful general purpose methods.
Definition: toolbox.h:45
static QList< T > sortedQSet(const QSet< T > &set) noexcept
Definition: toolbox.h:59
static QString decimalFixedPointToString(T value, qint32 pointPos) noexcept
Convert a fixed point decimal number from an integer to a QString.
Definition: toolbox.h:189
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:228
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:72
static QRectF boundingRectFromRadius(qreal rx, qreal ry) noexcept
Definition: toolbox.h:76
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:66
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:80
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