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 <optional/tl/optional.hpp>
32#include <type_traits>
33
34#include <QtCore>
35#include <QtGui>
36
37#include <algorithm>
38
39/*******************************************************************************
40 * Namespace / Forward Declarations
41 ******************************************************************************/
42namespace librepcb {
43
44/*******************************************************************************
45 * Class Toolbox
46 ******************************************************************************/
47
51class Toolbox final {
52 Q_DECLARE_TR_FUNCTIONS(Toolbox)
53
54public:
55 // Constructors / Destructor
56 Toolbox() = delete;
57 Toolbox(const Toolbox& other) = delete;
58 ~Toolbox() = delete;
59
60 // Operator Overloadings
61 Toolbox& operator=(const Toolbox& rhs) = delete;
62
63 // Static Methods
64
77 template <typename T>
78 static inline QSet<T> toSet(const QList<T>& list) noexcept {
79#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
80 return QSet<T>(list.begin(), list.end());
81#else
82 return list.toSet();
83#endif
84 }
85
86#if (QT_VERSION_MAJOR < 6)
99 template <typename T>
100 static inline QSet<T> toSet(const QVector<T>& vector) noexcept {
101#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
102 return QSet<T>(vector.begin(), vector.end());
103#else
104 return vector.toList().toSet();
105#endif
106 }
107#endif
108
121 template <typename T>
122 static inline QVector<T> toVector(const QSet<T>& set) noexcept {
123#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
124 return QVector<T>(set.begin(), set.end());
125#else
126 return set.toList().toVector();
127#endif
128 }
129
142 template <typename T>
143 static inline QList<T> toList(const QSet<T>& set) noexcept {
144#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
145 return QList<T>(set.begin(), set.end());
146#else
147 return set.toList();
148#endif
149 }
150
151 template <typename T>
152 static QList<T> sortedQSet(const QSet<T>& set) noexcept {
153 QList<T> list = set.values();
154 std::sort(list.begin(), list.end());
155 return list;
156 }
157
158 template <typename T, typename Compare>
159 static QList<T> sortedQSet(const QSet<T>& set, const Compare& cmp) noexcept {
160 QList<T> list = set.values();
161 std::sort(list.begin(), list.end(), cmp);
162 return list;
163 }
164
165 template <typename T>
166 static T sorted(const T& container) noexcept {
167 T copy(container);
168 std::sort(copy.begin(), copy.end());
169 return copy;
170 }
171
182 template <typename T, typename Compare>
183 static void sortNumeric(
184 T& container, Compare compare,
185 Qt::CaseSensitivity caseSensitivity = Qt::CaseInsensitive,
186 bool ignorePunctuation = false) noexcept {
187 QCollator collator;
188 collator.setNumericMode(true);
189 collator.setCaseSensitivity(caseSensitivity);
190 collator.setIgnorePunctuation(ignorePunctuation);
191 std::sort(container.begin(), container.end(),
192 [&collator, &compare](const typename T::value_type& lhs,
193 const typename T::value_type& rhs) {
194 return compare(collator, lhs, rhs);
195 });
196 }
197
205 template <typename T>
206 static void sortNumeric(
207 T& container, Qt::CaseSensitivity caseSensitivity = Qt::CaseInsensitive,
208 bool ignorePunctuation = false) noexcept {
209 return sortNumeric(
210 container,
211 [](const QCollator& collator, const typename T::value_type& lhs,
212 const typename T::value_type& rhs) { return collator(lhs, rhs); },
213 caseSensitivity, ignorePunctuation);
214 }
215
228 static bool isTextUpsideDown(const Angle& rotation) noexcept;
229
230 static QRectF boundingRectFromRadius(qreal radius) noexcept {
231 return QRectF(-radius, -radius, 2 * radius, 2 * radius);
232 }
233
234 static QRectF boundingRectFromRadius(qreal rx, qreal ry) noexcept {
235 return QRectF(-rx, -ry, 2 * rx, 2 * ry);
236 }
237
238 static QRectF adjustedBoundingRect(const QRectF& rect,
239 qreal offset) noexcept {
240 return rect.adjusted(-offset, -offset, offset, offset);
241 }
242
243 static QPainterPath shapeFromPath(
244 const QPainterPath& path, const QPen& pen, const QBrush& brush,
245 const UnsignedLength& minWidth = UnsignedLength(0)) noexcept;
246
247 static tl::optional<Length> arcRadius(const Point& p1, const Point& p2,
248 const Angle& angle) noexcept;
249 static tl::optional<Point> arcCenter(const Point& p1, const Point& p2,
250 const Angle& angle) noexcept;
251
261 static Angle arcAngle(const Point& p1, const Point& p2,
262 const Point& center = Point(0, 0)) noexcept;
263
272 static Angle angleBetweenPoints(const Point& p1, const Point& p2) noexcept;
273
288 static Point nearestPointOnLine(const Point& p, const Point& l1,
289 const Point& l2) noexcept;
290
303 const Point& p, const Point& l1, const Point& l2,
304 Point* nearest = nullptr) noexcept;
305
319 static QString incrementNumberInString(QString string) noexcept;
320
340 static QStringList expandRangesInString(const QString& string) noexcept;
341
355 static QString cleanUserInputString(const QString& input,
356 const QRegularExpression& removeRegex,
357 bool trim = true, bool toLower = false,
358 bool toUpper = false,
359 const QString& spaceReplacement = " ",
360 int maxLength = -1) noexcept;
361
375 static QString prettyPrintLocale(const QString& code) noexcept;
376
383 template <typename T>
384 static QString floatToString(T value, int decimals,
385 const QLocale& locale) noexcept {
386 QString s = locale.toString(value, 'f', decimals);
387 for (int i = 1; (i < decimals) && s.endsWith(locale.zeroDigit()); ++i) {
388 s.chop(1);
389 }
390 if (qAbs(value) >= 1000) {
391 s.remove(locale.groupSeparator());
392 }
393 return s;
394 }
395
402 template <typename T>
403 static QString decimalFixedPointToString(T value, qint32 pointPos) noexcept {
404 using UnsignedT = typename std::make_unsigned<T>::type;
405
406 if (value == 0) {
407 // special case
408 return "0.0";
409 }
410
411 UnsignedT valueAbs;
412 if (value < 0) {
413 valueAbs = -static_cast<UnsignedT>(value);
414 } else {
415 valueAbs = static_cast<UnsignedT>(value);
416 }
417
418 QString str = QString::number(valueAbs);
419 if (str.length() > pointPos) {
420 // pointPos must be > 0 for this to work correctly
421 str.insert(str.length() - pointPos, '.');
422 } else {
423 for (qint32 i = pointPos - str.length(); i != 0; i--) str.insert(0, '0');
424 str.insert(0, "0.");
425 }
426
427 while (str.endsWith('0') && !str.endsWith(".0")) str.chop(1);
428
429 if (value < 0) str.insert(0, '-');
430
431 return str;
432 }
433
441 template <typename T>
442 static T decimalFixedPointFromString(const QString& str, qint32 pointPos) {
443 using UnsignedT = typename std::make_unsigned<T>::type;
444
445 const T min = std::numeric_limits<T>::min();
446 const T max = std::numeric_limits<T>::max();
447 const UnsignedT max_u = std::numeric_limits<UnsignedT>::max();
448
449 enum class State {
450 INVALID,
451 START,
452 AFTER_SIGN,
453 LONELY_DOT,
454 INT_PART,
455 FRAC_PART,
456 EXP,
457 EXP_AFTER_SIGN,
458 EXP_DIGITS,
459 };
460 State state = State::START;
461 UnsignedT valueAbs = 0;
462 bool sign = false;
463 qint32 expOffset = pointPos;
464
465 const quint32 maxExp = std::numeric_limits<quint32>::max();
466 quint32 exp = 0;
467 bool expSign = false;
468
469 for (QChar c : str) {
470 if (state == State::INVALID) {
471 // break the loop, not the switch
472 break;
473 }
474 switch (state) {
475 case State::INVALID:
476 // already checked, but needed to avoid compiler warnings
477 break;
478
479 case State::START:
480 if (c == '-') {
481 sign = true;
482 state = State::AFTER_SIGN;
483 } else if (c == '+') {
484 state = State::AFTER_SIGN;
485 } else if (c == '.') {
486 state = State::LONELY_DOT;
487 } else if (c.isDigit()) {
488 valueAbs = static_cast<UnsignedT>(c.digitValue());
489 state = State::INT_PART;
490 } else {
491 state = State::INVALID;
492 }
493 break;
494
495 case State::AFTER_SIGN:
496 if (c == '.') {
497 state = State::LONELY_DOT;
498 } else if (c.isDigit()) {
499 valueAbs = static_cast<UnsignedT>(c.digitValue());
500 state = State::INT_PART;
501 } else {
502 state = State::INVALID;
503 }
504 break;
505
506 case State::LONELY_DOT:
507 if (c.isDigit()) {
508 valueAbs = static_cast<UnsignedT>(c.digitValue());
509 expOffset -= 1;
510 state = State::FRAC_PART;
511 } else {
512 state = State::INVALID;
513 }
514 break;
515
516 case State::INT_PART:
517 if (c == '.') {
518 state = State::FRAC_PART;
519 } else if (c == 'e' || c == 'E') {
520 state = State::EXP;
521 } else if (c.isDigit()) {
522 UnsignedT digit = static_cast<UnsignedT>(c.digitValue());
523 if (valueAbs > (max_u / 10)) {
524 // Would overflow
525 state = State::INVALID;
526 break;
527 }
528 valueAbs *= 10;
529 if (valueAbs > (max_u - digit)) {
530 // Would overflow
531 state = State::INVALID;
532 break;
533 }
534 valueAbs += digit;
535 } else {
536 state = State::INVALID;
537 }
538 break;
539
540 case State::FRAC_PART:
541 if (c == 'e' || c == 'E') {
542 state = State::EXP;
543 } else if (c.isDigit()) {
544 UnsignedT digit = static_cast<UnsignedT>(c.digitValue());
545 if (valueAbs > (max_u / 10)) {
546 // Would overflow
547 state = State::INVALID;
548 break;
549 }
550 valueAbs *= 10;
551 if (valueAbs > (max_u - digit)) {
552 // Would overflow
553 state = State::INVALID;
554 break;
555 }
556 valueAbs += digit;
557 expOffset -= 1;
558 } else {
559 state = State::INVALID;
560 }
561 break;
562
563 case State::EXP:
564 if (c == '-') {
565 expSign = true;
566 state = State::EXP_AFTER_SIGN;
567 } else if (c == '+') {
568 state = State::EXP_AFTER_SIGN;
569 } else if (c.isDigit()) {
570 exp = static_cast<quint32>(c.digitValue());
571 state = State::EXP_DIGITS;
572 } else {
573 state = State::INVALID;
574 }
575 break;
576
577 case State::EXP_AFTER_SIGN:
578 if (c.isDigit()) {
579 exp = static_cast<quint32>(c.digitValue());
580 state = State::EXP_DIGITS;
581 } else {
582 state = State::INVALID;
583 }
584 break;
585
586 case State::EXP_DIGITS:
587 if (c.isDigit()) {
588 quint32 digit = static_cast<quint32>(c.digitValue());
589 if (exp > (maxExp / 10)) {
590 // Would overflow
591 state = State::INVALID;
592 break;
593 }
594 exp *= 10;
595 if (exp > (maxExp - digit)) {
596 // Would overflow
597 state = State::INVALID;
598 break;
599 }
600 exp += digit;
601 } else {
602 state = State::INVALID;
603 }
604 }
605 }
606
607 bool ok = true;
608 switch (state) {
609 case State::INVALID:
610 case State::START:
611 case State::AFTER_SIGN:
612 case State::LONELY_DOT:
613 case State::EXP:
614 case State::EXP_AFTER_SIGN:
615 ok = false;
616 break;
617
618 case State::INT_PART:
619 case State::FRAC_PART:
620 case State::EXP_DIGITS:
621 break;
622 }
623
624 if (ok) {
625 quint32 expOffsetAbs;
626 if (expOffset < 0) {
627 expOffsetAbs = -static_cast<quint32>(expOffset);
628 } else {
629 expOffsetAbs = static_cast<quint32>(expOffset);
630 }
631
632 if (expSign == (expOffset < 0)) {
633 if (exp > (maxExp - expOffsetAbs)) {
634 // would overflow
635 ok = false;
636 } else {
637 exp += expOffsetAbs;
638 }
639 } else {
640 if (exp < expOffsetAbs) {
641 // would overflow
642 ok = false;
643 } else {
644 exp -= expOffsetAbs;
645 }
646 }
647 }
648
649 T result = 0;
650 if (ok) {
651 // No need to apply exponent or sign if valueAbs is zero
652 if (valueAbs != 0) {
653 if (expSign) {
654 for (quint32 i = 0; i < exp; i++) {
655 if ((valueAbs % 10) != 0) {
656 // more decimal digits than allowed
657 ok = false;
658 break;
659 }
660 valueAbs /= 10;
661 }
662 } else {
663 for (quint32 i = 0; i < exp; i++) {
664 if (valueAbs > (max_u / 10)) {
665 // would overflow
666 ok = false;
667 break;
668 }
669 valueAbs *= 10;
670 }
671 }
672 if (ok) {
673 if (sign) {
674 if (valueAbs > static_cast<UnsignedT>(min)) {
675 ok = false;
676 } else {
677 result = static_cast<T>(-valueAbs);
678 }
679 } else {
680 if (valueAbs > static_cast<UnsignedT>(max)) {
681 ok = false;
682 } else {
683 result = static_cast<T>(valueAbs);
684 }
685 }
686 }
687 }
688 }
689
690 if (!ok) {
691 throw RuntimeError(
692 __FILE__, __LINE__,
693 tr("Invalid fixed point number string: \"%1\"").arg(str));
694 }
695 return result;
696 }
697
698private:
702 static QStringList expandRangesInString(
703 const QString& input,
704 const QVector<std::tuple<int, int, QStringList>>& replacements) noexcept;
705};
706
707/*******************************************************************************
708 * End of File
709 ******************************************************************************/
710
711} // namespace librepcb
712
713#endif
The Angle class is used to represent an angle (for example 12.75 degrees)
Definition: angle.h:78
The Length class is used to represent a length (for example 12.75 millimeters)
Definition: length.h:83
The Point class is used to represent a point/coordinate/vector, for example (1.2mm; 5....
Definition: point.h:79
The RuntimeError class.
Definition: exceptions.h:218
The Toolbox class provides some useful general purpose methods.
Definition: toolbox.h:51
static QString incrementNumberInString(QString string) noexcept
Copy a string while incrementing its contained number.
Definition: toolbox.cpp:161
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 QList< T > toList(const QSet< T > &set) noexcept
Helper method to convert a QSet<T> to a QList<T>
Definition: toolbox.h:143
static QSet< T > toSet(const QList< T > &list) noexcept
Helper method to convert a QList<T> to a QSet<T>
Definition: toolbox.h:78
static QList< T > sortedQSet(const QSet< T > &set) noexcept
Definition: toolbox.h:152
static QRectF adjustedBoundingRect(const QRectF &rect, qreal offset) noexcept
Definition: toolbox.h:238
Toolbox(const Toolbox &other)=delete
static QStringList expandRangesInString(const QString &string) noexcept
Expand ranges like "1..5" in a string to all its values.
Definition: toolbox.cpp:179
static Angle angleBetweenPoints(const Point &p1, const Point &p2) noexcept
Calculate the angle between two points.
Definition: toolbox.cpp:124
static tl::optional< Length > arcRadius(const Point &p1, const Point &p2, const Angle &angle) noexcept
Definition: toolbox.cpp:64
static QSet< T > toSet(const QVector< T > &vector) noexcept
Helper method to convert a QVector<T> to a QSet<T>
Definition: toolbox.h:100
static QRectF boundingRectFromRadius(qreal radius) noexcept
Definition: toolbox.h:230
static QString decimalFixedPointToString(T value, qint32 pointPos) noexcept
Convert a fixed point decimal number from an integer to a QString.
Definition: toolbox.h:403
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:112
static QString floatToString(T value, int decimals, const QLocale &locale) noexcept
Convert a float or double to a localized string.
Definition: toolbox.h:384
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:152
Toolbox & operator=(const Toolbox &rhs)=delete
static tl::optional< Point > arcCenter(const Point &p1, const Point &p2, const Angle &angle) noexcept
Definition: toolbox.cpp:84
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:206
static QVector< T > toVector(const QSet< T > &set) noexcept
Helper method to convert a QSet<T> to a QVector<T>
Definition: toolbox.h:122
static T decimalFixedPointFromString(const QString &str, qint32 pointPos)
Convert a fixed point decimal number from a QString to an integer.
Definition: toolbox.h:442
static QList< T > sortedQSet(const QSet< T > &set, const Compare &cmp) noexcept
Definition: toolbox.h:159
static QString prettyPrintLocale(const QString &code) noexcept
Pretty print the name of a QLocale.
Definition: toolbox.cpp:265
static QRectF boundingRectFromRadius(qreal rx, qreal ry) noexcept
Definition: toolbox.h:234
static QPainterPath shapeFromPath(const QPainterPath &path, const QPen &pen, const QBrush &brush, const UnsignedLength &minWidth=UnsignedLength(0)) noexcept
Definition: toolbox.cpp:41
static T sorted(const T &container) noexcept
Definition: toolbox.h:166
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:133
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:242
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:183
Definition: occmodel.cpp:77
type_safe::constrained_type< Length, UnsignedLengthConstraint, UnsignedLengthVerifier > UnsignedLength
Definition: length.h:696
Definition: uuid.h:183