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