swift
measurementunit.h
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: Copyright (C) 2013 swift Project Community / Contributors
2 // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
3 
5 
6 #ifndef SWIFT_MISC_PQ_MEASUREMENTUNIT_H
7 #define SWIFT_MISC_PQ_MEASUREMENTUNIT_H
8 
9 #include <cmath>
10 #include <cstddef>
11 #include <string>
12 
13 #include <QCoreApplication>
14 #include <QDBusArgument>
15 #include <QHash>
16 #include <QList>
17 #include <QSharedData>
18 #include <QSharedDataPointer>
19 #include <QString>
20 #include <Qt>
21 #include <QtDebug>
22 #include <QtGlobal>
23 
24 #include "misc/math/mathutils.h"
25 #include "misc/mixin/mixinhash.h"
26 #include "misc/mixin/mixinicon.h"
27 #include "misc/mixin/mixinstring.h"
28 #include "misc/stringutils.h"
29 #include "misc/swiftmiscexport.h"
30 
31 namespace swift::misc::physical_quantities
32 {
37  public mixin::String<CMeasurementUnit>,
38  public mixin::Icon<CMeasurementUnit>
39  {
40  protected:
44  using ConverterFunction = double (*)(double);
45 
49  struct NilConverter
50  {
51  static double toDefault(double) { return 0.0; }
52  static double fromDefault(double) { return 0.0; }
53  };
54 
59  {
60  static double toDefault(double value) { return value; }
61  static double fromDefault(double value) { return value; }
62  };
63 
68  template <class Policy>
70  {
71  static double toDefault(double value)
72  {
73  return value * Policy::factor();
74  }
75  static double fromDefault(double value)
76  {
77  return value / Policy::factor();
78  }
79  };
80 
85  template <class Policy>
87  {
88  static double toDefault(double value)
89  {
90  return (value - Policy::offset()) * Policy::factor();
91  }
92  static double fromDefault(double value)
93  {
94  return value / Policy::factor() + Policy::offset();
95  }
96  };
97 
103  template <class FactorPolicy, class SubdivPolicy>
105  {
107  static double toDefault(double value)
108  {
110  double part2 = CMathUtils::fract(value) * SubdivPolicy::fraction();
111  value = CMathUtils::trunc(value) + part2 / SubdivPolicy::subfactor();
112  return value * FactorPolicy::factor();
113  }
115  static double fromDefault(double value)
116  {
118  double part1 = CMathUtils::trunc(value / FactorPolicy::factor());
119  double remaining = std::fmod(value / FactorPolicy::factor(), 1.0);
120  double part2 = remaining * SubdivPolicy::subfactor();
121  return part1 + part2 / SubdivPolicy::fraction();
122  }
123  };
124 
130  template <class FactorPolicy, class SubdivPolicy>
132  {
134  static double toDefault(double value)
135  {
137  double part2 = CMathUtils::fract(value) * SubdivPolicy::fraction();
138  double part3 = CMathUtils::fract(part2) * SubdivPolicy::fraction();
139  value = CMathUtils::trunc(value) +
140  (CMathUtils::trunc(part2) + part3 / SubdivPolicy::subfactor()) / SubdivPolicy::subfactor();
141  return value * FactorPolicy::factor();
142  }
144  static double fromDefault(double value)
145  {
147  double part1 = CMathUtils::trunc(value / FactorPolicy::factor());
148  double remaining = std::fmod(value / FactorPolicy::factor(), 1.0);
149  double part2 = CMathUtils::trunc(remaining * SubdivPolicy::subfactor());
150  remaining = std::fmod(remaining * SubdivPolicy::subfactor(), 1.0);
151  double part3 = remaining * SubdivPolicy::subfactor();
152  return part1 + part2 / SubdivPolicy::fraction() +
153  part3 / (SubdivPolicy::fraction() * SubdivPolicy::fraction());
154  }
155  };
156 
158  struct One
159  {
160  static double factor() { return 1; }
161  };
163  template <class Policy>
164  struct Two
165  {
166  static double factor() { return Policy::factor() * 2.0; }
167  };
169  template <class Policy>
170  struct Milli
171  {
172  static double factor() { return Policy::factor() / 1000.0; }
173  };
175  template <class Policy>
176  struct Centi
177  {
178  static double factor() { return Policy::factor() / 100.0; }
179  };
181  template <class Policy>
182  struct Hecto
183  {
184  static double factor() { return Policy::factor() * 100.0; }
185  };
187  template <class Policy>
188  struct Kilo
189  {
190  static double factor() { return Policy::factor() * 1000.0; }
191  };
193  template <class Policy>
194  struct Mega
195  {
196  static double factor() { return Policy::factor() * 1e+6; }
197  };
199  template <class Policy>
200  struct Giga
201  {
202  static double factor() { return Policy::factor() * 1e+9; }
203  };
205  template <int Subfactor>
207  {
208  static double fraction() { return 100.0; }
209  static double subfactor() { return float(Subfactor); }
210  };
211 
212  protected:
214  struct Data
215  {
217  template <class Converter>
218  constexpr Data(QLatin1String name, QLatin1String symbol, Converter, int displayDigits = 2,
219  double epsilon = 1e-9)
220  : m_name(name), m_symbol(symbol), m_epsilon(epsilon), m_displayDigits(displayDigits),
221  m_toDefault(Converter::toDefault), m_fromDefault(Converter::fromDefault)
222  {}
223 
225  constexpr Data(QLatin1String name, QLatin1String symbol) : m_name(name), m_symbol(symbol) {}
226 
227  QLatin1String m_name;
228  QLatin1String m_symbol;
229  double m_epsilon = 0.0;
230  int m_displayDigits = 0;
231  ConverterFunction m_toDefault = nullptr;
232  ConverterFunction m_fromDefault = nullptr;
233  };
234 
236  template <int N>
237  static constexpr QLatin1String constQLatin1(const char (&str)[N])
238  {
239  return QLatin1String(str, N - 1); // -1 because N includes the null terminator
240  }
241 
243  CMeasurementUnit(const Data &data) : m_data(&data) {}
244 
246  CMeasurementUnit(const Data &&) = delete;
247 
249  ~CMeasurementUnit() = default;
250 
252  CMeasurementUnit(const CMeasurementUnit &) = default;
253 
256 
257  private:
258  const Data *m_data = (static_cast<void>(throw std::logic_error("Uninitialized pimpl")), nullptr);
259 
260  public:
262  QString convertToQString(bool i18n = false) const { return this->getSymbol(i18n); }
263 
265  void marshallToDbus(QDBusArgument &argument) const { argument << m_data->m_symbol; }
266 
268  void unmarshallFromDbus(const QDBusArgument &)
269  {
270  // the concrete implementations will override this default
271  // this is required so I can also stream None
272  (*this) = CMeasurementUnit::None();
273  }
274 
276  void marshalToDataStream(QDataStream &stream) const { stream << QString(m_data->m_symbol); }
277 
279  void unmarshalFromDataStream(QDataStream &)
280  {
281  // the concrete implementations will override this default
282  // this is required so that None can be marshalled
283  *this = CMeasurementUnit::None();
284  }
285 
287  friend bool operator==(const CMeasurementUnit &a, const CMeasurementUnit &b)
288  {
289  if (&a == &b) return true;
290  return a.m_data->m_name == b.m_data->m_name;
291  }
292 
294  friend bool operator!=(const CMeasurementUnit &a, const CMeasurementUnit &b) { return !(a == b); }
295 
297  friend size_t qHash(const CMeasurementUnit &unit) { return ::qHash(unit.getName()); }
298 
300  QString getName(bool i18n = false) const
301  {
302  return i18n ? QCoreApplication::translate("CMeasurementUnit", m_data->m_name.latin1()) : m_data->m_name;
303  }
304 
306  QString getSymbol(bool i18n = false) const
307  {
308  return i18n ? QCoreApplication::translate("CMeasurementUnit", m_data->m_symbol.latin1()) : m_data->m_symbol;
309  }
310 
312  bool endsStringWithNameOrSymbol(const QString &candidate, Qt::CaseSensitivity cs = Qt::CaseSensitive) const
313  {
314  const QString c = candidate.trimmed();
315  return c.endsWith(this->getName(false), cs) || c.endsWith(this->getName(true)) ||
316  c.endsWith(this->getSymbol(false), cs) || c.endsWith(this->getSymbol(true));
317  }
318 
321  double roundValue(double value, int digits = -1) const;
322 
325  double roundToEpsilon(double value) const;
326 
329  virtual QString makeRoundedQString(double value, int digits = -1, bool withGroupSeparator = false,
330  bool i18n = false) const;
331 
334  virtual QString makeRoundedQStringWithUnit(double value, int digits = -1, bool withGroupSeparator = false,
335  bool i18n = false) const;
336 
338  double getEpsilon() const { return m_data->m_epsilon; }
339 
341  int getDisplayDigits() const { return m_data->m_displayDigits; }
342 
344  double convertFrom(double value, const CMeasurementUnit &unit) const;
345 
347  bool isEpsilon(double value) const
348  {
349  if (this->isNull()) return false;
350  if (qFuzzyIsNull(value)) return true;
351  return std::abs(value) <= m_data->m_epsilon;
352  }
353 
355  bool isNull() const { return m_data->m_toDefault == nullptr; }
356 
357  // --------------------------------------------------------------------
358  // -- static
359  // --------------------------------------------------------------------
360 
366  template <class U>
367  static U unitFromSymbol(const QString &symbol, bool strict = true)
368  {
369  if (symbol.isEmpty()) { return U::defaultUnit(); }
370 
371  static const bool cs = hasCaseSensitiveSymbols<U>();
372  for (const auto &unit : U::allUnits())
373  {
374  if (strict && cs)
375  {
376  if (unit.getSymbol() == symbol) { return unit; }
377  }
378  else
379  {
380  if (stringCompare(unit.getSymbol(), symbol, Qt::CaseInsensitive)) { return unit; }
381  }
382  }
383  if (strict) qFatal("Illegal unit name");
384  return U::defaultUnit();
385  }
386 
390  template <class U>
391  static const QStringList &allSymbols()
392  {
393  static const QStringList symbols = [] {
394  QStringList s;
395  for (const auto &unit : U::allUnits()) { s.push_back(unit.getSymbol()); }
396  return s;
397  }();
398  return symbols;
399  }
400 
404  template <class U>
405  static const QStringList &allSymbolsLowerCase()
406  {
407  static const QStringList symbols = [] {
408  QSet<QString> s;
409  for (const QString &symbol : allSymbols<U>()) { s.insert(symbol.toLower()); }
410  return s.values();
411  }();
412  return symbols;
413  }
414 
418  template <class U>
420  {
421  static const bool cs = [] { return (allSymbolsLowerCase<U>().size() != allSymbols<U>().size()); }();
422  return cs;
423  }
424 
429  template <class U>
430  static bool isValidUnitSymbol(const QString &symbol)
431  {
432  static const bool cs = hasCaseSensitiveSymbols<U>();
433  return cs ? isValidUnitSymbol<U>(symbol, Qt::CaseSensitive) :
434  isValidUnitSymbol<U>(symbol, Qt::CaseInsensitive);
435  }
436 
442  template <class U>
443  static bool isValidUnitSymbol(const QString &symbol, Qt::CaseSensitivity caseSensitivity)
444  {
445  if (symbol.isEmpty()) return false;
446  for (const auto &unit : U::allUnits())
447  {
448  if (stringCompare(unit.getSymbol(), symbol, caseSensitivity)) { return true; }
449  }
450  return false;
451  }
452 
458  template <class U>
459  static bool containsValidUnitSymbol(const QString &candidate,
460  Qt::CaseSensitivity caseSensitivity = Qt::CaseSensitive)
461  {
462  if (candidate.isEmpty()) return false;
463  for (const auto &unit : U::allUnits())
464  {
465  if (candidate.contains(unit.getSymbol(), caseSensitivity)) { return true; }
466  }
467  return false;
468  }
469 
475  template <class U>
476  static bool endWithValidUnitSymbol(const QString &candidate,
477  Qt::CaseSensitivity caseSensitivity = Qt::CaseSensitive)
478  {
479  if (candidate.isEmpty()) return false;
480  for (const auto &unit : U::allUnits())
481  {
482  if (candidate.endsWith(unit.getSymbol(), caseSensitivity)) { return true; }
483  }
484  return false;
485  }
486 
489  {
490  static constexpr CMeasurementUnit::Data none(constQLatin1("none"), constQLatin1(""), NilConverter(), 0, 0);
491  return none;
492  }
493  };
494 } // namespace swift::misc::physical_quantities
495 
496 #endif // SWIFT_MISC_PQ_MEASUREMENTUNIT_H
static double fract(double value)
Fractional part of value.
Definition: mathutils.h:58
static double trunc(double value, double epsilon=1e-10)
Nearest integer not greater in magnitude than value, correcting for epsilon.
Definition: mathutils.h:52
CRTP class template from which a derived class can inherit icon-related functions.
Definition: mixinicon.h:28
CRTP class template from which a derived class can inherit string streaming operations.
Definition: mixinstring.h:31
Base class for all units, such as meter, hertz.
void marshalToDataStream(QDataStream &stream) const
Marshal a value to a QDataStream.
CMeasurementUnit & operator=(const CMeasurementUnit &)=default
Copy assignment operator.
double(*)(double) ConverterFunction
Pointer to function for converting between units.
friend bool operator==(const CMeasurementUnit &a, const CMeasurementUnit &b)
Equal operator ==.
void unmarshalFromDataStream(QDataStream &)
Unmarshal a value from a QDataStream.
CMeasurementUnit(const Data &&)=delete
Constructor saves the address of its argument, so forbid rvalues.
double getEpsilon() const
Threshold for comparions.
bool isEpsilon(double value) const
Is given value <= epsilon?
CMeasurementUnit(const CMeasurementUnit &)=default
Copy constructor.
friend size_t qHash(const CMeasurementUnit &unit)
qHash overload, needed for storing value in a QSet.
void marshallToDbus(QDBusArgument &argument) const
Marshall without begin/endStructure, for when composed within another object.
friend bool operator!=(const CMeasurementUnit &a, const CMeasurementUnit &b)
Unequal operator !=.
void unmarshallFromDbus(const QDBusArgument &)
Unmarshall without begin/endStructure, for when composed within another object.
static bool isValidUnitSymbol(const QString &symbol)
Valid unit symbol?
bool endsStringWithNameOrSymbol(const QString &candidate, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Does a string end with name or symbol? E.g. 3meter, 3m, 3deg.
static bool endWithValidUnitSymbol(const QString &candidate, Qt::CaseSensitivity caseSensitivity=Qt::CaseSensitive)
Ends with valid unit symbol?
QString getSymbol(bool i18n=false) const
Unit name such as "m".
static const QStringList & allSymbols()
All symbols.
QString getName(bool i18n=false) const
Name such as "meter".
static bool containsValidUnitSymbol(const QString &candidate, Qt::CaseSensitivity caseSensitivity=Qt::CaseSensitive)
Contains valid unit symbol?
static CMeasurementUnit None()
Dimensionless unit.
static const QStringList & allSymbolsLowerCase()
All symbols case insensitive.
static bool hasCaseSensitiveSymbols()
Are symbols case sensitive?
static constexpr QLatin1String constQLatin1(const char(&str)[N])
Constant-initialize QLatin1String without using strlen.
static bool isValidUnitSymbol(const QString &symbol, Qt::CaseSensitivity caseSensitivity)
Valid unit symbol?
static U unitFromSymbol(const QString &symbol, bool strict=true)
Unit from symbol.
QString convertToQString(bool i18n=false) const
Cast as QString.
size_t qHash(const std::string &key, uint seed)
std::string qHash
Definition: metaclass.h:86
SWIFT_MISC_EXPORT bool stringCompare(const QString &c1, const QString &c2, Qt::CaseSensitivity cs)
String compare.
Concrete strategy pattern for converting unit with offset linear conversion.
static double toDefault(double value)
convert from this unit to the default unit
static double fromDefault(double value)
convert to this unit from the default unit
Metapolicy that can be used to modify template parameters of converters.
ConverterFunction m_toDefault
convert from this unit to default unit
constexpr Data(QLatin1String name, QLatin1String symbol, Converter, int displayDigits=2, double epsilon=1e-9)
Construct a unit with custom conversion.
int m_displayDigits
standard rounding for string conversions
constexpr Data(QLatin1String name, QLatin1String symbol)
Construct a null unit.
double m_epsilon
values with differences below epsilon are the equal
Metapolicy that can be used to modify template parameters of converters.
Metapolicy that can be used to modify template parameters of converters.
Concrete strategy pattern for converting unit that does nothing.
static double fromDefault(double value)
convert to this unit from the default unit
static double toDefault(double value)
convert from this unit to the default unit
Metapolicy that can be used to modify template parameters of converters.
Metapolicy that can be used to modify template parameters of converters.
Concrete strategy pattern for converting unit with linear conversion.
static double fromDefault(double value)
convert to this unit from the default unit
static double toDefault(double value)
convert from this unit to the default unit
Metapolicy that can be used to modify template parameters of converters.
Metapolicy that can be used to modify template parameters of converters.
Converter for default values, such as None, used with public constructor.
static double fromDefault(double)
convert to this unit from the default unit
static double toDefault(double)
convert from this unit to the default unit
Metapolicy that can be used to modify template parameters of converters.
Concrete strategy pattern for converting unit with two subdivision conversions.
static double fromDefault(double value)
convert to this unit from the default unit
static double toDefault(double value)
convert from this unit to the default unit
Concrete strategy pattern for converting unit with one subdivision conversion.
static double fromDefault(double value)
convert to this unit from the default unit
static double toDefault(double value)
convert from this unit to the default unit
Metapolicy that can be used to modify template parameters of converters.
#define SWIFT_MISC_EXPORT
Export a class or function from the library.