Branch data Line data Source code
1 : : /*************************************************************************** 2 : : qgsfractionnumericformat.h 3 : : -------------------------- 4 : : begin : March 2020 5 : : copyright : (C) 2020 by Nyall Dawson 6 : : email : nyall dot dawson at gmail dot com 7 : : *************************************************************************** 8 : : * * 9 : : * This program is free software; you can redistribute it and/or modify * 10 : : * it under the terms of the GNU General Public License as published by * 11 : : * the Free Software Foundation; either version 2 of the License, or * 12 : : * (at your option) any later version. * 13 : : * * 14 : : ***************************************************************************/ 15 : : #ifndef QGSFRACTIONNUMERICFORMAT_H 16 : : #define QGSFRACTIONNUMERICFORMAT_H 17 : : 18 : : #include "qgis_core.h" 19 : : #include "qgis_sip.h" 20 : : #include "qgis.h" 21 : : #include "qgsnumericformat.h" 22 : : #include <cmath> 23 : : 24 : : /** 25 : : * \ingroup core 26 : : * \brief A numeric formatter which returns a vulgar fractional representation of a decimal value (e.g. "1/2" instead of 0.5). 27 : : * 28 : : * \since QGIS 3.14 29 : : */ 30 : 10 : class CORE_EXPORT QgsFractionNumericFormat : public QgsNumericFormat 31 : : { 32 : : public: 33 : : 34 : : /** 35 : : * Default constructor 36 : : */ 37 : : QgsFractionNumericFormat(); 38 : : 39 : : QString id() const override; 40 : : QString visibleName() const override; 41 : : int sortKey() override; 42 : : QString formatDouble( double value, const QgsNumericFormatContext &context ) const override; 43 : : QgsNumericFormat *clone() const override SIP_FACTORY; 44 : : QgsNumericFormat *create( const QVariantMap &configuration, const QgsReadWriteContext &context ) const override SIP_FACTORY; 45 : : QVariantMap configuration( const QgsReadWriteContext &context ) const override; 46 : : double suggestSampleValue() const override; 47 : : 48 : : /** 49 : : * Returns TRUE if dedicated unicode characters should be used, when the are available for the 50 : : * particular fraction (e.g. ½, ¼). 51 : : * \see setUseDedicatedUnicodeCharacters() 52 : : * \see useUnicodeSuperSubscript() 53 : : */ 54 : : bool useDedicatedUnicodeCharacters() const; 55 : : 56 : : /** 57 : : * Sets whether dedicated unicode characters should be used, when the are available for the 58 : : * particular fraction (e.g. ½, ¼). 59 : : * \see useDedicatedUnicodeCharacters() 60 : : * \see setUseUnicodeSuperSubscript() 61 : : */ 62 : : void setUseDedicatedUnicodeCharacters( bool enabled ); 63 : : 64 : : /** 65 : : * Returns TRUE if unicode superscript and subscript characters should be used, (e.g. "⁶/₇"). 66 : : * \see setUseUnicodeSuperSubscript() 67 : : * \see useDedicatedUnicodeCharacters() 68 : : */ 69 : : bool useUnicodeSuperSubscript() const; 70 : : 71 : : /** 72 : : * Sets whether unicode superscript and subscript characters should be used, (e.g. "⁶/₇"). 73 : : * \see useUnicodeSuperSubscript() 74 : : * \see setUseDedicatedUnicodeCharacters() 75 : : */ 76 : : void setUseUnicodeSuperSubscript( bool enabled ); 77 : : 78 : : /** 79 : : * Returns TRUE if the thousands grouping separator will be shown. 80 : : * \see setShowThousandsSeparator() 81 : : */ 82 : : bool showThousandsSeparator() const; 83 : : 84 : : /** 85 : : * Sets whether the thousands grouping separator will be shown. 86 : : * \see showThousandsSeparator() 87 : : */ 88 : : void setShowThousandsSeparator( bool show ); 89 : : 90 : : /** 91 : : * Returns TRUE if a leading plus sign will be shown for positive values. 92 : : * \see setShowPlusSign() 93 : : */ 94 : : bool showPlusSign() const; 95 : : 96 : : /** 97 : : * Sets whether a leading plus sign will be shown for positive values. 98 : : * \see showPlusSign() 99 : : */ 100 : : void setShowPlusSign( bool show ); 101 : : 102 : : /** 103 : : * Returns any override for the thousands separator character. If an invalid QChar is returned, 104 : : * then the QGIS locale separator is used instead. 105 : : * 106 : : * \see setThousandsSeparator() 107 : : */ 108 : : QChar thousandsSeparator() const; 109 : : 110 : : /** 111 : : * Sets an override \a character for the thousands separator character. If an invalid QChar is set, 112 : : * then the QGIS locale separator is used instead. 113 : : * 114 : : * \see thousandsSeparator() 115 : : */ 116 : : void setThousandsSeparator( QChar character ); 117 : : 118 : : /** 119 : : * Converts a double \a value to a vulgar fraction (e.g. ⅓, ¼, etc) by attempting to calculate 120 : : * the corresponding \a numerator and \a denominator, within the specified \a tolerance. 121 : : * 122 : : * This method is based of Richard's algorithm (1981) from "Continued Fractions without Tears" (University of Minnesota). 123 : : * 124 : : * \param value input value to convert 125 : : * \param numerator will be set to calculated fraction numerator 126 : : * \param denominator will be set to the calculated fraction denominator 127 : : * \param sign will be set to the sign of the result (as -1 or +1 values) 128 : : * \param tolerance acceptable tolerance. Larger values will give "nicer" fractions. 129 : : * \returns TRUE if \a value was successfully converted to a fraction 130 : : */ 131 : 0 : static bool doubleToVulgarFraction( const double value, unsigned long long &numerator SIP_OUT, unsigned long long &denominator SIP_OUT, int &sign SIP_OUT, const double tolerance = 1e-10 ) 132 : : { 133 : 0 : sign = value < 0 ? -1 : 1; 134 : 0 : double g = std::fabs( value ); 135 : 0 : unsigned long long a = 0; 136 : 0 : unsigned long long b = 1; 137 : 0 : unsigned long long c = 1; 138 : 0 : unsigned long long d = 0; 139 : : unsigned long long s; 140 : 0 : unsigned int iteration = 0; 141 : 0 : do 142 : : { 143 : 0 : s = std::floor( g ); 144 : 0 : numerator = a + s * c; 145 : 0 : denominator = b + s * d; 146 : 0 : a = c; 147 : 0 : b = d; 148 : 0 : c = numerator; 149 : 0 : d = denominator; 150 : 0 : g = 1.0 / ( g - s ); 151 : 0 : if ( qgsDoubleNear( static_cast< double >( sign )*static_cast< double >( numerator ) / denominator, value, tolerance ) ) 152 : : { 153 : 0 : return true; 154 : : } 155 : 0 : } 156 : 0 : while ( iteration++ < 100 ); // limit to 100 iterations, should be sufficient for realistic purposes 157 : 0 : return false; 158 : 0 : } 159 : : 160 : : /** 161 : : * Converts numbers in an \a input string to unicode superscript equivalents. 162 : : * \see toUnicodeSubscript() 163 : : */ 164 : : static QString toUnicodeSuperscript( const QString &input ); 165 : : 166 : : /** 167 : : * Converts numbers in an \a input string to unicode subscript equivalents. 168 : : * \see toUnicodeSuperscript() 169 : : */ 170 : : static QString toUnicodeSubscript( const QString &input ); 171 : : 172 : : protected: 173 : : 174 : : /** 175 : : * Sets the format's \a configuration. 176 : : */ 177 : : virtual void setConfiguration( const QVariantMap &configuration, const QgsReadWriteContext &context ); 178 : : 179 : : private: 180 : : 181 : : bool mUseDedicatedUnicode = false; 182 : : bool mUseUnicodeSuperSubscript = true; 183 : : bool mShowThousandsSeparator = true; 184 : : bool mShowPlusSign = false; 185 : : QChar mThousandsSeparator; 186 : : }; 187 : : 188 : : #endif // QGSFRACTIONNUMERICFORMAT_H