File indexing completed on 2024-05-12 04:35:06
0001 /* This file is part of the TikZKit project. 0002 * 0003 * Copyright (C) 2013 Dominik Haumann <dhaumann@kde.org> 0004 * 0005 * This library is free software; you can redistribute it and/or modify 0006 * it under the terms of the GNU Library General Public License as published 0007 * by the Free Software Foundation, either version 2 of the License, or 0008 * (at your option) any later version. 0009 * 0010 * This library is distributed in the hope that it will be useful, 0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0013 * GNU Library General Public License for more details. 0014 * 0015 * You should have received a copy of the GNU Library General Public License 0016 * along with this library; see the file COPYING.LIB. If not, see 0017 * <http://www.gnu.org/licenses/>. 0018 */ 0019 0020 #include "Value.h" 0021 0022 #include <QRegularExpression> 0023 #include <QDebug> 0024 0025 namespace tikz { 0026 0027 QString Value::toString() const 0028 { 0029 if (! isValid()) { 0030 return QLatin1String("nan"); 0031 } 0032 0033 // use C locale with out thousand-',' separators 0034 QLocale locale = QLocale::c(); 0035 locale.setNumberOptions(QLocale::OmitGroupSeparator); 0036 QString number = locale.toString(m_value, 'f', 20); 0037 0038 // TikZ doesn't allow commas as separator 0039 Q_ASSERT(! number.contains(QLatin1Char(','))); 0040 0041 // allow only a single '.' as floating point separator 0042 Q_ASSERT(number.count(QLatin1Char('.')) <= 1); 0043 0044 int dotIndex = number.indexOf(QLatin1Char('.')); 0045 0046 // Rounding errors typically result in several consecutive '9' chars. 0047 // Catch this case and fix it to get a nice reacable number. 0048 int nineIndex; 0049 if ((nineIndex = number.indexOf(QLatin1String("099999"))) > dotIndex) { 0050 number.truncate(nineIndex + 1); 0051 number.replace(nineIndex, 1, "1"); 0052 } else if ((nineIndex = number.indexOf(QLatin1String("199999"))) > dotIndex) { 0053 number.truncate(nineIndex + 1); 0054 number.replace(nineIndex, 1, "2"); 0055 } else if ((nineIndex = number.indexOf(QLatin1String("299999"))) > dotIndex) { 0056 number.truncate(nineIndex + 1); 0057 number.replace(nineIndex, 1, "3"); 0058 } else if ((nineIndex = number.indexOf(QLatin1String("399999"))) > dotIndex) { 0059 number.truncate(nineIndex + 1); 0060 number.replace(nineIndex, 1, "4"); 0061 } else if ((nineIndex = number.indexOf(QLatin1String("499999"))) > dotIndex) { 0062 number.truncate(nineIndex + 1); 0063 number.replace(nineIndex, 1, "5"); 0064 } else if ((nineIndex = number.indexOf(QLatin1String("599999"))) > dotIndex) { 0065 number.truncate(nineIndex + 1); 0066 number.replace(nineIndex, 1, "6"); 0067 } else if ((nineIndex = number.indexOf(QLatin1String("699999"))) > dotIndex) { 0068 number.truncate(nineIndex + 1); 0069 number.replace(nineIndex, 1, "7"); 0070 } else if ((nineIndex = number.indexOf(QLatin1String("799999"))) > dotIndex) { 0071 number.truncate(nineIndex + 1); 0072 number.replace(nineIndex, 1, "8"); 0073 } else if ((nineIndex = number.indexOf(QLatin1String("899999"))) > dotIndex) { 0074 number.truncate(nineIndex + 1); 0075 number.replace(nineIndex, 1, "9"); 0076 } else if ((nineIndex = number.indexOf(QLatin1String(".999999"))) == dotIndex) { 0077 number.truncate(nineIndex); 0078 number = QString::number(number.toInt() + 1); 0079 dotIndex = -1; 0080 } 0081 0082 // we have 20 digits after the '.', which is usually too much. 0083 // Therefore, search for '00000' after the '.', and if we find these 0084 // 5 consecutive zeros, just kill the rest. 0085 // Example: the number 3.567 turns into 3.56700000000000017053. After 0086 // the truncation, it'll be '3.5670000000' 0087 if (dotIndex >= 0) { 0088 const int indexOfZeros = number.lastIndexOf(QLatin1String("00000")); 0089 if (indexOfZeros > dotIndex) { 0090 number.truncate(indexOfZeros); 0091 } 0092 } 0093 0094 // beautify step I: remove trailing zeros, e.g. 3.5670000000 -> 3.567 0095 while (dotIndex >= 0 && number.endsWith(QLatin1Char('0'))) { 0096 number.truncate(number.length() - 1); 0097 } 0098 0099 // beautify step II: remove trailing dot, if applicable, e.g. 3. -> 3 0100 if (number.endsWith(QLatin1Char('.'))) { 0101 number.truncate(number.length() - 1); 0102 } 0103 0104 QString suffix; 0105 switch (m_unit) { 0106 case Unit::Point: suffix = "pt"; break; 0107 case Unit::Millimeter: suffix = "mm"; break; 0108 case Unit::Centimeter: suffix = "cm"; break; 0109 case Unit::Inch: suffix = "in"; break; 0110 default: Q_ASSERT(false); 0111 } 0112 0113 return number + suffix; 0114 } 0115 0116 Value Value::fromString(const QString & str) 0117 { 0118 // we require a valid number 0119 if (str == QLatin1String("nan")) { 0120 return invalid(); 0121 } 0122 0123 // format example: 12.50cm 0124 static QRegularExpression re("([-+]?\\d*\\.?\\d*)\\s*(\\w*)"); 0125 0126 QRegularExpressionMatch match = re.match(str); 0127 0128 if (! match.hasMatch()) { 0129 Q_ASSERT(false); 0130 return Value(); 0131 } 0132 0133 const QString number = match.captured(1); 0134 const QString suffix = match.captured(2); 0135 0136 // qDebug() << str << "converts to:" << number << suffix; 0137 0138 Unit u; 0139 if (suffix.isEmpty()) { 0140 // assume pt 0141 qDebug() << "no unit given, implicit conversion to pt: " << str; 0142 u = Unit::Point; 0143 } else if (suffix == "pt") { 0144 u = Unit::Point; 0145 } else if (suffix == "mm") { 0146 u = Unit::Millimeter; 0147 } else if (suffix == "cm") { 0148 u = Unit::Centimeter; 0149 } else if (suffix == "in") { 0150 u = Unit::Inch; 0151 } else { 0152 Q_ASSERT(false); 0153 } 0154 0155 bool ok; 0156 const double val = number.toDouble(&ok); 0157 Q_ASSERT(ok); 0158 0159 return Value(val, u); 0160 } 0161 0162 } 0163 0164 namespace QTest { 0165 // Value: template specialization for QTest::toString() 0166 template<> 0167 char *toString(const tikz::Value & value) 0168 { 0169 const QString str = "Value[" + value.convertTo(tikz::Unit::Point).toString() + "]"; 0170 const QByteArray ba = str.toLatin1(); 0171 return qstrdup(ba.data()); 0172 } 0173 } 0174 0175 // kate: indent-width 4; replace-tabs on;