LibrePCB Developers Documentation
Loading...
Searching...
No Matches
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 <type_traits>
32
33#include <QtCore>
34#include <QtGui>
35
36#include <algorithm>
37#include <optional>
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 return QSet<T>(list.begin(), list.end());
80 }
81
94 template <typename T>
95 static inline QVector<T> toVector(const QSet<T>& set) noexcept {
96 return QVector<T>(set.begin(), set.end());
97 }
98
111 template <typename T>
112 static inline QList<T> toList(const QSet<T>& set) noexcept {
113 return QList<T>(set.begin(), set.end());
114 }
115
116 template <typename T>
117 static QList<T> sortedQSet(const QSet<T>& set) noexcept {
118 static_assert(!std::is_pointer<T>::value,
119 "Container holds pointers, sorting makes no sense.");
120 QList<T> list = set.values();
121 std::sort(list.begin(), list.end());
122 return list;
123 }
124
125 template <typename T, typename Compare>
126 static QList<T> sortedQSet(const QSet<T>& set, const Compare& cmp) noexcept {
127 QList<T> list = set.values();
128 std::sort(list.begin(), list.end(), cmp);
129 return list;
130 }
131
132 template <typename T>
133 static T sorted(const T& container) noexcept {
134 static_assert(!std::is_pointer<T>::value,
135 "Container holds pointers, sorting makes no sense.");
136 T copy(container);
137 std::sort(copy.begin(), copy.end());
138 return copy;
139 }
140
151 template <typename T, typename Compare>
152 static void sortNumeric(
153 T& container, Compare compare,
154 Qt::CaseSensitivity caseSensitivity = Qt::CaseInsensitive,
155 bool ignorePunctuation = false) noexcept {
156 QCollator collator;
157 collator.setNumericMode(true);
158 collator.setCaseSensitivity(caseSensitivity);
159 collator.setIgnorePunctuation(ignorePunctuation);
160 std::sort(container.begin(), container.end(),
161 [&collator, &compare](const typename T::value_type& lhs,
162 const typename T::value_type& rhs) {
163 return compare(collator, lhs, rhs);
164 });
165 }
166
174 template <typename T>
175 static void sortNumeric(
176 T& container, Qt::CaseSensitivity caseSensitivity = Qt::CaseInsensitive,
177 bool ignorePunctuation = false) noexcept {
178 return sortNumeric(
179 container,
180 [](const QCollator& collator, const typename T::value_type& lhs,
181 const typename T::value_type& rhs) { return collator(lhs, rhs); },
182 caseSensitivity, ignorePunctuation);
183 }
184
197 static bool isTextUpsideDown(const Angle& rotation) noexcept;
198
199 static QRectF boundingRectFromRadius(qreal radius) noexcept {
200 return QRectF(-radius, -radius, 2 * radius, 2 * radius);
201 }
202
203 static QRectF boundingRectFromRadius(qreal rx, qreal ry) noexcept {
204 return QRectF(-rx, -ry, 2 * rx, 2 * ry);
205 }
206
207 static QRectF adjustedBoundingRect(const QRectF& rect,
208 qreal offset) noexcept {
209 return rect.adjusted(-offset, -offset, offset, offset);
210 }
211
212 static QPainterPath shapeFromPath(
213 const QPainterPath& path, const QPen& pen, const QBrush& brush,
214 const UnsignedLength& minWidth = UnsignedLength(0)) noexcept;
215
216 static std::optional<Length> arcRadius(const Point& p1, const Point& p2,
217 const Angle& angle) noexcept;
218 static std::optional<Point> arcCenter(const Point& p1, const Point& p2,
219 const Angle& angle) noexcept;
220
230 static Angle arcAngle(const Point& p1, const Point& p2,
231 const Point& center = Point(0, 0)) noexcept;
232
246 static Angle arcAngleFrom3Points(const Point& start, const Point& mid,
247 const Point& end) noexcept;
248
257 static Angle angleBetweenPoints(const Point& p1, const Point& p2) noexcept;
258
273 static Point nearestPointOnLine(const Point& p, const Point& l1,
274 const Point& l2) noexcept;
275
288 const Point& p, const Point& l1, const Point& l2,
289 Point* nearest = nullptr) noexcept;
290
304 static QString incrementNumberInString(QString string) noexcept;
305
325 static QStringList expandRangesInString(const QString& string) noexcept;
326
340 static QString cleanUserInputString(const QString& input,
341 const QRegularExpression& removeRegex,
342 bool trim = true, bool toLower = false,
343 bool toUpper = false,
344 const QString& spaceReplacement = " ",
345 int maxLength = -1) noexcept;
346
360 static QString prettyPrintLocale(const QString& code) noexcept;
361
368 template <typename T>
369 static QString floatToString(T value, int decimals,
370 const QLocale& locale) noexcept {
371 QString s = locale.toString(value, 'f', decimals);
372 for (int i = 1; (i < decimals) && s.endsWith(locale.zeroDigit()); ++i) {
373 s.chop(1);
374 }
375 if (qAbs(value) >= 1000) {
376 s.remove(locale.groupSeparator());
377 }
378 return s;
379 }
380
387 template <typename T>
388 static QString decimalFixedPointToString(T value, qint32 pointPos) noexcept {
389 using UnsignedT = typename std::make_unsigned<T>::type;
390
391 if (value == 0) {
392 // special case
393 return "0.0";
394 }
395
396 UnsignedT valueAbs;
397 if (value < 0) {
398 valueAbs = -static_cast<UnsignedT>(value);
399 } else {
400 valueAbs = static_cast<UnsignedT>(value);
401 }
402
403 QString str = QString::number(valueAbs);
404 if (str.length() > pointPos) {
405 // pointPos must be > 0 for this to work correctly
406 str.insert(str.length() - pointPos, '.');
407 } else {
408 for (qint32 i = pointPos - str.length(); i != 0; i--) str.insert(0, '0');
409 str.insert(0, "0.");
410 }
411
412 while (str.endsWith('0') && !str.endsWith(".0")) str.chop(1);
413
414 if (value < 0) str.insert(0, '-');
415
416 return str;
417 }
418
426 template <typename T>
427 static T decimalFixedPointFromString(const QString& str, qint32 pointPos) {
428 using UnsignedT = typename std::make_unsigned<T>::type;
429
430 const T min = std::numeric_limits<T>::min();
431 const T max = std::numeric_limits<T>::max();
432 const UnsignedT max_u = std::numeric_limits<UnsignedT>::max();
433
434 enum class State {
435 INVALID,
436 START,
437 AFTER_SIGN,
438 LONELY_DOT,
439 INT_PART,
440 FRAC_PART,
441 EXP,
442 EXP_AFTER_SIGN,
443 EXP_DIGITS,
444 };
445 State state = State::START;
446 UnsignedT valueAbs = 0;
447 bool sign = false;
448 qint32 expOffset = pointPos;
449
450 const quint32 maxExp = std::numeric_limits<quint32>::max();
451 quint32 exp = 0;
452 bool expSign = false;
453
454 for (QChar c : str) {
455 if (state == State::INVALID) {
456 // break the loop, not the switch
457 break;
458 }
459 switch (state) {
460 case State::INVALID:
461 // already checked, but needed to avoid compiler warnings
462 break;
463
464 case State::START:
465 if (c == '-') {
466 sign = true;
467 state = State::AFTER_SIGN;
468 } else if (c == '+') {
469 state = State::AFTER_SIGN;
470 } else if (c == '.') {
471 state = State::LONELY_DOT;
472 } else if (c.isDigit()) {
473 valueAbs = static_cast<UnsignedT>(c.digitValue());
474 state = State::INT_PART;
475 } else {
476 state = State::INVALID;
477 }
478 break;
479
480 case State::AFTER_SIGN:
481 if (c == '.') {
482 state = State::LONELY_DOT;
483 } else if (c.isDigit()) {
484 valueAbs = static_cast<UnsignedT>(c.digitValue());
485 state = State::INT_PART;
486 } else {
487 state = State::INVALID;
488 }
489 break;
490
491 case State::LONELY_DOT:
492 if (c.isDigit()) {
493 valueAbs = static_cast<UnsignedT>(c.digitValue());
494 expOffset -= 1;
495 state = State::FRAC_PART;
496 } else {
497 state = State::INVALID;
498 }
499 break;
500
501 case State::INT_PART:
502 if (c == '.') {
503 state = State::FRAC_PART;
504 } else if (c == 'e' || c == 'E') {
505 state = State::EXP;
506 } else if (c.isDigit()) {
507 UnsignedT digit = static_cast<UnsignedT>(c.digitValue());
508 if (valueAbs > (max_u / 10)) {
509 // Would overflow
510 state = State::INVALID;
511 break;
512 }
513 valueAbs *= 10;
514 if (valueAbs > (max_u - digit)) {
515 // Would overflow
516 state = State::INVALID;
517 break;
518 }
519 valueAbs += digit;
520 } else {
521 state = State::INVALID;
522 }
523 break;
524
525 case State::FRAC_PART:
526 if (c == 'e' || c == 'E') {
527 state = State::EXP;
528 } else if (c.isDigit()) {
529 UnsignedT digit = static_cast<UnsignedT>(c.digitValue());
530 if (valueAbs > (max_u / 10)) {
531 // Would overflow
532 state = State::INVALID;
533 break;
534 }
535 valueAbs *= 10;
536 if (valueAbs > (max_u - digit)) {
537 // Would overflow
538 state = State::INVALID;
539 break;
540 }
541 valueAbs += digit;
542 expOffset -= 1;
543 } else {
544 state = State::INVALID;
545 }
546 break;
547
548 case State::EXP:
549 if (c == '-') {
550 expSign = true;
551 state = State::EXP_AFTER_SIGN;
552 } else if (c == '+') {
553 state = State::EXP_AFTER_SIGN;
554 } else if (c.isDigit()) {
555 exp = static_cast<quint32>(c.digitValue());
556 state = State::EXP_DIGITS;
557 } else {
558 state = State::INVALID;
559 }
560 break;
561
562 case State::EXP_AFTER_SIGN:
563 if (c.isDigit()) {
564 exp = static_cast<quint32>(c.digitValue());
565 state = State::EXP_DIGITS;
566 } else {
567 state = State::INVALID;
568 }
569 break;
570
571 case State::EXP_DIGITS:
572 if (c.isDigit()) {
573 quint32 digit = static_cast<quint32>(c.digitValue());
574 if (exp > (maxExp / 10)) {
575 // Would overflow
576 state = State::INVALID;
577 break;
578 }
579 exp *= 10;
580 if (exp > (maxExp - digit)) {
581 // Would overflow
582 state = State::INVALID;
583 break;
584 }
585 exp += digit;
586 } else {
587 state = State::INVALID;
588 }
589 }
590 }
591
592 bool ok = true;
593 switch (state) {
594 case State::INVALID:
595 case State::START:
596 case State::AFTER_SIGN:
597 case State::LONELY_DOT:
598 case State::EXP:
599 case State::EXP_AFTER_SIGN:
600 ok = false;
601 break;
602
603 case State::INT_PART:
604 case State::FRAC_PART:
605 case State::EXP_DIGITS:
606 break;
607 }
608
609 if (ok) {
610 quint32 expOffsetAbs;
611 if (expOffset < 0) {
612 expOffsetAbs = -static_cast<quint32>(expOffset);
613 } else {
614 expOffsetAbs = static_cast<quint32>(expOffset);
615 }
616
617 if (expSign == (expOffset < 0)) {
618 if (exp > (maxExp - expOffsetAbs)) {
619 // would overflow
620 ok = false;
621 } else {
622 exp += expOffsetAbs;
623 }
624 } else {
625 if (exp < expOffsetAbs) {
626 // would overflow
627 ok = false;
628 } else {
629 exp -= expOffsetAbs;
630 }
631 }
632 }
633
634 T result = 0;
635 if (ok) {
636 // No need to apply exponent or sign if valueAbs is zero
637 if (valueAbs != 0) {
638 if (expSign) {
639 for (quint32 i = 0; i < exp; i++) {
640 if ((valueAbs % 10) != 0) {
641 // more decimal digits than allowed
642 ok = false;
643 break;
644 }
645 valueAbs /= 10;
646 }
647 } else {
648 for (quint32 i = 0; i < exp; i++) {
649 if (valueAbs > (max_u / 10)) {
650 // would overflow
651 ok = false;
652 break;
653 }
654 valueAbs *= 10;
655 }
656 }
657 if (ok) {
658 if (sign) {
659 if (valueAbs > static_cast<UnsignedT>(min)) {
660 ok = false;
661 } else {
662 result = static_cast<T>(-valueAbs);
663 }
664 } else {
665 if (valueAbs > static_cast<UnsignedT>(max)) {
666 ok = false;
667 } else {
668 result = static_cast<T>(valueAbs);
669 }
670 }
671 }
672 }
673 }
674
675 if (!ok) {
676 throw RuntimeError(
677 __FILE__, __LINE__,
678 tr("Invalid fixed point number string: \"%1\"").arg(str));
679 }
680 return result;
681 }
682
683private:
687 static QStringList expandRangesInString(
688 const QString& input,
689 const QVector<std::tuple<int, int, QStringList>>& replacements) noexcept;
690};
691
692/*******************************************************************************
693 * End of File
694 ******************************************************************************/
695
696} // namespace librepcb
697
698#endif
The Angle class is used to represent an angle (for example 12.75 degrees)
Definition angle.h:76
The Length class is used to represent a length (for example 12.75 millimeters)
Definition length.h:82
The Point class is used to represent a point/coordinate/vector, for example (1.2mm; 5....
Definition point.h:78
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:179
static bool isTextUpsideDown(const Angle &rotation) noexcept
Check if a text with a given rotation is considered as upside down.
Definition toolbox.cpp:38
static QList< T > toList(const QSet< T > &set) noexcept
Helper method to convert a QSet<T> to a QList<T>
Definition toolbox.h:112
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:117
static QRectF adjustedBoundingRect(const QRectF &rect, qreal offset) noexcept
Definition toolbox.h:207
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:184
static Angle angleBetweenPoints(const Point &p1, const Point &p2) noexcept
Calculate the angle between two points.
Definition toolbox.cpp:142
static Angle arcAngleFrom3Points(const Point &start, const Point &mid, const Point &end) noexcept
Calculate the angle of an arc given by 3 points.
Definition toolbox.cpp:127
static QRectF boundingRectFromRadius(qreal radius) noexcept
Definition toolbox.h:199
static QString decimalFixedPointToString(T value, qint32 pointPos) noexcept
Convert a fixed point decimal number from an integer to a QString.
Definition toolbox.h:388
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:115
static std::optional< Length > arcRadius(const Point &p1, const Point &p2, const Angle &angle) noexcept
Definition toolbox.cpp:66
static QString floatToString(T value, int decimals, const QLocale &locale) noexcept
Convert a float or double to a localized string.
Definition toolbox.h:369
static std::optional< Point > arcCenter(const Point &p1, const Point &p2, const Angle &angle) noexcept
Definition toolbox.cpp:86
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:170
Toolbox & operator=(const Toolbox &rhs)=delete
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:175
static QVector< T > toVector(const QSet< T > &set) noexcept
Helper method to convert a QSet<T> to a QVector<T>
Definition toolbox.h:95
static T decimalFixedPointFromString(const QString &str, qint32 pointPos)
Convert a fixed point decimal number from a QString to an integer.
Definition toolbox.h:427
static QList< T > sortedQSet(const QSet< T > &set, const Compare &cmp) noexcept
Definition toolbox.h:126
static QString prettyPrintLocale(const QString &code) noexcept
Pretty print the name of a QLocale.
Definition toolbox.cpp:270
static QRectF boundingRectFromRadius(qreal rx, qreal ry) noexcept
Definition toolbox.h:203
static QPainterPath shapeFromPath(const QPainterPath &path, const QPen &pen, const QBrush &brush, const UnsignedLength &minWidth=UnsignedLength(0)) noexcept
Definition toolbox.cpp:43
static T sorted(const T &container) noexcept
Definition toolbox.h:133
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:151
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:247
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:152
Definition occmodel.cpp:77
type_safe::constrained_type< Length, UnsignedLengthConstraint, UnsignedLengthVerifier > UnsignedLength
Definition length.h:694
Definition uuid.h:186