From 5b0af477b74049bb7ec51e1f0dbd9b5c8e72c837 Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Mon, 25 Aug 2025 20:31:39 +0200 Subject: [PATCH 01/12] Gui: do not misuse UnitApi to format numbers in property editor --- src/Gui/propertyeditor/PropertyItem.cpp | 33 ++++++++----------------- 1 file changed, 10 insertions(+), 23 deletions(-) diff --git a/src/Gui/propertyeditor/PropertyItem.cpp b/src/Gui/propertyeditor/PropertyItem.cpp index 4570b6b6f4..fe04c47657 100644 --- a/src/Gui/propertyeditor/PropertyItem.cpp +++ b/src/Gui/propertyeditor/PropertyItem.cpp @@ -1796,12 +1796,8 @@ void PropertyVectorDistanceItem::setValue(const QVariant& variant) return; } const Base::Vector3d& value = variant.value(); - - Base::QuantityFormat format(Base::QuantityFormat::Default, highPrec); - std::string val = fmt::format("({}, {}, {})", - Base::UnitsApi::toNumber(value.x, format), - Base::UnitsApi::toNumber(value.y, format), - Base::UnitsApi::toNumber(value.z, format)); + std::string val = fmt::format("({:.{}g}, {:.{}g}, {:.{}g})", + value.x, highPrec, value.y, highPrec, value.z, highPrec); setPropertyValue(val); } @@ -2536,12 +2532,9 @@ void PropertyRotationItem::setValue(const QVariant& value) Base::Vector3d axis; double angle {}; h.getValue(axis, angle); - Base::QuantityFormat format(Base::QuantityFormat::Default, highPrec); - std::string val = fmt::format("App.Rotation(App.Vector({},{},{}),{})", - Base::UnitsApi::toNumber(axis.x, format), - Base::UnitsApi::toNumber(axis.y, format), - Base::UnitsApi::toNumber(axis.z, format), - Base::UnitsApi::toNumber(angle, format)); + std::string val = fmt::format("App.Rotation(App.Vector({:.{}g},{:.{}g},{:.{}g}),{:.{}g})", + axis.x, highPrec, axis.y, highPrec, axis.z, highPrec, + angle, highPrec); setPropertyValue(val); } @@ -2854,18 +2847,12 @@ void PropertyPlacementItem::setValue(const QVariant& value) Base::Vector3d axis; double angle {}; h.getValue(axis, angle); - - Base::QuantityFormat format(Base::QuantityFormat::Default, highPrec); std::string str = fmt::format("App.Placement(" - "App.Vector({},{},{})," - "App.Rotation(App.Vector({},{},{}),{}))", - Base::UnitsApi::toNumber(pos.x, format), - Base::UnitsApi::toNumber(pos.y, format), - Base::UnitsApi::toNumber(pos.z, format), - Base::UnitsApi::toNumber(axis.x, format), - Base::UnitsApi::toNumber(axis.y, format), - Base::UnitsApi::toNumber(axis.z, format), - Base::UnitsApi::toNumber(angle, format)); + "App.Vector({:.{}g},{:.{}g},{:.{}g})," + "App.Rotation(App.Vector({:.{}g},{:.{}g},{:.{}g}),{:.{}g}))", + pos.x, highPrec, pos.y, highPrec, pos.z, highPrec, + axis.x, highPrec, axis.y, highPrec, axis.z, highPrec, + angle, highPrec); setPropertyValue(str); } From 81e1321b324d58b81107b066fe56f0b724d92f76 Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Sun, 6 Apr 2025 22:16:21 +0200 Subject: [PATCH 02/12] Base: make UnitsApi::toString method of Quantity This static method takes Quantity as a parameter, so make it method of Quantity directly. --- src/Base/Quantity.cpp | 5 +++++ src/Base/Quantity.h | 3 +++ src/Base/UnitsApi.cpp | 6 ------ src/Base/UnitsApi.h | 6 ------ src/Gui/propertyeditor/PropertyItem.cpp | 2 +- 5 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/Base/Quantity.cpp b/src/Base/Quantity.cpp index 1803fd0dcb..073c4dc6be 100644 --- a/src/Base/Quantity.cpp +++ b/src/Base/Quantity.cpp @@ -241,6 +241,11 @@ Quantity Quantity::operator-() const return Quantity(-(this->myValue), this->myUnit); } +std::string Quantity::toString(const QuantityFormat& format) const +{ + return fmt::format("'{} {}'", UnitsApi::toNumber(myValue, format), myUnit.getString()); +} + std::string Quantity::getUserString() const { double dummy1 {}; // to satisfy GCC diff --git a/src/Base/Quantity.h b/src/Base/Quantity.h index 657f3af1b4..26632c1382 100644 --- a/src/Base/Quantity.h +++ b/src/Base/Quantity.h @@ -157,6 +157,9 @@ public: myFormat = fmt; } + std::string + toString(const QuantityFormat& format = QuantityFormat(QuantityFormat::Default)) const; + std::string getUserString() const; /// transfer to user preferred unit/potence std::string getUserString(double& factor, std::string& unitString) const; diff --git a/src/Base/UnitsApi.cpp b/src/Base/UnitsApi.cpp index 41328297fd..0615f5c887 100644 --- a/src/Base/UnitsApi.cpp +++ b/src/Base/UnitsApi.cpp @@ -27,7 +27,6 @@ #endif #include -#include #include "Exception.h" #include "UnitsApi.h" @@ -94,11 +93,6 @@ void UnitsApi::setSchema(const size_t num) schemas->select(num); } -std::string UnitsApi::toString(const Quantity& quantity, const QuantityFormat& format) -{ - return fmt::format("'{} {}'", toNumber(quantity, format), quantity.getUnit().getString()); -} - std::string UnitsApi::toNumber(const Quantity& quantity, const QuantityFormat& format) { return toNumber(quantity.getValue(), format); diff --git a/src/Base/UnitsApi.h b/src/Base/UnitsApi.h index d5a6d95af3..82475e9708 100644 --- a/src/Base/UnitsApi.h +++ b/src/Base/UnitsApi.h @@ -52,17 +52,11 @@ public: static std::string schemaTranslate(const Quantity& quant); /** - * toString & toNumber: * Quantity to string. Optionally apply format * The string is a number in C locale (i.e. the decimal separator is always a dot) * Scientific notation (if needed). */ - /** INCLUDES unit */ - static std::string - toString(const Quantity& quantity, - const QuantityFormat& format = QuantityFormat(QuantityFormat::Default)); - /** Does NOT include unit */ static std::string toNumber(const Quantity& quantity, diff --git a/src/Gui/propertyeditor/PropertyItem.cpp b/src/Gui/propertyeditor/PropertyItem.cpp index fe04c47657..23b43e80ef 100644 --- a/src/Gui/propertyeditor/PropertyItem.cpp +++ b/src/Gui/propertyeditor/PropertyItem.cpp @@ -1187,7 +1187,7 @@ void PropertyUnitItem::setValue(const QVariant& value) if (!hasExpression() && value.canConvert()) { const Base::Quantity& val = value.value(); Base::QuantityFormat format(Base::QuantityFormat::Default, highPrec); - setPropertyValue(Base::UnitsApi::toString(val, format)); + setPropertyValue(val.toString(format)); } } From 51aaaaff4546dc32ed83032fc4e4cb290d8da851 Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Sun, 6 Apr 2025 22:38:01 +0200 Subject: [PATCH 03/12] Base: make UnitsApi::toNumber method of Quantity This static method takes Quantity as a parameter, so make it method of Quantity directly. --- src/Base/Quantity.cpp | 22 +++++++++++++++++- src/Base/Quantity.h | 3 +++ src/Base/UnitsApi.cpp | 25 -------------------- src/Base/UnitsApi.h | 15 ------------ src/Base/UnitsApiPy.cpp | 3 ++- src/Mod/Mesh/Gui/DlgRegularSolidImp.cpp | 31 ++++++++++++------------- 6 files changed, 41 insertions(+), 58 deletions(-) diff --git a/src/Base/Quantity.cpp b/src/Base/Quantity.cpp index 073c4dc6be..61042b896a 100644 --- a/src/Base/Quantity.cpp +++ b/src/Base/Quantity.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #endif @@ -243,7 +244,26 @@ Quantity Quantity::operator-() const std::string Quantity::toString(const QuantityFormat& format) const { - return fmt::format("'{} {}'", UnitsApi::toNumber(myValue, format), myUnit.getString()); + return fmt::format("'{} {}'", toNumber(format), myUnit.getString()); +} + +std::string Quantity::toNumber(const QuantityFormat& format) const +{ + std::stringstream ss; + + switch (format.format) { + case QuantityFormat::Fixed: + ss << std::fixed; + break; + case QuantityFormat::Scientific: + ss << std::scientific; + break; + default: + break; + } + ss << std::setprecision(format.precision) << myValue; + + return ss.str(); } std::string Quantity::getUserString() const diff --git a/src/Base/Quantity.h b/src/Base/Quantity.h index 26632c1382..ec69975c65 100644 --- a/src/Base/Quantity.h +++ b/src/Base/Quantity.h @@ -160,6 +160,9 @@ public: std::string toString(const QuantityFormat& format = QuantityFormat(QuantityFormat::Default)) const; + std::string + toNumber(const QuantityFormat& format = QuantityFormat(QuantityFormat::Default)) const; + std::string getUserString() const; /// transfer to user preferred unit/potence std::string getUserString(double& factor, std::string& unitString) const; diff --git a/src/Base/UnitsApi.cpp b/src/Base/UnitsApi.cpp index 0615f5c887..1817b0064a 100644 --- a/src/Base/UnitsApi.cpp +++ b/src/Base/UnitsApi.cpp @@ -23,7 +23,6 @@ #include "PreCompiled.h" #ifndef _PreComp_ #include -#include #endif #include @@ -93,30 +92,6 @@ void UnitsApi::setSchema(const size_t num) schemas->select(num); } -std::string UnitsApi::toNumber(const Quantity& quantity, const QuantityFormat& format) -{ - return toNumber(quantity.getValue(), format); -} - -std::string UnitsApi::toNumber(const double value, const QuantityFormat& format) -{ - std::stringstream ss; - - switch (format.format) { - case QuantityFormat::Fixed: - ss << std::fixed; - break; - case QuantityFormat::Scientific: - ss << std::scientific; - break; - default: - break; - } - ss << std::setprecision(format.precision) << value; - - return ss.str(); -} - double UnitsApi::toDouble(PyObject* args, const Base::Unit& u) { if (PyUnicode_Check(args)) { diff --git a/src/Base/UnitsApi.h b/src/Base/UnitsApi.h index 82475e9708..e23507d694 100644 --- a/src/Base/UnitsApi.h +++ b/src/Base/UnitsApi.h @@ -51,21 +51,6 @@ public: static std::string schemaTranslate(const Quantity& quant); - /** - * Quantity to string. Optionally apply format - * The string is a number in C locale (i.e. the decimal separator is always a dot) - * Scientific notation (if needed). - */ - - /** Does NOT include unit */ - static std::string - toNumber(const Quantity& quantity, - const QuantityFormat& format = QuantityFormat(QuantityFormat::Default)); - - /** Does NOT include unit */ - static std::string - toNumber(double value, const QuantityFormat& format = QuantityFormat(QuantityFormat::Default)); - static double toDouble(PyObject* args, const Base::Unit& u = Base::Unit()); static void setDecimals(std::size_t); diff --git a/src/Base/UnitsApiPy.cpp b/src/Base/UnitsApiPy.cpp index 799ca43ad4..a688901aed 100644 --- a/src/Base/UnitsApiPy.cpp +++ b/src/Base/UnitsApiPy.cpp @@ -217,5 +217,6 @@ PyObject* UnitsApi::sToNumber(PyObject* /*self*/, PyObject* args) return nullptr; } - return Py::new_reference_to(Py::String(toNumber(value, qf))); + const Quantity quantity {value}; + return Py::new_reference_to(Py::String(quantity.toNumber(qf))); } diff --git a/src/Mod/Mesh/Gui/DlgRegularSolidImp.cpp b/src/Mod/Mesh/Gui/DlgRegularSolidImp.cpp index 1c541831fa..4433dca7a8 100644 --- a/src/Mod/Mesh/Gui/DlgRegularSolidImp.cpp +++ b/src/Mod/Mesh/Gui/DlgRegularSolidImp.cpp @@ -29,7 +29,6 @@ #include #include #include -#include #include #include #include @@ -132,9 +131,9 @@ void DlgRegularSolidImp::onCreateSolidButtonClicked() "App.ActiveDocument.{0}.Length={1}\n" "App.ActiveDocument.{0}.Width={2}\n" "App.ActiveDocument.{0}.Height={3}\n", name, - Base::UnitsApi::toNumber(ui->boxLength->value()), - Base::UnitsApi::toNumber(ui->boxWidth->value()), - Base::UnitsApi::toNumber(ui->boxHeight->value())); + ui->boxLength->value().toNumber(), + ui->boxWidth->value().toNumber(), + ui->boxHeight->value().toNumber()); break; case 1: name = doc->getUniqueObjectName("Cylinder"); @@ -145,9 +144,9 @@ void DlgRegularSolidImp::onCreateSolidButtonClicked() "App.ActiveDocument.{0}.EdgeLength={3}\n" "App.ActiveDocument.{0}.Closed={4}\n" "App.ActiveDocument.{0}.Sampling={5}\n", name, - Base::UnitsApi::toNumber(ui->cylinderRadius->value()), - Base::UnitsApi::toNumber(ui->cylinderLength->value()), - Base::UnitsApi::toNumber(ui->cylinderEdgeLength->value()), + ui->cylinderRadius->value().toNumber(), + ui->cylinderLength->value().toNumber(), + ui->cylinderEdgeLength->value().toNumber(), ui->cylinderClosed->isChecked() ? "True" : "False", ui->cylinderCount->value()); break; @@ -161,10 +160,10 @@ void DlgRegularSolidImp::onCreateSolidButtonClicked() "App.ActiveDocument.{0}.EdgeLength={4}\n" "App.ActiveDocument.{0}.Closed={5}\n" "App.ActiveDocument.{0}.Sampling={6}\n", name, - Base::UnitsApi::toNumber(ui->coneRadius1->value()), - Base::UnitsApi::toNumber(ui->coneRadius2->value()), - Base::UnitsApi::toNumber(ui->coneLength->value()), - Base::UnitsApi::toNumber(ui->coneEdgeLength->value()), + ui->coneRadius1->value().toNumber(), + ui->coneRadius2->value().toNumber(), + ui->coneLength->value().toNumber(), + ui->coneEdgeLength->value().toNumber(), ui->coneClosed->isChecked() ? "True" : "False", ui->coneCount->value()); break; @@ -174,7 +173,7 @@ void DlgRegularSolidImp::onCreateSolidButtonClicked() "App.ActiveDocument.addObject(\"Mesh::Sphere\",\"{0}\")\n" "App.ActiveDocument.{0}.Radius={1}\n" "App.ActiveDocument.{0}.Sampling={2}\n", name, - Base::UnitsApi::toNumber(ui->sphereRadius->value()), + ui->sphereRadius->value().toNumber(), ui->sphereCount->value()); break; case 4: @@ -184,8 +183,8 @@ void DlgRegularSolidImp::onCreateSolidButtonClicked() "App.ActiveDocument.{0}.Radius1={1}\n" "App.ActiveDocument.{0}.Radius2={2}\n" "App.ActiveDocument.{0}.Sampling={3}\n", name, - Base::UnitsApi::toNumber(ui->ellipsoidRadius1->value()), - Base::UnitsApi::toNumber(ui->ellipsoidRadius2->value()), + ui->ellipsoidRadius1->value().toNumber(), + ui->ellipsoidRadius2->value().toNumber(), ui->ellipsoidCount->value()); break; case 5: @@ -195,8 +194,8 @@ void DlgRegularSolidImp::onCreateSolidButtonClicked() "App.ActiveDocument.{0}.Radius1={1}\n" "App.ActiveDocument.{0}.Radius2={2}\n" "App.ActiveDocument.{0}.Sampling={3}\n", name, - Base::UnitsApi::toNumber(ui->toroidRadius1->value()), - Base::UnitsApi::toNumber(ui->toroidRadius2->value()), + ui->toroidRadius1->value().toNumber(), + ui->toroidRadius2->value().toNumber(), ui->toroidCount->value()); break; } From 5106c7e69a3d7fe85f40b7d7195f4a671c7609ee Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Wed, 27 Aug 2025 10:36:43 +0200 Subject: [PATCH 04/12] Base: UnitsApi: remove nowhere called init() --- src/Base/UnitsApi.cpp | 5 ----- src/Base/UnitsApi.h | 1 - 2 files changed, 6 deletions(-) diff --git a/src/Base/UnitsApi.cpp b/src/Base/UnitsApi.cpp index 1817b0064a..65ea8f9448 100644 --- a/src/Base/UnitsApi.cpp +++ b/src/Base/UnitsApi.cpp @@ -37,11 +37,6 @@ using Base::UnitsApi; using Base::UnitsSchema; using Base::UnitsSchemas; -void UnitsApi::init() -{ - schemas = std::make_unique(UnitsSchemasData::unitSchemasDataPack); -} - std::vector UnitsApi::getDescriptions() { return schemas->descriptions(); diff --git a/src/Base/UnitsApi.h b/src/Base/UnitsApi.h index e23507d694..bcf515ddc8 100644 --- a/src/Base/UnitsApi.h +++ b/src/Base/UnitsApi.h @@ -41,7 +41,6 @@ namespace Base class BaseExport UnitsApi { public: - static void init(); static std::unique_ptr createSchema(std::size_t num); static void setSchema(const std::string& name); static void setSchema(std::size_t num); From 1691fce36bd570a2d21178f5cb58d01bdcea12c8 Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Thu, 5 Jun 2025 14:29:26 +0200 Subject: [PATCH 05/12] Base: Quantity: remove redundant 'this' --- src/Base/Quantity.cpp | 59 +++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/src/Base/Quantity.cpp b/src/Base/Quantity.cpp index 61042b896a..d758ec8576 100644 --- a/src/Base/Quantity.cpp +++ b/src/Base/Quantity.cpp @@ -88,22 +88,21 @@ Quantity::Quantity(double value, const Unit& unit) {} Quantity::Quantity(double value, const std::string& unit) - : myValue {0.0} { if (unit.empty()) { - this->myValue = value; - this->myUnit = Unit(); + myValue = value; + myUnit = Unit(); return; } try { auto tmpQty = parse(unit); - this->myUnit = tmpQty.getUnit(); - this->myValue = value * tmpQty.getValue(); + myValue = value * tmpQty.getValue(); + myUnit = tmpQty.getUnit(); } catch (const Base::ParserError&) { - this->myValue = 0.0; - this->myUnit = Unit(); + myValue = 0.0; + myUnit = Unit(); } } @@ -114,7 +113,7 @@ double Quantity::getValueAs(const Quantity& other) const bool Quantity::operator==(const Quantity& that) const { - return (this->myValue == that.myValue) && (this->myUnit == that.myUnit); + return (myValue == that.myValue) && (myUnit == that.myUnit); } bool Quantity::operator!=(const Quantity& that) const @@ -124,62 +123,62 @@ bool Quantity::operator!=(const Quantity& that) const bool Quantity::operator<(const Quantity& that) const { - if (this->myUnit != that.myUnit) { + if (myUnit != that.myUnit) { throw Base::UnitsMismatchError( "Quantity::operator <(): quantities need to have same unit to compare"); } - return (this->myValue < that.myValue); + return (myValue < that.myValue); } bool Quantity::operator>(const Quantity& that) const { - if (this->myUnit != that.myUnit) { + if (myUnit != that.myUnit) { throw Base::UnitsMismatchError( "Quantity::operator >(): quantities need to have same unit to compare"); } - return (this->myValue > that.myValue); + return (myValue > that.myValue); } bool Quantity::operator<=(const Quantity& that) const { - if (this->myUnit != that.myUnit) { + if (myUnit != that.myUnit) { throw Base::UnitsMismatchError( "Quantity::operator <=(): quantities need to have same unit to compare"); } - return (this->myValue <= that.myValue); + return (myValue <= that.myValue); } bool Quantity::operator>=(const Quantity& that) const { - if (this->myUnit != that.myUnit) { + if (myUnit != that.myUnit) { throw Base::UnitsMismatchError( "Quantity::operator >=(): quantities need to have same unit to compare"); } - return (this->myValue >= that.myValue); + return (myValue >= that.myValue); } Quantity Quantity::operator*(const Quantity& other) const { - return Quantity(this->myValue * other.myValue, this->myUnit * other.myUnit); + return Quantity(myValue * other.myValue, myUnit * other.myUnit); } Quantity Quantity::operator*(double factor) const { - return Quantity(this->myValue * factor, this->myUnit); + return Quantity(myValue * factor, myUnit); } Quantity Quantity::operator/(const Quantity& other) const { - return Quantity(this->myValue / other.myValue, this->myUnit / other.myUnit); + return Quantity(myValue / other.myValue, myUnit / other.myUnit); } Quantity Quantity::operator/(double factor) const { - return Quantity(this->myValue / factor, this->myUnit); + return Quantity(myValue / factor, myUnit); } Quantity Quantity::pow(const Quantity& other) const @@ -188,27 +187,27 @@ Quantity Quantity::pow(const Quantity& other) const throw Base::UnitsMismatchError("Quantity::pow(): exponent must not have a unit"); } - return Quantity(std::pow(this->myValue, other.myValue), - this->myUnit.pow(static_cast(other.myValue))); + return Quantity(std::pow(myValue, other.myValue), + myUnit.pow(static_cast(other.myValue))); } Quantity Quantity::pow(double exp) const { - return Quantity(std::pow(this->myValue, exp), this->myUnit.pow(exp)); + return Quantity(std::pow(myValue, exp), myUnit.pow(exp)); } Quantity Quantity::operator+(const Quantity& other) const { - if (this->myUnit != other.myUnit) { + if (myUnit != other.myUnit) { throw Base::UnitsMismatchError("Quantity::operator +(): Unit mismatch in plus operation"); } - return Quantity(this->myValue + other.myValue, this->myUnit); + return Quantity(myValue + other.myValue, myUnit); } Quantity& Quantity::operator+=(const Quantity& other) { - if (this->myUnit != other.myUnit) { + if (myUnit != other.myUnit) { throw Base::UnitsMismatchError("Quantity::operator +=(): Unit mismatch in plus operation"); } @@ -219,16 +218,16 @@ Quantity& Quantity::operator+=(const Quantity& other) Quantity Quantity::operator-(const Quantity& other) const { - if (this->myUnit != other.myUnit) { + if (myUnit != other.myUnit) { throw Base::UnitsMismatchError("Quantity::operator -(): Unit mismatch in minus operation"); } - return Quantity(this->myValue - other.myValue, this->myUnit); + return Quantity(myValue - other.myValue, myUnit); } Quantity& Quantity::operator-=(const Quantity& other) { - if (this->myUnit != other.myUnit) { + if (myUnit != other.myUnit) { throw Base::UnitsMismatchError("Quantity::operator -=(): Unit mismatch in minus operation"); } @@ -239,7 +238,7 @@ Quantity& Quantity::operator-=(const Quantity& other) Quantity Quantity::operator-() const { - return Quantity(-(this->myValue), this->myUnit); + return Quantity(-myValue, myUnit); } std::string Quantity::toString(const QuantityFormat& format) const From 1c99a9377c06b46e6495889bfb17f1732a67c880 Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Thu, 4 Sep 2025 08:32:17 +0200 Subject: [PATCH 06/12] Base: Quantity: attribute pressure quantities to pressure unit Although CompressiveStrength is of the same dimension as Pressure, make it more straightforward and use base unit. --- src/Base/Quantity.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Base/Quantity.cpp b/src/Base/Quantity.cpp index d758ec8576..499e4247df 100644 --- a/src/Base/Quantity.cpp +++ b/src/Base/Quantity.cpp @@ -387,21 +387,21 @@ const Quantity Quantity::MilliNewtonPerMeter ( 1e-3 , Unit:: const Quantity Quantity::KiloNewtonPerMeter ( 1e3 , Unit::Stiffness ); const Quantity Quantity::MegaNewtonPerMeter ( 1e6 , Unit::Stiffness ); -const Quantity Quantity::Pascal ( 0.001 , Unit::CompressiveStrength ); // Pascal (kg/m/s^2 or N/m^2) -const Quantity Quantity::KiloPascal ( 1.00 , Unit::CompressiveStrength ); -const Quantity Quantity::MegaPascal ( 1000.0 , Unit::CompressiveStrength ); -const Quantity Quantity::GigaPascal ( 1e+6 , Unit::CompressiveStrength ); +const Quantity Quantity::Pascal ( 0.001 , Unit::Pressure ); // Pascal (kg/m/s^2 or N/m^2) +const Quantity Quantity::KiloPascal ( 1.00 , Unit::Pressure ); +const Quantity Quantity::MegaPascal ( 1000.0 , Unit::Pressure ); +const Quantity Quantity::GigaPascal ( 1e+6 , Unit::Pressure ); -const Quantity Quantity::MilliBar ( 0.1 , Unit::CompressiveStrength ); -const Quantity Quantity::Bar ( 100.0 , Unit::CompressiveStrength ); // 1 bar = 100 kPa +const Quantity Quantity::MilliBar ( 0.1 , Unit::Pressure ); +const Quantity Quantity::Bar ( 100.0 , Unit::Pressure ); // 1 bar = 100 kPa -const Quantity Quantity::Torr ( 101.325 / 760.0 , Unit::CompressiveStrength ); // Torr is a defined fraction of Pascal (kg/m/s^2 or N/m^2) -const Quantity Quantity::mTorr ( 0.101325 / 760.0 , Unit::CompressiveStrength ); // Torr is a defined fraction of Pascal (kg/m/s^2 or N/m^2) -const Quantity Quantity::yTorr ( 0.000101325 / 760.0 , Unit::CompressiveStrength ); // Torr is a defined fraction of Pascal (kg/m/s^2 or N/m^2) +const Quantity Quantity::Torr ( 101.325 / 760.0 , Unit::Pressure ); // Torr is a defined fraction of Pascal (kg/m/s^2 or N/m^2) +const Quantity Quantity::mTorr ( 0.101325 / 760.0 , Unit::Pressure ); // Torr is a defined fraction of Pascal (kg/m/s^2 or N/m^2) +const Quantity Quantity::yTorr ( 0.000101325 / 760.0 , Unit::Pressure ); // Torr is a defined fraction of Pascal (kg/m/s^2 or N/m^2) -const Quantity Quantity::PSI ( 6.894744825494 , Unit::CompressiveStrength ); // pounds/in^2 -const Quantity Quantity::KSI ( 6894.744825494 , Unit::CompressiveStrength ); // 1000 x pounds/in^2 -const Quantity Quantity::MPSI ( 6894744.825494 , Unit::CompressiveStrength ); // 1000 ksi +const Quantity Quantity::PSI ( 6.894744825494 , Unit::Pressure ); // pounds/in^2 +const Quantity Quantity::KSI ( 6894.744825494 , Unit::Pressure ); // 1000 x pounds/in^2 +const Quantity Quantity::MPSI ( 6894744.825494 , Unit::Pressure ); // 1000 ksi const Quantity Quantity::Watt ( 1e+6 , Unit::Power ); // Watt (kg*m^2/s^3) const Quantity Quantity::MilliWatt ( 1e+3 , Unit::Power ); From 4e27d4eebe566c03c38ff49ef76854a978092ea2 Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Tue, 26 Aug 2025 19:26:04 +0200 Subject: [PATCH 07/12] Gui: fix 'Minimum fractional inch' preference visibility Default Unit Schema is not yet changed in onUnitSystemIndexChanged callback handler, so index needs to be used to request currently selected one. Fixes: 1155f0d75281 ("Base: simplify UnitsSchemas management") --- src/Base/UnitsSchema.h | 2 +- src/Gui/PreferencePages/DlgSettingsGeneral.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Base/UnitsSchema.h b/src/Base/UnitsSchema.h index 6587d5359b..6a459bc520 100644 --- a/src/Base/UnitsSchema.h +++ b/src/Base/UnitsSchema.h @@ -36,7 +36,7 @@ class Quantity; /** * An individual schema object */ -class UnitsSchema +class BaseExport UnitsSchema { public: explicit UnitsSchema(UnitsSchemaSpec spec); diff --git a/src/Gui/PreferencePages/DlgSettingsGeneral.cpp b/src/Gui/PreferencePages/DlgSettingsGeneral.cpp index 48755f9f50..c668f5f38b 100644 --- a/src/Gui/PreferencePages/DlgSettingsGeneral.cpp +++ b/src/Gui/PreferencePages/DlgSettingsGeneral.cpp @@ -749,7 +749,8 @@ void DlgSettingsGeneral::onUnitSystemIndexChanged(const int index) } // Enable/disable the fractional inch option depending on system - const auto visible = UnitsApi::isMultiUnitLength(); + const auto schema = UnitsApi::createSchema(index); + const auto visible = schema->isMultiUnitLength(); ui->comboBox_FracInch->setVisible(visible); ui->fractionalInchLabel->setVisible(visible); } From 0278a1298da9162dd915645cd227d7b371e74516 Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Wed, 27 Aug 2025 11:40:50 +0200 Subject: [PATCH 08/12] Base: refactor unit formatting defaults Defaults for both precision (number of digits after decimal point) and denominator (number of fractions) are defined on various places making difficult to find which default is used for various tasks. Store these values at one central place: UnitsApi. Unless overriden by user, default values are defined by unitSchemasDataPack. --- src/App/Application.cpp | 3 +- src/Base/Quantity.cpp | 11 ++---- src/Base/Quantity.h | 15 -------- src/Base/UnitsApi.cpp | 34 +++++++++---------- src/Base/UnitsApi.h | 13 +++---- .../PreferencePages/DlgSettingsGeneral.cpp | 4 +-- 6 files changed, 30 insertions(+), 50 deletions(-) diff --git a/src/App/Application.cpp b/src/App/Application.cpp index 35957b7e8a..a5dd147c00 100644 --- a/src/App/Application.cpp +++ b/src/App/Application.cpp @@ -2782,8 +2782,7 @@ void Application::initApplication() ("User parameter:BaseApp/Preferences/Units"); Base::UnitsApi::setSchema(hGrp->GetInt("UserSchema", Base::UnitsApi::getDefSchemaNum())); Base::UnitsApi::setDecimals(hGrp->GetInt("Decimals", Base::UnitsApi::getDecimals())); - Base::QuantityFormat::setDefaultDenominator( - hGrp->GetInt("FracInch", Base::QuantityFormat::getDefaultDenominator())); + Base::UnitsApi::setDenominator(hGrp->GetInt("FracInch", Base::UnitsApi::getDenominator())); #if defined (_DEBUG) Base::Console().log("Application is built with debug information\n"); diff --git a/src/Base/Quantity.cpp b/src/Base/Quantity.cpp index 499e4247df..478a2f7f0a 100644 --- a/src/Base/Quantity.cpp +++ b/src/Base/Quantity.cpp @@ -57,23 +57,18 @@ using Base::Quantity; using Base::QuantityFormat; using Base::UnitsSchema; -// ====== Static attributes ========================= -// NOLINTNEXTLINE -int QuantityFormat::defaultDenominator = 8; // for 1/8" - - QuantityFormat::QuantityFormat() : option(OmitGroupSeparator | RejectGroupSeparator) , format(Fixed) - , precision(static_cast(UnitsApi::getDecimals())) - , denominator(defaultDenominator) + , precision(UnitsApi::getDecimals()) + , denominator(UnitsApi::getDenominator()) {} QuantityFormat::QuantityFormat(QuantityFormat::NumberFormat format, int decimals) : option(OmitGroupSeparator | RejectGroupSeparator) , format(format) , precision(decimals < 0 ? UnitsApi::getDecimals() : decimals) - , denominator(defaultDenominator) + , denominator(UnitsApi::getDenominator()) {} // ---------------------------------------------------------------------------- diff --git a/src/Base/Quantity.h b/src/Base/Quantity.h index ec69975c65..01f16609bc 100644 --- a/src/Base/Quantity.h +++ b/src/Base/Quantity.h @@ -52,21 +52,6 @@ struct BaseExport QuantityFormat int precision; int denominator; - // Default denominator of minimum fractional inch. Only used in certain - // schemas. - // NOLINTNEXTLINE - static int defaultDenominator; // i.e 8 for 1/8" - - static inline int getDefaultDenominator() - { - return defaultDenominator; - } - - static inline void setDefaultDenominator(int denom) - { - defaultDenominator = denom; - } - inline int getDenominator() const { return denominator; diff --git a/src/Base/UnitsApi.cpp b/src/Base/UnitsApi.cpp index 65ea8f9448..a58790e8a2 100644 --- a/src/Base/UnitsApi.cpp +++ b/src/Base/UnitsApi.cpp @@ -67,9 +67,24 @@ std::string UnitsApi::getBasicLengthUnit() return schemas->currentSchema()->getBasicLengthUnit(); } -std::size_t UnitsApi::getFractDenominator() +void UnitsApi::setDecimals(const int prec) { - return schemas->defFractDenominator(); + decimals = prec; +} + +int UnitsApi::getDecimals() +{ + return decimals < 0 ? schemas->getDecimals() : decimals; +} + +void UnitsApi::setDenominator(int frac) +{ + denominator = frac; +} + +int UnitsApi::getDenominator() +{ + return denominator < 0 ? schemas->defFractDenominator() : denominator; } std::unique_ptr UnitsApi::createSchema(const std::size_t num) @@ -121,18 +136,3 @@ std::string UnitsApi::schemaTranslate(const Quantity& quant) std::string dummy2; return schemas->currentSchema()->translate(quant, dummy1, dummy2); } - -void UnitsApi::setDecimals(const std::size_t prec) -{ - decimals = prec; -} - -size_t UnitsApi::getDecimals() -{ - return decimals; -} - -size_t UnitsApi::getDefDecimals() -{ - return schemas->getDecimals(); -} diff --git a/src/Base/UnitsApi.h b/src/Base/UnitsApi.h index bcf515ddc8..309f1d2e71 100644 --- a/src/Base/UnitsApi.h +++ b/src/Base/UnitsApi.h @@ -52,9 +52,11 @@ public: static double toDouble(PyObject* args, const Base::Unit& u = Base::Unit()); - static void setDecimals(std::size_t); - static std::size_t getDecimals(); - static std::size_t getDefDecimals(); + static void setDecimals(int); + static int getDecimals(); + + static void setDenominator(int); + static int getDenominator(); static std::vector getDescriptions(); static std::vector getNames(); @@ -64,7 +66,6 @@ public: static bool isMultiUnitAngle(); static bool isMultiUnitLength(); static std::string getBasicLengthUnit(); - static std::size_t getFractDenominator(); static std::size_t getDefSchemaNum() { @@ -76,8 +77,8 @@ public: protected: static inline auto schemas = std::make_unique(UnitsSchemasData::unitSchemasDataPack); - static inline std::size_t decimals {2}; - static inline std::size_t denominator {2}; + static inline int decimals {-1}; + static inline int denominator {-1}; // the python API wrapper methods static PyObject* sParseQuantity(PyObject* self, PyObject* args); diff --git a/src/Gui/PreferencePages/DlgSettingsGeneral.cpp b/src/Gui/PreferencePages/DlgSettingsGeneral.cpp index c668f5f38b..28571c79af 100644 --- a/src/Gui/PreferencePages/DlgSettingsGeneral.cpp +++ b/src/Gui/PreferencePages/DlgSettingsGeneral.cpp @@ -200,7 +200,7 @@ void DlgSettingsGeneral::saveUnitSystemSettings() hGrpu->SetInt("FracInch", FracInch); // Set the actual format value - QuantityFormat::setDefaultDenominator(FracInch); + UnitsApi::setDenominator(FracInch); // Set and save the Unit System if (ui->checkBox_projectUnitSystemIgnore->isChecked()) { @@ -272,7 +272,7 @@ void DlgSettingsGeneral::loadSettings() ui->checkBox_projectUnitSystemIgnore->setChecked(hGrpu->GetBool("IgnoreProjectSchema", false)); // Get the current user setting for the minimum fractional inch - FracInch = hGrpu->GetInt("FracInch", QuantityFormat::getDefaultDenominator()); + FracInch = hGrpu->GetInt("FracInch", UnitsApi::getDenominator()); // Convert fractional inch to the corresponding combobox index using this // handy little equation. From a5f039dd6acb28a3f9e48c36ebbe7eec61e4a671 Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Tue, 2 Sep 2025 23:17:22 +0200 Subject: [PATCH 09/12] Base: QuantityFormat: avoid storing formatting defaults User defined precision and fractional inch are stored on QuantityFormat construction making changes persistent to object life time. Change that so until not explicitely overriden, user defined values are always returned. Co-authored-by: Matthias Danner <28687794+matthiasdanner@users.noreply.github.com> --- src/Base/Quantity.cpp | 20 +++++++++++++++----- src/Base/Quantity.h | 16 ++++++++++------ src/Base/QuantityPyImp.cpp | 10 +++++----- src/Base/UnitsApiPy.cpp | 4 +--- src/Base/UnitsSchema.cpp | 3 ++- src/Gui/InputField.cpp | 4 ++-- src/Gui/QuantitySpinBox.cpp | 4 ++-- src/Mod/Sketcher/App/Constraint.cpp | 2 +- tests/src/Base/SchemaTests.cpp | 2 +- tests/src/Gui/QuantitySpinBox.cpp | 6 +++--- 10 files changed, 42 insertions(+), 29 deletions(-) diff --git a/src/Base/Quantity.cpp b/src/Base/Quantity.cpp index 478a2f7f0a..aad7638140 100644 --- a/src/Base/Quantity.cpp +++ b/src/Base/Quantity.cpp @@ -60,17 +60,27 @@ using Base::UnitsSchema; QuantityFormat::QuantityFormat() : option(OmitGroupSeparator | RejectGroupSeparator) , format(Fixed) - , precision(UnitsApi::getDecimals()) - , denominator(UnitsApi::getDenominator()) + , _precision(-1) + , _denominator(-1) {} QuantityFormat::QuantityFormat(QuantityFormat::NumberFormat format, int decimals) : option(OmitGroupSeparator | RejectGroupSeparator) , format(format) - , precision(decimals < 0 ? UnitsApi::getDecimals() : decimals) - , denominator(UnitsApi::getDenominator()) + , _precision(decimals) + , _denominator(-1) {} +int QuantityFormat::getPrecision() const +{ + return _precision < 0 ? UnitsApi::getDecimals() : _precision; +} + +int QuantityFormat::getDenominator() const +{ + return _denominator < 0 ? UnitsApi::getDenominator() : _denominator; +} + // ---------------------------------------------------------------------------- Quantity::Quantity() @@ -255,7 +265,7 @@ std::string Quantity::toNumber(const QuantityFormat& format) const default: break; } - ss << std::setprecision(format.precision) << myValue; + ss << std::setprecision(format.getPrecision()) << myValue; return ss.str(); } diff --git a/src/Base/Quantity.h b/src/Base/Quantity.h index 01f16609bc..a25616330e 100644 --- a/src/Base/Quantity.h +++ b/src/Base/Quantity.h @@ -49,18 +49,19 @@ struct BaseExport QuantityFormat using NumberOptions = int; NumberOptions option; NumberFormat format; - int precision; - int denominator; - inline int getDenominator() const + int getPrecision() const; + inline void setPrecision(int precision) { - return denominator; + _precision = precision; } - inline void setDenominator(int denom) + int getDenominator() const; + inline void setDenominator(int denominator) { - denominator = denom; + _denominator = denominator; } + QuantityFormat(); explicit QuantityFormat(NumberFormat format, int decimals = -1); inline char toFormat() const @@ -93,6 +94,9 @@ struct BaseExport QuantityFormat return Default; } } + +private: + int _precision, _denominator; }; /** diff --git a/src/Base/QuantityPyImp.cpp b/src/Base/QuantityPyImp.cpp index e8e97645b5..dc7b177b51 100644 --- a/src/Base/QuantityPyImp.cpp +++ b/src/Base/QuantityPyImp.cpp @@ -57,7 +57,7 @@ std::string QuantityPy::representation() const PyObject* QuantityPy::toStr(PyObject* args) const { - int prec = getQuantityPtr()->getFormat().precision; + int prec = getQuantityPtr()->getFormat().getPrecision(); if (!PyArg_ParseTuple(args, "|i", &prec)) { return nullptr; } @@ -669,9 +669,9 @@ Py::Dict QuantityPy::getFormat() const QuantityFormat fmt = getQuantityPtr()->getFormat(); Py::Dict dict; - dict.setItem("Precision", Py::Long(fmt.precision)); + dict.setItem("Precision", Py::Long(fmt.getPrecision())); dict.setItem("NumberFormat", Py::Char(fmt.toFormat())); - dict.setItem("Denominator", Py::Long(fmt.denominator)); + dict.setItem("Denominator", Py::Long(fmt.getDenominator())); return dict; } @@ -681,7 +681,7 @@ void QuantityPy::setFormat(Py::Dict arg) if (arg.hasKey("Precision")) { Py::Long prec(arg.getItem("Precision")); - fmt.precision = static_cast(prec); + fmt.setPrecision(static_cast(prec)); } if (arg.hasKey("NumberFormat")) { @@ -719,7 +719,7 @@ void QuantityPy::setFormat(Py::Dict arg) if (fracInch & (fracInch - 1)) { throw Py::ValueError("Denominator must be a power of two"); } - fmt.denominator = fracInch; + fmt.setDenominator(fracInch); } getQuantityPtr()->setFormat(fmt); diff --git a/src/Base/UnitsApiPy.cpp b/src/Base/UnitsApiPy.cpp index a688901aed..f6f8819892 100644 --- a/src/Base/UnitsApiPy.cpp +++ b/src/Base/UnitsApiPy.cpp @@ -208,9 +208,7 @@ PyObject* UnitsApi::sToNumber(PyObject* /*self*/, PyObject* args) } bool ok {}; - QuantityFormat qf; - qf.format = QuantityFormat::toFormat(format[0], &ok); - qf.precision = decimals; + QuantityFormat qf {QuantityFormat::toFormat(format[0], &ok), decimals}; if (!ok) { PyErr_SetString(PyExc_ValueError, "Invalid format string"); diff --git a/src/Base/UnitsSchema.cpp b/src/Base/UnitsSchema.cpp index 921d245dc5..da8d355c3d 100644 --- a/src/Base/UnitsSchema.cpp +++ b/src/Base/UnitsSchema.cpp @@ -98,7 +98,8 @@ UnitsSchema::toLocale(const Quantity& quant, const double factor, const std::str } auto valueString = - Lc.toString(quant.getValue() / factor, format.toFormat(), format.precision).toStdString(); + Lc.toString(quant.getValue() / factor, format.toFormat(), format.getPrecision()) + .toStdString(); auto notUnit = [](auto s) { return s.empty() || s == "°" || s == "″" || s == "′" || s == "\"" || s == "'"; diff --git a/src/Gui/InputField.cpp b/src/Gui/InputField.cpp index 2b977a17a6..80c125e517 100644 --- a/src/Gui/InputField.cpp +++ b/src/Gui/InputField.cpp @@ -563,13 +563,13 @@ QString InputField::getUnitText() int InputField::getPrecision() const { - return this->actQuantity.getFormat().precision; + return this->actQuantity.getFormat().getPrecision(); } void InputField::setPrecision(const int precision) { Base::QuantityFormat format = actQuantity.getFormat(); - format.precision = precision; + format.setPrecision(precision); actQuantity.setFormat(format); updateText(actQuantity); } diff --git a/src/Gui/QuantitySpinBox.cpp b/src/Gui/QuantitySpinBox.cpp index b7febd34e6..0b054fe2b9 100644 --- a/src/Gui/QuantitySpinBox.cpp +++ b/src/Gui/QuantitySpinBox.cpp @@ -743,14 +743,14 @@ bool QuantitySpinBox::isCheckedRangeInExpresion() const int QuantitySpinBox::decimals() const { Q_D(const QuantitySpinBox); - return d->quantity.getFormat().precision; + return d->quantity.getFormat().getPrecision(); } void QuantitySpinBox::setDecimals(int v) { Q_D(QuantitySpinBox); Base::QuantityFormat f = d->quantity.getFormat(); - f.precision = v; + f.setPrecision(v); d->quantity.setFormat(f); updateText(d->quantity); } diff --git a/src/Mod/Sketcher/App/Constraint.cpp b/src/Mod/Sketcher/App/Constraint.cpp index 0fc5fe0a2e..9734ac9195 100644 --- a/src/Mod/Sketcher/App/Constraint.cpp +++ b/src/Mod/Sketcher/App/Constraint.cpp @@ -136,7 +136,7 @@ Quantity Constraint::getPresentationValue() const QuantityFormat format = quantity.getFormat(); format.option = QuantityFormat::None; format.format = QuantityFormat::Default; - format.precision = 6; // QString's default + format.setPrecision(6); // QString's default quantity.setFormat(format); return quantity; } diff --git a/tests/src/Base/SchemaTests.cpp b/tests/src/Base/SchemaTests.cpp index 7ffc09be5d..bb4e5c14d0 100644 --- a/tests/src/Base/SchemaTests.cpp +++ b/tests/src/Base/SchemaTests.cpp @@ -69,7 +69,7 @@ protected: UnitsApi::setSchema(name); Quantity quantity {value, unit}; QuantityFormat format = quantity.getFormat(); - format.precision = precision; + format.setPrecision(precision); quantity.setFormat(format); return quantity.getSafeUserString(); } diff --git a/tests/src/Gui/QuantitySpinBox.cpp b/tests/src/Gui/QuantitySpinBox.cpp index 6b1fb34b2b..309c668353 100644 --- a/tests/src/Gui/QuantitySpinBox.cpp +++ b/tests/src/Gui/QuantitySpinBox.cpp @@ -51,18 +51,18 @@ private Q_SLOTS: { auto quant = qsb->value(); auto format = quant.getFormat(); - format.precision = 7; + format.setPrecision(7); quant.setFormat(format); qsb->setValue(quant); auto val1 = qsb->value(); - QCOMPARE(val1.getFormat().precision, 7); + QCOMPARE(val1.getFormat().getPrecision(), 7); // format shouldn't change after setting a double qsb->setValue(3.5); auto val2 = qsb->value(); - QCOMPARE(val2.getFormat().precision, 7); + QCOMPARE(val2.getFormat().getPrecision(), 7); } private: From e3d0baf1091f33f5b42be3be9469077b290a5c13 Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Wed, 27 Aug 2025 13:40:47 +0200 Subject: [PATCH 10/12] Base: pass format specifiers to UnitsSchemasData::runSpecial Make toFractional() aware of desired fractional inch denominator. Fixes: 1155f0d75281 ("Base: simplify UnitsSchemas management") --- src/Base/UnitsSchema.cpp | 8 +++++++- src/Base/UnitsSchemasData.h | 41 ++++++++++++++++++++++--------------- 2 files changed, 31 insertions(+), 18 deletions(-) diff --git a/src/Base/UnitsSchema.cpp b/src/Base/UnitsSchema.cpp index da8d355c3d..871ab828b7 100644 --- a/src/Base/UnitsSchema.cpp +++ b/src/Base/UnitsSchema.cpp @@ -79,7 +79,13 @@ UnitsSchema::translate(const Quantity& quant, double& factor, std::string& unitS } if (unitSpec->factor == 0) { - return UnitsSchemasData::runSpecial(unitSpec->unitString, value, factor, unitString); + const QuantityFormat& format = quant.getFormat(); + return UnitsSchemasData::runSpecial(unitSpec->unitString, + value, + format.getPrecision(), + format.getDenominator(), + factor, + unitString); } factor = unitSpec->factor; diff --git a/src/Base/UnitsSchemasData.h b/src/Base/UnitsSchemasData.h index 42286da2f3..718e145162 100644 --- a/src/Base/UnitsSchemasData.h +++ b/src/Base/UnitsSchemasData.h @@ -654,27 +654,27 @@ inline std::size_t greatestCommonDenominator(const std::size_t a, const std::siz /** * double -> [feet'] [inches" [+ fraction]"], e.g.: 3' 4" + 3/8" */ -inline std::string toFractional(const double value) +inline std::string toFractional(const double value, std::size_t denominator) { constexpr auto inchPerFoot {12}; constexpr auto mmPerInch {25.4}; auto numFractUnits = - static_cast(std::round(std::abs(value) / mmPerInch * defDenominator)); + static_cast(std::round(std::abs(value) / mmPerInch * denominator)); if (numFractUnits == 0) { return "0"; } const auto feet = - static_cast(std::floor(numFractUnits / (inchPerFoot * defDenominator))); - numFractUnits -= inchPerFoot * defDenominator * feet; + static_cast(std::floor(numFractUnits / (inchPerFoot * denominator))); + numFractUnits -= inchPerFoot * denominator * feet; - const auto inches = static_cast(std::floor(numFractUnits / defDenominator)); - const std::size_t fractNumerator = numFractUnits - (defDenominator * inches); + const auto inches = static_cast(std::floor(numFractUnits / denominator)); + std::size_t numerator = numFractUnits - (denominator * inches); - const std::size_t common_denom = greatestCommonDenominator(fractNumerator, defDenominator); - const std::size_t numerator = fractNumerator / common_denom; - const std::size_t denominator = defDenominator / common_denom; + const std::size_t common_denom = greatestCommonDenominator(numerator, denominator); + numerator /= common_denom; + denominator /= common_denom; bool addSpace {false}; std::string result; @@ -737,33 +737,40 @@ inline std::string toDms(const double value) */ // clang-format off -inline const std::map> specials +inline const std::map> specials { { - { "toDMS" , [](const double val, double& factor, std::string& unitString) { + { "toDMS" , [](const double val, [[maybe_unused]] const std::size_t precision, [[maybe_unused]] const std::size_t denominator, + double& factor, std::string& unitString) { factor = 1.0; unitString = "deg"; return toDms(val); }}, - { "toFractional" , [](const double val, double& factor, std::string& unitString) { + { "toFractional" , [](const double val, [[maybe_unused]] const std::size_t precision, const std::size_t denominator, + double& factor, std::string& unitString) { factor = 25.4; unitString = "in"; - return toFractional(val); + return toFractional(val, denominator); }} } }; // clang-format on -inline std::string -runSpecial(const std::string& name, const double value, double& factor, std::string& unitString) +inline std::string runSpecial(const std::string& name, + const double value, + const std::size_t precision, + const std::size_t denominator, + double& factor, + std::string& unitString) { - return specials.contains(name) ? specials.at(name)(value, factor, unitString) : ""; + return specials.contains(name) + ? specials.at(name)(value, precision, denominator, factor, unitString) + : ""; } /** * Build data pack */ - inline const UnitsSchemasDataPack unitSchemasDataPack {schemaSpecs, defDecimals, defDenominator}; From fa98e178d130e442bedfcbf842d70e416a369bae Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Wed, 27 Aug 2025 14:20:10 +0200 Subject: [PATCH 11/12] Base: add back Imperial for Civil Engineering unit conversions Fixes: 1155f0d75281 ("Base: simplify UnitsSchemas management") --- src/Base/UnitsSchemasData.h | 11 +++++++++-- tests/src/Base/SchemaTests.cpp | 4 ++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/Base/UnitsSchemasData.h b/src/Base/UnitsSchemasData.h index 718e145162..6f595d38eb 100644 --- a/src/Base/UnitsSchemasData.h +++ b/src/Base/UnitsSchemasData.h @@ -628,9 +628,16 @@ inline const UnitsSchemaSpec s8 }; inline const UnitsSchemaSpec s9 -{ 7, "ImperialCivil", "ft", false, true, QT_TRANSLATE_NOOP("UnitsApi", "Imperial for Civil Eng (ft, ft/s)"), false, +{ 7, "ImperialCivil", "ft", false, true, QT_TRANSLATE_NOOP("UnitsApi", "Imperial for Civil Eng (ft, lb, mph)"), false, { - { "Angle" , {{ 0 , "toDMS" , 0 }}} // <== ! + { "Length" , {{ 0 , "ft" , 12 * 25.4 }}}, + { "Area" , {{ 0 , "ft^2" , 92'903.04 }}}, + { "Volume" , {{ 0 , "ft^3" , 28'316'846.592 }}}, + { "Mass" , {{ 0 , "lb" , 0.45359237 }}}, + { "Pressure" , {{ 0 , "psi" , 6.894744825494 }}}, + { "Stiffness", {{ 0 , "lbf/in", 4.448222 / 0.0254 }}}, + { "Velocity" , {{ 0 , "mph" , 447.04 }}}, + { "Angle" , {{ 0 , "toDMS" , 0 }}} // <== ! } }; diff --git a/tests/src/Base/SchemaTests.cpp b/tests/src/Base/SchemaTests.cpp index bb4e5c14d0..2151751727 100644 --- a/tests/src/Base/SchemaTests.cpp +++ b/tests/src/Base/SchemaTests.cpp @@ -298,7 +298,7 @@ TEST_F(SchemaTest, imperial_decimal_0_mm_precision_1) TEST_F(SchemaTest, imperial_civil_0_mm_precision_0) { const std::string result = setWithPrecision("ImperialCivil", 0.0, Unit::Length, 0); - const auto expect {"0 mm"}; + const auto expect {"0 ft"}; EXPECT_EQ(result, expect); } @@ -306,7 +306,7 @@ TEST_F(SchemaTest, imperial_civil_0_mm_precision_0) TEST_F(SchemaTest, imperial_civil_0_mm_precision_1) { const std::string result = setWithPrecision("ImperialCivil", 0.0, Unit::Length, 1); - const auto expect {"0.0 mm"}; + const auto expect {"0.0 ft"}; EXPECT_EQ(result, expect); } From 9f87add8cca7fecaf8013012b9a5b85dd5469484 Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Wed, 3 Sep 2025 07:54:47 +0200 Subject: [PATCH 12/12] Base: UnitsSchema: use basic imperial conversion constants Use well defined basic constants for conversion of imperial units. This makes code hopefully easily readable and verificable. --- src/Base/CMakeLists.txt | 1 + src/Base/Quantity.cpp | 44 ++++++++++++----------- src/Base/Quantity.h | 9 ++--- src/Base/UnitsConvData.h | 38 ++++++++++++++++++++ src/Base/UnitsSchemasData.h | 69 +++++++++++++++++++------------------ tests/src/Base/Quantity.cpp | 14 ++++---- 6 files changed, 109 insertions(+), 66 deletions(-) create mode 100644 src/Base/UnitsConvData.h diff --git a/src/Base/CMakeLists.txt b/src/Base/CMakeLists.txt index dc6357a450..7e79c2fce8 100644 --- a/src/Base/CMakeLists.txt +++ b/src/Base/CMakeLists.txt @@ -152,6 +152,7 @@ SET(FreeCADBase_UNITAPI_SRCS UnitsApi.cpp UnitsApiPy.cpp UnitsApi.h + UnitsConvData.h UnitsSchema.h UnitsSchema.cpp UnitsSchemas.cpp diff --git a/src/Base/Quantity.cpp b/src/Base/Quantity.cpp index aad7638140..edd268fa51 100644 --- a/src/Base/Quantity.cpp +++ b/src/Base/Quantity.cpp @@ -36,6 +36,7 @@ #include "Quantity.h" #include "Tools.h" #include "UnitsApi.h" +#include "UnitsConvData.h" #include "UnitsSchema.h" /** \defgroup Units Units system @@ -324,6 +325,8 @@ void Quantity::setInvalid() // === Predefined types ===================================================== // clang-format off +using namespace Base::UnitsConvData; + const Quantity Quantity::NanoMetre ( 1.0e-6 , Unit::Length ); const Quantity Quantity::MicroMetre ( 1.0e-3 , Unit::Length ); const Quantity Quantity::MilliMetre ( 1.0 , Unit::Length ); @@ -365,22 +368,23 @@ const Quantity Quantity::Mole ( 1.0 , Unit:: const Quantity Quantity::Candela ( 1.0 , Unit::LuminousIntensity ); -const Quantity Quantity::Inch ( 25.4 , Unit::Length ); -const Quantity Quantity::Foot ( 304.8 , Unit::Length ); -const Quantity Quantity::Thou ( 0.0254 , Unit::Length ); -const Quantity Quantity::Yard ( 914.4 , Unit::Length ); -const Quantity Quantity::Mile ( 1609344.0 , Unit::Length ); +const Quantity Quantity::Inch ( in , Unit::Length ); +const Quantity Quantity::Foot ( ft , Unit::Length ); +const Quantity Quantity::Thou ( in / 1000 , Unit::Length ); +const Quantity Quantity::Yard ( yd , Unit::Length ); +const Quantity Quantity::Mile ( mi , Unit::Length ); -const Quantity Quantity::MilePerHour ( 447.04 , Unit::Velocity ); -const Quantity Quantity::SquareFoot ( 92903.04 , Unit::Area ); -const Quantity Quantity::CubicFoot ( 28316846.592 , Unit::Volume ); +const Quantity Quantity::MilePerHour ( mi / 3600 , Unit::Velocity ); -const Quantity Quantity::Pound ( 0.45359237 , Unit::Mass ); -const Quantity Quantity::Ounce ( 0.0283495231 , Unit::Mass ); -const Quantity Quantity::Stone ( 6.35029318 , Unit::Mass ); -const Quantity Quantity::Hundredweights ( 50.80234544 , Unit::Mass ); +const Quantity Quantity::SquareFoot ( ft * ft , Unit::Area ); +const Quantity Quantity::CubicFoot ( ft * ft * ft , Unit::Volume ); -const Quantity Quantity::PoundForce ( 4448.22 , Unit::Force ); // lbf are ~= 4.44822 Newton +const Quantity Quantity::Pound ( lb , Unit::Mass ); +const Quantity Quantity::Ounce ( lb / 16 , Unit::Mass ); +const Quantity Quantity::Stone ( lb * 14 , Unit::Mass ); +const Quantity Quantity::Hundredweights ( lb * 112 , Unit::Mass ); + +const Quantity Quantity::PoundForce ( 1000 * lbf , Unit::Force ); const Quantity Quantity::Newton ( 1000.0 , Unit::Force ); // Newton (kg*m/s^2) const Quantity Quantity::MilliNewton ( 1.0 , Unit::Force ); @@ -401,12 +405,12 @@ const Quantity Quantity::MilliBar ( 0.1 , Unit:: const Quantity Quantity::Bar ( 100.0 , Unit::Pressure ); // 1 bar = 100 kPa const Quantity Quantity::Torr ( 101.325 / 760.0 , Unit::Pressure ); // Torr is a defined fraction of Pascal (kg/m/s^2 or N/m^2) -const Quantity Quantity::mTorr ( 0.101325 / 760.0 , Unit::Pressure ); // Torr is a defined fraction of Pascal (kg/m/s^2 or N/m^2) -const Quantity Quantity::yTorr ( 0.000101325 / 760.0 , Unit::Pressure ); // Torr is a defined fraction of Pascal (kg/m/s^2 or N/m^2) +const Quantity Quantity::mTorr ( 101.325 / 760.0 / 1e3 , Unit::Pressure ); // Torr is a defined fraction of Pascal (kg/m/s^2 or N/m^2) +const Quantity Quantity::yTorr ( 101.325 / 760.0 / 1e6 , Unit::Pressure ); // Torr is a defined fraction of Pascal (kg/m/s^2 or N/m^2) -const Quantity Quantity::PSI ( 6.894744825494 , Unit::Pressure ); // pounds/in^2 -const Quantity Quantity::KSI ( 6894.744825494 , Unit::Pressure ); // 1000 x pounds/in^2 -const Quantity Quantity::MPSI ( 6894744.825494 , Unit::Pressure ); // 1000 ksi +const Quantity Quantity::PSI ( psi , Unit::Pressure ); +const Quantity Quantity::KSI ( psi * 1000 , Unit::Pressure ); +const Quantity Quantity::MPSI ( psi * 1000000 , Unit::Pressure ); const Quantity Quantity::Watt ( 1e+6 , Unit::Power ); // Watt (kg*m^2/s^3) const Quantity Quantity::MilliWatt ( 1e+3 , Unit::Power ); @@ -458,8 +462,8 @@ const Quantity Quantity::Calorie ( 4.1868e+6 , Unit:: const Quantity Quantity::KiloCalorie ( 4.1868e+9 , Unit::Work ); const Quantity Quantity::NewtonMeter ( 1e+6 , Unit::Moment ); // Joule (kg*m^2/s^2) -const Quantity Quantity::KMH ( 277.778 , Unit::Velocity ); // km/h -const Quantity Quantity::MPH ( 447.04 , Unit::Velocity ); // Mile/h +const Quantity Quantity::KMH ( 1e+6 / 3600 , Unit::Velocity ); // km/h +const Quantity Quantity::MPH ( mi / 3600 , Unit::Velocity ); // Mile/h const Quantity Quantity::AngMinute ( 1.0 / 60.0 , Unit::Angle ); // angular minute const Quantity Quantity::AngSecond ( 1.0 / 3600.0 , Unit::Angle ); // angular second diff --git a/src/Base/Quantity.h b/src/Base/Quantity.h index a25616330e..68e9be1dd3 100644 --- a/src/Base/Quantity.h +++ b/src/Base/Quantity.h @@ -195,7 +195,6 @@ public: /// sets the quantity invalid void setInvalid(); - /** Predefined Unit types. */ //@{ static const Quantity NanoMetre; @@ -243,14 +242,15 @@ public: static const Quantity Foot; static const Quantity Thou; static const Quantity Yard; + static const Quantity Mile; + + static const Quantity MilePerHour; static const Quantity Pound; static const Quantity Ounce; static const Quantity Stone; static const Quantity Hundredweights; - static const Quantity Mile; - static const Quantity MilePerHour; static const Quantity SquareFoot; static const Quantity CubicFoot; @@ -308,8 +308,6 @@ public: static const Quantity Weber; - // static const Quantity Oersted; - static const Quantity Farad; static const Quantity MilliFarad; static const Quantity MicroFarad; @@ -344,7 +342,6 @@ public: static const Quantity AngSecond; //@} - private: double myValue; Unit myUnit; diff --git a/src/Base/UnitsConvData.h b/src/Base/UnitsConvData.h new file mode 100644 index 0000000000..47ffb07afa --- /dev/null +++ b/src/Base/UnitsConvData.h @@ -0,0 +1,38 @@ +/************************************************************************ + * * + * 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 * + * * + ************************************************************************/ + +#ifndef BASE_UNITSCONVDATA_H +#define BASE_UNITSCONVDATA_H + +namespace Base::UnitsConvData +{ + +constexpr auto in {25.4}; +constexpr auto ft {12 * in}; +constexpr auto yd {3 * ft}; +constexpr auto mi {1760 * yd}; +constexpr auto lb {0.45359237}; +constexpr auto lbf {9.80665 * lb}; +constexpr auto psi {lbf / (in * in) * 1000}; + +} // namespace Base::UnitsConvData + +#endif diff --git a/src/Base/UnitsSchemasData.h b/src/Base/UnitsSchemasData.h index 6f595d38eb..1d909ea077 100644 --- a/src/Base/UnitsSchemasData.h +++ b/src/Base/UnitsSchemasData.h @@ -30,6 +30,7 @@ #include "fmt/format.h" #include "fmt/ranges.h" +#include "UnitsConvData.h" #include "UnitsSchemasSpecs.h" /** @@ -42,6 +43,8 @@ namespace Base::UnitsSchemasData constexpr std::size_t defDecimals {2}; constexpr std::size_t defDenominator {8}; +using namespace Base::UnitsConvData; + // NOLINTBEGIN // clang-format off inline const UnitsSchemaSpec s0 @@ -69,15 +72,15 @@ inline const UnitsSchemaSpec s1 inline const UnitsSchemaSpec s2 { 3, "ImperialDecimal", "in", false, false, QT_TRANSLATE_NOOP("UnitsApi", "Imperial decimal (in, lb)"), false, { - { "Length", {{ 0 , "in" , 25.4 }}}, + { "Length", {{ 0 , "in" , in }}}, { "Angle", {{ 0 , "°" , 1.0 }}}, - { "Area", {{ 0 , "in^2" , 645.16 }}}, - { "Volume", {{ 0 , "in^3" , 16387.064 }}}, - { "Mass", {{ 0 , "lb" , 0.45359237 }}}, - { "Pressure", {{ 0 , "psi" , 6.894744825494 }}}, - { "Stiffness", {{ 0 , "lbf/in" , 4.448222 / 0.0254 }}}, - { "Velocity", {{ 0 , "in/min" , 25.4 / 60 }}}, - { "Acceleration", {{ 0 , "in/min^2", 25.4 / 3600 }}} + { "Area", {{ 0 , "in^2" , in * in }}}, + { "Volume", {{ 0 , "in^3" , in * in * in }}}, + { "Mass", {{ 0 , "lb" , lb }}}, + { "Pressure", {{ 0 , "psi" , psi }}}, + { "Stiffness", {{ 0 , "lbf/in" , lbf / in * 1000 }}}, + { "Velocity", {{ 0 , "in/min" , in / 60 }}}, + { "Acceleration", {{ 0 , "in/min^2", in / 3600 }}} } }; @@ -582,36 +585,36 @@ inline const UnitsSchemaSpec s7 { 2, "Imperial", "in", false, false, QT_TRANSLATE_NOOP("UnitsApi", "US customary (in, lb)"), false, { { "Length", { - { 0.00000254 , "in" , 25.4 }, - { 2.54 , "thou" , 0.0254 }, - { 304.8 , "\"" , 25.4 }, - { 914.4 , "'" , 304.8 }, - { 1'609'344.0 , "yd" , 914.4 }, - { 1'609'344'000.0 , "mi" , 1'609'344.0 }, - { 0 , "in" , 25.4 }} + { 0.00000254 , "in" , in }, + { 2.54 , "thou" , in / 1000 }, + { 304.8 , "\"" , in }, + { 914.4 , "'" , ft }, + { 1'609'344.0 , "yd" , yd }, + { 1'609'344'000.0 , "mi" , mi }, + { 0 , "in" , in }} }, { "Angle", { { 0 , "°" , 1.0 }} }, { "Area", { - { 0 , "in^2" , 645.16 }} + { 0 , "in^2" , in * in }} }, { "Volume", { - { 0 , "in^3" , 16'387.064 }} + { 0 , "in^3" , in * in * in }} }, { "Mass", { - { 0 , "lb" , 0.45359237 }} + { 0 , "lb" , lb }} }, { "Pressure", { - { 6'894.744 , "psi" , 6.894744825494 }, - { 6'894'744.825 , "ksi" , 6'894.744825494 }, - { 0 , "psi" , 6.894744825494 }} + { 1000 * psi , "psi" , psi }, + { 1000000 * psi , "ksi" , 1000 * psi }, + { 0 , "psi" , psi }} }, { "Stiffness", { - { 0 , "lbf/in" , 4.448222 / 0.0254 }} + { 0 , "lbf/in" , lbf / in * 1000 }} }, { "Velocity", { - { 0 , "in/min" , 25.4 / 60 }} + { 0 , "in/min" , in / 60 }} } } }; @@ -621,22 +624,22 @@ inline const UnitsSchemaSpec s8 { { "Length" , {{ 0 , "toFractional" , 0 }}}, // <== ! { "Angle" , {{ 0 , "°" , 1.0 }}}, - { "Area" , {{ 0 , "sqft" , 92'903.04 }}}, - { "Volume" , {{ 0 , "cft" , 28'316'846.592 }}}, - { "Velocity" , {{ 0 , "in/min" , 25.4 / 60 }}} + { "Area" , {{ 0 , "sqft" , ft * ft }}}, + { "Volume" , {{ 0 , "cft" , ft * ft * ft }}}, + { "Velocity" , {{ 0 , "in/min" , in / 60 }}} } }; inline const UnitsSchemaSpec s9 { 7, "ImperialCivil", "ft", false, true, QT_TRANSLATE_NOOP("UnitsApi", "Imperial for Civil Eng (ft, lb, mph)"), false, { - { "Length" , {{ 0 , "ft" , 12 * 25.4 }}}, - { "Area" , {{ 0 , "ft^2" , 92'903.04 }}}, - { "Volume" , {{ 0 , "ft^3" , 28'316'846.592 }}}, - { "Mass" , {{ 0 , "lb" , 0.45359237 }}}, - { "Pressure" , {{ 0 , "psi" , 6.894744825494 }}}, - { "Stiffness", {{ 0 , "lbf/in", 4.448222 / 0.0254 }}}, - { "Velocity" , {{ 0 , "mph" , 447.04 }}}, + { "Length" , {{ 0 , "ft" , ft }}}, + { "Area" , {{ 0 , "ft^2" , ft * ft }}}, + { "Volume" , {{ 0 , "ft^3" , ft * ft * ft }}}, + { "Mass" , {{ 0 , "lb" , lb }}}, + { "Pressure" , {{ 0 , "psi" , psi }}}, + { "Stiffness", {{ 0 , "lbf/in", lbf / in * 1000 }}}, + { "Velocity" , {{ 0 , "mph" , mi / 3600 }}}, { "Angle" , {{ 0 , "toDMS" , 0 }}} // <== ! } }; diff --git a/tests/src/Base/Quantity.cpp b/tests/src/Base/Quantity.cpp index d333236271..ca05adecb7 100644 --- a/tests/src/Base/Quantity.cpp +++ b/tests/src/Base/Quantity.cpp @@ -198,25 +198,25 @@ protected: TEST_F(BaseQuantityLoc, psi_parse_spaced) { const auto qParsed = Quantity::parse("1 psi"); - EXPECT_EQ(qParsed.getValue(), 6.8947448254939996); + EXPECT_EQ(qParsed.getValue(), 6.8947572931683609); } TEST_F(BaseQuantityLoc, psi_parse_no_space) { const auto qParsed = Quantity::parse("1psi"); - EXPECT_EQ(qParsed.getValue(), 6.8947448254939996); + EXPECT_EQ(qParsed.getValue(), 6.8947572931683609); } TEST_F(BaseQuantityLoc, psi_parse_user_str) { const auto qParsed = Quantity::parse("1 psi"); - EXPECT_EQ(qParsed.getUserString(), "6894.74 Pa"); + EXPECT_EQ(qParsed.getUserString(), "6894.76 Pa"); } TEST_F(BaseQuantityLoc, psi_parse_safe_user_str) { const auto qParsed = Quantity::parse("1 psi"); - EXPECT_EQ(qParsed.getSafeUserString(), "6894.74 Pa"); + EXPECT_EQ(qParsed.getSafeUserString(), "6894.76 Pa"); } TEST_F(BaseQuantityLoc, psi_parse_unit_type) @@ -228,7 +228,7 @@ TEST_F(BaseQuantityLoc, psi_parse_unit_type) TEST_F(BaseQuantityLoc, psi_to_Pa) { const auto result = Quantity::parse("1 psi").getValueAs(Quantity::Pascal); - const auto expect = 6894.7448254939991; + const auto expect = 6894.7572931683608; EXPECT_EQ(result, expect); } @@ -236,7 +236,7 @@ TEST_F(BaseQuantityLoc, psi_to_Pa) TEST_F(BaseQuantityLoc, psi_to_KPa) { const auto result = Quantity::parse("1 psi").getValueAs(Quantity::KiloPascal); - const auto expect = 6.8947448254939996; + const auto expect = 6.8947572931683609; EXPECT_EQ(result, expect); } @@ -244,7 +244,7 @@ TEST_F(BaseQuantityLoc, psi_to_KPa) TEST_F(BaseQuantityLoc, psi_to_MPa) { const auto result = Quantity::parse("1 psi").getValueAs(Quantity::MegaPascal); - const auto expect = 0.0068947448254939999; + const auto expect = 0.0068947572931683607; EXPECT_EQ(result, expect); }