[TD] fix unit mismatch bug

- also make global decimal option italic since it only affects new dimensions
- also fix a typo in UnitsApi.h
This commit is contained in:
donovaly
2020-11-30 04:24:39 +01:00
committed by wmayer
parent f13c8974b5
commit 0baabcaa45
4 changed files with 138 additions and 79 deletions

View File

@@ -73,7 +73,7 @@ public:
// set the number of decimals
static void setDecimals(int);
// fet the number of decimals
// get the number of decimals
static int getDecimals();
/// set the application defaults
//static void setDefaults(void);

View File

@@ -581,6 +581,32 @@ bool DrawViewDimension::isMultiValueSchema(void) const
return result;
}
std::string DrawViewDimension::getBaseLengthUnit(Base::UnitSystem system)
{
switch (system) {
case Base::UnitSystem::SI1:
return "mm";
case Base::UnitSystem::SI2:
return "m";
case Base::UnitSystem::Imperial1:
return "in";
case Base::UnitSystem::ImperialDecimal:
return "in";
case Base::UnitSystem::Centimeters:
return "cm";
case Base::UnitSystem::ImperialBuilding:
return "ft";
case Base::UnitSystem::MmMin:
return "mm";
case Base::UnitSystem::ImperialCivil:
return "ft";
case Base::UnitSystem::FemMilliMeterNewton:
return "mm";
default:
return "Unknown schema";
}
}
std::string DrawViewDimension::formatValue(qreal value, QString qFormatSpec, int partial)
{
std::string result;
@@ -588,8 +614,8 @@ std::string DrawViewDimension::formatValue(qreal value, QString qFormatSpec, int
QString qUserStringUnits;
QString formattedValue;
bool angularMeasure = false;
Base::Quantity asQuantity;
asQuantity.setValue(value);
if ( (Type.isValue("Angle")) ||
@@ -600,7 +626,7 @@ std::string DrawViewDimension::formatValue(qreal value, QString qFormatSpec, int
asQuantity.setUnit(Base::Unit::Length);
}
QString qUserString = asQuantity.getUserString(); // this handles mm to inch/km/parsec etc
QString qUserString = asQuantity.getUserString(); // this handles mm to inch/km/parsec etc
// and decimal positions but won't give more than
// Global_Decimals precision
// really should be able to ask units for value
@@ -610,6 +636,9 @@ std::string DrawViewDimension::formatValue(qreal value, QString qFormatSpec, int
//if it is multi-unit schema, don't even try to use Alt Decimals
Base::UnitSystem unitSystem = Base::UnitsApi::getSchema();
// we need to know what length unit is used by the scheme
std::string BaseLengthUnit = getBaseLengthUnit(unitSystem);
//get formatSpec prefix/suffix/specifier
QStringList qsl = getPrefixSuffixSpec(qFormatSpec);
QString formatPrefix = qsl[0]; //FormatSpec prefix
@@ -619,24 +648,14 @@ std::string DrawViewDimension::formatValue(qreal value, QString qFormatSpec, int
//handle multi value schemes (yd/ft/in, dms, etc)
std::string genPrefix = getPrefix(); //general prefix - diameter, radius, etc
QString qMultiValueStr;
QString qGenPrefix = QString::fromUtf8(genPrefix.data(),genPrefix.size());
if ( (unitSystem == Base::UnitSystem::ImperialBuilding) &&
!angularMeasure ) {
multiValueSchema = true;
qMultiValueStr = qUserString;
if (!genPrefix.empty()) {
//qUserString from Quantity includes units - prefix + R + nnn ft + suffix
qMultiValueStr = formatPrefix + qGenPrefix + qUserString + formatSuffix;
}
formattedValue = qMultiValueStr;
} else if ((unitSystem == Base::UnitSystem::ImperialCivil) &&
angularMeasure) {
QString qGenPrefix = QString::fromUtf8(genPrefix.data(), genPrefix.size());
if ((unitSystem == Base::UnitSystem::ImperialCivil) && angularMeasure) {
QString dispMinute = QString::fromUtf8("\'");
QString dispSecond = QString::fromUtf8("\"");
QString schemeMinute = QString::fromUtf8("M");
QString schemeSecond = QString::fromUtf8("S");
QString displaySub = qUserString.replace(schemeMinute,dispMinute);
displaySub = displaySub.replace(schemeSecond,dispSecond);
QString displaySub = qUserString.replace(schemeMinute, dispMinute);
displaySub = displaySub.replace(schemeSecond, dispSecond);
multiValueSchema = true;
qMultiValueStr = displaySub;
if (!genPrefix.empty()) {
@@ -645,85 +664,119 @@ std::string DrawViewDimension::formatValue(qreal value, QString qFormatSpec, int
}
formattedValue = qMultiValueStr;
} else {
//handle single value schemes
if (formatSpecifier.isEmpty()) {
Base::Console().Warning("Warning - no numeric format in formatSpec %s - %s\n",
qPrintable(qFormatSpec), getNameInDocument());
return Base::Tools::toStdString(qFormatSpec);
}
QRegExp rxUnits(QString::fromUtf8(" \\D*$")); //space + any non digits at end of string
QString userVal = qUserString;
userVal.remove(rxUnits); //getUserString(defaultDecimals) without units
QLocale loc;
double userValNum = loc.toDouble(userVal);
// for older TD drawings the formatSpecifier "%g" was used, but the number of decimals was
// neverheless 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 (useDecimals() && formatSpecifier.contains(QString::fromLatin1("%g"), Qt::CaseInsensitive)) {
int globalPrecision = Base::UnitsApi::getDecimals();
// change formatSpecifier to e.g. "%.2f"
QString newSpecifier = QString::fromStdString("%." + std::to_string(globalPrecision) + "f");
formatSpecifier.replace(QString::fromLatin1("%g"), newSpecifier, Qt::CaseInsensitive);
}
// qUserString is the value + unit with default decimals, so extract the unit
// we cannot just use unit.getString() because this would convert '°' to 'deg'
QRegExp rxUnits(QString::fromUtf8(" \\D*$")); // space + any non digits at end of string
int pos = 0;
if ((pos = rxUnits.indexIn(qUserString, 0)) != -1) {
qUserStringUnits = rxUnits.cap(0); //entire capture - non numerics at end of qUserString
if ((pos = rxUnits.indexIn(qUserString, 0)) != -1) {
qUserStringUnits = rxUnits.cap(0); // entire capture - non numerics at end of qUserString
}
formattedValue = userVal; //sensible default
#if QT_VERSION >= 0x050000
formattedValue = QString::asprintf(Base::Tools::toStdString(formatSpecifier).c_str(),userValNum);
#else
// we can have 2 possible results:
// - the value in the base unit but without displayed unit
// - the value + unit (not necessarily the base unit!)
// the user can overwrite the decimal settings, so we must in every case use the formatSpecifier
// if useDecimals(), then formatSpecifier = global decimals, otherwise it is %.2f
QLocale loc;
double userVal;
bool checkDecimals = true;
if (showUnits() || (Type.isValue("Angle")) || (Type.isValue("Angle3Pt"))) {
formattedValue = qUserString; // result value + unit (not necessarily base unit!)
// remove unit
formattedValue.remove(rxUnits);
// to number
userVal = loc.toDouble(formattedValue);
if (userVal >= 1.0)
// we can assure we didn't make an error > 10% via getUserString()
checkDecimals = false;
}
if (checkDecimals){
// get value in the base unit with default decimals
// for the conversion we use the same method as in DlgUnitsCalculator::valueChanged
// get the conversion factor for the unit
double convertValue = Base::Quantity::parse(QString::fromLatin1("1") + QString::fromStdString(BaseLengthUnit)).getValue();
// the result is now just val / convertValue because val is always in the base unit
userVal = asQuantity.getValue() / convertValue;
}
// we reformat the value
#if QT_VERSION >= 0x050000
formattedValue = QString::asprintf(Base::Tools::toStdString(formatSpecifier).c_str(), userVal);
#else
QString qs2;
formattedValue = qs2.sprintf(Base::Tools::toStdString(formatSpecifier).c_str(),userValNum);
#endif
QString repl = userVal;
if (useDecimals()) {
if (showUnits() || (Type.isValue("Angle")) ||(Type.isValue("Angle3Pt")) ) {
repl = qUserString;
} else {
repl = userVal;
}
} else {
if (showUnits() || (Type.isValue("Angle")) || (Type.isValue("Angle3Pt"))) {
repl = formattedValue + qUserStringUnits;
} else {
repl = formattedValue;
}
formattedValue = qs2.sprintf(Base::Tools::toStdString(formatSpecifier).c_str(), userVal);
#endif
// if abs(1 - userVal / formattedValue) > 0.1 we know that we make an error greater than 10%
// then we need more digits
if (abs(userVal - formattedValue.toDouble()) > 0.1 * userVal) {
int i = 1;
do { // increase decimals step by step until error is < 10 %
formattedValue = QLocale().toString(userVal, 'f', i);
++i;
} while (abs(userVal - loc.toDouble(formattedValue)) > 0.1 * userVal);
// We purposely don't reset the formatSpecifier.
// Why "%.1f" is overwritten for a value of e.g. "0.001" is obvious,
// moreover such cases only occurs when
// changing unit schemes on existing drawings. Moreover a typical case is that
// you accidentally used e.g. a building scheme, see your mistake and go back
// then you would end up with e.g. "%.5f" and must manually correct this.
}
qFormatSpec.replace(formatSpecifier,repl);
//this next bit is so inelegant!!!
// replace decimal sign if necessary
QChar dp = QChar::fromLatin1('.');
if (loc.decimalPoint() != dp) {
qFormatSpec.replace(dp,loc.decimalPoint());
formattedValue.replace(dp,loc.decimalPoint());
}
//Remove space between dimension and degree sign
if ((Type.isValue("Angle")) || (Type.isValue("Angle3Pt"))) {
QRegExp space(QString::fromUtf8("\\s"));
qFormatSpec.remove(space);
formattedValue.replace(dp, loc.decimalPoint());
}
}
//formattedValue - formatted numeric value
//qUserStringUnits - unit abbrev
//qFormatSpec - prefix + formattedValue w/units + suffix
//partial = 0 --> prefix + formattedValue w/units +suffix
// prefix 4' 11" suffix
result = qFormatSpec.toUtf8().constData();
if ((unitSystem == Base::UnitSystem::ImperialBuilding) &&
!angularMeasure) {
multiValueSchema = true;
qMultiValueStr = formattedValue;
if (!genPrefix.empty()) {
//qUserString from Quantity includes units - prefix + R + nnn ft + suffix
qMultiValueStr = formatPrefix + qGenPrefix + qUserString + formatSuffix;
}
std::string ssPrefix = Base::Tools::toStdString(formatPrefix);
std::string ssSuffix = Base::Tools::toStdString(formatSuffix);
std::string ssUnits = Base::Tools::toStdString(qUserStringUnits);
if (multiValueSchema) {
result = ssPrefix +
Base::Tools::toStdString(qMultiValueStr) +
ssSuffix +
ssUnits;
formattedValue = qMultiValueStr;
}
if (partial == 1) { //prefix number suffix
result = ssPrefix +
Base::Tools::toStdString(formattedValue) +
ssSuffix;
} else if (partial == 2) { //just the unit
result = formattedValue.toStdString();
if (partial == 0) { // then also multiValueSchema is true
result = Base::Tools::toStdString(formatPrefix) +
Base::Tools::toStdString(qMultiValueStr) +
Base::Tools::toStdString(formatSuffix) +
Base::Tools::toStdString(qUserStringUnits);
}
else if (partial == 1) { // prefix number suffix
result = Base::Tools::toStdString(formatPrefix) +
result +
Base::Tools::toStdString(formatSuffix);
}
else if (partial == 2) { // just the unit
if ((Type.isValue("Angle")) || (Type.isValue("Angle3Pt"))) {
QRegExp space(QString::fromUtf8("\\s"));
qUserStringUnits.remove(space);
// remove space between dimension and unit if unit is not "deg"
if ( !qUserStringUnits.contains(QString::fromLatin1("deg")) ) {
QRegExp space(QString::fromUtf8("\\s"));
qUserStringUnits.remove(space);
}
result = Base::Tools::toStdString(qUserStringUnits);
} else if (showUnits()) {
result = Base::Tools::toStdString(qUserStringUnits);
@@ -759,7 +812,6 @@ std::pair<std::string, std::string> DrawViewDimension::getFormattedToleranceValu
std::string DrawViewDimension::getFormattedDimensionValue(int partial)
{
// Base::Console().Message("DVD::getFormattedValue(%d)\n", partial);
QString qFormatSpec = QString::fromUtf8(FormatSpec.getStrValue().data());
if (Arbitrary.getValue()) {
@@ -774,9 +826,8 @@ QStringList DrawViewDimension::getPrefixSuffixSpec(QString fSpec)
QStringList result;
QString formatPrefix;
QString formatSuffix;
QString formatted;
//find the %x.y tag in FormatSpec
QRegExp rxFormat(QString::fromUtf8("%[+-]?[0-9]*\\.*[0-9]*[aefgAEFG]")); //printf double format spec
QRegExp rxFormat(QString::fromUtf8("%[+-]?[0-9]*\\.*[0-9]*[aefgAEFG]")); //printf double format spec
QString match;
int pos = 0;
if ((pos = rxFormat.indexIn(fSpec, 0)) != -1) {

View File

@@ -27,6 +27,7 @@
# include <App/DocumentObject.h>
# include <App/FeaturePython.h>
# include <App/PropertyLinks.h>
# include <Base/UnitsApi.h>
#include "DrawView.h"
@@ -157,6 +158,8 @@ public:
bool isMultiValueSchema(void) const;
std::string getBaseLengthUnit(Base::UnitSystem system);
pointPair getArrowPositions(void);
void saveArrowPositions(const Base::Vector2d positions[]);

View File

@@ -112,6 +112,11 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<italic>true</italic>
</font>
</property>
<property name="toolTip">
<string>Use system setting for number of decimals</string>
</property>