Download src/Mod/TechDraw/App/DimensionFormatter.cpp from AbdulElahGwaith/FreeCAD: direct link, hf CLI and curl.
- Browser
- Download file 16.4 kB
-
https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/TechDraw/App/DimensionFormatter.cpp
- Command line
-
hf download hf://AbdulElahGwaith/FreeCAD/src/Mod/TechDraw/App/DimensionFormatter.cpp
-
curl -L -o DimensionFormatter.cpp https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/TechDraw/App/DimensionFormatter.cpp
16.4 kB
| /*************************************************************************** | |
| * Copyright (c) 2022 WandererFan <wandererfan@gmail.com> * | |
| * * | |
| * This file is part of the FreeCAD CAx development system. * | |
| * * | |
| * This library is free software; you can redistribute it and/or * | |
| * modify it under the terms of the GNU Library General Public * | |
| * License as published by the Free Software Foundation; either * | |
| * version 2 of the License, or (at your option) any later version. * | |
| * * | |
| * This library is distributed in the hope that it will be useful, * | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of * | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * | |
| * GNU Library General Public License for more details. * | |
| * * | |
| * You should have received a copy of the GNU Library General Public * | |
| * License along with this library; see the file COPYING.LIB. If not, * | |
| * write to the Free Software Foundation, Inc., 59 Temple Place, * | |
| * Suite 330, Boston, MA 02111-1307, USA * | |
| * * | |
| ***************************************************************************/ | |
| // TODO: Cyclic dependency issue with DrawViewDimension | |
| using namespace TechDraw; | |
| bool DimensionFormatter::isMultiValueSchema() const | |
| { | |
| const bool angularMeasure = | |
| (m_dimension->Type.isValue("Angle") || m_dimension->Type.isValue("Angle3Pt")); | |
| return (Base::UnitsApi::isMultiUnitAngle() && angularMeasure) | |
| || (Base::UnitsApi::isMultiUnitLength() && !angularMeasure); | |
| } | |
| std::string DimensionFormatter::formatValue(const qreal value, | |
| const QString& qFormatSpec, | |
| const Format partial, | |
| const bool isDim) const | |
| { | |
| bool distanceMeasure{true}; | |
| const bool angularMeasure = | |
| m_dimension->Type.isValue("Angle") || m_dimension->Type.isValue("Angle3Pt"); | |
| const bool areaMeasure = m_dimension->Type.isValue("Area"); | |
| Base::Unit unit{Base::Unit::Length}; | |
| if (angularMeasure) { | |
| unit = Base::Unit::Angle; | |
| distanceMeasure = false; | |
| } | |
| else if (areaMeasure) { | |
| unit = Base::Unit::Area; | |
| distanceMeasure = false; | |
| } | |
| Base::Quantity asQuantity {value, unit}; | |
| QStringList qsl = getPrefixSuffixSpec(qFormatSpec); | |
| const std::string formatPrefix = qsl[0].toStdString(); | |
| const std::string formatSuffix = qsl[1].toStdString(); | |
| QString formatSpecifier = qsl[2]; | |
| // this handles mm to inch/km/parsec etc and decimal positions but | |
| // won't give more than Global_Decimals precision | |
| std::string basicString = formatPrefix + asQuantity.getUserString() + formatSuffix; | |
| if (isMultiValueSchema() || partial == Format::UNALTERED) { | |
| return basicString; // Don't even try to use Alt Decimals or hide units | |
| } | |
| if (formatSpecifier.isEmpty()) { | |
| Base::Console().warning("Warning - no numeric format in Format Spec %s - %s\n", | |
| qPrintable(qFormatSpec), | |
| m_dimension->getNameInDocument()); | |
| return qFormatSpec.toStdString(); | |
| } | |
| // for older TD drawings the formatSpecifier "%g" was used, but the number of decimals was | |
| // nevertheless limited. To keep old drawings, we limit the number of decimals too | |
| // if the TD preferences option to use the global decimal number is set | |
| // the formatSpecifier can have a prefix and/or suffix | |
| if (m_dimension->useDecimals() | |
| && formatSpecifier.contains(QStringLiteral("%g"), Qt::CaseInsensitive)) { | |
| const int globalPrecision = Base::UnitsApi::getDecimals(); | |
| // change formatSpecifier to e.g. "%.2f" | |
| const QString newSpecifier = | |
| QString::fromStdString("%." + std::to_string(globalPrecision) + "f"); | |
| formatSpecifier.replace(QStringLiteral("%g"), newSpecifier, Qt::CaseInsensitive); | |
| } | |
| double factor{1.0}; | |
| std::string unitText{""}; | |
| asQuantity.getUserString(factor, unitText); | |
| std::string super2{"²"}; | |
| std::string squareTag{"^2"}; | |
| if (unitText.empty()) { | |
| if (distanceMeasure) { | |
| unitText = Base::UnitsApi::getBasicLengthUnit(); | |
| } else if (areaMeasure) { | |
| unitText = Base::UnitsApi::getBasicLengthUnit() + squareTag; | |
| } else if (angularMeasure) { | |
| unitText = "°"; | |
| } | |
| } | |
| double userVal = asQuantity.getValue(); | |
| if (distanceMeasure || areaMeasure) { | |
| userVal /= factor; | |
| } | |
| // convert ^2 to superscript 2 for display | |
| if (areaMeasure) { | |
| size_t tagPosition = unitText.find(squareTag); | |
| if (tagPosition != std::string::npos) { | |
| unitText = unitText.replace(tagPosition, 2, super2); | |
| } | |
| } | |
| QString formattedValue = formatValueToSpec(userVal, formatSpecifier); | |
| // replace decimal sign if necessary | |
| constexpr QChar dp = QChar::fromLatin1('.'); | |
| if (const QLocale loc; loc.decimalPoint() != dp) { | |
| formattedValue.replace(dp, loc.decimalPoint()); | |
| } | |
| // formattedValue is now in formatSpec format with local decimal separator | |
| std::string formattedValueString = formattedValue.toStdString(); | |
| if (partial == Format::UNALTERED) { | |
| return basicString; | |
| } | |
| if (partial == Format::FORMATTED) { | |
| std::string unitStr {}; | |
| if (angularMeasure) { | |
| unitStr = "°"; | |
| } | |
| else if ((m_dimension->showUnits() || areaMeasure) | |
| && !(isDim && m_dimension->haveTolerance())) { | |
| unitStr = " " + unitText; | |
| } | |
| return formatPrefix + formattedValueString + unitStr + formatSuffix; | |
| } | |
| if (partial == Format::UNIT) { | |
| return angularMeasure || m_dimension->showUnits() || areaMeasure ? unitText : ""; | |
| } | |
| return formattedValueString; | |
| } | |
| //! get the formatted OverTolerance value | |
| // wf: is this a leftover from when we only had 1 tolerance instead of over/under? | |
| std::string DimensionFormatter::getFormattedToleranceValue(const Format partial) const | |
| { | |
| QString FormatSpec = QString::fromUtf8(m_dimension->FormatSpecOverTolerance.getStrValue().data()); | |
| QString ToleranceString; | |
| if (m_dimension->ArbitraryTolerances.getValue()) | |
| ToleranceString = FormatSpec; | |
| else | |
| ToleranceString = QString::fromUtf8(formatValue(m_dimension->OverTolerance.getValue(), | |
| FormatSpec, | |
| partial, | |
| false).c_str()); | |
| return ToleranceString.toStdString(); | |
| } | |
| //! get formatted over and under tolerances | |
| std::pair<std::string, std::string> DimensionFormatter::getFormattedToleranceValues(const Format partial) const | |
| { | |
| QString underFormatSpec = QString::fromUtf8(m_dimension->FormatSpecUnderTolerance.getStrValue().data()); | |
| QString overFormatSpec = QString::fromUtf8(m_dimension->FormatSpecOverTolerance.getStrValue().data()); | |
| std::pair<std::string, std::string> tolerances; | |
| QString underTolerance, overTolerance; | |
| if (m_dimension->ArbitraryTolerances.getValue()) { | |
| underTolerance = underFormatSpec; | |
| overTolerance = overFormatSpec; | |
| } else { | |
| underTolerance = QString::fromUtf8(formatValue(m_dimension->UnderTolerance.getValue(), | |
| underFormatSpec, | |
| partial, | |
| false).c_str()); | |
| overTolerance = QString::fromUtf8(formatValue(m_dimension->OverTolerance.getValue(), | |
| overFormatSpec, | |
| partial, | |
| false).c_str()); | |
| } | |
| tolerances.first = underTolerance.toStdString(); | |
| tolerances.second = overTolerance.toStdString(); | |
| return tolerances; | |
| } | |
| //partial = 2 unit only | |
| std::string DimensionFormatter::getFormattedDimensionValue(const Format partial) const | |
| { | |
| QString qFormatSpec = QString::fromUtf8(m_dimension->FormatSpec.getStrValue().data()); | |
| if ( (m_dimension->Arbitrary.getValue() && !m_dimension->EqualTolerance.getValue()) | |
| || (m_dimension->Arbitrary.getValue() && m_dimension->TheoreticalExact.getValue()) ) { | |
| return m_dimension->FormatSpec.getStrValue(); | |
| } | |
| if (m_dimension->Arbitrary.getValue()) { | |
| return m_dimension->FormatSpec.getStrValue(); | |
| } | |
| // if there is an equal over-/undertolerance (so only 1 tolerance to show with +/-) and | |
| // not theoretically exact (which has no tolerance), and | |
| // tolerance has been specified, ie | |
| // (OverTolerance != 0.0 (so a tolerance has been specified) or | |
| // ArbitraryTolerances are specified) | |
| // concatenate the tolerance to dimension | |
| // TODO: why all this QString if returned as std::string??? | |
| if (m_dimension->EqualTolerance.getValue() && | |
| !m_dimension->TheoreticalExact.getValue() && | |
| (!DrawUtil::fpCompare(m_dimension->OverTolerance.getValue(), 0.0) || | |
| m_dimension->ArbitraryTolerances.getValue())) { | |
| QString labelText = QString::fromUtf8(formatValue(m_dimension->getDimValue(), | |
| qFormatSpec, | |
| Format::FORMATTED, | |
| true).c_str()); //just the number pref/spec[unit]/suf | |
| QString unitText = QString::fromUtf8(formatValue(m_dimension->getDimValue(), | |
| qFormatSpec, | |
| Format::UNIT, | |
| false).c_str()); //just the unit | |
| QString tolerance = QString::fromStdString(getFormattedToleranceValue(Format::FORMATTED).c_str()); | |
| // tolerance might start with a plus sign that we don't want, so cut it off | |
| // note plus sign is not at pos = 0! | |
| QRegularExpression plus(QStringLiteral("^\\s*\\+")); | |
| tolerance.remove(plus); | |
| return (labelText + | |
| QString::fromUtf8(" \xC2\xB1 ") + // +/- symbol | |
| tolerance).toStdString(); | |
| // Unreachable code?? | |
| if (partial == Format::UNIT) { | |
| return unitText.toStdString(); | |
| } | |
| return ""; | |
| } | |
| //tolerance not specified, so just format dimension value? | |
| std::string formattedValue = formatValue(m_dimension->getDimValue(), qFormatSpec, partial, true); | |
| return formattedValue; | |
| } | |
| // format the value using the formatSpec. Also, handle the non-standard format- | |
| // specifiers '%w', which works as %f but without trailing zeros | |
| // and '%r', which round the value to the given precision. | |
| QString DimensionFormatter::formatValueToSpec(const double value, QString formatSpecifier) const | |
| { | |
| QString formattedValue; | |
| constexpr auto format = [](QString f, double value){ | |
| return QString::fromStdString(fmt::sprintf(f.toStdString(), value)); | |
| }; | |
| QRegularExpression wrRegExp(QStringLiteral("%(?<dec>.*)(?<spec>[wWrR])")); | |
| QRegularExpressionMatch wrMatch = wrRegExp.match(formatSpecifier); | |
| if (! wrMatch.hasMatch()) { | |
| if (isNumericFormat(formatSpecifier)) { | |
| formattedValue = format(formatSpecifier, value); | |
| } | |
| return formattedValue; | |
| } | |
| QString spec = wrMatch.captured(QStringLiteral("spec")).toLower(); | |
| QString dec = wrMatch.captured(QStringLiteral("dec")); | |
| if (spec == QStringLiteral("w")) { | |
| formattedValue = format(QStringLiteral("%") + dec + QStringLiteral("f"), value); | |
| if (formattedValue.contains(QStringLiteral("."))){ | |
| // First, cut trailing zeros | |
| while(formattedValue.endsWith(QStringLiteral("0"))) | |
| { | |
| formattedValue.chop(1); | |
| } | |
| // Second, try to cut also decimal dot | |
| if(formattedValue.endsWith(QStringLiteral("."))) | |
| { | |
| formattedValue.chop(1); | |
| } | |
| } | |
| } | |
| else if (spec == QStringLiteral("r")) { | |
| // round the value to the given precision | |
| double rounder = dec.toDouble(); | |
| double roundValue = std::round(value / rounder) * rounder; | |
| // format the result with the same decimal count than the rounder | |
| int dotIndex = dec.indexOf(QStringLiteral(".")); | |
| int nDecimals = 0; | |
| if (dotIndex >= 0){ | |
| // remove trailing zeros to avoid decimal overwriting | |
| while(dec.endsWith(QStringLiteral("0"))) | |
| { | |
| dec.chop(1); | |
| } | |
| nDecimals = dec.size() - dotIndex - 1; | |
| } | |
| formatSpecifier = QStringLiteral("%.") + QString::number(nDecimals) + QStringLiteral("f"); | |
| formattedValue = format(formatSpecifier, roundValue); | |
| } | |
| return formattedValue; | |
| } | |
| bool DimensionFormatter::isNumericFormat(const QString& formatSpecifier) const | |
| { | |
| //printf double format spec | |
| const QRegularExpression rxFormat(QStringLiteral("%[+-]?[0-9]*\\.*[0-9]*[aefgwAEFGW]")); | |
| QRegularExpressionMatch rxMatch; | |
| return formatSpecifier.indexOf(rxFormat, 0, &rxMatch) != -1; | |
| } | |
| //TODO: similar code here and above | |
| QStringList DimensionFormatter::getPrefixSuffixSpec(const QString& fSpec) const | |
| { | |
| QStringList result; | |
| //find the %x.y tag in FormatSpec | |
| QRegularExpression rxFormat(QStringLiteral("%[+-]?[0-9]*\\.*[0-9]*[aefgrwAEFGRW]")); //printf double format spec | |
| QRegularExpressionMatch rxMatch; | |
| int pos = fSpec.indexOf(rxFormat, 0, &rxMatch); | |
| if (pos != -1) { | |
| QString match = rxMatch.captured(0); //entire capture of rx | |
| QString formatPrefix = fSpec.left(pos); | |
| result.append(formatPrefix); | |
| QString formatSuffix = fSpec.right(fSpec.size() - pos - match.size()); | |
| result.append(formatSuffix); | |
| result.append(match); | |
| } else { //printf format not found! | |
| Base::Console().warning("Warning - no numeric format in formatSpec %s - %s\n", | |
| qPrintable(fSpec), m_dimension->getNameInDocument()); | |
| result.append(QString()); | |
| result.append(QString()); | |
| result.append(fSpec); | |
| } | |
| return result; | |
| } | |
| std::string DimensionFormatter::getDefaultFormatSpec(bool isToleranceFormat) const | |
| { | |
| std::string prefFormat = Preferences::formatSpec(); | |
| QString formatSpec; | |
| QString qPrefix; | |
| if (prefFormat.empty()) { | |
| QString format1 = QStringLiteral("%."); | |
| QString format2 = QStringLiteral("f"); | |
| int precision; | |
| if (m_dimension->useDecimals()) { | |
| precision = Base::UnitsApi::getDecimals(); | |
| } else { | |
| precision = Preferences::altDecimals(); | |
| } | |
| QString formatPrecision = QString::number(precision); | |
| std::string prefix = m_dimension->getPrefixForDimType(); | |
| if (!prefix.empty()) { | |
| qPrefix = QString::fromUtf8(prefix.data(), prefix.size()); | |
| } | |
| formatSpec = qPrefix + format1 + formatPrecision + format2; | |
| } else { | |
| std::string prefix = m_dimension->getPrefixForDimType(); | |
| qPrefix = QString::fromUtf8(prefix.data(), prefix.size()); | |
| formatSpec = qPrefix + QString::fromStdString(prefFormat); | |
| } | |
| if (isToleranceFormat) { | |
| formatSpec.replace(QStringLiteral("%"), QStringLiteral("%+")); | |
| } | |
| return formatSpec.toStdString(); | |
| } | |