9 #include <QDBusArgument>
10 #include <QJsonObject>
43 namespace swift::misc::physical_quantities
45 template <
class MU,
class PQ>
51 template <
class MU,
class PQ>
57 template <
class MU,
class PQ>
60 m_unit = CMeasurementUnit::unitFromSymbol<MU>(unitName);
63 template <
class MU,
class PQ>
66 return m_unit.getSymbol(
true);
69 template <
class MU,
class PQ>
71 : m_value(unit.isNull() ? 0.0 : value), m_unit(unit)
73 Q_ASSERT_X(!std::isnan(
value), Q_FUNC_INFO,
"nan value");
74 Q_ASSERT_X(!std::isinf(
value), Q_FUNC_INFO,
"infinity");
77 template <
class MU,
class PQ>
83 template <
class MU,
class PQ>
86 if (
this == &other)
return true;
88 if (this->isNull())
return other.
isNull();
89 if (other.
isNull())
return false;
91 double diff = std::abs(m_value - other.
value(m_unit));
92 return diff <= m_unit.getEpsilon();
95 template <
class MU,
class PQ>
98 m_value += other.
value(m_unit);
102 template <
class MU,
class PQ>
108 template <
class MU,
class PQ>
114 template <
class MU,
class PQ>
117 m_value -= other.
value(m_unit);
121 template <
class MU,
class PQ>
124 return m_unit.isEpsilon(m_value);
127 template <
class MU,
class PQ>
130 return !this->isZeroEpsilonConsidered() && m_value > 0;
133 template <
class MU,
class PQ>
136 return !this->isZeroEpsilonConsidered() && m_value < 0;
139 template <
class MU,
class PQ>
142 if (this->isNull() || qFuzzyIsNull(m_value)) {
return *this->derived(); }
143 if (m_value < 0) { m_value *= -1.0; }
144 return *this->derived();
147 template <
class MU,
class PQ>
150 if (this->isNull() || qFuzzyIsNull(m_value)) {
return *this->derived(); }
151 if (m_value > 0) { m_value *= -1.0; }
152 return *this->derived();
155 template <
class MU,
class PQ>
158 if (this->isNull() || qFuzzyIsNull(m_value)) {
return *this->derived(); }
159 if (m_value >= 0) {
return *this->derived(); }
160 PQ copy(*this->derived());
161 return copy.makePositive();
164 template <
class MU,
class PQ>
167 constexpr
double NaN = std::numeric_limits<double>::quiet_NaN();
168 argument << (this->isNull() ? NaN : this->value(UnitClass::defaultUnit()));
171 template <
class MU,
class PQ>
175 m_unit = UnitClass::defaultUnit();
176 if (std::isnan(m_value)) { this->setNull(); }
179 template <
class MU,
class PQ>
186 template <
class MU,
class PQ>
193 template <
class MU,
class PQ>
196 constexpr
double NaN = std::numeric_limits<double>::quiet_NaN();
197 stream << (this->isNull() ? NaN : this->value(UnitClass::defaultUnit()));
200 template <
class MU,
class PQ>
204 m_unit = UnitClass::defaultUnit();
205 if (std::isnan(m_value)) { this->setNull(); }
208 template <
class MU,
class PQ>
215 template <
class MU,
class PQ>
218 PQ copy = *derived();
223 template <
class MU,
class PQ>
230 template <
class MU,
class PQ>
233 PQ copy = *derived();
238 template <
class MU,
class PQ>
241 PQ copy = *derived();
246 template <
class MU,
class PQ>
249 if (*
this == other)
return false;
251 if (isNull() < other.
isNull()) {
return true; }
252 if (isNull() > other.
isNull()) {
return false; }
253 if (isNull() && other.
isNull()) {
return false; }
255 return (m_value < other.
value(m_unit));
258 template <
class MU,
class PQ>
262 if (m_unit == newUnit || this->isNull()) {
return *derived(); }
263 if (newUnit.isNull()) { this->setNull(); }
266 m_value = newUnit.convertFrom(m_value, m_unit);
272 template <
class MU,
class PQ>
275 if (m_unit == newUnit || this->isNull()) {
return *derived(); }
277 copy.switchUnit(newUnit);
281 template <
class MU,
class PQ>
284 return m_unit.isNull();
287 template <
class MU,
class PQ>
291 m_unit = MU::nullUnit();
294 template <
class MU,
class PQ>
297 if (this->isNull()) {
return 0.0; }
301 template <
class MU,
class PQ>
304 if (!this->isNull()) { m_value = value; }
307 template <
class MU,
class PQ>
313 template <
class MU,
class PQ>
317 Q_ASSERT_X(!unit.isNull(), Q_FUNC_INFO,
"Cannot convert to null");
318 if (this->isNull()) {
return this->convertToQString(i18n); }
319 return unit.makeRoundedQStringWithUnit(this->value(unit), digits, withGroupSeparator, i18n);
322 template <
class MU,
class PQ>
325 if (this->isNull()) {
return QStringLiteral(
"null"); }
326 return this->valueRoundedWithUnit(m_unit, digits, withGroupSeparator, i18n);
329 template <
class MU,
class PQ>
332 if (this->isNull()) {
return; }
333 m_value = m_unit.roundToEpsilon(m_value);
336 template <
class MU,
class PQ>
339 Q_ASSERT_X(!unit.isNull(), Q_FUNC_INFO,
"Cannot convert to null");
340 return unit.roundValue(this->value(unit), digits);
343 template <
class MU,
class PQ>
346 Q_ASSERT_X(!unit.isNull(), Q_FUNC_INFO,
"Cannot convert to null");
347 const double v = this->value(unit);
351 template <
class MU,
class PQ>
354 return this->valueInteger(m_unit);
357 template <
class MU,
class PQ>
360 if (this->isNull()) {
return false; }
362 const double diff = std::abs(this->value() - this->valueInteger());
363 return diff <= m_unit.getEpsilon();
366 template <
class MU,
class PQ>
369 return this->valueRounded(m_unit, digits);
372 template <
class MU,
class PQ>
375 if (this->isNull()) {
return QStringLiteral(
"null"); }
376 const double v = this->valueRounded(unit, digits);
377 return QString::number(v,
'f', digits);
380 template <
class MU,
class PQ>
383 Q_ASSERT_X(!unit.isNull(), Q_FUNC_INFO,
"Cannot convert to null");
384 return unit.convertFrom(m_value, m_unit);
387 template <
class MU,
class PQ>
390 if (this->isNull()) {
return QStringLiteral(
"null"); }
391 return this->valueRoundedWithUnit(this->getUnit(), -1, i18n);
394 template <
class MU,
class PQ>
397 if (pq1.isNull()) {
return pq2; }
398 if (pq2.isNull()) {
return pq1; }
399 return pq1 > pq2 ? pq1 : pq2;
402 template <
class MU,
class PQ>
405 if (pq1.isNull()) {
return pq2; }
406 if (pq2.isNull()) {
return pq1; }
407 return pq1 < pq2 ? pq1 : pq2;
410 template <
class MU,
class PQ>
413 static const PQ n(0, MU::nullUnit());
417 template <
class MU,
class PQ>
422 return qHash(this->valueRoundedWithUnit(MU::defaultUnit()));
425 template <
class MU,
class PQ>
429 json.insert(
"value", QJsonValue(m_value));
430 json.insert(
"unit", QJsonValue(m_unit.getSymbol()));
434 template <
class MU,
class PQ>
437 const QJsonValue unit = json.value(
"unit");
438 const QJsonValue value = json.value(
"value");
439 if (unit.isUndefined()) {
throw CJsonException(
"Missing 'unit'"); }
440 if (value.isUndefined()) {
throw CJsonException(
"Missing 'value'"); }
442 this->setUnitBySymbol(unit.toString());
443 m_value = value.toDouble();
446 template <
class MU,
class PQ>
449 *
this = CPqString::parse<PQ>(value, mode);
452 template <
class MU,
class PQ>
454 const MU &defaultUnitIfMissing)
458 const QString v = value + defaultUnitIfMissing.getSymbol();
459 this->parseFromString(v, mode);
461 else { this->parseFromString(value, mode); }
464 template <
class MU,
class PQ>
470 template <
class MU,
class PQ>
472 const MU &defaultUnitIfMissing)
479 pq.parseFromString(v, mode);
483 template <
class MU,
class PQ>
487 pq.parseFromString(value, mode);
491 template <
class MU,
class PQ>
494 if (index.
isMyself()) {
return QVariant::fromValue(*derived()); }
498 case IndexValue:
return QVariant::fromValue(m_value);
499 case IndexUnit:
return QVariant::fromValue(m_unit);
500 case IndexValueRounded0DigitsWithUnit:
return QVariant::fromValue(this->valueRoundedWithUnit(0));
501 case IndexValueRounded1DigitsWithUnit:
return QVariant::fromValue(this->valueRoundedWithUnit(1));
502 case IndexValueRounded2DigitsWithUnit:
return QVariant::fromValue(this->valueRoundedWithUnit(2));
503 case IndexValueRounded3DigitsWithUnit:
return QVariant::fromValue(this->valueRoundedWithUnit(3));
504 case IndexValueRounded6DigitsWithUnit:
return QVariant::fromValue(this->valueRoundedWithUnit(6));
509 template <
class MU,
class PQ>
514 (*this) = variant.value<PQ>();
520 case IndexValue: m_value = variant.toDouble();
break;
521 case IndexUnit: m_unit = variant.value<MU>();
break;
522 case IndexValueRounded0DigitsWithUnit:
523 case IndexValueRounded1DigitsWithUnit:
524 case IndexValueRounded2DigitsWithUnit:
525 case IndexValueRounded3DigitsWithUnit:
526 case IndexValueRounded6DigitsWithUnit: this->parseFromString(variant.toString());
break;
531 template <
class MU,
class PQ>
534 if (index.
isMyself()) {
return compareImpl(*derived(), pq); }
538 case IndexValue:
return Compare::compare(m_value, pq.m_value);
545 template <
class MU,
class PQ>
548 if (a < b) {
return -1; }
549 else if (a > b) {
return 1; }
553 template <
class MU,
class PQ>
554 PQ
const *CPhysicalQuantity<MU, PQ>::derived()
const
556 return static_cast<PQ
const *
>(
this);
559 template <
class MU,
class PQ>
560 PQ *CPhysicalQuantity<MU, PQ>::derived()
562 return static_cast<PQ *
>(
this);
568 template class CPhysicalQuantity<CLengthUnit, CLength>;
569 template class CPhysicalQuantity<CPressureUnit, CPressure>;
570 template class CPhysicalQuantity<CFrequencyUnit, CFrequency>;
571 template class CPhysicalQuantity<CMassUnit, CMass>;
572 template class CPhysicalQuantity<CTemperatureUnit, CTemperature>;
573 template class CPhysicalQuantity<CSpeedUnit, CSpeed>;
574 template class CPhysicalQuantity<CAngleUnit, CAngle>;
575 template class CPhysicalQuantity<CTimeUnit, CTime>;
576 template class CPhysicalQuantity<CAccelerationUnit, CAcceleration>;
Thrown when a convertFromJson method encounters an unrecoverable error in JSON data.
Non-owning reference to a CPropertyIndex with a subset of its features.
QString toQString(bool i18n=false) const
Cast as QString.
CastType frontCasted() const
First element casted to given type, usually the PropertIndex enum.
bool isMyself() const
Myself index, used with nesting.
Tag type signifying overloaded marshalling methods that preserve data at the expense of size.
void setPropertyByIndex(CPropertyIndexRef index, const QVariant &variant)
Set property by index.
QVariant propertyByIndex(CPropertyIndexRef index) const
Property by index.
A physical quantity such as "5m", "20s", "1500ft/s".
double value() const
Value in current unit.
CPhysicalQuantity & operator+=(const CPhysicalQuantity &other)
Plus operator +=.
bool isNegativeWithEpsilonConsidered() const
Value <= 0 epsilon considered.
void setValueSameUnit(double value)
Change value without changing unit.
QString convertToQString(bool i18n=false) const
Cast as QString.
void parseFromString(const QString &value)
Parse value from string.
void unmarshallFromDbus(const QDBusArgument &argument)
Unmarshall without begin/endStructure, for when composed within another object.
static PQ parsedFromString(const QString &value, CPqString::SeparatorMode mode=CPqString::SeparatorBestGuess)
Parsed from given string.
PQ switchedUnit(const MU &newUnit) const
Return copy with switched unit.
const MU & getUnit() const
Unit.
PQ operator-() const
Unary operator -.
PQ operator/(double divide) const
Operator /.
PQ & switchUnit(const MU &newUnit)
Change unit, and convert value to maintain the same quantity.
void setUnitBySymbol(const QString &unitName)
Set unit by string.
size_t getValueHash() const
qHash overload, needed for storing value in a QSet.
bool isInteger() const
Is value an integer.
QJsonObject toJson() const
Cast to JSON object.
bool isNull() const
Is quantity null?
void setPropertyByIndex(CPropertyIndexRef index, const QVariant &variant)
Set property by index.
static const PQ & minValue(const PQ &pq1, const PQ &pq2)
Minimum of 2 quantities.
int comparePropertyByIndex(CPropertyIndexRef index, const PQ &pq) const
Compare for index.
CPhysicalQuantity & operator-=(const CPhysicalQuantity &other)
Minus operator-=.
double value(MU unit) const
Value in given unit.
void unmarshalFromDataStream(QDataStream &stream)
Unmarshal a value from a QDataStream.
CPhysicalQuantity(double value, MU unit)
Constructor with double.
QString valueRoundedAsString(MU unit, int digits=-1) const
Rounded value in given unit.
void roundToEpsilon()
Round current value in current unit to epsilon.
void addValueSameUnit(double value)
Add to the value in the current unit.
QVariant propertyByIndex(CPropertyIndexRef index) const
Property by index.
void setUnit(const MU &unit)
Simply set unit, do no calclulate conversion.
QString getUnitSymbol() const
Unit.
void substractValueSameUnit(double value)
Substract from the value in the current unit.
CPhysicalQuantity & operator/=(double divide)
Divide operator /=.
void marshalToDataStream(QDataStream &stream) const
Marshal a value to a QDataStream.
int valueInteger() const
As integer value in current unit.
void convertFromJson(const QJsonObject &json)
Assign from JSON object.
void setCurrentUnitValue(double value)
Set value in current unit.
const PQ & makePositive()
Make value always positive.
static const PQ & maxValue(const PQ &pq1, const PQ &pq2)
Maximum of 2 quantities.
static const PQ & null()
NULL PQ.
void marshallToDbus(QDBusArgument &argument) const
Marshall without begin/endStructure, for when composed within another object.
CPhysicalQuantity & operator*=(double multiply)
Multiply operator *=.
QString valueRoundedWithUnit(const MU &unit, int digits=-1, bool withGroupSeparator=false, bool i18n=false) const
Value to QString with the given unit, e.g. "5.00m".
bool isPositiveWithEpsilonConsidered() const
Value >= 0 epsilon considered.
double valueRounded(MU unit, int digits=-1) const
Rounded value in given unit.
PQ abs() const
Absolute value (always >=0)
const PQ & makeNegative()
Make value always negative.
PQ operator*(double multiply) const
Operator *.
bool isZeroEpsilonConsidered() const
Quantity value <= epsilon.
SeparatorMode
Number separators / group separators.
@ SeparatorQtDefault
100000.00 no group separator
bool is09OrSeparatorOnlyString(const QString &testString)
String with 0-9/separator only.
#define SWIFT_DEFINE_PQ_MIXINS(MU, PQ)
Explicit template definition of mixins for a CPhysicalQuantity subclass.
#define SWIFT_VERIFY_X(COND, WHERE, WHAT)
A weaker kind of assert.