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 QList<T> list = set.values();
119 std::sort(list.begin(), list.end());
120 return list;
121 }
122
123 template <typename T, typename Compare>
124 static QList<T> sortedQSet(const QSet<T>& set, const Compare& cmp) noexcept {
125 QList<T> list = set.values();
126 std::sort(list.begin(), list.end(), cmp);
127 return list;
128 }
129
130 template <typename T>
131 static T sorted(const T& container) noexcept {
132 T copy(container);
133 std::sort(copy.begin(), copy.end());
134 return copy;
135 }
136
147 template <typename T, typename Compare>
148 static void sortNumeric(
149 T& container, Compare compare,
150 Qt::CaseSensitivity caseSensitivity = Qt::CaseInsensitive,
151 bool ignorePunctuation = false) noexcept {
152 QCollator collator;
153 collator.setNumericMode(true);
154 collator.setCaseSensitivity(caseSensitivity);
155 collator.setIgnorePunctuation(ignorePunctuation);
156 std::sort(container.begin(), container.end(),
157 [&collator, &compare](const typename T::value_type& lhs,
158 const typename T::value_type& rhs) {
159 return compare(collator, lhs, rhs);
160 });
161 }
162
170 template <typename T>
171 static void sortNumeric(
172 T& container, Qt::CaseSensitivity caseSensitivity = Qt::CaseInsensitive,
173 bool ignorePunctuation = false) noexcept {
174 return sortNumeric(
175 container,
176 [](const QCollator& collator, const typename T::value_type& lhs,
177 const typename T::value_type& rhs) { return collator(lhs, rhs); },
178 caseSensitivity, ignorePunctuation);
179 }
180
193 static bool isTextUpsideDown(const Angle& rotation) noexcept;
194
195 static QRectF boundingRectFromRadius(qreal radius) noexcept {
196 return QRectF(-radius, -radius, 2 * radius, 2 * radius);
197 }
198
199 static QRectF boundingRectFromRadius(qreal rx, qreal ry) noexcept {
200 return QRectF(-rx, -ry, 2 * rx, 2 * ry);
201 }
202
203 static QRectF adjustedBoundingRect(const QRectF& rect,
204 qreal offset) noexcept {
205 return rect.adjusted(-offset, -offset, offset, offset);
206 }
207
208 static QPainterPath shapeFromPath(
209 const QPainterPath& path, const QPen& pen, const QBrush& brush,
210 const UnsignedLength& minWidth = UnsignedLength(0)) noexcept;
211
212 static std::optional<Length> arcRadius(const Point& p1, const Point& p2,
213 const Angle& angle) noexcept;
214 static std::optional<Point> arcCenter(const Point& p1, const Point& p2,
215 const Angle& angle) noexcept;
216
226 static Angle arcAngle(const Point& p1, const Point& p2,
227 const Point& center = Point(0, 0)) noexcept;
228
242 static Angle arcAngleFrom3Points(const Point& start, const Point& mid,
243 const Point& end) noexcept;
244
253 static Angle angleBetweenPoints(const Point& p1, const Point& p2) noexcept;
254
269 static Point nearestPointOnLine(const Point& p, const Point& l1,
270 const Point& l2) noexcept;
271
284 const Point& p, const Point& l1, const Point& l2,
285 Point* nearest = nullptr) noexcept;
286
300 static QString incrementNumberInString(QString string) noexcept;
301
321 static QStringList expandRangesInString(const QString& string) noexcept;
322
336 static QString cleanUserInputString(const QString& input,
337 const QRegularExpression& removeRegex,
338 bool trim = true, bool toLower = false,
339 bool toUpper = false,
340 const QString& spaceReplacement = " ",
341 int maxLength = -1) noexcept;
342
356 static QString prettyPrintLocale(const QString& code) noexcept;
357
364 template <typename T>
365 static QString floatToString(T value, int decimals,
366 const QLocale& locale) noexcept {
367 QString s = locale.toString(value, 'f', decimals);
368 for (int i = 1; (i < decimals) && s.endsWith(locale.zeroDigit()); ++i) {
369 s.chop(1);
370 }
371 if (qAbs(value) >= 1000) {
372 s.remove(locale.groupSeparator());
373 }
374 return s;
375 }
376
383 template <typename T>
384 static QString decimalFixedPointToString(T value, qint32 pointPos) noexcept {
385 using UnsignedT = typename std::make_unsigned<T>::type;
386
387 if (value == 0) {
388 // special case
389 return "0.0";
390 }
391
392 UnsignedT valueAbs;
393 if (value < 0) {
394 valueAbs = -static_cast<UnsignedT>(value);
395 } else {
396 valueAbs = static_cast<UnsignedT>(value);
397 }
398
399 QString str = QString::number(valueAbs);
400 if (str.length() > pointPos) {
401 // pointPos must be > 0 for this to work correctly
402 str.insert(str.length() - pointPos, '.');
403 } else {
404 for (qint32 i = pointPos - str.length(); i != 0; i--) str.insert(0, '0');
405 str.insert(0, "0.");
406 }
407
408 while (str.endsWith('0') && !str.endsWith(".0")) str.chop(1);
409
410 if (value < 0) str.insert(0, '-');
411
412 return str;
413 }
414
422 template <typename T>
423 static T decimalFixedPointFromString(const QString& str, qint32 pointPos) {
424 using UnsignedT = typename std::make_unsigned<T>::type;
425
426 const T min = std::numeric_limits<T>::min();
427 const T max = std::numeric_limits<T>::max();
428 const UnsignedT max_u = std::numeric_limits<UnsignedT>::max();
429
430 enum class State {
431 INVALID,
432 START,
433 AFTER_SIGN,
434 LONELY_DOT,
435 INT_PART,
436 FRAC_PART,
437 EXP,
438 EXP_AFTER_SIGN,
439 EXP_DIGITS,
440 };
441 State state = State::START;
442 UnsignedT valueAbs = 0;
443 bool sign = false;
444 qint32 expOffset = pointPos;
445
446 const quint32 maxExp = std::numeric_limits<quint32>::max();
447 quint32 exp = 0;
448 bool expSign = false;
449
450 for (QChar c : str) {
451 if (state == State::INVALID) {
452 // break the loop, not the switch
453 break;
454 }
455 switch (state) {
456 case State::INVALID:
457 // already checked, but needed to avoid compiler warnings
458 break;
459
460 case State::START:
461 if (c == '-') {
462 sign = true;
463 state = State::AFTER_SIGN;
464 } else if (c == '+') {
465 state = State::AFTER_SIGN;
466 } else if (c == '.') {
467 state = State::LONELY_DOT;
468 } else if (c.isDigit()) {
469 valueAbs = static_cast<UnsignedT>(c.digitValue());
470 state = State::INT_PART;
471 } else {
472 state = State::INVALID;
473 }
474 break;
475
476 case State::AFTER_SIGN:
477 if (c == '.') {
478 state = State::LONELY_DOT;
479 } else if (c.isDigit()) {
480 valueAbs = static_cast<UnsignedT>(c.digitValue());
481 state = State::INT_PART;
482 } else {
483 state = State::INVALID;
484 }
485 break;
486
487 case State::LONELY_DOT:
488 if (c.isDigit()) {
489 valueAbs = static_cast<UnsignedT>(c.digitValue());
490 expOffset -= 1;
491 state = State::FRAC_PART;
492 } else {
493 state = State::INVALID;
494 }
495 break;
496
497 case State::INT_PART:
498 if (c == '.') {
499 state = State::FRAC_PART;
500 } else if (c == 'e' || c == 'E') {
501 state = State::EXP;
502 } else if (c.isDigit()) {
503 UnsignedT digit = static_cast<UnsignedT>(c.digitValue());
504 if (valueAbs > (max_u / 10)) {
505 // Would overflow
506 state = State::INVALID;
507 break;
508 }
509 valueAbs *= 10;
510 if (valueAbs > (max_u - digit)) {
511 // Would overflow
512 state = State::INVALID;
513 break;
514 }
515 valueAbs += digit;
516 } else {
517 state = State::INVALID;
518 }
519 break;
520
521 case State::FRAC_PART:
522 if (c == 'e' || c == 'E') {
523 state = State::EXP;
524 } else if (c.isDigit()) {
525 UnsignedT digit = static_cast<UnsignedT>(c.digitValue());
526 if (valueAbs > (max_u / 10)) {
527 // Would overflow
528 state = State::INVALID;
529 break;
530 }
531 valueAbs *= 10;
532 if (valueAbs > (max_u - digit)) {
533 // Would overflow
534 state = State::INVALID;
535 break;
536 }
537 valueAbs += digit;
538 expOffset -= 1;
539 } else {
540 state = State::INVALID;
541 }
542 break;
543
544 case State::EXP:
545 if (c == '-') {
546 expSign = true;
547 state = State::EXP_AFTER_SIGN;
548 } else if (c == '+') {
549 state = State::EXP_AFTER_SIGN;
550 } else if (c.isDigit()) {
551 exp = static_cast<quint32>(c.digitValue());
552 state = State::EXP_DIGITS;
553 } else {
554 state = State::INVALID;
555 }
556 break;
557
558 case State::EXP_AFTER_SIGN:
559 if (c.isDigit()) {
560 exp = static_cast<quint32>(c.digitValue());
561 state = State::EXP_DIGITS;
562 } else {
563 state = State::INVALID;
564 }
565 break;
566
567 case State::EXP_DIGITS:
568 if (c.isDigit()) {
569 quint32 digit = static_cast<quint32>(c.digitValue());
570 if (exp > (maxExp / 10)) {
571 // Would overflow
572 state = State::INVALID;
573 break;
574 }
575 exp *= 10;
576 if (exp > (maxExp - digit)) {
577 // Would overflow
578 state = State::INVALID;
579 break;
580 }
581 exp += digit;
582 } else {
583 state = State::INVALID;
584 }
585 }
586 }
587
588 bool ok = true;
589 switch (state) {
590 case State::INVALID:
591 case State::START:
592 case State::AFTER_SIGN:
593 case State::LONELY_DOT:
594 case State::EXP:
595 case State::EXP_AFTER_SIGN:
596 ok = false;
597 break;
598
599 case State::INT_PART:
600 case State::FRAC_PART:
601 case State::EXP_DIGITS:
602 break;
603 }
604
605 if (ok) {
606 quint32 expOffsetAbs;
607 if (expOffset < 0) {
608 expOffsetAbs = -static_cast<quint32>(expOffset);
609 } else {
610 expOffsetAbs = static_cast<quint32>(expOffset);
611 }
612
613 if (expSign == (expOffset < 0)) {
614 if (exp > (maxExp - expOffsetAbs)) {
615 // would overflow
616 ok = false;
617 } else {
618 exp += expOffsetAbs;
619 }
620 } else {
621 if (exp < expOffsetAbs) {
622 // would overflow
623 ok = false;
624 } else {
625 exp -= expOffsetAbs;
626 }
627 }
628 }
629
630 T result = 0;
631 if (ok) {
632 // No need to apply exponent or sign if valueAbs is zero
633 if (valueAbs != 0) {
634 if (expSign) {
635 for (quint32 i = 0; i < exp; i++) {
636 if ((valueAbs % 10) != 0) {
637 // more decimal digits than allowed
638 ok = false;
639 break;
640 }
641 valueAbs /= 10;
642 }
643 } else {
644 for (quint32 i = 0; i < exp; i++) {
645 if (valueAbs > (max_u / 10)) {
646 // would overflow
647 ok = false;
648 break;
649 }
650 valueAbs *= 10;
651 }
652 }
653 if (ok) {
654 if (sign) {
655 if (valueAbs > static_cast<UnsignedT>(min)) {
656 ok = false;
657 } else {
658 result = static_cast<T>(-valueAbs);
659 }
660 } else {
661 if (valueAbs > static_cast<UnsignedT>(max)) {
662 ok = false;
663 } else {
664 result = static_cast<T>(valueAbs);
665 }
666 }
667 }
668 }
669 }
670
671 if (!ok) {
672 throw RuntimeError(
673 __FILE__, __LINE__,
674 tr("Invalid fixed point number string: \"%1\"").arg(str));
675 }
676 return result;
677 }
678
679private:
683 static QStringList expandRangesInString(
684 const QString& input,
685 const QVector<std::tuple<int, int, QStringList>>& replacements) noexcept;
686};
687
688/*******************************************************************************
689 * End of File
690 ******************************************************************************/
691
692} // namespace librepcb
693
694#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:203
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:195
static QString decimalFixedPointToString(T value, qint32 pointPos) noexcept
Convert a fixed point decimal number from an integer to a QString.
Definition toolbox.h:384
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:365
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:171
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:423
static QList< T > sortedQSet(const QSet< T > &set, const Compare &cmp) noexcept
Definition toolbox.h:124
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:199
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:131
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:148
Definition occmodel.cpp:76
type_safe::constrained_type< Length, UnsignedLengthConstraint, UnsignedLengthVerifier > UnsignedLength
Definition length.h:694
Definition uuid.h:186