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