/* Copyright (C) 2013 * swift project Community / Contributors * * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, * including this file, may be copied, modified, propagated, or distributed except according to the terms * contained in the LICENSE file. */ #include "blackmisc/comparefunctions.h" #include "blackmisc/dictionary.h" #include "blackmisc/pq/measurementunit.h" #include "blackmisc/pq/physicalquantity.h" #include "blackmisc/pq/pqstring.h" #include "blackmisc/propertyindex.h" #include "blackmisc/propertyindexvariantmap.h" #include "blackmisc/variant.h" #include "blackmisc/verify.h" #include "blackmisc/pq/length.h" #include "blackmisc/pq/pressure.h" #include "blackmisc/pq/frequency.h" #include "blackmisc/pq/mass.h" #include "blackmisc/pq/temperature.h" #include "blackmisc/pq/speed.h" #include "blackmisc/pq/angle.h" #include "blackmisc/pq/time.h" #include "blackmisc/pq/acceleration.h" #include #include #include #include #include #include #include #include #include #include namespace BlackMisc { namespace PhysicalQuantities { template MU CPhysicalQuantity::getUnit() const { return this->m_unit; } template void CPhysicalQuantity::setUnitBySymbol(const QString &unitName) { this->m_unit = CMeasurementUnit::unitFromSymbol(unitName); } template QString CPhysicalQuantity::getUnitSymbol() const { return this->m_unit.getSymbol(true); } template CPhysicalQuantity::CPhysicalQuantity(double value, MU unit) : m_value(unit.isNull() ? 0.0 : value), m_unit(unit) { } template CPhysicalQuantity::CPhysicalQuantity(const QString &unitString) : m_value(0.0), m_unit(MU::nullUnit()) { this->parseFromString(unitString); } template bool CPhysicalQuantity::operator ==(const CPhysicalQuantity &other) const { if (this == &other) return true; if (this->isNull()) return other.isNull(); if (other.isNull()) return false; double diff = std::abs(this->m_value - other.value(this->m_unit)); return diff <= this->m_unit.getEpsilon(); } template bool CPhysicalQuantity::operator !=(const CPhysicalQuantity &other) const { return !((*this) == other); } template CPhysicalQuantity &CPhysicalQuantity::operator +=(const CPhysicalQuantity &other) { this->m_value += other.value(this->m_unit); return *this; } template PQ CPhysicalQuantity::operator +(const PQ &other) const { PQ copy(other); copy += *this; return copy; } template void CPhysicalQuantity::addValueSameUnit(double value) { this->m_value += value; } template void CPhysicalQuantity::substractValueSameUnit(double value) { this->m_value -= value; } template CPhysicalQuantity &CPhysicalQuantity::operator -=(const CPhysicalQuantity &other) { this->m_value -= other.value(this->m_unit); return *this; } template PQ CPhysicalQuantity::operator -(const PQ &other) const { PQ copy = *derived(); copy -= other; return copy; } template bool CPhysicalQuantity::isZeroEpsilonConsidered() const { return this->m_unit.isEpsilon(this->m_value); } template bool CPhysicalQuantity::isPositiveWithEpsilonConsidered() const { return !this->isZeroEpsilonConsidered() && this->m_value > 0; } template bool CPhysicalQuantity::isNegativeWithEpsilonConsidered() const { return !this->isZeroEpsilonConsidered() && this->m_value < 0; } template void CPhysicalQuantity::makePositive() { if (this->m_value < 0) { this->m_value *= -1.0; } } template void CPhysicalQuantity::makeNegative() { if (this->m_value > 0) { this->m_value *= -1.0; } } template void CPhysicalQuantity::marshallToDbus(QDBusArgument &argument) const { constexpr double NaN = std::numeric_limits::quiet_NaN(); argument << (this->isNull() ? NaN : this->value(UnitClass::defaultUnit())); // argument << this->m_value; // argument << this->m_unit; } template void CPhysicalQuantity::unmarshallFromDbus(const QDBusArgument &argument) { argument >> this->m_value; this->m_unit = UnitClass::defaultUnit(); if (std::isnan(this->m_value)) { this->setNull(); } // argument >> this->m_value; // argument >> this->m_unit; } template CPhysicalQuantity &CPhysicalQuantity::operator *=(double factor) { this->m_value *= factor; return *this; } template PQ CPhysicalQuantity::operator *(double factor) const { PQ copy = *derived(); copy *= factor; return copy; } template CPhysicalQuantity &CPhysicalQuantity::operator /=(double divisor) { this->m_value /= divisor; return *this; } template PQ CPhysicalQuantity::operator /(double divisor) const { PQ copy = *derived(); copy /= divisor; return copy; } template bool CPhysicalQuantity::operator <(const CPhysicalQuantity &other) const { if (*this == other) return false; if (this->isNull() || other.isNull()) return false; return (this->m_value < other.value(this->m_unit)); } template bool CPhysicalQuantity::operator >(const CPhysicalQuantity &other) const { return other < *this; } template bool CPhysicalQuantity::operator >=(const CPhysicalQuantity &other) const { if (*this == other) return true; return *this > other; } template bool CPhysicalQuantity::operator <=(const CPhysicalQuantity &other) const { if (*this == other) return true; return *this < other; } template PQ &CPhysicalQuantity::switchUnit(MU newUnit) { if (this->m_unit != newUnit) { this->m_value = newUnit.convertFrom(this->m_value, this->m_unit); this->m_unit = newUnit; } return *derived(); } template bool CPhysicalQuantity::isNull() const { return this->m_unit.isNull(); } template void CPhysicalQuantity::setNull() { this->m_value = 0; this->m_unit = MU::nullUnit(); } template double CPhysicalQuantity::value() const { if (this->isNull()) { return 0.0; } return this->m_value; } template void CPhysicalQuantity::setCurrentUnitValue(double value) { if (!this->isNull()) { this->m_value = value; } } template void CPhysicalQuantity::setValueSameUnit(double baseValue) { this->m_value = baseValue; } template QString CPhysicalQuantity::valueRoundedWithUnit(MU unit, int digits, bool i18n) const { Q_ASSERT_X(!unit.isNull(), Q_FUNC_INFO, "Cannot convert to null"); if (this->isNull()) { return this->convertToQString(i18n); } return unit.makeRoundedQStringWithUnit(this->value(unit), digits, i18n); } template QString CPhysicalQuantity::valueRoundedWithUnit(int digits, bool i18n) const { if (this->isNull()) { return this->convertToQString(i18n); } return this->valueRoundedWithUnit(this->m_unit, digits, i18n); } template void CPhysicalQuantity::roundToEpsilon() { if (this->isNull()) { return; } this->m_value = this->m_unit.roundToEpsilon(this->m_value); } template double CPhysicalQuantity::valueRounded(MU unit, int digits) const { Q_ASSERT_X(!unit.isNull(), Q_FUNC_INFO, "Cannot convert to null"); return unit.roundValue(this->value(unit), digits); } template int CPhysicalQuantity::valueInteger(MU unit) const { Q_ASSERT_X(!unit.isNull(), Q_FUNC_INFO, "Cannot convert to null"); double v = unit.roundValue(this->value(unit), 0); return static_cast(v); } template double CPhysicalQuantity::valueRounded(int digits) const { return this->valueRounded(this->m_unit, digits); } template double CPhysicalQuantity::value(MU unit) const { Q_ASSERT_X(!unit.isNull(), Q_FUNC_INFO, "Cannot convert to null"); return unit.convertFrom(this->m_value, this->m_unit); } template QString CPhysicalQuantity::convertToQString(bool i18n) const { if (this->isNull()) { return i18n ? QCoreApplication::translate("CPhysicalQuantity", "undefined") : "undefined"; } return this->valueRoundedWithUnit(this->getUnit(), -1, i18n); } template uint CPhysicalQuantity::getValueHash() const { QList hashs; // there is no double qHash // also unit and rounding has to be considered hashs << qHash(this->valueRoundedWithUnit(MU::defaultUnit())); return BlackMisc::calculateHash(hashs, "PQ"); } template QJsonObject CPhysicalQuantity::toJson() const { QJsonObject json; json.insert("value", QJsonValue(this->m_value)); json.insert("unit", QJsonValue(this->m_unit.getSymbol())); return json; } template void CPhysicalQuantity::convertFromJson(const QJsonObject &json) { const QJsonValue unit = json.value("unit"); const QJsonValue value = json.value("value"); if (unit.isUndefined()) { throw CJsonException("Missing 'unit'"); } if (value.isUndefined()) { throw CJsonException("Missing 'value'"); } this->setUnitBySymbol(unit.toString()); this->m_value = value.toDouble(); } template void CPhysicalQuantity::parseFromString(const QString &value, CPqString::SeparatorMode mode) { *this = CPqString::parse(value, mode); } template void CPhysicalQuantity::parseFromString(const QString &value) { *this = CPqString::parse(value, CPqString::SeparatorsCLocale); } template CVariant CPhysicalQuantity::propertyByIndex(const CPropertyIndex &index) const { if (index.isMyself()) { return CVariant::from(*derived()); } ColumnIndex i = index.frontCasted(); switch (i) { case IndexValue: return CVariant::from(this->m_value); case IndexUnit: return CVariant::from(this->m_unit); case IndexValueRounded0DigitsWithUnit: return CVariant::from(this->valueRoundedWithUnit(0)); case IndexValueRounded1DigitsWithUnit: return CVariant::from(this->valueRoundedWithUnit(1)); case IndexValueRounded2DigitsWithUnit: return CVariant::from(this->valueRoundedWithUnit(2)); case IndexValueRounded3DigitsWithUnit: return CVariant::from(this->valueRoundedWithUnit(3)); case IndexValueRounded6DigitsWithUnit: return CVariant::from(this->valueRoundedWithUnit(6)); default: return Mixin::Index::propertyByIndex(index); } } template void CPhysicalQuantity::setPropertyByIndex(const CPropertyIndex &index, const CVariant &variant) { if (index.isMyself()) { (*this) = variant.to(); return; } ColumnIndex i = index.frontCasted(); switch (i) { case IndexValue: this->m_value = variant.toDouble(); break; case IndexUnit: this->m_unit = variant.to(); break; case IndexValueRounded0DigitsWithUnit: case IndexValueRounded1DigitsWithUnit: case IndexValueRounded2DigitsWithUnit: case IndexValueRounded3DigitsWithUnit: case IndexValueRounded6DigitsWithUnit: this->parseFromString(variant.toQString()); break; default: Mixin::Index::setPropertyByIndex(index, variant); break; } } template int CPhysicalQuantity::comparePropertyByIndex(const CPropertyIndex &index, const PQ &pq) const { if (index.isMyself()) { return compareImpl(*derived(), pq); } ColumnIndex i = index.frontCasted(); switch (i) { case IndexValue: return Compare::compare(this->m_value, pq.m_value); default: break; } BLACK_VERIFY_X(false, Q_FUNC_INFO, qUtf8Printable("No comparison for index " + index.toQString())); return 0; } template int CPhysicalQuantity::compareImpl(const PQ &a, const PQ &b) { if (a.isNull() > b.isNull()) { return -1; } if (a.isNull() < b.isNull()) { return 1; } if (a < b) { return -1; } else if (a > b) { return 1; } else { return 0; } } template PQ const *CPhysicalQuantity::derived() const { return static_cast(this); } template PQ *CPhysicalQuantity::derived() { return static_cast(this); } // see here for the reason of thess forward instantiations // https://isocpp.org/wiki/faq/templates#separate-template-fn-defn-from-decl //! \cond PRIVATE template class BLACKMISC_EXPORT_DEFINE_TEMPLATE CPhysicalQuantity; template class BLACKMISC_EXPORT_DEFINE_TEMPLATE CPhysicalQuantity; template class BLACKMISC_EXPORT_DEFINE_TEMPLATE CPhysicalQuantity; template class BLACKMISC_EXPORT_DEFINE_TEMPLATE CPhysicalQuantity; template class BLACKMISC_EXPORT_DEFINE_TEMPLATE CPhysicalQuantity; template class BLACKMISC_EXPORT_DEFINE_TEMPLATE CPhysicalQuantity; template class BLACKMISC_EXPORT_DEFINE_TEMPLATE CPhysicalQuantity; template class BLACKMISC_EXPORT_DEFINE_TEMPLATE CPhysicalQuantity; template class BLACKMISC_EXPORT_DEFINE_TEMPLATE CPhysicalQuantity; //! \endcond } // namespace } // namespace