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_LENGTH_H
21 #define LIBREPCB_LENGTH_H
22
23 /*******************************************************************************
24 * Includes
25 ******************************************************************************/
26 #include "../fileio/sexpression.h"
27
28 #include <type_safe/constrained_type.hpp>
29
30 #include <QtCore>
31
32 /*******************************************************************************
33 * Namespace / Forward Declarations
34 ******************************************************************************/
35 namespace librepcb {
36
37 /*******************************************************************************
38 * Typedefs
39 ******************************************************************************/
40
41 /**
42 * @brief This type is the ONLY base type to store all lengths (always in
43 * nanometers)!
44 *
45 * This is the base type of the class ::librepcb::Length.
46 *
47 * This type is normally a 64bit signed integer. 32bit integers could handle
48 * these values also, but is limited to +/-2.147 meters. Maybe this is not
49 * enough for large PCBs or schematics, so it's better to use 64bit variables
50 * ;-)
51 *
52 * @note Set the define USE_32BIT_LENGTH_UNITS in the *.pro file if you want to
53 * use 32bit integers instead of 64bit integers for all length units (maybe your
54 * platform cannot handle 64bit as efficient as 32bit integers).
55 *
56 * @see ::librepcb::Length
57 */
58 #ifdef USE_32BIT_LENGTH_UNITS
59 typedef qint32 LengthBase_t;
60 #else
61 typedef qint64 LengthBase_t;
62 #endif
63
64 /*******************************************************************************
65 * Class Length
66 ******************************************************************************/
67
68 /**
69 * @brief The Length class is used to represent a length (for example 12.75
70 * millimeters)
71 *
72 * This class is used to represent ALL length values in Symbols, Schematics,
73 * Footprints, Layouts and so on. You should never use another length type, like
74 * integer or float! It's very important to have a consistent length type over
75 * the whole project.
76 *
77 * All lengths are stored in the integer base type ::librepcb::LengthBase_t. The
78 * internal unit is always nanometers, but this class provides also some
79 * converting methods to other units. Read the documentation of
80 * ::librepcb::LengthBase_t for more details.
81 */
82 class Length {
Q_DECLARE_TR_FUNCTIONS(Length)83 Q_DECLARE_TR_FUNCTIONS(Length)
84
85 public:
86 // Constructors / Destructor
87
88 /**
89 * @brief Default Constructor
90 *
91 * The length will be initialized with zero nanometers.
92 */
93 constexpr Length() noexcept : Length(0) {}
94
95 /**
96 * @brief Copy Constructor
97 *
98 * @param length Another Length object
99 */
Length(const Length & length)100 constexpr Length(const Length& length) noexcept
101 : mNanometers(length.mNanometers) {}
102
103 /**
104 * @brief Constructor with length in nanometers
105 *
106 * @param nanometers The length in nanometers
107 */
Length(LengthBase_t nanometers)108 constexpr Length(LengthBase_t nanometers) noexcept
109 : mNanometers(nanometers) {}
110
111 /**
112 * @brief Destructor
113 */
114 ~Length() = default;
115
116 // Setters
117
118 /**
119 * @brief Set the length in nanometers
120 *
121 * @param nanometers The length in nanometers
122 */
setLengthNm(LengthBase_t nanometers)123 void setLengthNm(LengthBase_t nanometers) noexcept {
124 mNanometers = nanometers;
125 }
126
127 /**
128 * @brief Set the length in millimeters
129 *
130 * @param millimeters The length in millimeters
131 *
132 * @warning Please note that this method can decrease the precision of the
133 * length! If you need a length which is located exactly on the grid of a
134 * QGraphicsView (which is often required), you need to call mapToGrid()
135 * afterwards!
136 *
137 * @throws RangeError If the argument is out of range, a RangeError
138 * exception will be thrown
139 */
setLengthMm(qreal millimeters)140 void setLengthMm(qreal millimeters) { setLengthFromFloat(millimeters * 1e6); }
141
142 /**
143 * @brief Set the length in millimeters, represented in a QString
144 *
145 * @param millimeters The length in millimeters in a QString with locale "C"
146 *
147 * @note This method is useful to read lengths from files! The problem with
148 * decreased precision does NOT exist by using this method!
149 *
150 * @throw Exception If the string is not valid or the number is out of
151 * range, an Exception will be thrown
152 *
153 * @see #toMmString(), #fromMm(const QString&, const Length&)
154 */
setLengthMm(const QString & millimeters)155 void setLengthMm(const QString& millimeters) {
156 mNanometers = mmStringToNm(millimeters);
157 }
158
159 /**
160 * @brief Set the length in inches
161 *
162 * @param inches The length in inches
163 *
164 * @warning Please note that this method can decrease the precision of the
165 * length! If you need a length which is located exactly on the grid of a
166 * QGraphicsView (which is often required), you need to call mapToGrid()
167 * afterwards!
168 *
169 * @throws RangeError If the argument is out of range, a RangeError
170 * exception will be thrown
171 */
setLengthInch(qreal inches)172 void setLengthInch(qreal inches) { setLengthFromFloat(inches * sNmPerInch); }
173
174 /**
175 * @brief Set the length in mils (1/1000 inch)
176 *
177 * @param mils The length in mils
178 *
179 * @warning Please note that this method can decrease the precision of the
180 * length! If you need a length which is located exactly on the grid of a
181 * QGraphicsView (which is often required), you need to call mapToGrid()
182 * afterwards!
183 *
184 * @throws RangeError If the argument is out of range, a RangeError
185 * exception will be thrown
186 */
setLengthMil(qreal mils)187 void setLengthMil(qreal mils) { setLengthFromFloat(mils * sNmPerMil); }
188
189 /**
190 * @brief Set the length in pixels (from QGraphics* objects)
191 *
192 * @param pixels The length in pixels, from a QGraphics* object
193 *
194 * @note This method is useful to read lengths from QGraphics* objects.
195 *
196 * @warning Please note that this method can decrease the precision of the
197 * length! If you need a length which is located exactly on the grid of a
198 * QGraphicsView (which is often required), you need to call mapToGrid()
199 * afterwards!
200 *
201 * @throws RangeError If the argument is out of range, a RangeError
202 * exception will be thrown
203 */
setLengthPx(qreal pixels)204 void setLengthPx(qreal pixels) { setLengthFromFloat(pixels * sNmPerPixel); }
205
206 // Conversions
207
208 /**
209 * @brief Get the length in nanometers
210 *
211 * @return The length in nanometers
212 */
toNm()213 LengthBase_t toNm() const noexcept { return mNanometers; }
214
215 /**
216 * @brief Get the length in nanometers as a QString
217 *
218 * @return The length in nanometers as a QString. The used locale is always
219 * "C".
220 */
toNmString()221 QString toNmString() const noexcept { return QString::number(toNm()); }
222
223 /**
224 * @brief Get the length in millimeters
225 *
226 * @return The length in millimeters
227 *
228 * @warning Be careful with this method, as it can decrease the precision!
229 */
toMm()230 qreal toMm() const noexcept { return (qreal)mNanometers / 1e6; }
231
232 /**
233 * @brief Get the length in millimeters as a QString
234 *
235 * @return The length in millimeters as a QString. The used locale is always
236 * "C".
237 *
238 * @note This method is useful to store lengths in files. The problem with
239 * decreased precision does NOT exist by using this method!
240 *
241 * @see #setLengthMm(const QString&), #fromMm(const QString&, const Length&)
242 */
243 QString toMmString() const noexcept;
244
245 /**
246 * @brief Get the length in inches
247 *
248 * @return The length in inches
249 *
250 * @warning Be careful with this method, as it can decrease the precision!
251 */
toInch()252 qreal toInch() const noexcept { return (qreal)mNanometers / sNmPerInch; }
253
254 /**
255 * @brief Get the length in mils (1/1000 inches)
256 *
257 * @return The length in mils
258 *
259 * @warning Be careful with this method, as it can decrease the precision!
260 */
toMil()261 qreal toMil() const noexcept { return (qreal)mNanometers / sNmPerMil; }
262
263 /**
264 * @brief Get the length in pixels (for QGraphics* objects)
265 *
266 * @return The length in QGraphics* pixels
267 *
268 * @note This method is useful to set the length/position of a QGraphics*
269 * object.
270 *
271 * @warning Be careful with this method, as it can decrease the precision!
272 */
toPx()273 qreal toPx() const noexcept { return mNanometers * sPixelsPerNm; }
274
275 // General Methods
276
277 /**
278 * @brief Get a Length object with absolute value (mNanometers >= 0)
279 *
280 * @return A new Length object with absolute value
281 *
282 * @see ::librepcb::Length::makeAbs()
283 */
284 Length abs() const noexcept;
285
286 /**
287 * @brief Make the length absolute (mNanometers >= 0)
288 *
289 * @return A reference to the modified object
290 *
291 * @see ::librepcb::Length::abs()
292 */
293 Length& makeAbs() noexcept;
294
295 /**
296 * @brief Get a Length object which is mapped to a specific grid interval
297 *
298 * @param gridInterval The grid interval in nanometers (e.g. 2540000
299 * for 2.54mm). If this parameter is zero, this method will do nothing.
300 *
301 * @return A new Length object which is mapped to the grid
302 *
303 * @see mapToGrid()
304 */
305 Length mappedToGrid(const Length& gridInterval) const noexcept;
306
307 /**
308 * @brief Map this Length object to a specific grid interval
309 *
310 * @param gridInterval The grid interval in nanometers (e.g. 2540000
311 * for 2.54mm). If this parameter is zero, this method will do nothing.
312 *
313 * @return A reference to the modified object
314 *
315 * @see mappedToGrid()
316 */
317 Length& mapToGrid(const Length& gridInterval) noexcept;
318
319 /**
320 * @brief Get a Length object which is scaled with a specific factor
321 *
322 * @param factor The scale factor (1.0 does nothing)
323 *
324 * @return A new Length object which is scaled
325 *
326 * @warning Be careful with this method, as it can decrease the precision!
327 * To scale with an integer factor, use #operator*() instead.
328 *
329 * @see scale()
330 */
331 Length scaled(qreal factor) const noexcept;
332
333 /**
334 * @brief Scale this Length object with a specific factor
335 *
336 * @param factor The scale factor (1.0 does nothing)
337 *
338 * @return A reference to the modified object
339 *
340 * @warning Be careful with this method, as it can decrease the precision!
341 * To scale with an integer factor, use #operator*=() instead.
342 *
343 * @see scaled()
344 */
345 Length& scale(qreal factor) noexcept;
346
347 // Static Functions
348
349 /**
350 * @brief Get a Length object with a specific length and map it to a specific
351 * grid
352 *
353 * @param millimeters See setLengthMm(qreal)
354 * @param gridInterval See mapToGrid()
355 *
356 * @return A new Length object with a length which is mapped to the specified
357 * grid
358 *
359 * @warning Please note that this method can decrease the precision of the
360 * length! If you need a length which is located exactly on the grid of a
361 * QGraphicsView (which is often required), you need to call mapToGrid()
362 * afterwards!
363 *
364 * @throws RangeError If the argument is out of range, a RangeError
365 * exception will be thrown
366 */
367 static Length fromMm(qreal millimeters,
368 const Length& gridInterval = Length(0));
369
370 /**
371 * @brief Get a Length object with a specific length and map it to a specific
372 * grid
373 *
374 * This method can be used to create a Length object from a QString which
375 * contains a floating point number in millimeters, like QString("123.456")
376 * for 123.456 millimeters. The string must not depend on the locale settings
377 * (see QLocale), it have always to represent a number in the "C" locale. The
378 * maximum count of decimals after the decimal point is 6, because the 6th
379 * decimal represents one nanometer.
380 *
381 * @param millimeters See setLengthMm(const QString&)
382 * @param gridInterval See mapToGrid()
383 *
384 * @return A new Length object with a length which is mapped to the specified
385 * grid
386 *
387 * @note This method is useful to read lengths from files! The problem with
388 * decreased precision does NOT exist by using this method!
389 *
390 * @throw Exception If the argument is invalid or out of range, an
391 * Exception will be thrown
392 *
393 * @see #setLengthMm(const QString&), #toMmString()
394 */
395 static Length fromMm(const QString& millimeters,
396 const Length& gridInterval = Length(0));
397
398 /**
399 * @brief Get a Length object with a specific length and map it to a specific
400 * grid
401 *
402 * @param inches See setLengthInch()
403 * @param gridInterval See mapToGrid()
404 *
405 * @return A new Length object with a length which is mapped to the specified
406 * grid
407 *
408 * @warning Please note that this method can decrease the precision of the
409 * length! If you need a length which is located exactly on the grid of a
410 * QGraphicsView (which is often required), you need to call mapToGrid()
411 * afterwards!
412 *
413 * @throws RangeError If the argument is out of range, a RangeError
414 * exception will be thrown
415 */
416 static Length fromInch(qreal inches, const Length& gridInterval = Length(0));
417
418 /**
419 * @brief Get a Length object with a specific length and map it to a specific
420 * grid
421 *
422 * @param mils See setLengthMil()
423 * @param gridInterval See mapToGrid()
424 *
425 * @return A new Length object with a length which is mapped to the specified
426 * grid
427 *
428 * @warning Please note that this method can decrease the precision of the
429 * length! If you need a length which is located exactly on the grid of a
430 * QGraphicsView (which is often required), you need to call mapToGrid()
431 * afterwards!
432 *
433 * @throws RangeError If the argument is out of range, a RangeError
434 * exception will be thrown
435 */
436 static Length fromMil(qreal mils, const Length& gridInterval = Length(0));
437
438 /**
439 * @brief Get a Length object with a specific length and map it to a specific
440 * grid
441 *
442 * @param pixels See setLengthPx()
443 * @param gridInterval See mapToGrid()
444 *
445 * @return A new Length object with a length which is mapped to the specified
446 * grid
447 *
448 * @note This method is useful to set the length/position of a QGraphics*
449 * object.
450 *
451 * @warning Please note that this method can decrease the precision of the
452 * length! If you need a length which is located exactly on the grid of a
453 * QGraphicsView (which is often required), you need to call mapToGrid()
454 * afterwards!
455 *
456 * @throws RangeError If the argument is out of range, a RangeError
457 * exception will be thrown
458 */
459 static Length fromPx(qreal pixels, const Length& gridInterval = Length(0));
460
461 /**
462 * @brief Get the smallest possible length value
463 *
464 * @return Smallest possible length
465 */
466 static Length min() noexcept;
467
468 /**
469 * @brief Get the highest possible length value
470 *
471 * @return Highest possible length
472 */
473 static Length max() noexcept;
474
475 // Operators
476 Length& operator=(const Length& rhs) {
477 mNanometers = rhs.mNanometers;
478 return *this;
479 }
480 Length& operator+=(const Length& rhs) {
481 mNanometers += rhs.mNanometers;
482 return *this;
483 }
484 Length& operator-=(const Length& rhs) {
485 mNanometers -= rhs.mNanometers;
486 return *this;
487 }
488 Length& operator*=(const Length& rhs) {
489 mNanometers *= rhs.mNanometers;
490 return *this;
491 }
492 Length& operator*=(LengthBase_t rhs) {
493 mNanometers *= rhs;
494 return *this;
495 }
496 Length& operator/=(const Length& rhs) {
497 mNanometers /= rhs.mNanometers;
498 return *this;
499 }
500 Length& operator/=(LengthBase_t rhs) {
501 mNanometers /= rhs;
502 return *this;
503 }
504 Length operator+(const Length& rhs) const {
505 return Length(mNanometers + rhs.mNanometers);
506 }
507 Length operator-() const { return Length(-mNanometers); }
508 Length operator-(const Length& rhs) const {
509 return Length(mNanometers - rhs.mNanometers);
510 }
511 Length operator*(const Length& rhs) const {
512 return Length(mNanometers * rhs.mNanometers);
513 }
514 Length operator*(LengthBase_t rhs) const { return Length(mNanometers * rhs); }
515 Length operator/(const Length& rhs) const {
516 return Length(mNanometers / rhs.mNanometers);
517 }
518 Length operator/(LengthBase_t rhs) const { return Length(mNanometers / rhs); }
519 Length operator%(const Length& rhs) const {
520 return Length(mNanometers % rhs.mNanometers);
521 }
522 constexpr bool operator>(const Length& rhs) const {
523 return mNanometers > rhs.mNanometers;
524 }
525 constexpr bool operator>(LengthBase_t rhs) const { return mNanometers > rhs; }
526 constexpr bool operator<(const Length& rhs) const {
527 return mNanometers < rhs.mNanometers;
528 }
529 constexpr bool operator<(LengthBase_t rhs) const { return mNanometers < rhs; }
530 constexpr bool operator>=(const Length& rhs) const {
531 return mNanometers >= rhs.mNanometers;
532 }
533 constexpr bool operator>=(LengthBase_t rhs) const {
534 return mNanometers >= rhs;
535 }
536 constexpr bool operator<=(const Length& rhs) const {
537 return mNanometers <= rhs.mNanometers;
538 }
539 constexpr bool operator<=(LengthBase_t rhs) const {
540 return mNanometers <= rhs;
541 }
542 constexpr bool operator==(const Length& rhs) const {
543 return mNanometers == rhs.mNanometers;
544 }
545 constexpr bool operator==(LengthBase_t rhs) const {
546 return mNanometers == rhs;
547 }
548 constexpr bool operator!=(const Length& rhs) const {
549 return mNanometers != rhs.mNanometers;
550 }
551 constexpr bool operator!=(LengthBase_t rhs) const {
552 return mNanometers != rhs;
553 }
554
555 private:
556 // Private Functions
557
558 /**
559 * @brief Set the length from a floating point number in nanometers
560 *
561 * This is a helper method for the setLength*() methods.
562 *
563 * @param nanometers A floating point number in nanometers.
564 *
565 * @note The parameter is NOT an integer although we don't use numbers smaller
566 * than one nanometer. This way, the range of this parameter is much greater
567 * and we can compare the value with the range of an integer. If the value is
568 * outside the range of an integer, we will throw an exception. If we would
569 * pass the length as an integer, we couldn't detect such under-/overflows!
570 */
571 void setLengthFromFloat(qreal nanometers);
572
573 // Private Static Functions
574
575 /**
576 * @brief Map a length in nanometers to a grid interval in nanometers
577 *
578 * This is a helper function for mapToGrid().
579 *
580 * @param nanometers The length we want to map to the grid
581 * @param gridInterval The grid interval
582 *
583 * @return The length which is mapped to the grid (always a multiple of
584 * gridInterval)
585 */
586 static LengthBase_t mapNmToGrid(LengthBase_t nanometers,
587 const Length& gridInterval) noexcept;
588
589 /**
590 * @brief Convert a length from a QString (in millimeters) to an integer (in
591 * nanometers)
592 *
593 * This is a helper function for Length(const QString&) and setLengthMm().
594 *
595 * @param millimeters A QString which contains a floating point number with
596 * maximum six decimals after the decimal point. The locale of the string have
597 * to be "C"! Example: QString("-1234.56") for -1234.56mm
598 *
599 * @return The length in nanometers
600 */
601 static LengthBase_t mmStringToNm(const QString& millimeters);
602
603 // Private Member Variables
604 LengthBase_t mNanometers; ///< the length in nanometers
605
606 // Static Length Converting Constants
607 static constexpr LengthBase_t sNmPerInch = 25400000; ///< 1 inch = 25.4mm
608 static constexpr LengthBase_t sNmPerMil = 25400; ///< 1 inch = 25.4mm
609 static constexpr LengthBase_t sPixelsPerInch =
610 72; ///< 72 dpi for the QGraphics* objects
611 static constexpr qreal sNmPerPixel = (qreal)sNmPerInch / sPixelsPerInch;
612 static constexpr qreal sPixelsPerNm = (qreal)sPixelsPerInch / sNmPerInch;
613 };
614
615 /*******************************************************************************
616 * Non-Member Functions
617 ******************************************************************************/
618
619 template <>
serialize(const Length & obj)620 inline SExpression serialize(const Length& obj) {
621 return SExpression::createToken(obj.toMmString());
622 }
623
624 template <>
deserialize(const SExpression & sexpr,const Version & fileFormat)625 inline Length deserialize(const SExpression& sexpr, const Version& fileFormat) {
626 Q_UNUSED(fileFormat);
627 return Length::fromMm(sexpr.getValue());
628 }
629
630 inline QDataStream& operator<<(QDataStream& stream, const Length& length) {
631 stream << length.toMm();
632 return stream;
633 }
634
635 inline QDebug operator<<(QDebug stream, const Length& length) {
636 stream << QString("Length(%1mm)").arg(length.toMm());
637 return stream;
638 }
639
640 inline uint qHash(const Length& key, uint seed = 0) noexcept {
641 return ::qHash(key.toNm(), seed);
642 }
643
644 /*******************************************************************************
645 * Class UnsignedLength
646 ******************************************************************************/
647
648 struct UnsignedLengthVerifier {
649 template <typename Value, typename Predicate>
650 static constexpr auto verify(Value&& val, const Predicate& p) ->
651 typename std::decay<Value>::type {
652 return p(val) ? std::forward<Value>(val)
653 : (throw RuntimeError(__FILE__, __LINE__,
654 Length::tr("Value must be >= 0!")),
655 std::forward<Value>(val));
656 }
657 };
658
659 struct UnsignedLengthConstraint {
operatorUnsignedLengthConstraint660 constexpr bool operator()(const Length& l) const noexcept { return l >= 0; }
661 };
662
663 /**
664 * UnsignedLength is a wrapper around a librepcb::Length object which is
665 * guaranteed to always contain an unsigned (i.e. >= 0) value.
666 *
667 * The constructor throws an exception if constructed from a librepcb::Length
668 * object with a negative value!
669 */
670 using UnsignedLength =
671 type_safe::constrained_type<Length, UnsignedLengthConstraint,
672 UnsignedLengthVerifier>;
673
674 inline UnsignedLength operator+(const UnsignedLength& lhs,
675 const UnsignedLength& rhs) noexcept {
676 return UnsignedLength(*lhs +
677 *rhs); // will not throw as long as there's no overflow
678 }
679
680 inline UnsignedLength& operator+=(UnsignedLength& lhs,
681 const UnsignedLength& rhs) noexcept {
682 lhs = lhs + rhs; // will not throw as long as there's no overflow
683 return lhs;
684 }
685
686 inline Length operator*(const UnsignedLength& lhs, LengthBase_t rhs) noexcept {
687 return (*lhs) * rhs;
688 }
689 inline Length operator/(const UnsignedLength& lhs, LengthBase_t rhs) noexcept {
690 return (*lhs) / rhs;
691 }
692 inline Length operator+(const Length& lhs, const UnsignedLength& rhs) noexcept {
693 return lhs + *rhs;
694 }
695 inline Length operator+(const UnsignedLength& lhs, const Length& rhs) noexcept {
696 return *lhs + rhs;
697 }
698 inline Length operator-(const Length& lhs, const UnsignedLength& rhs) noexcept {
699 return lhs - *rhs;
700 }
701 inline Length operator-(const UnsignedLength& lhs, const Length& rhs) noexcept {
702 return *lhs - rhs;
703 }
704 inline Length operator-(const UnsignedLength& lhs) noexcept {
705 return -(*lhs);
706 }
707 inline bool operator>(const UnsignedLength& lhs, const Length& rhs) noexcept {
708 return (*lhs) > rhs;
709 }
710 inline bool operator>(const UnsignedLength& lhs, LengthBase_t rhs) noexcept {
711 return (*lhs) > rhs;
712 }
713 inline bool operator>=(const UnsignedLength& lhs, const Length& rhs) noexcept {
714 return (*lhs) >= rhs;
715 }
716 inline bool operator>=(const UnsignedLength& lhs, LengthBase_t rhs) noexcept {
717 return (*lhs) >= rhs;
718 }
719 inline bool operator<(const UnsignedLength& lhs, const Length& rhs) noexcept {
720 return (*lhs) <= rhs;
721 }
722 inline bool operator<(const UnsignedLength& lhs, LengthBase_t rhs) noexcept {
723 return (*lhs) <= rhs;
724 }
725 inline bool operator==(const UnsignedLength& lhs, const Length& rhs) noexcept {
726 return (*lhs) == rhs;
727 }
728 inline bool operator==(const UnsignedLength& lhs, LengthBase_t rhs) noexcept {
729 return (*lhs) == rhs;
730 }
731 inline bool operator!=(const UnsignedLength& lhs, const Length& rhs) noexcept {
732 return (*lhs) != rhs;
733 }
734 inline bool operator!=(const UnsignedLength& lhs, LengthBase_t rhs) noexcept {
735 return (*lhs) != rhs;
736 }
737
738 template <>
serialize(const UnsignedLength & obj)739 inline SExpression serialize(const UnsignedLength& obj) {
740 return SExpression::createToken(obj->toMmString());
741 }
742
743 template <>
deserialize(const SExpression & sexpr,const Version & fileFormat)744 inline UnsignedLength deserialize(const SExpression& sexpr,
745 const Version& fileFormat) {
746 return UnsignedLength(deserialize<Length>(sexpr, fileFormat)); // can throw
747 }
748
749 inline QDataStream& operator<<(QDataStream& stream,
750 const UnsignedLength& length) {
751 stream << length->toMm();
752 return stream;
753 }
754
755 inline QDebug operator<<(QDebug stream, const UnsignedLength& length) {
756 stream << QString("UnsignedLength(%1mm)").arg(length->toMm());
757 return stream;
758 }
759
760 inline uint qHash(const UnsignedLength& key, uint seed = 0) noexcept {
761 return ::qHash(key->toNm(), seed);
762 }
763
764 /*******************************************************************************
765 * Class PositiveLength
766 ******************************************************************************/
767
768 struct PositiveLengthVerifier {
769 template <typename Value, typename Predicate>
770 static constexpr auto verify(Value&& val, const Predicate& p) ->
771 typename std::decay<Value>::type {
772 return p(val) ? std::forward<Value>(val)
773 : (throw RuntimeError(__FILE__, __LINE__,
774 Length::tr("Value must be > 0!")),
775 std::forward<Value>(val));
776 }
777 };
778
779 struct PositiveLengthConstraint {
operatorPositiveLengthConstraint780 constexpr bool operator()(const Length& l) const noexcept { return l > 0; }
781 };
782
783 /**
784 * PositiveLength is a wrapper around a librepcb::Length object which is
785 * guaranteed to always contain a positive (i.e. > 0) value.
786 *
787 * The constructor throws an exception if constructed from a librepcb::Length
788 * object with a negative or zero value!
789 */
790 using PositiveLength =
791 type_safe::constrained_type<Length, PositiveLengthConstraint,
792 PositiveLengthVerifier>;
793
positiveToUnsigned(const PositiveLength & l)794 inline UnsignedLength positiveToUnsigned(const PositiveLength& l) noexcept {
795 return UnsignedLength(*l);
796 }
797
798 inline PositiveLength operator+(const PositiveLength& lhs,
799 const PositiveLength& rhs) noexcept {
800 return PositiveLength(*lhs +
801 *rhs); // will not throw as long as there's no overflow
802 }
803
804 inline PositiveLength operator+(const PositiveLength& lhs,
805 const UnsignedLength& rhs) noexcept {
806 return PositiveLength(*lhs +
807 *rhs); // will not throw as long as there's no overflow
808 }
809
810 inline PositiveLength operator+(const UnsignedLength& lhs,
811 const PositiveLength& rhs) noexcept {
812 return PositiveLength(*lhs +
813 *rhs); // will not throw as long as there's no overflow
814 }
815
816 inline PositiveLength& operator+=(PositiveLength& lhs,
817 const PositiveLength& rhs) noexcept {
818 lhs = lhs + rhs; // will not throw as long as there's no overflow
819 return lhs;
820 }
821
822 inline PositiveLength& operator+=(PositiveLength& lhs,
823 const UnsignedLength& rhs) noexcept {
824 lhs = lhs + rhs; // will not throw as long as there's no overflow
825 return lhs;
826 }
827
828 inline UnsignedLength& operator+=(UnsignedLength& lhs,
829 const PositiveLength& rhs) noexcept {
830 lhs = positiveToUnsigned(
831 lhs + rhs); // will not throw as long as there's no overflow
832 return lhs;
833 }
834
835 inline Length operator*(const PositiveLength& lhs, LengthBase_t rhs) noexcept {
836 return (*lhs) * rhs;
837 }
838 inline Length operator/(const PositiveLength& lhs, LengthBase_t rhs) noexcept {
839 return (*lhs) / rhs;
840 }
841 inline Length operator+(const Length& lhs, const PositiveLength& rhs) noexcept {
842 return lhs + *rhs;
843 }
844 inline Length operator+(const PositiveLength& lhs, const Length& rhs) noexcept {
845 return *lhs + rhs;
846 }
847 inline Length operator-(const Length& lhs, const PositiveLength& rhs) noexcept {
848 return lhs - *rhs;
849 }
850 inline Length operator-(const PositiveLength& lhs, const Length& rhs) noexcept {
851 return *lhs - rhs;
852 }
853 inline Length operator-(const UnsignedLength& lhs,
854 const PositiveLength& rhs) noexcept {
855 return *lhs - *rhs;
856 }
857 inline Length operator-(const PositiveLength& lhs,
858 const UnsignedLength& rhs) noexcept {
859 return *lhs - *rhs;
860 }
861 inline Length operator-(const PositiveLength& lhs) noexcept {
862 return -(*lhs);
863 }
864 inline bool operator>(const UnsignedLength& lhs,
865 const PositiveLength& rhs) noexcept {
866 return (*lhs) > (*rhs);
867 }
868 inline bool operator>(const PositiveLength& lhs,
869 const UnsignedLength& rhs) noexcept {
870 return (*lhs) > (*rhs);
871 }
872 inline bool operator>(const PositiveLength& lhs, const Length& rhs) noexcept {
873 return (*lhs) > rhs;
874 }
875 inline bool operator>(const PositiveLength& lhs, LengthBase_t rhs) noexcept {
876 return (*lhs) > rhs;
877 }
878 inline bool operator>=(const UnsignedLength& lhs,
879 const PositiveLength& rhs) noexcept {
880 return (*lhs) >= (*rhs);
881 }
882 inline bool operator>=(const PositiveLength& lhs,
883 const UnsignedLength& rhs) noexcept {
884 return (*lhs) >= (*rhs);
885 }
886 inline bool operator>=(const PositiveLength& lhs, const Length& rhs) noexcept {
887 return (*lhs) >= rhs;
888 }
889 inline bool operator>=(const PositiveLength& lhs, LengthBase_t rhs) noexcept {
890 return (*lhs) >= rhs;
891 }
892 inline bool operator<(const UnsignedLength& lhs,
893 const PositiveLength& rhs) noexcept {
894 return (*lhs) < (*rhs);
895 }
896 inline bool operator<(const PositiveLength& lhs,
897 const UnsignedLength& rhs) noexcept {
898 return (*lhs) < (*rhs);
899 }
900 inline bool operator<(const PositiveLength& lhs, const Length& rhs) noexcept {
901 return (*lhs) <= rhs;
902 }
903 inline bool operator<(const PositiveLength& lhs, LengthBase_t rhs) noexcept {
904 return (*lhs) <= rhs;
905 }
906 inline bool operator==(const UnsignedLength& lhs,
907 const PositiveLength& rhs) noexcept {
908 return (*lhs) == (*rhs);
909 }
910 inline bool operator==(const PositiveLength& lhs,
911 const UnsignedLength& rhs) noexcept {
912 return (*lhs) == (*rhs);
913 }
914 inline bool operator==(const PositiveLength& lhs, const Length& rhs) noexcept {
915 return (*lhs) == rhs;
916 }
917 inline bool operator==(const PositiveLength& lhs, LengthBase_t rhs) noexcept {
918 return (*lhs) == rhs;
919 }
920 inline bool operator!=(const UnsignedLength& lhs,
921 const PositiveLength& rhs) noexcept {
922 return (*lhs) != (*rhs);
923 }
924 inline bool operator!=(const PositiveLength& lhs,
925 const UnsignedLength& rhs) noexcept {
926 return (*lhs) != (*rhs);
927 }
928 inline bool operator!=(const PositiveLength& lhs, const Length& rhs) noexcept {
929 return (*lhs) != rhs;
930 }
931 inline bool operator!=(const PositiveLength& lhs, LengthBase_t rhs) noexcept {
932 return (*lhs) != rhs;
933 }
934
935 template <>
serialize(const PositiveLength & obj)936 inline SExpression serialize(const PositiveLength& obj) {
937 return SExpression::createToken(obj->toMmString());
938 }
939
940 template <>
deserialize(const SExpression & sexpr,const Version & fileFormat)941 inline PositiveLength deserialize(const SExpression& sexpr,
942 const Version& fileFormat) {
943 return PositiveLength(deserialize<Length>(sexpr, fileFormat)); // can throw
944 }
945
946 inline QDataStream& operator<<(QDataStream& stream,
947 const PositiveLength& length) {
948 stream << length->toMm();
949 return stream;
950 }
951
952 inline QDebug operator<<(QDebug stream, const PositiveLength& length) {
953 stream << QString("PositiveLength(%1mm)").arg(length->toMm());
954 return stream;
955 }
956
957 inline uint qHash(const PositiveLength& key, uint seed = 0) noexcept {
958 return ::qHash(key->toNm(), seed);
959 }
960
961 /*******************************************************************************
962 * End of File
963 ******************************************************************************/
964
965 } // namespace librepcb
966
967 Q_DECLARE_METATYPE(librepcb::Length)
968
969 #endif // LIBREPCB_LENGTH_H
970