diff --git a/.github/workflows/sub_buildPixi.yml b/.github/workflows/sub_buildPixi.yml index 55efff3977..c3c562ad06 100644 --- a/.github/workflows/sub_buildPixi.yml +++ b/.github/workflows/sub_buildPixi.yml @@ -106,9 +106,9 @@ jobs: mkdir -p ${{ env.reportdir }} echo "reportFile=${{ env.reportfilename }}" >> $GITHUB_OUTPUT - - uses: prefix-dev/setup-pixi@v0.8.2 + - uses: prefix-dev/setup-pixi@v0.8.3 with: - pixi-version: v0.41.3 + pixi-version: v0.43.3 cache: false - name: Restore Compiler Cache diff --git a/cMake/FindEigen3.cmake b/cMake/FindEigen3.cmake index eda8143186..57ee44be2a 100644 --- a/cMake/FindEigen3.cmake +++ b/cMake/FindEigen3.cmake @@ -1,8 +1,8 @@ # - Try to find Eigen3 lib # # This module supports requiring a minimum version, e.g. you can do -# find_package(Eigen3 3.1.2) -# to require version 3.1.2 or newer of Eigen3. +# find_package(Eigen3 3.4.0) +# to require version 3.4.0 or newer of Eigen3. # # Once done this will define # @@ -17,7 +17,7 @@ if(NOT Eigen3_FIND_VERSION) set(Eigen3_FIND_VERSION_MAJOR 3) - set(Eigen3_FIND_VERSION_MINOR 0) + set(Eigen3_FIND_VERSION_MINOR 4) set(Eigen3_FIND_VERSION_PATCH 0) set(Eigen3_FIND_VERSION "${Eigen3_FIND_VERSION_MAJOR}.${Eigen3_FIND_VERSION_MINOR}.${Eigen3_FIND_VERSION_PATCH}") endif(NOT Eigen3_FIND_VERSION) diff --git a/cMake/FreeCAD_Helpers/InitializeFreeCADBuildOptions.cmake b/cMake/FreeCAD_Helpers/InitializeFreeCADBuildOptions.cmake index 4d860ffbcb..ebe6938865 100644 --- a/cMake/FreeCAD_Helpers/InitializeFreeCADBuildOptions.cmake +++ b/cMake/FreeCAD_Helpers/InitializeFreeCADBuildOptions.cmake @@ -178,7 +178,7 @@ macro(InitializeFreeCADBuildOptions) endif(MSVC) if(NOT MSVC) option(BUILD_FEM_NETGEN "Build the FreeCAD FEM module with the NETGEN mesher" OFF) - option(FREECAD_USE_PCL "Build the features that use PCL libs" OFF) + option(FREECAD_USE_PCL "Build the features that use PCL libs" ON) endif(NOT MSVC) # if this is set override some options diff --git a/cMake/FreeCAD_Helpers/SetupEigen.cmake b/cMake/FreeCAD_Helpers/SetupEigen.cmake index 492519a9d9..37f6326f8c 100644 --- a/cMake/FreeCAD_Helpers/SetupEigen.cmake +++ b/cMake/FreeCAD_Helpers/SetupEigen.cmake @@ -11,19 +11,10 @@ macro(SetupEigen) "=================\n") endif(NOT EIGEN3_FOUND) - if (EIGEN3_FOUND AND ${EIGEN3_VERSION} VERSION_LESS "3.3.1") + if (EIGEN3_FOUND AND ${EIGEN3_VERSION} VERSION_LESS "3.4.0") message(WARNING "Disable module flatmesh because it requires " - "minimum Eigen3 version 3.3.1 but version ${EIGEN3_VERSION} was found") + "minimum Eigen3 version 3.4.0 but version ${EIGEN3_VERSION} was found") set (BUILD_FLAT_MESH OFF) endif() - # Older versions raise the warning -Wdeprecated-copy with clang10/gcc10 - if (EIGEN3_FOUND AND ${EIGEN3_VERSION} VERSION_LESS "3.3.8") - unset(_flag_found CACHE) - check_cxx_compiler_flag("-Wno-deprecated-copy" _flag_found) - if (_flag_found) - set (EIGEN3_NO_DEPRECATED_COPY "-Wno-deprecated-copy") - endif () - endif() - endmacro(SetupEigen) diff --git a/cMake/FreeCadMacros.cmake b/cMake/FreeCadMacros.cmake index 5a169f587d..1303484e09 100644 --- a/cMake/FreeCadMacros.cmake +++ b/cMake/FreeCadMacros.cmake @@ -143,35 +143,47 @@ macro(generate_from_xml BASE_NAME) ) endmacro(generate_from_xml) -macro(generate_from_py BASE_NAME) +macro(generate_from_py_impl BASE_NAME SUFFIX) set(TOOL_PATH "${CMAKE_SOURCE_DIR}/src/Tools/bindings/generate.py") file(TO_NATIVE_PATH "${TOOL_PATH}" TOOL_NATIVE_PATH) file(TO_NATIVE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/${BASE_NAME}.pyi" SOURCE_NATIVE_PATH) - set(SOURCE_CPP_PATH "${CMAKE_CURRENT_BINARY_DIR}/${BASE_NAME}.cpp" ) + set(SOURCE_CPP_PATH "${CMAKE_CURRENT_BINARY_DIR}/${BASE_NAME}Py${SUFFIX}.cpp") + set(SOURCE_H_PATH "${CMAKE_CURRENT_BINARY_DIR}/${BASE_NAME}Py${SUFFIX}.h") # BASE_NAME may include also a path name GET_FILENAME_COMPONENT(OUTPUT_PATH "${SOURCE_CPP_PATH}" PATH) file(TO_NATIVE_PATH "${OUTPUT_PATH}" OUTPUT_NATIVE_PATH) if(NOT EXISTS "${SOURCE_CPP_PATH}") - # assures the source files are generated at least once + # Ensure the source files are generated at least once. message(STATUS "${SOURCE_CPP_PATH}") - execute_process(COMMAND "${Python3_EXECUTABLE}" "${TOOL_NATIVE_PATH}" --outputPath "${OUTPUT_NATIVE_PATH}" "${SOURCE_NATIVE_PATH}" - WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" COMMAND_ERROR_IS_FATAL ANY + execute_process( + COMMAND "${Python3_EXECUTABLE}" "${TOOL_NATIVE_PATH}" --outputPath "${OUTPUT_NATIVE_PATH}" "${SOURCE_NATIVE_PATH}" + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + COMMAND_ERROR_IS_FATAL ANY ) endif() + add_custom_command( - OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${BASE_NAME}_.h" "${CMAKE_CURRENT_BINARY_DIR}/${BASE_NAME}_.cpp" + OUTPUT "${SOURCE_H_PATH}" "${SOURCE_CPP_PATH}" COMMAND ${Python3_EXECUTABLE} "${TOOL_NATIVE_PATH}" --outputPath "${OUTPUT_NATIVE_PATH}" ${BASE_NAME}.pyi MAIN_DEPENDENCY "${BASE_NAME}.pyi" DEPENDS - "${CMAKE_SOURCE_DIR}/src/Tools/bindings/templates/templateClassPyExport.py" - "${TOOL_PATH}" + "${CMAKE_SOURCE_DIR}/src/Tools/bindings/templates/templateClassPyExport.py" + "${TOOL_PATH}" WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" - COMMENT "Building ${BASE_NAME}.h/.cpp out of ${BASE_NAME}.pyi" + COMMENT "Building ${BASE_NAME}Py${SUFFIX}.h/.cpp out of ${BASE_NAME}.pyi" ) +endmacro(generate_from_py_impl) + +macro(generate_from_py BASE_NAME) + generate_from_py_impl(${BASE_NAME} "") endmacro(generate_from_py) +macro(generate_from_py_ BASE_NAME) + generate_from_py_impl(${BASE_NAME} "_") +endmacro(generate_from_py_) + macro(generate_embed_from_py BASE_NAME OUTPUT_FILE) set(TOOL_PATH "${CMAKE_SOURCE_DIR}/src/Tools/PythonToCPP.py") file(TO_NATIVE_PATH "${TOOL_PATH}" TOOL_NATIVE_PATH) diff --git a/package/ubuntu/install-apt-packages.sh b/package/ubuntu/install-apt-packages.sh index 7622a183a0..2fff06ea6e 100755 --- a/package/ubuntu/install-apt-packages.sh +++ b/package/ubuntu/install-apt-packages.sh @@ -33,6 +33,7 @@ packages=( libocct-visualization-dev libopencv-dev libproj-dev + libpcl-dev libpyside2-dev libqt5opengl5-dev libqt5svg5-dev diff --git a/src/3rdParty/salomesmesh/src/StdMeshers/StdMeshers_Quadrangle_2D.cpp b/src/3rdParty/salomesmesh/src/StdMeshers/StdMeshers_Quadrangle_2D.cpp index 5ebf9dbfb4..02173a2abe 100644 --- a/src/3rdParty/salomesmesh/src/StdMeshers/StdMeshers_Quadrangle_2D.cpp +++ b/src/3rdParty/salomesmesh/src/StdMeshers/StdMeshers_Quadrangle_2D.cpp @@ -4218,7 +4218,7 @@ bool StdMeshers_Quadrangle_2D::check() return isOK; } -/*//================================================================================ +//================================================================================ /*! * \brief Finds vertices at the most sharp face corners * \param [in] theFace - the FACE diff --git a/src/App/Application.cpp b/src/App/Application.cpp index 6c7c5222d5..0f3fce5d17 100644 --- a/src/App/Application.cpp +++ b/src/App/Application.cpp @@ -2045,6 +2045,7 @@ void Application::initTypes() App::PropertyElectricalResistance ::init(); App::PropertyElectricCharge ::init(); App::PropertySurfaceChargeDensity ::init(); + App::PropertyVolumeChargeDensity ::init(); App::PropertyElectricCurrent ::init(); App::PropertyElectricPotential ::init(); App::PropertyElectromagneticPotential ::init(); @@ -2635,8 +2636,8 @@ void Application::initConfig(int argc, char ** argv) Py_DECREF(pyModule); } - const char* pythonpath = Base::Interpreter().init(argc,argv); - if (pythonpath) + std::string pythonpath = Base::Interpreter().init(argc,argv); + if (!pythonpath.empty()) mConfig["PythonSearchPath"] = pythonpath; else Base::Console().Warning("Encoding of Python paths failed\n"); diff --git a/src/App/Datums.cpp b/src/App/Datums.cpp index f595277856..0d315ed859 100644 --- a/src/App/Datums.cpp +++ b/src/App/Datums.cpp @@ -33,10 +33,6 @@ #include "Datums.h" -#ifndef M_PI -#define M_PI 3.14159265358979323846 -#endif - using namespace App; PROPERTY_SOURCE(App::DatumElement, App::GeoFeature) @@ -243,14 +239,16 @@ App::DocumentObjectExecReturn* LocalCoordinateSystem::execute() const std::vector& LocalCoordinateSystem::getSetupData() { + using std::numbers::pi; + static const std::vector setupData = { // clang-format off {App::Line::getClassTypeId(), AxisRoles[0], tr("X-axis"), Base::Rotation()}, - {App::Line::getClassTypeId(), AxisRoles[1], tr("Y-axis"), Base::Rotation(Base::Vector3d(1, 1, 1), M_PI * 2 / 3)}, - {App::Line::getClassTypeId(), AxisRoles[2], tr("Z-axis"), Base::Rotation(Base::Vector3d(1,-1, 1), M_PI * 2 / 3)}, + {App::Line::getClassTypeId(), AxisRoles[1], tr("Y-axis"), Base::Rotation(Base::Vector3d(1, 1, 1), pi * 2 / 3)}, + {App::Line::getClassTypeId(), AxisRoles[2], tr("Z-axis"), Base::Rotation(Base::Vector3d(1,-1, 1), pi * 2 / 3)}, {App::Plane::getClassTypeId(), PlaneRoles[0], tr("XY-plane"), Base::Rotation()}, {App::Plane::getClassTypeId(), PlaneRoles[1], tr("XZ-plane"), Base::Rotation(1.0, 0.0, 0.0, 1.0)}, - {App::Plane::getClassTypeId(), PlaneRoles[2], tr("YZ-plane"), Base::Rotation(Base::Vector3d(1, 1, 1), M_PI * 2 / 3)}, + {App::Plane::getClassTypeId(), PlaneRoles[2], tr("YZ-plane"), Base::Rotation(Base::Vector3d(1, 1, 1), pi * 2 / 3)}, {App::Point::getClassTypeId(), PointRoles[0], tr("Origin"), Base::Rotation()} // clang-format on }; diff --git a/src/App/Document.cpp b/src/App/Document.cpp index 3b7e3c2ea7..5b22ef1b8a 100644 --- a/src/App/Document.cpp +++ b/src/App/Document.cpp @@ -20,41 +20,6 @@ * * ***************************************************************************/ - -/*! -\defgroup Document Document -\ingroup APP -\brief The Base class of the FreeCAD Document - -This (besides the App::Application class) is the most important class in FreeCAD. -It contains all the data of the opened, saved, or newly created FreeCAD Document. -The App::Document manages the Undo and Redo mechanism and the linking of documents. - -\namespace App \class App::Document -This is besides the Application class the most important class in FreeCAD -It contains all the data of the opened, saved or newly created FreeCAD Document. -The Document manage the Undo and Redo mechanism and the linking of documents. - -Note: the documents are not free objects. They are completely handled by the -App::Application. Only the Application can Open or destroy a document. - -\section Exception Exception handling -As the document is the main data structure of FreeCAD we have to take a close -look at how Exceptions affect the integrity of the App::Document. - -\section UndoRedo Undo Redo an Transactions -Undo Redo handling is one of the major mechanism of a document in terms of -user friendliness and speed (no one will wait for Undo too long). - -\section Dependency Graph and dependency handling -The FreeCAD document handles the dependencies of its DocumentObjects with -an adjacence list. This gives the opportunity to calculate the shortest -recompute path. Also, it enables more complicated dependencies beyond trees. - -@see App::Application -@see App::DocumentObject -*/ - #include "PreCompiled.h" #ifndef _PreComp_ diff --git a/src/App/Document.h b/src/App/Document.h index 369e29377c..dce5b1a553 100644 --- a/src/App/Document.h +++ b/src/App/Document.h @@ -57,7 +57,11 @@ class Transaction; class StringHasher; using StringHasherRef = Base::Reference; -/// The document class +/** + * @brief The document class + * @ingroup DocumentGroup + * @details For a more high-level discussion see the topic @ref DocumentGroup "Document". + */ class AppExport Document: public App::PropertyContainer { PROPERTY_HEADER_WITH_OVERRIDE(App::Document); diff --git a/src/App/DocumentObject.cpp b/src/App/DocumentObject.cpp index bc5d291e7b..df544afd0b 100644 --- a/src/App/DocumentObject.cpp +++ b/src/App/DocumentObject.cpp @@ -55,10 +55,6 @@ FC_LOG_LEVEL_INIT("App", true, true) using namespace App; -/** \defgroup DocObject Document Object - \ingroup APP - \brief Base class of all objects handled in the Document -*/ PROPERTY_SOURCE(App::DocumentObject, App::TransactionalObject) diff --git a/src/App/DocumentObject.h b/src/App/DocumentObject.h index 860d3af357..bd62977774 100644 --- a/src/App/DocumentObject.h +++ b/src/App/DocumentObject.h @@ -101,7 +101,10 @@ public: }; -/** Base class of all Classes handled in the Document +/** + * @brief %Base class of all objects handled in the @ref App::Document "Document". + * @ingroup DocObject + * @details For a more high-level overview see topic @ref DocObject "Document Object". */ class AppExport DocumentObject: public App::TransactionalObject { diff --git a/src/App/Expression.cpp b/src/App/Expression.cpp index 115db3dd9b..a081b351d4 100644 --- a/src/App/Expression.cpp +++ b/src/App/Expression.cpp @@ -34,6 +34,8 @@ #include #include +#include +#include #include #include #include @@ -52,29 +54,11 @@ #include "ExpressionParser.h" -/** \defgroup Expression Expressions framework - \ingroup APP - \brief The expression system allows users to write expressions and formulas that produce values -*/ - using namespace Base; using namespace App; FC_LOG_LEVEL_INIT("Expression", true, true) -#ifndef M_PI -#define M_PI 3.14159265358979323846 -#endif -#ifndef M_E -#define M_E 2.71828182845904523536 -#endif -#ifndef DOUBLE_MAX -# define DOUBLE_MAX 1.7976931348623157E+308 /* max decimal value of a "double"*/ -#endif -#ifndef DOUBLE_MIN -# define DOUBLE_MIN 2.2250738585072014E-308 /* min decimal value of a "double"*/ -#endif - #if defined(_MSC_VER) #define strtoll _strtoi64 #pragma warning(disable : 4003) @@ -336,22 +320,22 @@ static inline int essentiallyInteger(double a, long &l, int &i) { double intpart; if (std::modf(a,&intpart) == 0.0) { if (intpart<0.0) { - if (intpart >= INT_MIN) { + if (intpart >= std::numeric_limits::min()) { i = static_cast(intpart); l = i; return 1; } - if (intpart >= LONG_MIN) { + if (intpart >= std::numeric_limits::min()) { l = static_cast(intpart); return 2; } } - else if (intpart <= INT_MAX) { + else if (intpart <= std::numeric_limits::max()) { i = static_cast(intpart); l = i; return 1; } - else if (intpart <= static_cast(LONG_MAX)) { + else if (intpart <= static_cast(std::numeric_limits::max())) { l = static_cast(intpart); return 2; } @@ -363,12 +347,12 @@ static inline bool essentiallyInteger(double a, long &l) { double intpart; if (std::modf(a,&intpart) == 0.0) { if (intpart<0.0) { - if (intpart >= LONG_MIN) { + if (intpart >= std::numeric_limits::min()) { l = static_cast(intpart); return true; } } - else if (intpart <= static_cast(LONG_MAX)) { + else if (intpart <= static_cast(std::numeric_limits::max())) { l = static_cast(intpart); return true; } @@ -2150,6 +2134,8 @@ Base::Vector3d FunctionExpression::extractVectorArgument( Py::Object FunctionExpression::evaluate(const Expression *expr, int f, const std::vector &args) { + using std::numbers::pi; + if(!expr || !expr->getOwner()) _EXPR_THROW("Invalid owner.", expr); @@ -2186,7 +2172,7 @@ Py::Object FunctionExpression::evaluate(const Expression *expr, int f, const std Py::Object pyobj = args[0]->getPyValue(); if (PyObject_TypeCheck(pyobj.ptr(), &Base::MatrixPy::Type)) { auto m = static_cast(pyobj.ptr())->value(); - if (fabs(m.determinant()) <= DBL_EPSILON) + if (fabs(m.determinant()) <= std::numeric_limits::epsilon()) _EXPR_THROW("Cannot invert singular matrix.", expr); m.inverseGauss(); return Py::asObject(new Base::MatrixPy(m)); @@ -2227,7 +2213,7 @@ Py::Object FunctionExpression::evaluate(const Expression *expr, int f, const std Rotation rotation = Base::Rotation( Vector3d(static_cast(f == MROTATEX), static_cast(f == MROTATEY), static_cast(f == MROTATEZ)), - rotationAngle.getValue() * M_PI / 180.0); + rotationAngle.getValue() * pi / 180.0); Base::Matrix4D rotationMatrix; rotation.getValue(rotationMatrix); @@ -2366,7 +2352,7 @@ Py::Object FunctionExpression::evaluate(const Expression *expr, int f, const std switch (f) { case VANGLE: - return Py::asObject(new QuantityPy(new Quantity(vector1.GetAngle(vector2) * 180 / M_PI, Unit::Angle))); + return Py::asObject(new QuantityPy(new Quantity(vector1.GetAngle(vector2) * 180 / pi, Unit::Angle))); case VCROSS: return Py::asObject(new Base::VectorPy(vector1.Cross(vector2))); case VDOT: @@ -2425,7 +2411,7 @@ Py::Object FunctionExpression::evaluate(const Expression *expr, int f, const std _EXPR_THROW("Unit must be either empty or an angle.", expr); // Convert value to radians - value *= M_PI / 180.0; + value *= pi / 180.0; unit = Unit(); break; case ACOS: @@ -2434,7 +2420,7 @@ Py::Object FunctionExpression::evaluate(const Expression *expr, int f, const std if (!v1.isDimensionless()) _EXPR_THROW("Unit must be empty.", expr); unit = Unit::Angle; - scaler = 180.0 / M_PI; + scaler = 180.0 / pi; break; case EXP: case LOG: @@ -2466,7 +2452,7 @@ Py::Object FunctionExpression::evaluate(const Expression *expr, int f, const std if (v1.getUnit() != v2.getUnit()) _EXPR_THROW("Units must be equal.",expr); unit = Unit::Angle; - scaler = 180.0 / M_PI; + scaler = 180.0 / pi; break; case MOD: if (e2.isNone()) diff --git a/src/App/Expression.h b/src/App/Expression.h index 7ff8352d0c..a17a2267ba 100644 --- a/src/App/Expression.h +++ b/src/App/Expression.h @@ -108,11 +108,14 @@ protected: int _changed{0}; }; -/** - * Base class for expressions. - * - */ +/** + * @brief %Base class for expressions. + * @ingroup ExpressionFramework + * + * @details For a high-level overview of the %Expression framework see topic + * @ref ExpressionFramework "Expression Framework". + */ class AppExport Expression : public Base::BaseClass { TYPESYSTEM_HEADER_WITH_OVERRIDE(); diff --git a/src/App/ExpressionParser.l b/src/App/ExpressionParser.l index 49e6e3672c..566dab95e0 100644 --- a/src/App/ExpressionParser.l +++ b/src/App/ExpressionParser.l @@ -341,15 +341,15 @@ EXPO [eE][-+]?[0-9]+ {DIGIT}+{EXPO} COUNTCHARS; yylval.fvalue = num_change(yytext,',','.'); return yylval.fvalue == 1 ? ONE : NUM; {DIGIT}+ { COUNTCHARS; yylval.ivalue = strtoll( yytext, NULL, 10 ); - if (yylval.ivalue == LLONG_MIN) + if (yylval.ivalue == std::numeric_limits::min()) throw Base::UnderflowError("Integer underflow"); - else if (yylval.ivalue == LLONG_MAX) + else if (yylval.ivalue == std::numeric_limits::max()) throw Base::OverflowError("Integer overflow"); if (yylval.ivalue == 1) { yylval.fvalue = 1; return ONE; } else return INTEGER; } -"pi" COUNTCHARS; yylval.constant.fvalue = M_PI; yylval.constant.name = "pi"; return CONSTANT; // constant pi -"e" COUNTCHARS; yylval.constant.fvalue = M_E; yylval.constant.name = "e"; return CONSTANT; // constant e +"pi" COUNTCHARS; yylval.constant.fvalue = std::numbers::pi; yylval.constant.name = "pi"; return CONSTANT; // constant pi +"e" COUNTCHARS; yylval.constant.fvalue = std::numbers::e; yylval.constant.name = "e"; return CONSTANT; // constant e "None" COUNTCHARS; yylval.constant.fvalue = 0; yylval.constant.name = "None"; return CONSTANT; "True" COUNTCHARS; yylval.constant.fvalue = 1; yylval.constant.name = "True"; return CONSTANT; diff --git a/src/App/Link.cpp b/src/App/Link.cpp index 2762040d42..9312aa13d2 100644 --- a/src/App/Link.cpp +++ b/src/App/Link.cpp @@ -2624,7 +2624,8 @@ Link::Link() { LINK_PROPS_ADD(LINK_PARAMS_LINK); LinkExtension::initExtension(this); - static const PropertyIntegerConstraint::Constraints s_constraints = {0, INT_MAX, 1}; + static const PropertyIntegerConstraint::Constraints s_constraints = { + 0, std::numeric_limits::max(), 1}; ElementCount.setConstraints(&s_constraints); } diff --git a/src/App/ObjectIdentifier.cpp b/src/App/ObjectIdentifier.cpp index 28637d638a..0c80b13672 100644 --- a/src/App/ObjectIdentifier.cpp +++ b/src/App/ObjectIdentifier.cpp @@ -130,7 +130,7 @@ ObjectIdentifier::ObjectIdentifier(const App::PropertyContainer* _owner, } if (!property.empty()) { addComponent(SimpleComponent(property)); - if (index != INT_MAX) { + if (index != std::numeric_limits::max()) { addComponent(ArrayComponent(index)); } } @@ -179,7 +179,7 @@ ObjectIdentifier::ObjectIdentifier(const Property& prop, int index) setDocumentObjectName(docObj); addComponent(SimpleComponent(String(prop.getName()))); - if (index != INT_MAX) { + if (index != std::numeric_limits::max()) { addComponent(ArrayComponent(index)); } } @@ -703,8 +703,9 @@ Py::Object ObjectIdentifier::Component::get(const Py::Object& pyobj) const } else { assert(isRange()); + constexpr int max = std::numeric_limits::max(); Py::Object slice(PySlice_New(Py::Long(begin).ptr(), - end != INT_MAX ? Py::Long(end).ptr() : nullptr, + end != max ? Py::Long(end).ptr() : nullptr, step != 1 ? Py::Long(step).ptr() : nullptr), true); PyObject* r = PyObject_GetItem(pyobj.ptr(), slice.ptr()); @@ -742,8 +743,9 @@ void ObjectIdentifier::Component::set(Py::Object& pyobj, const Py::Object& value } else { assert(isRange()); + constexpr int max = std::numeric_limits::max(); Py::Object slice(PySlice_New(Py::Long(begin).ptr(), - end != INT_MAX ? Py::Long(end).ptr() : nullptr, + end != max ? Py::Long(end).ptr() : nullptr, step != 1 ? Py::Long(step).ptr() : nullptr), true); if (PyObject_SetItem(pyobj.ptr(), slice.ptr(), value.ptr()) < 0) { @@ -770,8 +772,9 @@ void ObjectIdentifier::Component::del(Py::Object& pyobj) const } else { assert(isRange()); + constexpr int max = std::numeric_limits::max(); Py::Object slice(PySlice_New(Py::Long(begin).ptr(), - end != INT_MAX ? Py::Long(end).ptr() : nullptr, + end != max ? Py::Long(end).ptr() : nullptr, step != 1 ? Py::Long(step).ptr() : nullptr), true); if (PyObject_DelItem(pyobj.ptr(), slice.ptr()) < 0) { @@ -897,11 +900,11 @@ void ObjectIdentifier::Component::toString(std::ostream& ss, bool toPython) cons break; case Component::RANGE: ss << '['; - if (begin != INT_MAX) { + if (begin != std::numeric_limits::max()) { ss << begin; } ss << ':'; - if (end != INT_MAX) { + if (end != std::numeric_limits::max()) { ss << end; } if (step != 1) { diff --git a/src/App/ObjectIdentifier.h b/src/App/ObjectIdentifier.h index c62f69c47c..60cb5e19c6 100644 --- a/src/App/ObjectIdentifier.h +++ b/src/App/ObjectIdentifier.h @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -211,13 +212,13 @@ public: Component(const String& _name = String(), typeEnum _type = SIMPLE, - int begin = INT_MAX, - int end = INT_MAX, + int begin = std::numeric_limits::max(), + int end = std::numeric_limits::max(), int step = 1); // explicit bombs Component(String&& _name, typeEnum _type = SIMPLE, - int begin = INT_MAX, - int end = INT_MAX, + int begin = std::numeric_limits::max(), + int end = std::numeric_limits::max(), int step = 1); // explicit bombs static Component SimpleComponent(const char* _component); @@ -227,7 +228,9 @@ public: static Component ArrayComponent(int _index); - static Component RangeComponent(int _begin, int _end = INT_MAX, int _step = 1); + static Component RangeComponent(int _begin, + int _end = std::numeric_limits::max(), + int _step = 1); static Component MapComponent(const String& _key); static Component MapComponent(String&& _key); @@ -325,7 +328,9 @@ public: return Component::ArrayComponent(_index); } - static Component RangeComponent(int _begin, int _end = INT_MAX, int _step = 1) + static Component RangeComponent(int _begin, + int _end = std::numeric_limits::max(), + int _step = 1) { return Component::RangeComponent(_begin, _end, _step); } @@ -342,11 +347,12 @@ public: explicit ObjectIdentifier(const App::PropertyContainer* _owner = nullptr, const std::string& property = std::string(), - int index = INT_MAX); + int index = std::numeric_limits::max()); ObjectIdentifier(const App::PropertyContainer* _owner, bool localProperty); - ObjectIdentifier(const App::Property& prop, int index = INT_MAX); // explicit bombs + ObjectIdentifier(const App::Property& prop, + int index = std::numeric_limits::max()); // explicit bombs FC_DEFAULT_CTORS(ObjectIdentifier) { diff --git a/src/App/PreCompiled.h b/src/App/PreCompiled.h index 38d675970a..bd32e3058f 100644 --- a/src/App/PreCompiled.h +++ b/src/App/PreCompiled.h @@ -50,7 +50,6 @@ #include #include #include -#include #ifdef FC_OS_WIN32 #include @@ -74,6 +73,7 @@ #include #include #include +#include #include #include #include diff --git a/src/App/Property.h b/src/App/Property.h index 5448777649..38b95d8df3 100644 --- a/src/App/Property.h +++ b/src/App/Property.h @@ -44,14 +44,19 @@ namespace App class PropertyContainer; class ObjectIdentifier; -/** Base class of all properties - * This is the father of all properties. Properties are objects which are used - * in the document tree to parametrize e.g. features and their graphical output. +/** + * @brief %Base class of all properties + * @ingroup PropertyFramework + * + * @details This is the father of all properties. Properties are objects which that used + * in the document tree to parameterize e.g. features and their graphical output. * They are also used to gain access from the scripting facility. - * /par + * @par * This abstract base class defines all methods shared by all - * possible properties. It is also possible to define user properties - * and use them in the framework... + * possible properties. It is also possible to define user properties + * and use them in the framework. + * + * For a more high-level overview see topic @ref PropertyFramework "Property Framework". */ class AppExport Property: public Base::Persistence { diff --git a/src/App/PropertyContainer.cpp b/src/App/PropertyContainer.cpp index 9efdd7a0fd..1b083e8552 100644 --- a/src/App/PropertyContainer.cpp +++ b/src/App/PropertyContainer.cpp @@ -640,50 +640,3 @@ void PropertyData::visitProperties(OffsetBase offsetBase, }; } -/** \defgroup PropFrame Property framework - \ingroup APP - \brief System to access object properties -\section Introduction -The property framework introduces the ability to access attributes (member variables) of a class by name without -knowing the class type. It's like the reflection mechanism of Java or C#. -This ability is introduced by the App::PropertyContainer class and can be used by all derived classes. - -This makes it possible in the first place to make an automatic mapping to python (e.g. in App::FeaturePy) and -abstract editing properties in Gui::PropertyEditor. - -\section Examples - -Here some little examples how to use it: - -\code -// search in PropertyList -Property *prop = _pcFeature->getPropertyByName(attr); -if(prop) -{ - return prop->getPyObject(); -} -\endcode - -or: - -\code -void PropertyContainer::Restore(Base::Reader &reader) -{ - reader.readElement("Properties"); - int Cnt = reader.getAttributeAsInteger("Count"); - - for(int i=0 ;iRestore(reader); - - reader.readEndElement("Property"); - } - reader.readEndElement("Properties"); -} -\endcode - -*/ diff --git a/src/App/PropertyStandard.cpp b/src/App/PropertyStandard.cpp index 77f12cd513..4ef6b2e335 100644 --- a/src/App/PropertyStandard.cpp +++ b/src/App/PropertyStandard.cpp @@ -1250,7 +1250,7 @@ void PropertyFloatConstraint::setPyObject(PyObject* value) double stepSize = valConstr[3]; // need a value > 0 - if (stepSize < DBL_EPSILON) { + if (stepSize < std::numeric_limits::epsilon()) { throw Base::ValueError("Step size must be greater than zero"); } @@ -1282,7 +1282,8 @@ TYPESYSTEM_SOURCE(App::PropertyPrecision, App::PropertyFloatConstraint) //************************************************************************** // Construction/Destruction // -const PropertyFloatConstraint::Constraints PrecisionStandard = {0.0, DBL_MAX, 0.001}; +const PropertyFloatConstraint::Constraints PrecisionStandard = { + 0.0, std::numeric_limits::max(), 0.001}; PropertyPrecision::PropertyPrecision() { diff --git a/src/App/PropertyUnits.cpp b/src/App/PropertyUnits.cpp index b4ed219cd9..153b64bfa2 100644 --- a/src/App/PropertyUnits.cpp +++ b/src/App/PropertyUnits.cpp @@ -22,7 +22,6 @@ #include "PreCompiled.h" #ifndef _PreComp_ -#include #endif #include @@ -37,7 +36,8 @@ using namespace Base; using namespace std; -const PropertyQuantityConstraint::Constraints LengthStandard = {0.0, DBL_MAX, 1.0}; +const PropertyQuantityConstraint::Constraints LengthStandard = { + 0.0, std::numeric_limits::max(), 1.0}; const PropertyQuantityConstraint::Constraints AngleStandard = {-360, 360, 1.0}; //************************************************************************** @@ -403,6 +403,17 @@ PropertySurfaceChargeDensity::PropertySurfaceChargeDensity() setUnit(Base::Unit::SurfaceChargeDensity); } +//************************************************************************** +// PropertyVolumeChargeDensity +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +TYPESYSTEM_SOURCE(App::PropertyVolumeChargeDensity, App::PropertyQuantity) + +PropertyVolumeChargeDensity::PropertyVolumeChargeDensity() +{ + setUnit(Base::Unit::VolumeChargeDensity); +} + //************************************************************************** // PropertyElectricCurrent //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ diff --git a/src/App/PropertyUnits.h b/src/App/PropertyUnits.h index e81a8fd9c3..2e2cca365c 100644 --- a/src/App/PropertyUnits.h +++ b/src/App/PropertyUnits.h @@ -374,6 +374,19 @@ public: ~PropertySurfaceChargeDensity() override = default; }; +/** VolumeChargeDensity property + * This is a property for representing volume charge density. It is basically a float + * property. On the Gui it has a quantity like C/m^3. + */ +class AppExport PropertyVolumeChargeDensity: public PropertyQuantity +{ + TYPESYSTEM_HEADER_WITH_OVERRIDE(); + +public: + PropertyVolumeChargeDensity(); + ~PropertyVolumeChargeDensity() override = default; +}; + /** ElectricCurrent property * This is a property for representing electric currents. It is basically a * float property. On the Gui it has a quantity like A. diff --git a/src/App/core-app.dox b/src/App/core-app.dox index 1a5f38b0e9..01a52d80eb 100644 --- a/src/App/core-app.dox +++ b/src/App/core-app.dox @@ -1,15 +1,117 @@ -/** \defgroup APP App - * \ingroup CORE - * \brief The part of FreeCAD that works without GUI (console or server mode) -*/ +/** + * @defgroup APP App + * @ingroup CORE + * @brief The part of FreeCAD that works without GUI (console or server mode) + * @details It contains the App namespace and defines core concepts such as + * @ref DocumentGroup "Document", @ref DocumentObjectGroup "Document Object", + * the @ref ExpressionFramework "Expression Framework", and the @ref + * PropertyFramework "Property Framework". + */ -/** \namespace App - \ingroup APP - \brief The FreeCAD Application layer +/** + * @defgroup DocumentGroup Document + * @ingroup APP + * @brief The class that represents a FreeCAD document + * + * This (besides the App::Application class) is the most important class in FreeCAD. + * It contains all the data of the opened, saved, or newly created FreeCAD Document. + * The App::Document manages the Undo and Redo mechanism and the linking of documents. + * + * Note: the documents are not free objects. They are completely handled by the + * App::Application. Only the Application can Open or destroy a document. + * + * \section Exception Exception handling + * As the document is the main data structure of FreeCAD we have to take a close + * look at how Exceptions affect the integrity of the App::Document. + * + * \section UndoRedo Undo Redo an Transactions + * Undo Redo handling is one of the major mechanism of a document in terms of + * user friendliness and speed (no one will wait for Undo too long). + * + * \section Dependency Graph and dependency handling + * The FreeCAD document handles the dependencies of its DocumentObjects with + * an adjacency list. This gives the opportunity to calculate the shortest + * recompute path. Also, it enables more complicated dependencies beyond trees. + * + * @see App::Application + * @see App::DocumentObject + */ - This namespace includes Application services of FreeCAD like: - - The Application class - - The Document class +/** + * @defgroup DocumentObjectGroup Document Object + * @ingroup APP + * @brief %Base class of all objects handled in the Document. + */ + +/** + * @defgroup ExpressionFramework Expressions framework + * @ingroup APP + * @brief The expression system allows users to write expressions and formulas that produce values + */ + +/** + * @defgroup PropertyFramework Property framework + * @ingroup APP + * @brief System to access object properties. + * + * @section propframe_intro Introduction + * + * The property framework introduces the ability to access attributes (member + * variables) of a class by name without knowing the class type. It's like the + * reflection mechanism of Java or C#. This ability is introduced by the + * App::PropertyContainer class and can be used by all derived classes. + * + * This makes it possible in the first place to make an automatic mapping to python (e.g. in App::FeaturePy) and + * abstract editing properties in Gui::PropertyEditor. + * + * @section Examples + * + * Here some little examples how to use it: + * + * @code + * // search in PropertyList + * Property *prop = _pcFeature->getPropertyByName(attr); + * if(prop) + * { + * return prop->getPyObject(); + * } + * @endcode + * + * or: + * + * @code + * void PropertyContainer::Restore(Base::Reader &reader) + * { + * reader.readElement("Properties"); + * int Cnt = reader.getAttributeAsInteger("Count"); + * + * for(int i=0 ;iRestore(reader); + * + * reader.readEndElement("Property"); + * } + * reader.readEndElement("Properties"); + * } + * @endcode + */ -*/ +/** + * @namespace App + * @ingroup APP + * @brief The namespace for the part of FreeCAD that works without GUI. + * @details This namespace includes %Application services of FreeCAD that such as: + * - The Application class + * - The Document class + * - The DocumentObject classes + * - The Expression classes + * - The Property classes + * + * For a more high-level discussion see the topic @ref APP "App". + */ + diff --git a/src/App/lex.ExpressionParser.c b/src/App/lex.ExpressionParser.c index 2916336b02..cc9777e092 100644 --- a/src/App/lex.ExpressionParser.c +++ b/src/App/lex.ExpressionParser.c @@ -9583,12 +9583,12 @@ YY_RULE_SETUP case 138: YY_RULE_SETUP #line 351 "ExpressionParser.l" -COUNTCHARS; yylval.constant.fvalue = M_PI; yylval.constant.name = "pi"; return CONSTANT; // constant pi +COUNTCHARS; yylval.constant.fvalue = std::numbers::pi; yylval.constant.name = "pi"; return CONSTANT; // constant pi YY_BREAK case 139: YY_RULE_SETUP #line 352 "ExpressionParser.l" -COUNTCHARS; yylval.constant.fvalue = M_E; yylval.constant.name = "e"; return CONSTANT; // constant e +COUNTCHARS; yylval.constant.fvalue = std::numbers::e; yylval.constant.name = "e"; return CONSTANT; // constant e YY_BREAK case 140: YY_RULE_SETUP diff --git a/src/Base/Console.h b/src/Base/Console.h index e377c2d904..6e3a6e66b8 100644 --- a/src/Base/Console.h +++ b/src/Base/Console.h @@ -26,6 +26,7 @@ // Std. configurations #include +#include #include #include #include @@ -759,6 +760,11 @@ public: template inline void DeveloperError(const std::string& notifier, const char* pMsg, Args&&... args); template + /// A noexcept DeveloperError for use in destructors. When compiled in debug, terminates via an + /// assert. In release, the exception is silently caught and dropped. + inline void + DestructorError(const std::string& notifier, const char* pMsg, Args&&... args) noexcept; + template inline void UserError(const std::string& notifier, const char* pMsg, Args&&... args); template inline void TranslatedUserError(const std::string& notifier, const char* pMsg, Args&&... args); @@ -1083,6 +1089,21 @@ inline void Base::ConsoleSingleton::DeveloperError(const std::string& notifier, Base::ContentType::Untranslatable>(notifier, pMsg, std::forward(args)...); } +template +inline void Base::ConsoleSingleton::DestructorError(const std::string& notifier, + const char* pMsg, + Args&&... args) noexcept +{ + try { + Send(notifier, pMsg, std::forward(args)...); + } + catch (...) { + assert("An exception was thrown while attempting console output in a destructor" && false); + } +} + template inline void Base::ConsoleSingleton::UserError(const std::string& notifier, const char* pMsg, Args&&... args) diff --git a/src/Base/FileInfo.cpp b/src/Base/FileInfo.cpp index a466d41ef2..024a190efd 100644 --- a/src/Base/FileInfo.cpp +++ b/src/Base/FileInfo.cpp @@ -32,7 +32,6 @@ #if defined(FC_OS_LINUX) || defined(FC_OS_CYGWIN) || defined(FC_OS_MACOSX) || defined(FC_OS_BSD) #include #include -#include #elif defined(FC_OS_WIN32) #include #include diff --git a/src/Base/Interpreter.cpp b/src/Base/Interpreter.cpp index 561c7798d8..de026dd469 100644 --- a/src/Base/Interpreter.cpp +++ b/src/Base/Interpreter.cpp @@ -538,8 +538,36 @@ void InterpreterSingleton::addPythonPath(const char* Path) list.append(Py::String(Path)); } +std::string InterpreterSingleton::getPythonPath() +{ + // Construct something that looks like the output of the now-deprecated Py_GetPath + PyGILStateLocker lock; + PyObject* path = PySys_GetObject("path"); + std::string result; + const char* separator = ":"; // Use ":" on Unix-like systems, ";" on Windows +#ifdef FC_OS_WIN32 + separator = ";"; +#endif + Py_ssize_t length = PyList_Size(path); + for (Py_ssize_t i = 0; i < length; ++i) { + PyObject* item = PyList_GetItem(path, i); // Borrowed reference + if (!item) { + throw Base::RuntimeError("Failed to retrieve item from path"); + } + const char* item_str = PyUnicode_AsUTF8(item); + if (!item_str) { + throw Base::RuntimeError("Failed to convert path item to UTF-8 string"); + } + if (!result.empty()) { + result += separator; + } + result += item_str; + } + return result; +} + #if PY_VERSION_HEX < 0x030b0000 -const char* InterpreterSingleton::init(int argc, char* argv[]) +std::string InterpreterSingleton::init(int argc, char* argv[]) { if (!Py_IsInitialized()) { Py_SetProgramName(Py_DecodeLocale(argv[0], nullptr)); @@ -617,7 +645,7 @@ void initInterpreter(int argc, char* argv[]) Py_Initialize(); } } // namespace -const char* InterpreterSingleton::init(int argc, char* argv[]) +std::string InterpreterSingleton::init(int argc, char* argv[]) { try { if (!Py_IsInitialized()) { @@ -626,9 +654,7 @@ const char* InterpreterSingleton::init(int argc, char* argv[]) PythonStdOutput::init_type(); this->_global = PyEval_SaveThread(); } - - PyGILStateLocker lock; - return Py_EncodeLocale(Py_GetPath(), nullptr); + return getPythonPath(); } catch (const Base::Exception& e) { e.ReportException(); diff --git a/src/Base/Interpreter.h b/src/Base/Interpreter.h index f2787a4d22..e2196a3897 100644 --- a/src/Base/Interpreter.h +++ b/src/Base/Interpreter.h @@ -285,6 +285,8 @@ public: bool loadModule(const char* psModName); /// Add an additional python path void addPythonPath(const char* Path); + /// Get the path (replaces the deprecated Py_GetPath method) + std::string getPythonPath(); static void addType(PyTypeObject* Type, PyObject* Module, const char* Name); /// Add a module and return a PyObject to it PyObject* addModule(Py::ExtensionModuleBase*); @@ -313,7 +315,7 @@ public: */ //@{ /// init the interpreter and returns the module search path - const char* init(int argc, char* argv[]); + std::string init(int argc, char* argv[]); int runCommandLine(const char* prompt); void replaceStdOutput(); static InterpreterSingleton& Instance(); diff --git a/src/Base/Matrix.cpp b/src/Base/Matrix.cpp index 065fc1b569..aa0623b2b7 100644 --- a/src/Base/Matrix.cpp +++ b/src/Base/Matrix.cpp @@ -429,7 +429,7 @@ bool Matrix4D::toAxisAngle(Vector3d& rclBase, rfAngle = acos(fCos); // in [0,PI] if (rfAngle > 0.0) { - if (rfAngle < D_PI) { + if (rfAngle < std::numbers::pi) { rclDir.x = (dMtrx4D[2][1] - dMtrx4D[1][2]); rclDir.y = (dMtrx4D[0][2] - dMtrx4D[2][0]); rclDir.z = (dMtrx4D[1][0] - dMtrx4D[0][1]); @@ -1013,8 +1013,8 @@ std::array Matrix4D::decompose() const residualMatrix = rotationMatrix * residualMatrix; // To keep signs of the scale factors equal if (residualMatrix.determinant() < 0) { - rotationMatrix.rotZ(D_PI); - residualMatrix.rotZ(D_PI); + rotationMatrix.rotZ(std::numbers::pi); + residualMatrix.rotZ(std::numbers::pi); } rotationMatrix.inverseGauss(); // extract scale diff --git a/src/Base/MatrixPyImp.cpp b/src/Base/MatrixPyImp.cpp index 029f57af2c..9aaa9e8d63 100644 --- a/src/Base/MatrixPyImp.cpp +++ b/src/Base/MatrixPyImp.cpp @@ -218,7 +218,7 @@ PyObject* MatrixPy::number_power_handler(PyObject* self, PyObject* other, PyObje } if (b < 0) { - if (fabs(a.determinant()) > DBL_EPSILON) { + if (fabs(a.determinant()) > std::numeric_limits::epsilon()) { a.inverseGauss(); } else { @@ -667,7 +667,7 @@ PyObject* MatrixPy::invert() { PY_TRY { - if (fabs(getMatrixPtr()->determinant()) > DBL_EPSILON) { + if (fabs(getMatrixPtr()->determinant()) > std::numeric_limits::epsilon()) { getMatrixPtr()->inverseGauss(); Py_Return; } @@ -682,7 +682,7 @@ PyObject* MatrixPy::inverse() { PY_TRY { - if (fabs(getMatrixPtr()->determinant()) > DBL_EPSILON) { + if (fabs(getMatrixPtr()->determinant()) > std::numeric_limits::epsilon()) { Base::Matrix4D m = *getMatrixPtr(); m.inverseGauss(); return new MatrixPy(m); diff --git a/src/Base/PlacementPyImp.cpp b/src/Base/PlacementPyImp.cpp index d57791d69f..8513895157 100644 --- a/src/Base/PlacementPyImp.cpp +++ b/src/Base/PlacementPyImp.cpp @@ -99,7 +99,8 @@ int PlacementPy::PyInit(PyObject* args, PyObject* /*kwd*/) &angle)) { // NOTE: The first parameter defines the translation, the second the rotation axis // and the last parameter defines the rotation angle in degree. - Base::Rotation rot(static_cast(d)->value(), angle / 180.0 * D_PI); + Base::Rotation rot(static_cast(d)->value(), + angle / 180.0 * std::numbers::pi); *getPlacementPtr() = Base::Placement(static_cast(o)->value(), rot); return 0; } diff --git a/src/Base/PreCompiled.h b/src/Base/PreCompiled.h index 1e5fa96dbb..5a26c2e4f6 100644 --- a/src/Base/PreCompiled.h +++ b/src/Base/PreCompiled.h @@ -36,13 +36,8 @@ #include #include #include -#include #include -#ifdef FC_OS_WIN32 -#define _USE_MATH_DEFINES -#endif // FC_OS_WIN32 #include -#include #include #ifdef FC_OS_WIN32 @@ -61,7 +56,6 @@ #include #include #include -#include #endif // STL @@ -69,6 +63,7 @@ #include #include #include +#include #include #include #include diff --git a/src/Base/Quantity.cpp b/src/Base/Quantity.cpp index 41002c6f66..e566227b17 100644 --- a/src/Base/Quantity.cpp +++ b/src/Base/Quantity.cpp @@ -22,9 +22,9 @@ #include "PreCompiled.h" #ifndef _PreComp_ -#define _USE_MATH_DEFINES #include #include +#include #endif #include @@ -443,8 +443,8 @@ const Quantity Quantity::AngSecond(1.0 / 3600.0, Unit(0, 0, 0, 0, 0, 0, 0, 1)); const Quantity Quantity::Degree(1.0, Unit(0, 0, 0, 0, 0, 0, 0, 1)); // degree (internal standard angle) -const Quantity Quantity::Radian(180 / M_PI, Unit(0, 0, 0, 0, 0, 0, 0, 1)); // radian -const Quantity Quantity::Gon(360.0 / 400.0, Unit(0, 0, 0, 0, 0, 0, 0, 1)); // gon +const Quantity Quantity::Radian(180 / std::numbers::pi, Unit(0, 0, 0, 0, 0, 0, 0, 1)); // radian +const Quantity Quantity::Gon(360.0 / 400.0, Unit(0, 0, 0, 0, 0, 0, 0, 1)); // gon // === Parser & Scanner stuff =============================================== @@ -568,7 +568,7 @@ Quantity Quantity::parse(const std::string& string) QuantityParser::yy_scan_string(string.c_str()); QuantityParser::StringBufferCleaner cleaner(my_string_buffer); // set the global return variables - QuantResult = Quantity(DOUBLE_MIN); + QuantResult = Quantity(std::numeric_limits::min()); // run the parser QuantityParser::yyparse(); diff --git a/src/Base/Quantity.h b/src/Base/Quantity.h index 94351aa6ea..ccfc33cb96 100644 --- a/src/Base/Quantity.h +++ b/src/Base/Quantity.h @@ -27,15 +27,6 @@ #include "Unit.h" #include -// NOLINTBEGIN -#ifndef DOUBLE_MAX -#define DOUBLE_MAX 1.7976931348623157E+308 /* max decimal value of a "double"*/ -#endif -#ifndef DOUBLE_MIN -#define DOUBLE_MIN 2.2250738585072014E-308 /* min decimal value of a "double"*/ -#endif -// NOLINTEND - namespace Base { class UnitsSchema; diff --git a/src/Base/QuantityLexer.c b/src/Base/QuantityLexer.c index c21f3d9fa8..547916b50b 100644 --- a/src/Base/QuantityLexer.c +++ b/src/Base/QuantityLexer.c @@ -1613,12 +1613,12 @@ YY_RULE_SETUP case 135: YY_RULE_SETUP #line 228 "QuantityParser.l" -{yylval = Quantity(M_PI) ; return NUM;} // constant pi +{yylval = Quantity(std::numbers::pi) ; return NUM;} // constant pi YY_BREAK case 136: YY_RULE_SETUP #line 229 "QuantityParser.l" -{yylval = Quantity(M_E) ; return NUM;} // constant e +{yylval = Quantity(std::numbers::e) ; return NUM;} // constant e YY_BREAK case 137: YY_RULE_SETUP diff --git a/src/Base/QuantityParser.c b/src/Base/QuantityParser.c index bb8ae3915b..e38a289ab8 100644 --- a/src/Base/QuantityParser.c +++ b/src/Base/QuantityParser.c @@ -68,12 +68,12 @@ #define YYSTYPE Quantity #define yyparse Quantity_yyparse #define yyerror Quantity_yyerror - #ifndef DOUBLE_MAX - # define DOUBLE_MAX 1.7976931348623157E+308 /* max decimal value of a "double"*/ - #endif - #ifndef DOUBLE_MIN - # define DOUBLE_MIN 2.2250738585072014E-308 /* min decimal value of a "double"*/ - #endif + + + + + + #line 79 "QuantityParser.c" /* yacc.c:339 */ @@ -1301,7 +1301,7 @@ yyreduce: { case 2: #line 34 "QuantityParser.y" /* yacc.c:1646 */ - { QuantResult = Quantity(DOUBLE_MIN); /* empty input */ } + { QuantResult = Quantity(std::numeric_limits::min()); /* empty input */ } #line 1305 "QuantityParser.c" /* yacc.c:1646 */ break; diff --git a/src/Base/QuantityParser.l b/src/Base/QuantityParser.l index 1a52592715..bd697d4dea 100644 --- a/src/Base/QuantityParser.l +++ b/src/Base/QuantityParser.l @@ -225,8 +225,8 @@ CGRP '\,'[0-9][0-9][0-9] ","?{DIGIT}+{EXPO}? { yylval = Quantity(num_change(yytext,',','.'));return NUM; } -"pi" {yylval = Quantity(M_PI) ; return NUM;} // constant pi -"e" {yylval = Quantity(M_E) ; return NUM;} // constant e +"pi" {yylval = Quantity(std::numbers::pi) ; return NUM;} // constant pi +"e" {yylval = Quantity(std::numbers::e) ; return NUM;} // constant e "acos" return ACOS; "asin" return ASIN; diff --git a/src/Base/QuantityParser.y b/src/Base/QuantityParser.y index 906b9cab6f..b652151c81 100644 --- a/src/Base/QuantityParser.y +++ b/src/Base/QuantityParser.y @@ -25,12 +25,12 @@ #define YYSTYPE Quantity #define yyparse Quantity_yyparse #define yyerror Quantity_yyerror - #ifndef DOUBLE_MAX - # define DOUBLE_MAX 1.7976931348623157E+308 /* max decimal value of a "double"*/ - #endif - #ifndef DOUBLE_MIN - # define DOUBLE_MIN 2.2250738585072014E-308 /* min decimal value of a "double"*/ - #endif + + + + + + %} @@ -49,7 +49,7 @@ %% - input: { QuantResult = Quantity(DOUBLE_MIN); /* empty input */ } + input: { QuantResult = Quantity(std::numeric_limits::min()); /* empty input */ } | num { QuantResult = $1 ; } | unit { QuantResult = $1 ; } | quantity { QuantResult = $1 ; } diff --git a/src/Base/QuantityPyImp.cpp b/src/Base/QuantityPyImp.cpp index 85078c38f6..5b999d6aaa 100644 --- a/src/Base/QuantityPyImp.cpp +++ b/src/Base/QuantityPyImp.cpp @@ -88,7 +88,7 @@ int QuantityPy::PyInit(PyObject* args, PyObject* /*kwd*/) } PyErr_Clear(); // set by PyArg_ParseTuple() - double f = DOUBLE_MAX; + double f = std::numeric_limits::max(); if (PyArg_ParseTuple(args, "dO!", &f, &(Base::UnitPy::Type), &object)) { *self = Quantity(f, *(static_cast(object)->getUnitPtr())); return 0; @@ -110,7 +110,7 @@ int QuantityPy::PyInit(PyObject* args, PyObject* /*kwd*/) int i8 = 0; PyErr_Clear(); // set by PyArg_ParseTuple() if (PyArg_ParseTuple(args, "|diiiiiiii", &f, &i1, &i2, &i3, &i4, &i5, &i6, &i7, &i8)) { - if (f < DOUBLE_MAX) { + if (f < std::numeric_limits::max()) { *self = Quantity(f, Unit {static_cast(i1), static_cast(i2), @@ -207,7 +207,7 @@ PyObject* QuantityPy::getValueAs(PyObject* args) } if (!quant.isValid()) { - double f = DOUBLE_MAX; + double f = std::numeric_limits::max(); int i1 = 0; int i2 = 0; int i3 = 0; @@ -218,7 +218,7 @@ PyObject* QuantityPy::getValueAs(PyObject* args) int i8 = 0; PyErr_Clear(); if (PyArg_ParseTuple(args, "d|iiiiiiii", &f, &i1, &i2, &i3, &i4, &i5, &i6, &i7, &i8)) { - if (f < DOUBLE_MAX) { + if (f < std::numeric_limits::max()) { quant = Quantity(f, Unit {static_cast(i1), static_cast(i2), diff --git a/src/Base/Rotation.cpp b/src/Base/Rotation.cpp index da65a99808..e9e1776e80 100644 --- a/src/Base/Rotation.cpp +++ b/src/Base/Rotation.cpp @@ -245,11 +245,12 @@ void Rotation::setValue(const Matrix4D& m) void Rotation::setValue(const Vector3d& axis, double fAngle) { + using std::numbers::pi; // Taken from // // normalization of the angle to be in [0, 2pi[ _angle = fAngle; - double theAngle = fAngle - floor(fAngle / (2.0 * D_PI)) * (2.0 * D_PI); + double theAngle = fAngle - floor(fAngle / (2.0 * pi)) * (2.0 * pi); this->quat[3] = cos(theAngle / 2.0); Vector3d norm = axis; @@ -691,9 +692,9 @@ void Rotation::setYawPitchRoll(double y, double p, double r) { // The Euler angles (yaw,pitch,roll) are in XY'Z''-notation // convert to radians - y = (y / 180.0) * D_PI; - p = (p / 180.0) * D_PI; - r = (r / 180.0) * D_PI; + y = (y / 180.0) * std::numbers::pi; + p = (p / 180.0) * std::numbers::pi; + r = (r / 180.0) * std::numbers::pi; double c1 = cos(y / 2.0); double s1 = sin(y / 2.0); @@ -710,6 +711,8 @@ void Rotation::setYawPitchRoll(double y, double p, double r) void Rotation::getYawPitchRoll(double& y, double& p, double& r) const { + using std::numbers::pi; + double q00 = quat[0] * quat[0]; double q11 = quat[1] * quat[1]; double q22 = quat[2] * quat[2]; @@ -722,30 +725,31 @@ void Rotation::getYawPitchRoll(double& y, double& p, double& r) const double q23 = quat[2] * quat[3]; double qd2 = 2.0 * (q13 - q02); + // Tolerance copied from OCC "gp_Quaternion.cxx" + constexpr double tolerance = 16 * std::numeric_limits::epsilon(); // handle gimbal lock - if (fabs(qd2 - 1.0) <= 16 * DBL_EPSILON) { // Tolerance copied from OCC "gp_Quaternion.cxx" + if (fabs(qd2 - 1.0) <= tolerance) { // north pole y = 0.0; - p = D_PI / 2.0; + p = pi / 2.0; r = 2.0 * atan2(quat[0], quat[3]); } - else if (fabs(qd2 + 1.0) - <= 16 * DBL_EPSILON) { // Tolerance copied from OCC "gp_Quaternion.cxx" + else if (fabs(qd2 + 1.0) <= tolerance) { // south pole y = 0.0; - p = -D_PI / 2.0; + p = -pi / 2.0; r = 2.0 * atan2(quat[0], quat[3]); } else { y = atan2(2.0 * (q01 + q23), (q00 + q33) - (q11 + q22)); - p = qd2 > 1.0 ? D_PI / 2.0 : (qd2 < -1.0 ? -D_PI / 2.0 : asin(qd2)); + p = qd2 > 1.0 ? pi / 2.0 : (qd2 < -1.0 ? -pi / 2.0 : asin(qd2)); r = atan2(2.0 * (q12 + q03), (q22 + q33) - (q00 + q11)); } // convert to degree - y = (y / D_PI) * 180; - p = (p / D_PI) * 180; - r = (r / D_PI) * 180; + y = (y / pi) * 180; + p = (p / pi) * 180; + r = (r / pi) * 180; } bool Rotation::isSame(const Rotation& q) const @@ -978,15 +982,17 @@ void Rotation::setEulerAngles(EulerSequence theOrder, double theBeta, double theGamma) { + using std::numbers::pi; + if (theOrder == Invalid || theOrder >= EulerSequenceLast) { throw Base::ValueError("invalid euler sequence"); } EulerSequence_Parameters o = translateEulerSequence(theOrder); - theAlpha *= D_PI / 180.0; - theBeta *= D_PI / 180.0; - theGamma *= D_PI / 180.0; + theAlpha *= pi / 180.0; + theBeta *= pi / 180.0; + theGamma *= pi / 180.0; double a = theAlpha; double b = theBeta; @@ -1048,7 +1054,7 @@ void Rotation::getEulerAngles(EulerSequence theOrder, EulerSequence_Parameters o = translateEulerSequence(theOrder); if (o.isTwoAxes) { double sy = sqrt(M(o.i, o.j) * M(o.i, o.j) + M(o.i, o.k) * M(o.i, o.k)); - if (sy > 16 * DBL_EPSILON) { + if (sy > 16 * std::numeric_limits::epsilon()) { theAlpha = atan2(M(o.i, o.j), M(o.i, o.k)); theGamma = atan2(M(o.j, o.i), -M(o.k, o.i)); } @@ -1060,7 +1066,7 @@ void Rotation::getEulerAngles(EulerSequence theOrder, } else { double cy = sqrt(M(o.i, o.i) * M(o.i, o.i) + M(o.j, o.i) * M(o.j, o.i)); - if (cy > 16 * DBL_EPSILON) { + if (cy > 16 * std::numeric_limits::epsilon()) { theAlpha = atan2(M(o.k, o.j), M(o.k, o.k)); theGamma = atan2(M(o.j, o.i), M(o.i, o.i)); } @@ -1081,7 +1087,7 @@ void Rotation::getEulerAngles(EulerSequence theOrder, theGamma = aFirst; } - theAlpha *= 180.0 / D_PI; - theBeta *= 180.0 / D_PI; - theGamma *= 180.0 / D_PI; + theAlpha *= 180.0 / std::numbers::pi; + theBeta *= 180.0 / std::numbers::pi; + theGamma *= 180.0 / std::numbers::pi; } diff --git a/src/Base/Tools.h b/src/Base/Tools.h index f0d767ab00..182d804000 100644 --- a/src/Base/Tools.h +++ b/src/Base/Tools.h @@ -28,6 +28,7 @@ #include #endif #include +#include #include #include #include @@ -127,20 +128,16 @@ inline T sgn(T t) return (t > 0) ? T(1) : T(-1); } -#ifndef M_PI -#define M_PI 3.14159265358979323846 -#endif - template inline T toRadians(T d) { - return static_cast((d * M_PI) / 180.0); + return static_cast((d * std::numbers::pi) / 180.0); } template inline T toDegrees(T r) { - return static_cast((r / M_PI) * 180.0); + return static_cast((r / std::numbers::pi) * 180.0); } inline float fromPercent(const long value) diff --git a/src/Base/Tools2D.cpp b/src/Base/Tools2D.cpp index 625f152ca5..59bc296830 100644 --- a/src/Base/Tools2D.cpp +++ b/src/Base/Tools2D.cpp @@ -43,7 +43,7 @@ double Vector2d::GetAngle(const Vector2d& vec) const if ((fDivid < -1e-10) || (fDivid > 1e-10)) { fNum = (*this * vec) / fDivid; if (fNum < -1) { - return D_PI; + return std::numbers::pi; } if (fNum > 1) { return 0.0; @@ -52,7 +52,7 @@ double Vector2d::GetAngle(const Vector2d& vec) const return acos(fNum); } - return -FLOAT_MAX; // division by zero + return -std::numeric_limits::max(); // division by zero } void Vector2d::ProjectToLine(const Vector2d& point, const Vector2d& line) @@ -173,13 +173,13 @@ bool Line2d::Intersect(const Line2d& rclLine, Vector2d& rclV) const m1 = (clV2.y - clV1.y) / (clV2.x - clV1.x); } else { - m1 = DOUBLE_MAX; + m1 = std::numeric_limits::max(); } if (fabs(rclLine.clV2.x - rclLine.clV1.x) > 1e-10) { m2 = (rclLine.clV2.y - rclLine.clV1.y) / (rclLine.clV2.x - rclLine.clV1.x); } else { - m2 = DOUBLE_MAX; + m2 = std::numeric_limits::max(); } if (m1 == m2) { /****** RETURN ERR (parallel lines) *************/ return false; @@ -189,11 +189,11 @@ bool Line2d::Intersect(const Line2d& rclLine, Vector2d& rclV) const b2 = rclLine.clV1.y - m2 * rclLine.clV1.x; // calc intersection - if (m1 == DOUBLE_MAX) { + if (m1 == std::numeric_limits::max()) { rclV.x = clV1.x; rclV.y = m2 * rclV.x + b2; } - else if (m2 == DOUBLE_MAX) { + else if (m2 == std::numeric_limits::max()) { rclV.x = rclLine.clV1.x; rclV.y = m1 * rclV.x + b1; } diff --git a/src/Base/Tools2D.h b/src/Base/Tools2D.h index 9bc10d19c3..a76fd88bb2 100644 --- a/src/Base/Tools2D.h +++ b/src/Base/Tools2D.h @@ -32,15 +32,6 @@ #include #endif -// NOLINTBEGIN -#ifndef DOUBLE_MAX -#define DOUBLE_MAX 1.7976931348623157E+308 /* max decimal value of a "double"*/ -#endif -#ifndef DOUBLE_MIN -#define DOUBLE_MIN 2.2250738585072014E-308 /* min decimal value of a "double"*/ -#endif -// NOLINTEND - namespace Base { @@ -462,8 +453,8 @@ inline bool Line2d::Contains(const Vector2d& rclV) const inline BoundBox2d::BoundBox2d() { - MinX = MinY = DOUBLE_MAX; - MaxX = MaxY = -DOUBLE_MAX; + MinX = MinY = std::numeric_limits::max(); + MaxX = MaxY = -std::numeric_limits::max(); } inline BoundBox2d::BoundBox2d(double fX1, double fY1, double fX2, double fY2) @@ -480,7 +471,8 @@ inline bool BoundBox2d::IsValid() const inline bool BoundBox2d::IsInfinite() const { - return MaxX >= DOUBLE_MAX && MaxY >= DOUBLE_MAX && MinX <= -DOUBLE_MAX && MinY <= -DOUBLE_MAX; + constexpr double max = std::numeric_limits::max(); + return MaxX >= max && MaxY >= max && MinX <= -max && MinY <= -max; } inline bool BoundBox2d::IsEqual(const BoundBox2d& bbox, double tolerance) const @@ -525,8 +517,8 @@ inline Vector2d BoundBox2d::GetCenter() const inline void BoundBox2d::SetVoid() { - MinX = MinY = DOUBLE_MAX; - MaxX = MaxY = -DOUBLE_MAX; + MinX = MinY = std::numeric_limits::max(); + MaxX = MaxY = -std::numeric_limits::max(); } inline void BoundBox2d::Add(const Vector2d& v) diff --git a/src/Base/Unit.cpp b/src/Base/Unit.cpp index 17a30adb44..baa782972d 100644 --- a/src/Base/Unit.cpp +++ b/src/Base/Unit.cpp @@ -587,7 +587,7 @@ std::string Unit::getString() const std::string Unit::getTypeString() const { - static std::array, 56> unitSpecs {{ + static std::array, 57> unitSpecs {{ { Unit::Acceleration, "Acceleration" }, { Unit::AmountOfSubstance, "AmountOfSubstance" }, { Unit::Angle, "Angle" }, @@ -604,6 +604,7 @@ std::string Unit::getTypeString() const { Unit::ElectricalResistance, "ElectricalResistance" }, { Unit::ElectricCharge, "ElectricCharge" }, { Unit::SurfaceChargeDensity, "SurfaceChargeDensity" }, + { Unit::VolumeChargeDensity, "VolumeChargeDensity" }, { Unit::ElectricCurrent, "ElectricCurrent" }, { Unit::ElectricPotential, "ElectricPotential" }, { Unit::ElectromagneticPotential, "ElectromagneticPotential" }, @@ -683,6 +684,7 @@ const Unit Unit::ElectricalInductance (2, 1, -2, -2); const Unit Unit::ElectricalResistance (2, 1, -3, -2); const Unit Unit::ElectricCharge (0, 0, 1, 1); const Unit Unit::SurfaceChargeDensity (-2, 0, 1, 1); +const Unit Unit::VolumeChargeDensity (-3, 0, 1, 1); const Unit Unit::ElectricPotential (2, 1, -3, -1); const Unit Unit::ElectromagneticPotential (1, 1, -2, -1); const Unit Unit::Force (1, 1, -2); diff --git a/src/Base/Unit.h b/src/Base/Unit.h index b47d5c26ae..77b2273b3d 100644 --- a/src/Base/Unit.h +++ b/src/Base/Unit.h @@ -111,6 +111,7 @@ public: static const Unit ElectricPotential; static const Unit ElectricCharge; static const Unit SurfaceChargeDensity; + static const Unit VolumeChargeDensity; static const Unit MagneticFieldStrength; static const Unit MagneticFlux; static const Unit MagneticFluxDensity; diff --git a/src/Base/UnitsSchemaInternal.cpp b/src/Base/UnitsSchemaInternal.cpp index 4ef3fd217d..f06286ff3e 100644 --- a/src/Base/UnitsSchemaInternal.cpp +++ b/src/Base/UnitsSchemaInternal.cpp @@ -370,14 +370,42 @@ UnitsSchemaInternal::schemaTranslate(const Quantity& quant, double& factor, std: factor = 1.0; } else if (unit == Unit::SurfaceChargeDensity) { - unitString = "C/m^2"; - factor = 1e-6; + if (UnitValue <= 1e-4) { + unitString = "C/m^2"; + factor = 1e-6; + } + else if (UnitValue <= 1e-2) { + unitString = "C/cm^2"; + factor = 1e-2; + } + else { + unitString = "C/mm^2"; + factor = 1; + } + } + else if (unit == Unit::VolumeChargeDensity) { + if (UnitValue <= 1e-4) { + unitString = "C/m^3"; + factor = 1e-9; + } + else if (UnitValue <= 1e-2) { + unitString = "C/cm^3"; + factor = 1e-3; + } + else { + unitString = "C/mm^3"; + factor = 1; + } } else if (unit == Unit::CurrentDensity) { - if (UnitValue <= 1e3) { + if (UnitValue <= 1e-4) { unitString = "A/m^2"; factor = 1e-6; } + else if (UnitValue <= 1e-2) { + unitString = "A/cm^2"; + factor = 1e-2; + } else { unitString = "A/mm^2"; factor = 1; diff --git a/src/Base/UnitsSchemaMKS.cpp b/src/Base/UnitsSchemaMKS.cpp index 0727c161c4..42ea0b0c79 100644 --- a/src/Base/UnitsSchemaMKS.cpp +++ b/src/Base/UnitsSchemaMKS.cpp @@ -320,6 +320,10 @@ UnitsSchemaMKS::schemaTranslate(const Quantity& quant, double& factor, std::stri unitString = "C/m^2"; factor = 1e-6; } + else if (unit == Unit::VolumeChargeDensity) { + unitString = "C/m^3"; + factor = 1e-9; + } else if (unit == Unit::CurrentDensity) { if (UnitValue <= 1e3) { unitString = "A/m^2"; diff --git a/src/Base/Vector3D.h b/src/Base/Vector3D.h index 62ad1fb5f1..6b8ec5423c 100644 --- a/src/Base/Vector3D.h +++ b/src/Base/Vector3D.h @@ -24,34 +24,9 @@ #ifndef BASE_VECTOR3D_H #define BASE_VECTOR3D_H - +#include #include -#include - -#ifndef F_PI -#define F_PI 3.1415926f -#endif - -#ifndef D_PI -#define D_PI 3.141592653589793 -#endif - -#ifndef FLOAT_MAX -#define FLOAT_MAX 3.402823466E+38F -#endif - -#ifndef FLOAT_MIN -#define FLOAT_MIN 1.175494351E-38F -#endif - -#ifndef DOUBLE_MAX -#define DOUBLE_MAX 1.7976931348623157E+308 /* max decimal value of a "double"*/ -#endif - -#ifndef DOUBLE_MIN -#define DOUBLE_MIN 2.2250738585072014E-308 /* min decimal value of a "double"*/ -#endif - +#include namespace Base { @@ -60,21 +35,22 @@ struct float_traits { }; +// TODO: Remove these specializations and use the default implementation for all types. template<> struct float_traits { using float_type = float; - [[nodiscard]] static constexpr float_type pi() + [[nodiscard]] static consteval float_type pi() { - return F_PI; + return std::numbers::pi_v; } - [[nodiscard]] static constexpr float_type epsilon() + [[nodiscard]] static consteval float_type epsilon() { - return FLT_EPSILON; + return std::numeric_limits::epsilon(); } - [[nodiscard]] static constexpr float_type maximum() + [[nodiscard]] static consteval float_type maximum() { - return FLT_MAX; + return std::numeric_limits::max(); } }; @@ -82,17 +58,17 @@ template<> struct float_traits { using float_type = double; - [[nodiscard]] static constexpr float_type pi() + [[nodiscard]] static consteval float_type pi() { - return D_PI; + return std::numbers::pi_v; } - [[nodiscard]] static constexpr float_type epsilon() + [[nodiscard]] static consteval float_type epsilon() { - return DBL_EPSILON; + return std::numeric_limits::epsilon(); } - [[nodiscard]] static constexpr float_type maximum() + [[nodiscard]] static consteval float_type maximum() { - return DBL_MAX; + return std::numeric_limits::max(); } }; @@ -275,7 +251,7 @@ template float_type x = v1.x - v2.x; float_type y = v1.y - v2.y; float_type z = v1.z - v2.z; - return static_cast(sqrt((x * x) + (y * y) + (z * z))); + return static_cast(std::sqrt((x * x) + (y * y) + (z * z))); } /// Returns the squared distance between two points diff --git a/src/Base/swigpyrun.cpp b/src/Base/swigpyrun.cpp index 3213ad2a48..87fc3f59c5 100644 --- a/src/Base/swigpyrun.cpp +++ b/src/Base/swigpyrun.cpp @@ -31,10 +31,8 @@ #elif defined(__GNUC__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wmissing-field-initializers" -#if __GNUC__ >= 8 #pragma GCC diagnostic ignored "-Wcast-function-type" #endif -#endif #include "PyExport.h" #include "Exception.h" namespace Swig_python diff --git a/src/Doc/CMakeLists.txt b/src/Doc/CMakeLists.txt index 4f41818662..d0f659e64f 100644 --- a/src/Doc/CMakeLists.txt +++ b/src/Doc/CMakeLists.txt @@ -31,22 +31,22 @@ if(DOXYGEN_FOUND) # directory order seems important for correct macro expansion # (files containing macros definitions must be parsed before the files using them) - SET(DOXYGEN_SOURCE_DIR ${COIN3D_INCLUDE_DIRS}/Inventor/fields/SoSubField.h - ${CMAKE_SOURCE_DIR}/src/CXX - ${CMAKE_SOURCE_DIR}/src/3rdParty/zipios++ + SET(DOXYGEN_SOURCE_DIR ${COIN3D_INCLUDE_DIRS}/Inventor/fields/SoSubField.h + ${CMAKE_SOURCE_DIR}/src/3rdParty/PyCXX/CXX + ${CMAKE_SOURCE_DIR}/src/3rdParty/zipios++ ${CMAKE_SOURCE_DIR}/src/3rdParty - ${CMAKE_SOURCE_DIR}/src/Build - ${CMAKE_SOURCE_DIR}/src/Base - ${CMAKE_BINARY_DIR}/src/Base - ${CMAKE_SOURCE_DIR}/src/App - ${CMAKE_BINARY_DIR}/src/App - ${CMAKE_SOURCE_DIR}/src/Gui - ${CMAKE_BINARY_DIR}/src/Gui + ${CMAKE_SOURCE_DIR}/src/Build + ${CMAKE_SOURCE_DIR}/src/Base + ${CMAKE_BINARY_DIR}/src/Base + ${CMAKE_SOURCE_DIR}/src/App + ${CMAKE_BINARY_DIR}/src/App + ${CMAKE_SOURCE_DIR}/src/Gui + ${CMAKE_BINARY_DIR}/src/Gui ${CMAKE_SOURCE_DIR}/src/Mod ${CMAKE_BINARY_DIR}/src/Mod - #${CMAKE_SOURCE_DIR}/src/Main #nothing to document there ATM... + ${CMAKE_SOURCE_DIR}/src/Main #nothing to document there ATM... ${CMAKE_SOURCE_DIR}/src/Doc - ${CMAKE_BINARY_DIR}/src/Doc + ${CMAKE_BINARY_DIR}/src/Doc ) STRING(REGEX REPLACE ";" " " DOXYGEN_INPUT_LIST "${DOXYGEN_SOURCE_DIR}") diff --git a/src/Doc/FreecadDoxygenLayout.xml b/src/Doc/FreecadDoxygenLayout.xml index 49194389a5..89698cb1d9 100644 --- a/src/Doc/FreecadDoxygenLayout.xml +++ b/src/Doc/FreecadDoxygenLayout.xml @@ -2,7 +2,7 @@ - + diff --git a/src/Doc/mainpage.dox.in b/src/Doc/mainpage.dox.in index 5ab0e6c5dc..d3803f155f 100644 --- a/src/Doc/mainpage.dox.in +++ b/src/Doc/mainpage.dox.in @@ -22,64 +22,112 @@ /** - \mainpage FreeCAD source documentation + @mainpage FreeCAD Source Documentation - This is the source documentation of FreeCAD. - It is automatically generated from the source code, and describes the different - components of the FreeCAD source code, for both the parts written in C++ and Python. - - For general help and documentation about the FreeCAD application and how to use it, head - first to the Wiki Documentation. - - Refer to the - general - introduction to the FreeCAD source code page for a broad view of how the source - code is organized, and browse the modules, classes and namespaces from the menu above. - - The Developers section - of the wiki also contains additional information for developers, and the - Powerusers / python scripting - section contains coding documentation that is specifically aimed at python scripting, but - that will also be useful to C++ coders, as most of the APIs are identical or very similar. - - For space reasons, this on-line version doesn't include the source code headers nor the full - inheritance graphics. But you can build a full version yourself with everything included, - as explained in building - the source code documentation. You can also browse through the source - code on github. + @section toc Table of Contents + - @ref intro_main "Introduction" + - @ref organization "Organization of the API Documentation" + - @ref other_doc "Other Documentation" - \if DEV_DOCUMENTATION + @section intro_main Introduction - \mainpage FreeCAD source documentation + This is the source documentation of [FreeCAD](https://www.freecad.org). It + is automatically generated from the source code and describes the different + components of the FreeCAD source code, for both the parts written in C++ + and Python. The next section discusses the organization of this API + documentation and the last section describes other forms of documentation + that FreeCAD has. + + @section organization Organization of the API Documentation + + This documentation is divided into several topics of which the most important are: + - @ref CORE "Core" + - @ref BASE "Base" + - @ref APP "App" + - @ref GUI "Gui" + - @ref WORKBENCHES "Workbenches" + - @ref EMBEDDED "Embedded 3rd party packages" + + Firstly, topic @ref CORE "Core" contains the core namespaces and classes + that all of FreeCAD relies on. This consists of namespace %Base (see topic + @ref BASE "Base") that contains fundamental base classes, the namespace + %App (see topic @ref APP "App") that contains classes for FreeCAD in + command-line mode, and namespace %Gui (see topic @ref GUI "Gui") that + contains the classes for FreeCAD's GUI. + + FreeCAD is highly modular and most of its functionality is within modules + that are called @ref WORKBENCHES "Workbenches". Workbenches can be written + in C++ or Python or in a combination of the two. FreeCAD also has external + workbenches that can be loaded from the Addon Manager. These external + workbenches need to be written in Python. + + Finally, there is the topic @ref EMBEDDED "Embedded 3rd party packages" + that contains 3rd-party software of which the source is embedded in + FreeCAD. This means that during compilation, these packages will be + compiled and linked to FreeCAD as opposed to non-embedded 3rd-party + packages that are only linked to FreeCAD. An example of an important + non-embedded 3rd party software package is [Open CASCADE + Technology](https://dev.opencascade.org/doc/overview/html/). Its API + documentation can be found + [here](https://dev.opencascade.org/doc/refman/html/index.html). + + @section other_doc Other Documentation + + For general help and documentation about the FreeCAD application and how to + use it, head first to the [Wiki Documentation](https://www.freecad.org/wiki). + + Refer to the [Wiki page "The FreeCAD source + code"](https://www.freecad.org/wiki/The_FreeCAD_source_code) for a broad + view of how the source code is organized, and browse the modules, classes + and namespaces from the menu above. + + The [Developer hub](https://www.freecad.org/wiki/Developer_hub) of the wiki + contains additional information for developers and the [Power users / + Python scripting section](https://www.freecad.org/wiki/Power_users_hub) + contains coding documentation that is specifically aimed at Python + scripting, but that will also be useful to C++ coders, as most of the APIs + are identical or very similar. + + For space reasons, this on-line version doesn't include the source code + headers nor the full inheritance graphics. But you can build a full version + yourself with everything included, as explained in Wiki page [Source + documentation](https://wiki.freecad.org/Source_documentation#Complete_documentation). You + can also browse through the [source code on + GitHub](https://github.com/FreeCAD/FreeCAD). + + @if DEV_DOCUMENTATION + + @mainpage FreeCAD source documentation + + @image html freecadsplash.png - \image html freecadsplash.png - Welcome to FreeCAD source documentation! - + We hope you'll find it a convenient tool to explore, understand and experiment with the internals of the program. - + Being generated from the actual state of the code, this documentation is by no means a well structured corpus or a textbook, but more an utility to browse through the sources. - + If you seek information on FreeCAD at large, please consult our Wiki - + FreeCAD being a fast moving target, don't hesitate to rebuild an up to date doc from source as often as needed (\ref makingdocs "more info"). CPU cycles are cheap - nowadays! - + nowadays! + The homepage of FreeCAD is here. - - \endif + + @endif @GIT_REVISION_INFO@ */ -/** \namespace std - \brief The Standard namespace for the C++ library -*/ +/** + * @namespace std + * @brief The Standard namespace for the C++ library + */ diff --git a/src/Doc/primary-groups.dox b/src/Doc/primary-groups.dox index 7ec6329e65..d54bdc7504 100644 --- a/src/Doc/primary-groups.dox +++ b/src/Doc/primary-groups.dox @@ -1,15 +1,15 @@ -/** \defgroup CORE Core - These are the core components of FreeCAD. +/** @defgroup CORE Core + The core components of FreeCAD. - It groups the Base classes, and the main components of FreeCAD core, - spread over their App and Gui sides. Core components are programmed in - C++ but provide a broad Python API too. Most of FreeCAD functionality, + FreeCAD's core consists of Base classes, and the main components of FreeCAD + core, spread over their App and Gui sides. Core components are programmed + in C++ but provide a broad Python API too. Most of FreeCAD functionality, however, is defined in Workbenches. */ -/** \defgroup WORKBENCHES Workbenches +/** @defgroup WORKBENCHES Workbenches */ -/** \defgroup EMBEDDED Embedded 3rd party libraries +/** @defgroup EMBEDDED Embedded 3rd party libraries Important tools and libraries incorporated to FreeCAD. */ diff --git a/src/Doc/templates/header.html b/src/Doc/templates/header.html index 1cce457543..337cceeff0 100644 --- a/src/Doc/templates/header.html +++ b/src/Doc/templates/header.html @@ -34,7 +34,7 @@
  • Index
  • -
  • Modules
  • +
  • Topics
  • Classes
  • diff --git a/src/Gui/AxisOrigin.pyi b/src/Gui/AxisOrigin.pyi new file mode 100644 index 0000000000..adece2cfd0 --- /dev/null +++ b/src/Gui/AxisOrigin.pyi @@ -0,0 +1,75 @@ +from Base.Metadata import export, constmethod +from Base.BaseClass import BaseClass +from typing import Any, Final, Tuple, Dict + + +@export( + Constructor=True, + Delete=True, +) +class AxisOrigin(BaseClass): + """ + Gui.AxisOrigin class. + + Class for creating a Coin3D representation of a coordinate system. + + Author: Zheng, Lei (realthunder.dev@gmail.com) + Licence: LGPL + """ + + @constmethod + def getElementPicked(self, pickedPoint: Any) -> str: + """ + getElementPicked(pickedPoint) -> str + + Returns the picked element name. + + pickedPoint : coin.SoPickedPoint + """ + ... + + @constmethod + def getDetailPath(self, subname: str, path: Any) -> Any: + """ + getDetailPath(subname, path) -> coin.SoDetail or None + + Returns Coin detail of a subelement. + Note: Not fully implemented. Currently only returns None. + + subname : str + String reference to the subelement. + path: coin.SoPath + Output Coin path leading to the returned element detail. + """ + ... + + AxisLength: float = 0.0 + """Get/set the axis length.""" + + LineWidth: float = 0.0 + """Get/set the axis line width for rendering.""" + + PointSize: float = 0.0 + """Get/set the origin point size for rendering.""" + + Scale: float = 0.0 + """Get/set auto scaling factor, 0 to disable.""" + + Plane: Tuple[Any, ...] = () + """Get/set axis plane size and distance to axis line.""" + + Labels: Dict[str, str] = {} + """ + Get/set axis component names as a dictionary. + Available keys are: + 'O': origin + 'X': x axis + 'Y': y axis + 'Z': z axis + 'XY': xy plane + 'XZ': xz plane + 'YZ': yz plane + """ + + Node: Final[Any] = ... + """Get the Coin3D node.""" diff --git a/src/Gui/AxisOriginPy.xml b/src/Gui/AxisOriginPy.xml deleted file mode 100644 index 6d8d0bd2c5..0000000000 --- a/src/Gui/AxisOriginPy.xml +++ /dev/null @@ -1,93 +0,0 @@ - - - - - - Gui.AxisOrigin class. - -Class for creating a Coin3D representation of a coordinate system. - - - - getElementPicked(pickedPoint) -> str - -Returns the picked element name. - -pickedPoint : coin.SoPickedPoint - - - - - getDetailPath(subname, path) -> coin.SoDetail or None - -Returns Coin detail of a subelement. -Note: Not fully implemented. Currently only returns None. - -subname : str - String reference to the subelement. -path: coin.SoPath - Output Coin path leading to the returned element detail. - - - - - Get/set the axis length. - - - - - - Get/set the axis line width for rendering. - - - - - - Get/set the origin point size for rendering. - - - - - - Get/set auto scaling factor, 0 to disable. - - - - - - Get/set axis plane size and distance to axis line. - - - - - - Get/set axis component names as a dictionary. -Available keys are: -'O': origin -'X': x axis -'Y': y axis -'Z': z axis -'XY': xy plane -'XZ': xz plane -'YZ': yz plane - - - - - - Get the Coin3D node. - - - - - diff --git a/src/Gui/CMakeLists.txt b/src/Gui/CMakeLists.txt index a98cee9f14..2c1acb63d4 100644 --- a/src/Gui/CMakeLists.txt +++ b/src/Gui/CMakeLists.txt @@ -270,38 +270,38 @@ if(FREECAD_USE_PYSIDE) add_definitions(-DHAVE_PYSIDE${PYSIDE_MAJOR_VERSION}) endif(FREECAD_USE_PYSIDE) -generate_from_xml(DocumentPy) -generate_from_xml(PythonWorkbenchPy) -generate_from_xml(ViewProviderPy) -generate_from_xml(ViewProviderDocumentObjectPy) -generate_from_xml(ViewProviderGeometryObjectPy) -generate_from_xml(ViewProviderExtensionPy) -generate_from_xml(WorkbenchPy) -generate_from_xml(Selection/SelectionObjectPy) -generate_from_xml(LinkViewPy) -generate_from_xml(ViewProviderLinkPy) -generate_from_xml(AxisOriginPy) -generate_from_xml(CommandPy) -generate_from_xml(Navigation/NavigationStylePy) +generate_from_py(Document) +generate_from_py(PythonWorkbench) +generate_from_py(ViewProvider) +generate_from_py(ViewProviderDocumentObject) +generate_from_py(ViewProviderGeometryObject) +generate_from_py(ViewProviderExtension) +generate_from_py(Workbench) +generate_from_py(Selection/SelectionObject) +generate_from_py(LinkView) +generate_from_py(ViewProviderLink) +generate_from_py(AxisOrigin) +generate_from_py(Command) +generate_from_py(Navigation/NavigationStyle) generate_embed_from_py(FreeCADGuiInit GuiInitScript.h) -# The XML files -SET(FreeCADGui_XML_SRCS - ViewProviderDocumentObjectPy.xml - ViewProviderGeometryObjectPy.xml - ViewProviderPy.xml - ViewProviderExtensionPy.xml - PythonWorkbenchPy.xml - WorkbenchPy.xml - Selection/SelectionObjectPy.xml - DocumentPy.xml - LinkViewPy.xml - ViewProviderLinkPy.xml - AxisOriginPy.xml - CommandPy.xml +# The Pyi files +SET(FreeCADGui_Pyi_SRCS + ViewProviderDocumentObject.pyi + ViewProviderGeometryObject.pyi + ViewProvider.pyi + ViewProviderExtension.pyi + PythonWorkbench.pyi + Workbench.pyi + Selection/SelectionObject.pyi + Document.pyi + LinkView.pyi + ViewProviderLink.pyi + AxisOrigin.pyi + Command.pyi ) -SOURCE_GROUP("XML" FILES ${FreeCADApp_XML_SRCS}) +SOURCE_GROUP("Pyi" FILES ${FreeCADGui_Pyi_SRCS}) # The 3D Connexion SDK files if(FREECAD_USE_3DCONNEXION_LEGACY AND MSVC) @@ -947,7 +947,7 @@ SET(Navigation_CPP_SRCS SET(Navigation_SRCS ${Navigation_CPP_SRCS} Navigation/NavigationStyle.h - Navigation/NavigationStylePy.xml + Navigation/NavigationStyle.pyi Navigation/GestureNavigationStyle.h Navigation/NavigationAnimator.h Navigation/NavigationAnimation.h diff --git a/src/Gui/CallTips.cpp b/src/Gui/CallTips.cpp index 93221d7366..e3712f9c22 100644 --- a/src/Gui/CallTips.cpp +++ b/src/Gui/CallTips.cpp @@ -748,7 +748,7 @@ QString CallTipsList::stripWhiteSpace(const QString& str) const { QString stripped = str; QStringList lines = str.split(QLatin1String("\n")); - int minspace=INT_MAX; + int minspace=std::numeric_limits::max(); int line=0; for (QStringList::iterator it = lines.begin(); it != lines.end(); ++it, ++line) { if (it->size() > 0 && line > 0) { @@ -766,7 +766,7 @@ QString CallTipsList::stripWhiteSpace(const QString& str) const } // remove all leading tabs from each line - if (minspace > 0 && minspace < INT_MAX) { + if (minspace > 0 && minspace < std::numeric_limits::max()) { int line=0; QStringList strippedlines; for (QStringList::iterator it = lines.begin(); it != lines.end(); ++it, ++line) { diff --git a/src/Gui/Clipping.cpp b/src/Gui/Clipping.cpp index e56eb2997b..d2495c3720 100644 --- a/src/Gui/Clipping.cpp +++ b/src/Gui/Clipping.cpp @@ -109,20 +109,21 @@ Clipping::Clipping(Gui::View3DInventor* view, QWidget* parent) d->ui.setupUi(this); setupConnections(); - d->ui.clipView->setRange(-INT_MAX, INT_MAX); + constexpr int max = std::numeric_limits::max(); + d->ui.clipView->setRange(-max, max); d->ui.clipView->setSingleStep(0.1f); - d->ui.clipX->setRange(-INT_MAX, INT_MAX); + d->ui.clipX->setRange(-max, max); d->ui.clipX->setSingleStep(0.1f); - d->ui.clipY->setRange(-INT_MAX, INT_MAX); + d->ui.clipY->setRange(-max, max); d->ui.clipY->setSingleStep(0.1f); - d->ui.clipZ->setRange(-INT_MAX, INT_MAX); + d->ui.clipZ->setRange(-max, max); d->ui.clipZ->setSingleStep(0.1f); - d->ui.dirX->setRange(-INT_MAX, INT_MAX); + d->ui.dirX->setRange(-max, max); d->ui.dirX->setSingleStep(0.1f); - d->ui.dirY->setRange(-INT_MAX, INT_MAX); + d->ui.dirY->setRange(-max, max); d->ui.dirY->setSingleStep(0.1f); - d->ui.dirZ->setRange(-INT_MAX, INT_MAX); + d->ui.dirZ->setRange(-max, max); d->ui.dirZ->setSingleStep(0.1f); d->ui.dirZ->setValue(1.0f); diff --git a/src/Gui/Command.pyi b/src/Gui/Command.pyi new file mode 100644 index 0000000000..3ff64ae94d --- /dev/null +++ b/src/Gui/Command.pyi @@ -0,0 +1,183 @@ +from Base.Metadata import constmethod +from Base.PyObjectBase import PyObjectBase +from typing import Any, Dict, List, Optional + + +class Command(PyObjectBase): + """ + FreeCAD Python wrapper of Command functions + + Author: Werner Mayer (wmayer[at]users.sourceforge.net) + Licence: LGPL + """ + + @staticmethod + def get(name: str) -> Optional["Command"]: + """ + get(name) -> Gui.Command or None + + Get a given command by name or None if it doesn't exist. + + name : str + Command name. + """ + ... + + @staticmethod + def update() -> None: + """ + update() -> None + + Update active status of all commands. + """ + ... + + @staticmethod + def listAll() -> List[str]: + """ + listAll() -> list of str + + Returns the name of all commands. + """ + ... + + @staticmethod + def listByShortcut(string: str, useRegExp: bool = False) -> List[str]: + """ + listByShortcut(string, useRegExp=False) -> list of str + + Returns a list of all commands, filtered by shortcut. + Shortcuts are converted to uppercase and spaces removed + prior to comparison. + + string : str + Shortcut to be searched. + useRegExp : bool + Filter using regular expression. + """ + ... + + def run(self, item: int = 0) -> None: + """ + run(item=0) -> None + + Runs the given command. + + item : int + Item to be run. + """ + ... + + @constmethod + def isActive(self) -> bool: + """ + isActive() -> bool + + Returns True if the command is active, False otherwise. + """ + ... + + def getShortcut(self) -> str: + """ + getShortcut() -> str + + Returns string representing shortcut key accelerator for command. + """ + ... + + def setShortcut(self, string: str) -> bool: + """ + setShortcut(string) -> bool + + Sets shortcut for given command, returns True for success. + + string : str + Shortcut to be set. + """ + ... + + def resetShortcut(self) -> bool: + """ + resetShortcut() -> bool + + Resets shortcut for given command back to the default, returns True for success. + """ + ... + + def getInfo(self) -> Dict[Any, Any]: + """ + getInfo() -> dict + + Return information about this command. + """ + ... + + def getAction(self) -> List[Any]: + """ + getAction() -> list of QAction + + Return the associated QAction object. + """ + ... + + @staticmethod + def createCustomCommand( + *, + macroFile: str, + menuText: str, + toolTip: str, + whatsThis: str, + statusTip: str, + pixmap: str, + shortcut: str + ) -> str: + """ + createCustomCommand(macroFile, menuText, toolTip, whatsThis, statusTip, pixmap, shortcut) -> str + + Create a custom command for a macro. Returns name of the created command. + + macroFile : str + Macro file. + menuText : str + Menu text. Optional. + toolTip : str + Tool tip text. Optional. + whatsThis : str + `What's this?` text. Optional. + statusTip : str + Status tip text. Optional. + pixmap : str + Pixmap name. Optional. + shortcut : str + Shortcut key sequence. Optional. + """ + ... + + @staticmethod + def removeCustomCommand(name: str) -> bool: + """ + removeCustomCommand(name) -> bool + + Remove the custom command if it exists. + Given the name of a custom command, this removes that command. + It is not an error to remove a non-existent command, the function + simply does nothing in that case. + Returns True if something was removed, or False if not. + + name : str + Command name. + """ + ... + + @staticmethod + def findCustomCommand(name: str) -> Optional[str]: + """ + findCustomCommand(name) -> str or None + + Given the name of a macro, return the name of the custom command for that macro + or None if there is no command matching that macro script name. + + name : str + Macro name. + """ + ... diff --git a/src/Gui/CommandPy.xml b/src/Gui/CommandPy.xml deleted file mode 100644 index 2fa2f65639..0000000000 --- a/src/Gui/CommandPy.xml +++ /dev/null @@ -1,157 +0,0 @@ - - - - - - FreeCAD Python wrapper of Command functions - - - - get(name) -> Gui.Command or None - -Get a given command by name or None if it doesn't exist. - -name : str - Command name. - - - - - update() -> None - -Update active status of all commands. - - - - - listAll() -> list of str - -Returns the name of all commands. - - - - - listByShortcut(string, useRegExp=False) -> list of str - -Returns a list of all commands, filtered by shortcut. -Shortcuts are converted to uppercase and spaces removed -prior to comparison. - -string : str - Shortcut to be searched. -useRegExp : bool - Filter using regular expression. - - - - - run(item=0) -> None - -Runs the given command. - -item : int - Item to be run. - - - - - isActive() -> bool - -Returns True if the command is active, False otherwise. - - - - - getShortcut() -> str - -Returns string representing shortcut key accelerator for command. - - - - - setShortcut(string) -> bool - -Sets shortcut for given command, returns True for success. - -string : str - Shortcut to be set. - - - - - resetShortcut() -> bool - -Resets shortcut for given command back to the default, returns True for success. - - - - - getInfo() -> dict - -Return information about this command. - - - - - getAction() -> list of QAction - -Return the associated QAction object. - - - - - createCustomCommand(macroFile, menuText, toolTip, whatsThis, statusTip, pixmap, shortcut) -> str - -Create a custom command for a macro. Returns name of the created command. - -macroFile : str - Macro file. -menuText : str - Menu text. Optional. -toolTip : str - Tool tip text. Optional. -whatsThis : str - `What's this?` text. Optional. -statusTip : str - Status tip text. Optional. -pixmap : str - Pixmap name. Optional. -shortcut : str - Shortcut key sequence. Optional. - - - - - removeCustomCommand(name) -> bool - -Remove the custom command if it exists. -Given the name of a custom command, this removes that command. -It is not an error to remove a non-existent command, the function -simply does nothing in that case. -Returns True if something was removed, or False if not. - -name : str - Command name. - - - - - findCustomCommand(name) -> str or None - -Given the name of a macro, return the name of the custom command for that macro -or None if there is no command matching that macro script name. - -name : str - Macro name. - - - - diff --git a/src/Gui/DemoMode.cpp b/src/Gui/DemoMode.cpp index e253246b3d..d69224d891 100644 --- a/src/Gui/DemoMode.cpp +++ b/src/Gui/DemoMode.cpp @@ -166,7 +166,7 @@ SbVec3f DemoMode::getDirection(Gui::View3DInventor* view) const SbRotation inv = rot.inverse(); SbVec3f vec(this->viewAxis); inv.multVec(vec, vec); - if (vec.length() < FLT_EPSILON) { + if (vec.length() < std::numeric_limits::epsilon()) { vec = this->viewAxis; } vec.normalize(); diff --git a/src/Gui/Dialogs/DlgParameterImp.cpp b/src/Gui/Dialogs/DlgParameterImp.cpp index 2036e65ee4..8139e994ba 100644 --- a/src/Gui/Dialogs/DlgParameterImp.cpp +++ b/src/Gui/Dialogs/DlgParameterImp.cpp @@ -905,7 +905,7 @@ void ParameterValue::onCreateUIntItem() DlgInputDialogImp::UIntBox); dlg.setWindowTitle(QObject::tr("New unsigned item")); UIntSpinBox* edit = dlg.getUIntBox(); - edit->setRange(0, UINT_MAX); + edit->setRange(0, std::numeric_limits::max()); if (dlg.exec() == QDialog::Accepted) { QString value = edit->text(); unsigned long val = value.toULong(&ok); @@ -1249,7 +1249,7 @@ void ParameterUInt::changeValue() DlgInputDialogImp::UIntBox); dlg.setWindowTitle(QObject::tr("Change value")); UIntSpinBox* edit = dlg.getUIntBox(); - edit->setRange(0, UINT_MAX); + edit->setRange(0, std::numeric_limits::max()); edit->setValue(text(2).toULong()); if (dlg.exec() == QDialog::Accepted) { QString value = edit->text(); diff --git a/src/Gui/Document.cpp b/src/Gui/Document.cpp index f1207b0cbf..a3e1e75acb 100644 --- a/src/Gui/Document.cpp +++ b/src/Gui/Document.cpp @@ -672,13 +672,14 @@ void Document::setEditingTransform(const Base::Matrix4D &mat) { void Document::resetEdit() { bool vpIsNotNull = d->_editViewProvider != nullptr; + bool vpHasChanged = d->_editViewProvider != d->_editViewProviderPrevious; int modeToRestore = d->_editModePrevious; Gui::ViewProvider* vpToRestore = d->_editViewProviderPrevious; bool shouldRestorePrevious = d->_editWantsRestorePrevious; Application::Instance->setEditDocument(nullptr); - if (vpIsNotNull && shouldRestorePrevious) { + if (vpIsNotNull && vpHasChanged && shouldRestorePrevious) { setEdit(vpToRestore, modeToRestore); } } diff --git a/src/Gui/Document.pyi b/src/Gui/Document.pyi new file mode 100644 index 0000000000..3c585d7c7c --- /dev/null +++ b/src/Gui/Document.pyi @@ -0,0 +1,255 @@ +from Base.Metadata import constmethod +from Base.Persistence import Persistence +from Base.Matrix import Matrix +from typing import Any, Final, List, Optional + + +class Document(Persistence): + """ + This is a Document class + + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + def show(self, objName: str) -> None: + """ + show(objName) -> None + + Show an object. + + objName : str + Name of the `Gui.ViewProvider` to show. + """ + ... + + def hide(self, objName: str) -> None: + """ + hide(objName) -> None + + Hide an object. + + objName : str + Name of the `Gui.ViewProvider` to hide. + """ + ... + + def setPos(self, objName: str, matrix: Matrix) -> None: + """ + setPos(objName, matrix) -> None + + Set the position of an object. + + objName : str + Name of the `Gui.ViewProvider`. + + matrix : Base.Matrix + Transformation to apply on the object. + """ + ... + + def setEdit(self, obj: Any, mod: int = 0, subName: Optional[str] = None) -> bool: + """ + setEdit(obj, mod=0, subName) -> bool + + Set an object in edit mode. + + obj : str, App.DocumentObject, Gui.ViewPrivider + Object to set in edit mode. + mod : int + Edit mode. + subName : str + Subelement name. Optional. + """ + ... + + def getInEdit(self) -> Optional[Any]: + """ + getInEdit() -> Gui.ViewProviderDocumentObject or None + + Returns the current object in edit mode or None if there is no such object. + """ + ... + + def resetEdit(self) -> None: + """ + resetEdit() -> None + + End the current editing. + """ + ... + + def addAnnotation(self, annoName: str, fileName: str, modName: str) -> None: + """ + addAnnotation(annoName, fileName, modName) -> None + + Add an Inventor object from a file. + + annoName : str + Annotation name. + fileName : str + File name. + modName : str + Display mode name. Optional. + """ + ... + + def update(self) -> None: + """ + update() -> None + + Update the view representations of all objects. + """ + ... + + def getObject(self, objName: str) -> Optional[Any]: + """ + getObject(objName) -> object or None + + Return the object with the given name. If no one exists, return None. + + ObjName : str + Object name. + """ + ... + + def activeObject(self) -> Optional[Any]: + """ + activeObject() -> object or None + + The active object of the document. Deprecated, use ActiveObject. + """ + ... + + def activeView(self) -> Optional[Any]: + """ + activeView() -> object or None + + The active view of the document. Deprecated, use ActiveView. + """ + ... + + def createView(self, type: str) -> Optional[Any]: + """ + createView(type) -> object or None + + Return a newly created view of a given type. + + type : str + Type name. + """ + ... + + @constmethod + def mdiViewsOfType(self, type: str) -> List[Any]: + """ + mdiViewsOfType(type) -> list of MDIView + + Return a list of mdi views of a given type. + + type : str + Type name. + """ + ... + + def save(self) -> bool: + """ + save() -> bool + + Attempts to save the document + """ + ... + + def saveAs(self) -> bool: + """ + saveAs() -> bool + + Attempts to save the document under a new name + """ + ... + + def sendMsgToViews(self, msg: str) -> None: + """ + sendMsgToViews(msg) -> None + + Send a message to all views of the document. + + msg : str + """ + ... + + def mergeProject(self, fileName: str) -> None: + """ + mergeProject(fileName) -> None + + Merges this document with another project file. + + fileName : str + File name. + """ + ... + + def toggleTreeItem( + self, obj: Any, mod: int = 0, subName: Optional[str] = None + ) -> None: + """ + toggleTreeItem(obj, mod=0, subName) -> None + + Change TreeItem of a document object. + + obj : App.DocumentObject + mod : int + Item mode. + 0: Toggle, 1: Collapse, 2: Expand, 3: Expand path. + subName : str + Subelement name. Optional. + """ + ... + + def scrollToTreeItem(self, obj: Any) -> None: + """ + scrollToTreeItem(obj) -> None + + Scroll the tree view to the item of a view object. + + obj : Gui.ViewProviderDocumentObject + """ + ... + + def toggleInSceneGraph(self, obj: Any) -> None: + """ + toggleInSceneGraph(obj) -> None + + Add or remove view object from scene graph of all views depending + on its canAddToSceneGraph(). + + obj : Gui.ViewProvider + """ + ... + + ActiveObject: Any = ... + """The active object of the document.""" + + ActiveView: Any = ... + """The active view of the document.""" + + EditingTransform: Any = ... + """The editing transformation matrix.""" + + InEditInfo: Any = ... + """A tuple(obj,subname,subElement,editMode) of editing object reference, or None if no object is in edit.""" + + EditMode: Final[int] = 0 + """Current edit mode. Only meaningful when there is a current object in edit.""" + + Document: Final[Any] = ... + """The related App document to this Gui document.""" + + Transacting: Final[bool] = False + """Indicate whether the document is undoing/redoing.""" + + Modified: bool = False + """Returns True if the document is marked as modified, and False otherwise.""" + + TreeRootObjects: Final[List[Any]] = [] + """The list of tree root objects.""" diff --git a/src/Gui/DocumentPy.xml b/src/Gui/DocumentPy.xml deleted file mode 100644 index 2823a7acc4..0000000000 --- a/src/Gui/DocumentPy.xml +++ /dev/null @@ -1,263 +0,0 @@ - - - - - - This is a Document class - - - - show(objName) -> None - -Show an object. - -objName : str - Name of the `Gui.ViewProvider` to show. - - - - - hide(objName) -> None - -Hide an object. - -objName : str - Name of the `Gui.ViewProvider` to hide. - - - - - setPos(objName, matrix) -> None - -Set the position of an object. - -objName : str - Name of the `Gui.ViewProvider`. - -matrix : Base.Matrix - Transformation to apply on the object. - - - - - setEdit(obj, mod=0, subName) -> bool - -Set an object in edit mode. - -obj : str, App.DocumentObject, Gui.ViewPrivider - Object to set in edit mode. -mod : int - Edit mode. -subName : str - Subelement name. Optional. - - - - - getInEdit() -> Gui.ViewProviderDocumentObject or None - -Returns the current object in edit mode or None if there is no such object. - - - - - resetEdit() -> None - -End the current editing. - - - - - addAnnotation(annoName, fileName, modName) -> None - -Add an Inventor object from a file. - -annoName : str - Annotation name. -fileName : str - File name. -modName : str - Display mode name. Optional. - - - - - update() -> None - -Update the view representations of all objects. - - - - - getObject(objName) -> object or None - -Return the object with the given name. If no one exists, return None. - -ObjName : str - Object name. - - - - - activeObject() -> object or None - -The active object of the document. Deprecated, use ActiveObject. - - - - - activeView() -> object or None - -The active view of the document. Deprecated, use ActiveView. - - - - - createView(type) -> object or None - -Return a newly created view of a given type. - -type : str - Type name. - - - - - mdiViewsOfType(type) -> list of MDIView - -Return a list of mdi views of a given type. - -type : str - Type name. - - - - - save() -> bool - -Attempts to save the document - - - - - saveAs() -> bool - -Attempts to save the document under a new name - - - - - sendMsgToViews(msg) -> None - -Send a message to all views of the document. - -msg : str - - - - - mergeProject(fileName) -> None - -Merges this document with another project file. - -fileName : str - File name. - - - - - toggleTreeItem(obj, mod=0, subName) -> None - -Change TreeItem of a document object. - -obj : App.DocumentObject -mod : int - Item mode. - 0: Toggle, 1: Collapse, 2: Expand, 3: Expand path. -subName : str - Subelement name. Optional. - - - - - scrollToTreeItem(obj) -> None - -Scroll the tree view to the item of a view object. - -obj : Gui.ViewProviderDocumentObject - - - - - toggleInSceneGraph(obj) -> None - -Add or remove view object from scene graph of all views depending -on its canAddToSceneGraph(). - -obj : Gui.ViewProvider - - - - - The active object of the document. - - - - - - The active view of the document. - - - - - - The editing transformation matrix. - - - - - - A tuple(obj,subname,subElement,editMode) of editing object reference, or None if no object is in edit. - - - - - - Current edit mode. Only meaningful when there is a current object in edit. - - - - - - The related App document to this Gui document. - - - - - - Indicate whether the document is undoing/redoing. - - - - - - Returns True if the document is marked as modified, and False otherwise. - - - - - - The list of tree root objects. - - - - - diff --git a/src/Gui/EditableDatumLabel.cpp b/src/Gui/EditableDatumLabel.cpp index 5fa800935e..0604bc0a2d 100644 --- a/src/Gui/EditableDatumLabel.cpp +++ b/src/Gui/EditableDatumLabel.cpp @@ -152,8 +152,8 @@ void EditableDatumLabel::startEdit(double val, QObject* eventFilteringObj, bool spinBox = new QuantitySpinBox(mdi); spinBox->setUnit(Base::Unit::Length); - spinBox->setMinimum(-INT_MAX); - spinBox->setMaximum(INT_MAX); + spinBox->setMinimum(-std::numeric_limits::max()); + spinBox->setMaximum(std::numeric_limits::max()); spinBox->setButtonSymbols(QAbstractSpinBox::NoButtons); spinBox->setKeyboardTracking(false); spinBox->setFocusPolicy(Qt::ClickFocus); // prevent passing focus with tab. diff --git a/src/Gui/EditorView.cpp b/src/Gui/EditorView.cpp index f0ac94567a..0ad911d0b8 100644 --- a/src/Gui/EditorView.cpp +++ b/src/Gui/EditorView.cpp @@ -139,14 +139,15 @@ EditorView::EditorView(TextEdit* editor, QWidget* parent) d->activityTimer = new QTimer(this); // clang-format off + connectionList << connect(d->activityTimer, &QTimer::timeout, - this, &EditorView::checkTimestamp); + this, &EditorView::checkTimestamp) << connect(d->textEdit->document(), &QTextDocument::modificationChanged, - this, &EditorView::setWindowModified); + this, &EditorView::setWindowModified) << connect(d->textEdit->document(), &QTextDocument::undoAvailable, - this, &EditorView::undoAvailable); + this, &EditorView::undoAvailable) << connect(d->textEdit->document(), &QTextDocument::redoAvailable, - this, &EditorView::redoAvailable); + this, &EditorView::redoAvailable) << connect(d->textEdit->document(), &QTextDocument::contentsChange, this, &EditorView::contentsChange); // clang-format on @@ -156,6 +157,10 @@ EditorView::EditorView(TextEdit* editor, QWidget* parent) EditorView::~EditorView() { d->activityTimer->stop(); + // to avoid the assert introduced a debug version of Qt >6.3. See QTBUG-105473 + for (auto conn : connectionList) { // NOLINT(performance-for-range-copy) + disconnect(conn); + } delete d->activityTimer; delete d; getWindowParameter()->Detach(this); diff --git a/src/Gui/EditorView.h b/src/Gui/EditorView.h index 8546a94397..44d7395ede 100644 --- a/src/Gui/EditorView.h +++ b/src/Gui/EditorView.h @@ -116,6 +116,7 @@ private: private: EditorViewP* d; + QList connectionList; }; class PythonEditor; diff --git a/src/Gui/InputField.cpp b/src/Gui/InputField.cpp index 0d432566a9..e53fc5d1c4 100644 --- a/src/Gui/InputField.cpp +++ b/src/Gui/InputField.cpp @@ -71,8 +71,8 @@ InputField::InputField(QWidget * parent) ExpressionWidget(), validInput(true), actUnitValue(0), - Maximum(DOUBLE_MAX), - Minimum(-DOUBLE_MAX), + Maximum(std::numeric_limits::max()), + Minimum(-std::numeric_limits::max()), StepSize(1.0), HistorySize(5), SaveSize(5) diff --git a/src/Gui/Inventor/SoFCBackgroundGradient.cpp b/src/Gui/Inventor/SoFCBackgroundGradient.cpp index 37eb809127..323c18a91e 100644 --- a/src/Gui/Inventor/SoFCBackgroundGradient.cpp +++ b/src/Gui/Inventor/SoFCBackgroundGradient.cpp @@ -25,10 +25,8 @@ #ifndef _PreComp_ #include #include -#ifdef FC_OS_WIN32 - #define _USE_MATH_DEFINES -#endif #include +#include #ifdef FC_OS_MACOSX #include #else @@ -39,17 +37,23 @@ #include "SoFCBackgroundGradient.h" static const std::array big_circle = []{ - static const float pi2 = boost::math::constants::two_pi(); + constexpr float pi = std::numbers::pi_v; + constexpr float sqrt2 = std::numbers::sqrt2_v; std::array result; int c = 0; - for (GLfloat i = 0; i < pi2; i += pi2 / 32, c++) { - result[c][0] = M_SQRT2*cosf(i); result[c][1] = M_SQRT2*sinf(i); + for (GLfloat i = 0; i < 2 * pi; i += 2 * pi / 32, c++) { + result[c][0] = sqrt2 * cosf(i); + result[c][1] = sqrt2 * sinf(i); } return result; }(); static const std::array small_oval = []{ - static const float pi2 = boost::math::constants::two_pi(); + constexpr float pi = std::numbers::pi_v; + constexpr float sqrt2 = std::numbers::sqrt2_v; + static const float sqrt1_2 = std::sqrt(1 / 2.F); + std::array result; int c = 0; - for (GLfloat i = 0; i < pi2; i += pi2 / 32, c++) { - result[c][0] = 0.3*M_SQRT2*cosf(i); result[c][1] = M_SQRT1_2*sinf(i); + for (GLfloat i = 0; i < 2 * pi; i += 2 * pi / 32, c++) { + result[c][0] = 0.3 * sqrt2 * cosf(i); + result[c][1] = sqrt1_2 * sinf(i); } return result; }(); diff --git a/src/Gui/Language/Translator.cpp b/src/Gui/Language/Translator.cpp index 3955a2a887..dff0d3d2a2 100644 --- a/src/Gui/Language/Translator.cpp +++ b/src/Gui/Language/Translator.cpp @@ -45,7 +45,7 @@ using namespace Gui; * The internationalization of FreeCAD makes heavy use of the internationalization * support of Qt. For more details refer to your Qt documentation. * - * \section stepbystep Step by step + * \section stepbystep_language Step by step * To integrate a new language into FreeCAD or one of its application modules * you have to perform the following steps: * @@ -132,6 +132,7 @@ void Translator::destruct () Translator::Translator() { // This is needed for Qt's lupdate + // clang-format off d = new TranslatorP; d->mapLanguageTopLevelDomain[QT_TR_NOOP("Afrikaans" )] = "af"; d->mapLanguageTopLevelDomain[QT_TR_NOOP("Arabic" )] = "ar"; @@ -143,6 +144,7 @@ Translator::Translator() d->mapLanguageTopLevelDomain[QT_TR_NOOP("Chinese Traditional" )] = "zh-TW"; d->mapLanguageTopLevelDomain[QT_TR_NOOP("Croatian" )] = "hr"; d->mapLanguageTopLevelDomain[QT_TR_NOOP("Czech" )] = "cs"; + d->mapLanguageTopLevelDomain[QT_TR_NOOP("Danish" )] = "da"; d->mapLanguageTopLevelDomain[QT_TR_NOOP("Dutch" )] = "nl"; d->mapLanguageTopLevelDomain[QT_TR_NOOP("English" )] = "en"; d->mapLanguageTopLevelDomain[QT_TR_NOOP("Filipino" )] = "fil"; @@ -176,7 +178,6 @@ Translator::Translator() d->mapLanguageTopLevelDomain[QT_TR_NOOP("Ukrainian" )] = "uk"; d->mapLanguageTopLevelDomain[QT_TR_NOOP("Valencian" )] = "val-ES"; d->mapLanguageTopLevelDomain[QT_TR_NOOP("Vietnamese" )] = "vi"; - d->mapLanguageTopLevelDomain[QT_TR_NOOP("Danish")] = "da"; auto hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/General"); auto entries = hGrp->GetASCII("AdditionalLanguageDomainEntries", ""); @@ -190,6 +191,7 @@ Translator::Translator() QString tld = match.captured(2); d->mapLanguageTopLevelDomain[language.toStdString()] = tld.toStdString(); } + // clang-format on d->activatedLanguage = "English"; diff --git a/src/Gui/LinkView.pyi b/src/Gui/LinkView.pyi new file mode 100644 index 0000000000..fbd1dd47f5 --- /dev/null +++ b/src/Gui/LinkView.pyi @@ -0,0 +1,182 @@ +from Base.Metadata import export, constmethod +from Base.BaseClass import BaseClass +from typing import Any, Final, List, Dict, Tuple, overload + + +@export( + Include="Gui/ViewProviderLink.h", + Constructor=True, + Delete=True, +) +class LinkView(BaseClass): + """ + Helper class to link to a view object + + Author: Zheng, Lei (realthunder.dev@gmail.com) + Licence: LGPL + """ + + def reset(self) -> None: + """ + Reset the link view and clear the links + """ + ... + + def setMaterial(self, material: Any) -> None: + """ + setMaterial(Material): set the override material of the entire linked object + + setMaterial([Material,...]): set the materials for the elements of the link + array/group. + + setMaterial({Int:Material,...}): set the material for the elements of the + link array/group by index. + + If material is None, then the material is unset. If the material of an element + is unset, it defaults to the override material of the linked object, if there + is one + """ + ... + + @overload + def setMaterial(self, material: None) -> None: ... + + @overload + def setMaterial(self, material: Any) -> None: ... + + @overload + def setMaterial(self, material: List[Any]) -> None: ... + + @overload + def setMaterial(self, material: Dict[int, Any]) -> None: ... + + def setType(self, type: int, sublink: bool = True) -> None: + """ + setType(type, sublink=True): set the link type. + + type=0: override transformation and visibility + type=1: override visibility + type=2: no override + type=-1: sub-object link with override visibility + type=-2: sub-object link with override transformation and visibility + + sublink: auto delegate to the sub-object references in the link, if there is + one and only one. + """ + ... + + @overload + def setType(self, type: int) -> None: ... + + @overload + def setType(self, type: int, sublink: bool) -> None: ... + + def setTransform(self, matrix: Any) -> None: + """ + setTransform(matrix): set transformation of the linked object + + setTransform([matrix,...]): set transformation for the elements of the link + array/group + + setTransform({index:matrix,...}): set transformation for elements of the link + array/group by index + """ + ... + + @overload + def setTransform(self, matrix: Any) -> None: ... + + @overload + def setTransform(self, matrix: List[Any]) -> None: ... + + @overload + def setTransform(self, matrix: Dict[int, Any]) -> None: ... + + def setChildren( + self, children: List[Any], vis: List[Any] = [], type: int = 0 + ) -> None: + """ + setChildren([obj...],vis=[],type=0) + Group a list of children objects. Note, this mode of operation is incompatible + with link array. Calling this function will deactivate link array. And calling + setSize() will reset all linked children. + + vis: initial visibility status of the children + + type: children linking type, + 0: override transformation and visibility, + 1: override visibility, + 2: override none. + """ + ... + + def setLink(self, obj: Any, subname: Any = None) -> None: + """ + setLink(object): Set the link + + setLink(object, subname): Set the link with a sub-object reference + + setLink(object, [subname,...]): Set the link with a list of sub object references + + object: The linked document object or its view object + + subname: a string or tuple/list of strings sub-name references to sub object + or sub elements (e.g. Face1, Edge2) belonging to the linked object. + The sub-name must end with a '.' if it is referencing an sub-object, + or else it is considered a sub-element reference. + """ + ... + + @overload + def setLink(self, obj: Any) -> None: ... + + @overload + def setLink(self, obj: Any, subname: str) -> None: ... + + @overload + def setLink(self, obj: Any, subname: List[str]) -> None: ... + + def getDetailPath(self, element: Any) -> Tuple[Any, Any]: + """ + getDetailPath(element): get the 3d path an detail of an element. + + Return a tuple(path,detail) for the coin3D SoPath and SoDetail of the element + """ + ... + + def getElementPicked(self, pickPoint: Any) -> Any: + """ + getElementPicked(pickPoint): get the element under a 3d pick point. + """ + ... + + def getBoundBox(self, vobj: Any = None) -> Any: + """ + getBoundBox(vobj=None): get the bounding box. + """ + ... + + @constmethod + def getChildren(self) -> Any: + """ + Get children view objects + """ + ... + + LinkedView: Final[Any] = ... + """The linked view object""" + + SubNames: Final[Any] = ... + """The sub-object reference of the link""" + + RootNode: Final[Any] = ... + """A pivy node holding the cloned representation of the linked view object""" + + Owner: Any = ... + """The owner view object of this link handle""" + + Visibilities: Any = ... + """Get/set the child element visibility""" + + Count: int = 0 + """Set the element size to create an array of linked object""" diff --git a/src/Gui/LinkViewPy.xml b/src/Gui/LinkViewPy.xml deleted file mode 100644 index 70abbe9871..0000000000 --- a/src/Gui/LinkViewPy.xml +++ /dev/null @@ -1,165 +0,0 @@ - - - - - - Helper class to link to a view object - - - - Reset the link view and clear the links - - - - - -setMaterial(Material): set the override material of the entire linked object - -setMaterial([Material,...]): set the materials for the elements of the link - array/group. - -setMaterial({Int:Material,...}): set the material for the elements of the - link array/group by index. - -If material is None, then the material is unset. If the material of an element -is unset, it defaults to the override material of the linked object, if there -is one - - - - - - -setType(type, sublink=True): set the link type. - -type=0: override transformation and visibility -type=1: override visibility -type=2: no override -type=-1: sub-object link with override visibility -type=-2: sub-object link with override transformation and visibility - -sublink: auto delegate to the sub-object references in the link, if there is - one and only one. - - - - - - -setTransform(matrix): set transformation of the linked object - -setTransform([matrix,...]): set transformation for the elements of the link - array/group - -setTransform({index:matrix,...}): set transformation for elements of the link - array/group by index - - - - - - -setChildren([obj...],vis=[],type=0) -Group a list of children objects. Note, this mode of operation is incompatible -with link array. Calling this function will deactivate link array. And calling -setSize() will reset all linked children. - -vis: initial visibility status of the children - -type: children linking type, - 0: override transformation and visibility, - 1: override visibility, - 2: override none. - - - - - - -setLink(object): Set the link - -setLink(object, subname): Set the link with a sub-object reference - -setLink(object, [subname,...]): Set the link with a list of sub object references - -object: The linked document object or its view object - -subname: a string or tuple/list of strings sub-name references to sub object - or sub elements (e.g. Face1, Edge2) belonging to the linked object. - The sub-name must end with a '.' if it is referencing an sub-object, - or else it is considered a sub-element reference. - - - - - - -getDetailPath(element): get the 3d path an detail of an element. - -Return a tuple(path,detail) for the coin3D SoPath and SoDetail of the element - - - - - - getElementPicked(pickPoint): get the element under a 3d pick point. - - - - - getBoundBox(vobj=None): get the bounding box. - - - - - The linked view object - - - - - - The sub-object reference of the link - - - - - - A pivy node holding the cloned representation of the linked view object - - - - - - The owner view object of this link handle - - - - - - Get/set the child element visibility - - - - - - Set the element size to create an array of linked object - - - - - - Get children view objects - - - - diff --git a/src/Gui/NaviCube.cpp b/src/Gui/NaviCube.cpp index 2780fdc703..5cff61a54d 100644 --- a/src/Gui/NaviCube.cpp +++ b/src/Gui/NaviCube.cpp @@ -23,7 +23,7 @@ #include "PreCompiled.h" #ifndef _PreComp_ # include -# include +# include # ifdef FC_OS_WIN32 # include # endif @@ -553,7 +553,7 @@ void NaviCubeImplementation::addButtonFace(PickId pickId, const SbVec3f& directi case PickId::DotBackside: { int steps = 16; for (int i = 0; i < steps; i++) { - float angle = 2.0f * M_PI * ((float)i+0.5) / (float)steps; + float angle = 2.0f * std::numbers::pi_v * ((float)i+0.5) / (float)steps; pointData.emplace_back(10. * cos(angle) + 87.); pointData.emplace_back(10. * sin(angle) - 87.); } @@ -659,8 +659,8 @@ void NaviCubeImplementation::setSize(int size) void NaviCubeImplementation::prepare() { - static const float pi = boost::math::constants::pi(); - static const float pi1_2 = boost::math::constants::half_pi(); + constexpr float pi = std::numbers::pi_v; + constexpr float pi1_2 = pi / 2; createCubeFaceTextures(); @@ -817,7 +817,7 @@ void NaviCubeImplementation::drawNaviCube(bool pickMode, float opacity) glOrtho(-2.1, 2.1, -2.1, 2.1, NEARVAL, FARVAL); } else { - const float dim = NEARVAL * float(tan(M_PI / 8.0)) * 1.1; + const float dim = NEARVAL * float(tan(std::numbers::pi / 8.0)) * 1.1; glFrustum(-dim, dim, -dim, dim, NEARVAL, FARVAL); } glMatrixMode(GL_MODELVIEW); @@ -1010,15 +1010,11 @@ SbRotation NaviCubeImplementation::getNearestOrientation(PickId pickId) { angle *= -1; } - static const float pi = boost::math::constants::pi(); - static const float pi2 = boost::math::constants::two_pi(); - static const float pi1_2 = boost::math::constants::half_pi(); - static const float pi1_3 = boost::math::constants::third_pi(); - static const float pi2_3 = boost::math::constants::two_thirds_pi(); + constexpr float pi = std::numbers::pi_v; // Make angle positive if (angle < 0) { - angle += pi2; + angle += 2 * pi; } // f is a small value used to control orientation priority when the camera is almost exactly between two @@ -1030,23 +1026,23 @@ SbRotation NaviCubeImplementation::getNearestOrientation(PickId pickId) { // Find the angle to rotate to the nearest orientation if (m_Faces[pickId].type == ShapeId::Corner) { // 6 possible orientations for the corners - if (angle <= (M_PI / 6 + f)) { + if (angle <= (pi / 6 + f)) { angle = 0; } - else if (angle <= (M_PI_2 + f)) { - angle = pi1_3; + else if (angle <= (pi / 2 + f)) { + angle = pi / 3; } - else if (angle < (5 * M_PI / 6 - f)) { - angle = pi2_3; + else if (angle < (5 * pi / 6 - f)) { + angle = 2 * pi / 3; } - else if (angle <= (M_PI + M_PI / 6 + f)) { + else if (angle <= (pi + pi / 6 + f)) { angle = pi; } - else if (angle < (M_PI + M_PI_2 - f)) { - angle = pi + pi1_3; + else if (angle < (pi + pi / 2 - f)) { + angle = pi + pi / 3; } - else if (angle < (M_PI + 5 * M_PI / 6 - f)) { - angle = pi + pi2_3; + else if (angle < (pi + 5 * pi / 6 - f)) { + angle = pi + 2 * pi / 3; } else { angle = 0; @@ -1054,17 +1050,17 @@ SbRotation NaviCubeImplementation::getNearestOrientation(PickId pickId) { } else { // 4 possible orientations for the main and edge faces - if (angle <= (M_PI_4 + f)) { + if (angle <= (pi / 4 + f)) { angle = 0; } - else if (angle <= (3 * M_PI_4 + f)) { - angle = pi1_2; + else if (angle <= (3 * pi / 4 + f)) { + angle = pi / 2; } - else if (angle < (M_PI + M_PI_4 - f)) { + else if (angle < (pi + pi / 4 - f)) { angle = pi; } - else if (angle < (M_PI + 3 * M_PI_4 - f)) { - angle = pi + pi1_2; + else if (angle < (pi + 3 * pi / 4 - f)) { + angle = pi + pi / 2; } else { angle = 0; @@ -1089,7 +1085,7 @@ bool NaviCubeImplementation::mouseReleased(short x, short y) } else { PickId pickId = pickFace(x, y); long step = Base::clamp(long(m_NaviStepByTurn), 4L, 36L); - float rotStepAngle = (2 * M_PI) / step; + float rotStepAngle = (2 * std::numbers::pi) / step; if (m_Faces[pickId].type == ShapeId::Main || m_Faces[pickId].type == ShapeId::Edge || m_Faces[pickId].type == ShapeId::Corner) { // Handle the cube faces diff --git a/src/Gui/Navigation/NavigationAnimation.cpp b/src/Gui/Navigation/NavigationAnimation.cpp index 943af87706..29b0529006 100644 --- a/src/Gui/Navigation/NavigationAnimation.cpp +++ b/src/Gui/Navigation/NavigationAnimation.cpp @@ -25,6 +25,8 @@ #include "NavigationAnimation.h" #include +#include + using namespace Gui; NavigationAnimation::NavigationAnimation(NavigationStyle* navigation) @@ -69,8 +71,8 @@ void FixedTimeAnimation::initialize() SbVec3f rotationAxisPost; float angle; SbRotation(navigation->getCamera()->orientation.getValue().inverse() * targetOrientation).getValue(rotationAxisPost, angle); - if (angle > M_PI) { - angle -= float(2 * M_PI); + if (angle > std::numbers::pi) { + angle -= float(2 * std::numbers::pi); } // Convert post-multiplication axis to a pre-multiplication axis @@ -130,9 +132,9 @@ SpinningAnimation::SpinningAnimation(NavigationStyle* navigation, const SbVec3f& : NavigationAnimation(navigation) , rotationAxis(axis) { - setDuration((2 * M_PI / velocity) * 1000.0); + setDuration((2 * std::numbers::pi / velocity) * 1000.0); setStartValue(0.0); - setEndValue(2 * M_PI); + setEndValue(2 * std::numbers::pi); setLoopCount(-1); } diff --git a/src/Gui/Navigation/NavigationStyle.cpp b/src/Gui/Navigation/NavigationStyle.cpp index 3f48db267d..9a0e3fca08 100644 --- a/src/Gui/Navigation/NavigationStyle.cpp +++ b/src/Gui/Navigation/NavigationStyle.cpp @@ -39,6 +39,9 @@ # include #endif +#include +#include + #include #include @@ -719,7 +722,8 @@ void NavigationStyle::zoom(SoCamera * cam, float diffvalue) const float distorigo = newpos.length(); // sqrt(FLT_MAX) == ~ 1e+19, which should be both safe for further // calculations and ok for the end-user and app-programmer. - if (distorigo > float(sqrt(FLT_MAX))) { + float maxDistance = std::sqrt(std::numeric_limits::max()); + if (distorigo > maxDistance) { // do nothing here } else { diff --git a/src/Gui/Navigation/NavigationStyle.pyi b/src/Gui/Navigation/NavigationStyle.pyi new file mode 100644 index 0000000000..286d25766f --- /dev/null +++ b/src/Gui/Navigation/NavigationStyle.pyi @@ -0,0 +1,15 @@ +from Base.Metadata import export +from Base.BaseClass import BaseClass + + +@export( + Include="Gui/Navigation/NavigationStyle.h", +) +class NavigationStyle(BaseClass): + """ + This is the base class for navigation styles + + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + ... diff --git a/src/Gui/Navigation/NavigationStylePy.xml b/src/Gui/Navigation/NavigationStylePy.xml deleted file mode 100644 index 314eda00ee..0000000000 --- a/src/Gui/Navigation/NavigationStylePy.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - This is the base class for navigation styles - - - - diff --git a/src/Gui/OverlayManager.cpp b/src/Gui/OverlayManager.cpp index f7b437e54b..41cb897bc8 100644 --- a/src/Gui/OverlayManager.cpp +++ b/src/Gui/OverlayManager.cpp @@ -1819,7 +1819,7 @@ bool OverlayManager::eventFilter(QObject *o, QEvent *ev) } if (hit <= 0) { - d->_lastPos.setX(INT_MAX); + d->_lastPos.setX(std::numeric_limits::max()); if (ev->type() == QEvent::Wheel) { d->wheelDelay = QTime::currentTime().addMSecs(OverlayParams::getDockOverlayWheelDelay()); d->wheelPos = pos; diff --git a/src/Gui/PreCompiled.h b/src/Gui/PreCompiled.h index c3a5f43345..c665016cf7 100644 --- a/src/Gui/PreCompiled.h +++ b/src/Gui/PreCompiled.h @@ -50,8 +50,6 @@ #include #include #include -#include -#include #ifdef FC_OS_WIN32 #include @@ -69,6 +67,7 @@ #include #include #include +#include #include #include #include diff --git a/src/Gui/PreferencePages/DlgSettingsCacheDirectory.cpp b/src/Gui/PreferencePages/DlgSettingsCacheDirectory.cpp index 0c395195e6..9e5bd6b5ba 100644 --- a/src/Gui/PreferencePages/DlgSettingsCacheDirectory.cpp +++ b/src/Gui/PreferencePages/DlgSettingsCacheDirectory.cpp @@ -182,7 +182,7 @@ void ApplicationCache::setPeriod(ApplicationCache::Period period) numDays = 365; break; case Period::Never: - numDays = INT_MAX; + numDays = std::numeric_limits::max(); break; } } diff --git a/src/Gui/PreferencePages/DlgSettingsDocumentImp.cpp b/src/Gui/PreferencePages/DlgSettingsDocumentImp.cpp index 4297531a3f..cb352c303f 100644 --- a/src/Gui/PreferencePages/DlgSettingsDocumentImp.cpp +++ b/src/Gui/PreferencePages/DlgSettingsDocumentImp.cpp @@ -60,7 +60,7 @@ DlgSettingsDocumentImp::DlgSettingsDocumentImp(QWidget* parent) ui->prefSaveBackupDateFormat->setToolTip(tip); ui->FormatTimeDocsLabel->setText(link); - ui->prefCountBackupFiles->setMaximum(INT_MAX); + ui->prefCountBackupFiles->setMaximum(std::numeric_limits::max()); ui->prefCompression->setMinimum(Z_NO_COMPRESSION); ui->prefCompression->setMaximum(Z_BEST_COMPRESSION); connect(ui->prefLicenseType, qOverload(&QComboBox::currentIndexChanged), diff --git a/src/Gui/PreferencePages/DlgSettingsLightSources.ui b/src/Gui/PreferencePages/DlgSettingsLightSources.ui index 6c09e0ec81..59f516abd5 100644 --- a/src/Gui/PreferencePages/DlgSettingsLightSources.ui +++ b/src/Gui/PreferencePages/DlgSettingsLightSources.ui @@ -164,7 +164,7 @@ AmbientLightIntensity - View + View/LightSources @@ -181,7 +181,7 @@ BacklightColor - View + View/LightSources @@ -267,7 +267,7 @@ AmbientLightColor - View + View/LightSources @@ -293,7 +293,7 @@ HeadlightIntensity - View + View/LightSources @@ -332,7 +332,7 @@ BacklightIntensity - View + View/LightSources @@ -367,7 +367,7 @@ FillLightIntensity - View + View/LightSources @@ -404,7 +404,7 @@ FillLightColor - View + View/LightSources @@ -431,7 +431,7 @@ HeadlightColor - View + View/LightSources diff --git a/src/Gui/PythonWorkbench.pyi b/src/Gui/PythonWorkbench.pyi new file mode 100644 index 0000000000..f8d2943c0e --- /dev/null +++ b/src/Gui/PythonWorkbench.pyi @@ -0,0 +1,142 @@ +from Base.Metadata import export +from Workbench import Workbench +from warnings import deprecated + + +@export( + Twin="PythonBaseWorkbench", + TwinPointer="PythonBaseWorkbench", + Include="Gui/Workbench.h", +) +class PythonWorkbench(Workbench): + """ + This is the class for Python workbenches + + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + def appendMenu(self) -> None: + """ + Append a new menu + """ + ... + + def removeMenu(self) -> None: + """ + Remove a menu + """ + ... + + def appendContextMenu(self) -> None: + """ + Append a new context menu item + """ + ... + + def removeContextMenu(self) -> None: + """ + Remove a context menu item + """ + ... + + def appendToolbar(self) -> None: + """ + Append a new toolbar + """ + ... + + def removeToolbar(self) -> None: + """ + Remove a toolbar + """ + ... + + def appendCommandbar(self) -> None: + """ + Append a new command bar + """ + ... + + def removeCommandbar(self) -> None: + """ + Remove a command bar + """ + ... + + @deprecated + def AppendMenu(self) -> None: + """ + deprecated -- use appendMenu + """ + ... + + @deprecated + def RemoveMenu(self) -> None: + """ + deprecated -- use removeMenu + """ + ... + + @deprecated + def ListMenus(self) -> None: + """ + deprecated -- use listMenus + """ + ... + + @deprecated + def AppendContextMenu(self) -> None: + """ + deprecated -- use appendContextMenu + """ + ... + + @deprecated + def RemoveContextMenu(self) -> None: + """ + deprecated -- use removeContextMenu + """ + ... + + @deprecated + def AppendToolbar(self) -> None: + """ + deprecated -- use appendToolbar + """ + ... + + @deprecated + def RemoveToolbar(self) -> None: + """ + deprecated -- use removeToolbar + """ + ... + + @deprecated + def ListToolbars(self) -> None: + """ + deprecated -- use listToolbars + """ + ... + + @deprecated + def AppendCommandbar(self) -> None: + """ + deprecated -- use appendCommandBar + """ + ... + + @deprecated + def RemoveCommandbar(self) -> None: + """ + deprecated -- use removeCommandBar + """ + ... + + @deprecated + def ListCommandbars(self) -> None: + """ + deprecated -- use listCommandBars + """ + ... diff --git a/src/Gui/PythonWorkbenchPy.xml b/src/Gui/PythonWorkbenchPy.xml deleted file mode 100644 index 08ae38e86f..0000000000 --- a/src/Gui/PythonWorkbenchPy.xml +++ /dev/null @@ -1,113 +0,0 @@ - - - - - - This is the class for Python workbenches - - - - Append a new menu - - - - - Remove a menu - - - - - Append a new context menu item - - - - - Remove a context menu item - - - - - Append a new toolbar - - - - - Remove a toolbar - - - - - Append a new command bar - - - - - Remove a command bar - - - - - deprecated -- use appendMenu - - - - - deprecated -- use removeMenu - - - - - deprecated -- use listMenus - - - - - deprecated -- use appendContextMenu - - - - - deprecated -- use removeContextMenu - - - - - deprecated -- use appendToolbar - - - - - deprecated -- use removeToolbar - - - - - deprecated -- use listToolbars - - - - - deprecated -- use appendCommandBar - - - - - deprecated -- use removeCommandBar - - - - - deprecated -- use listCommandBars - - - - - diff --git a/src/Gui/QuantitySpinBox.cpp b/src/Gui/QuantitySpinBox.cpp index abfbb04e34..cc18e8be11 100644 --- a/src/Gui/QuantitySpinBox.cpp +++ b/src/Gui/QuantitySpinBox.cpp @@ -66,8 +66,8 @@ public: pendingEmit(false), checkRangeInExpression(false), unitValue(0), - maximum(DOUBLE_MAX), - minimum(-DOUBLE_MAX), + maximum(std::numeric_limits::max()), + minimum(-std::numeric_limits::max()), singleStep(1.0), q_ptr(q) { diff --git a/src/Gui/Quarter/QuarterWidget.cpp b/src/Gui/Quarter/QuarterWidget.cpp index 3560d595de..b69b87ca5d 100644 --- a/src/Gui/Quarter/QuarterWidget.cpp +++ b/src/Gui/Quarter/QuarterWidget.cpp @@ -76,9 +76,7 @@ #include #include -#if COIN_MAJOR_VERSION >= 4 #include -#endif #include #include @@ -1245,11 +1243,7 @@ QuarterWidget::setNavigationModeFile(const QUrl & url) QFile file(filenametmp); if (file.open(QIODevice::ReadOnly)){ QByteArray fileContents = file.readAll(); -#if COIN_MAJOR_VERSION >= 4 stateMachine = ScXML::readBuffer(SbByteBuffer(fileContents.size(), fileContents.constData())); -#else - stateMachine = ScXML::readBuffer(fileContents.constData()); -#endif file.close(); } } diff --git a/src/Gui/Quarter/SoQTQuarterAdaptor.cpp b/src/Gui/Quarter/SoQTQuarterAdaptor.cpp index ecfbf9a142..e98bd2d77f 100644 --- a/src/Gui/Quarter/SoQTQuarterAdaptor.cpp +++ b/src/Gui/Quarter/SoQTQuarterAdaptor.cpp @@ -20,6 +20,8 @@ #include "PreCompiled.h" +#include + #include #include #include @@ -303,7 +305,7 @@ void SIM::Coin3D::Quarter::SoQTQuarterAdaptor::convertOrtho2Perspective(const So SbRotation camrot = in->orientation.getValue(); - float focaldist = float(in->height.getValue() / (2.0*tan(M_PI / 8.0))); // NOLINT + float focaldist = float(in->height.getValue() / (2.0*tan(std::numbers::pi / 8.0))); // NOLINT SbVec3f offset(0,0,focaldist-in->focalDistance.getValue()); @@ -313,7 +315,7 @@ void SIM::Coin3D::Quarter::SoQTQuarterAdaptor::convertOrtho2Perspective(const So out->focalDistance.setValue(focaldist); // 45° is the default value of this field in SoPerspectiveCamera. - out->heightAngle = (float)(M_PI / 4.0); // NOLINT + out->heightAngle = (float)(std::numbers::pi / 4.0); // NOLINT } void SIM::Coin3D::Quarter::SoQTQuarterAdaptor::convertPerspective2Ortho(const SoPerspectiveCamera* in, @@ -568,7 +570,7 @@ void SIM::Coin3D::Quarter::SoQTQuarterAdaptor::seeksensorCB(void* data, SoSensor bool end = (par == 1.0F); - par = (float)((1.0 - cos(M_PI * par)) * 0.5); // NOLINT + par = (float)((1.0 - cos(std::numbers::pi * par)) * 0.5); // NOLINT thisp->getSoRenderManager()->getCamera()->position = thisp->m_camerastartposition + (thisp->m_cameraendposition - thisp->m_camerastartposition) * par; diff --git a/src/Gui/Selection/SelectionFilter.h b/src/Gui/Selection/SelectionFilter.h index 90d3430bb7..563acd90af 100644 --- a/src/Gui/Selection/SelectionFilter.h +++ b/src/Gui/Selection/SelectionFilter.h @@ -172,8 +172,9 @@ private: struct Node_Slice { - explicit Node_Slice(int min=1,int max=INT_MAX):Min(min),Max(max){} - int Min,Max; + explicit Node_Slice(int min = 1, int max = std::numeric_limits::max()) + : Min(min), Max(max) {} + int Min, Max; }; diff --git a/src/Gui/Selection/SelectionObject.pyi b/src/Gui/Selection/SelectionObject.pyi new file mode 100644 index 0000000000..efd2e3fd59 --- /dev/null +++ b/src/Gui/Selection/SelectionObject.pyi @@ -0,0 +1,62 @@ +from Base.Metadata import export +from Base.BaseClass import BaseClass +from typing import Any, Final, Tuple + + +@export( + Include="Gui/Selection/SelectionObject.h", + Delete=True, +) +class SelectionObject(BaseClass): + """ + This class represents selections made by the user. It holds information about the object, document and sub-element of the selection. + + Author: Juergen Riegel (FreeCAD@juergen-riegel.net) + Licence: LGPL + """ + + def remove(self) -> None: + """ + Remove this selection item from the selection. + remove() -> None + -- + This object becomes invalid. + """ + ... + + def isObjectTypeOf(self, type: Any) -> bool: + """ + Test for a certain father class. + isObjectTypeOf(type) -> Bool + """ + ... + + ObjectName: Final[str] = "" + """Name of the selected object""" + + SubElementNames: Final[Tuple[str, ...]] = () + """Name of the selected sub-element if any""" + + FullName: Final[str] = "" + """Name of the selected object""" + + TypeName: Final[str] = "" + """Type name of the selected object""" + + DocumentName: Final[str] = "" + """Name of the document of the selected object""" + + Document: Final[Any] = ... + """Document of the selected object""" + + Object: Final[Any] = ... + """Selected object""" + + SubObjects: Final[Tuple[Any, ...]] = () + """Selected sub-element, if any""" + + PickedPoints: Final[Tuple[Any, ...]] = () + """Picked points for selection""" + + HasSubObjects: Final[bool] = False + """Selected sub-element, if any""" diff --git a/src/Gui/Selection/SelectionObjectPy.xml b/src/Gui/Selection/SelectionObjectPy.xml deleted file mode 100644 index 5cd194e455..0000000000 --- a/src/Gui/Selection/SelectionObjectPy.xml +++ /dev/null @@ -1,95 +0,0 @@ - - - - - - This class represents selections made by the user. It holds information about the object, document and sub-element of the selection. - - - - Remove this selection item from the selection. -remove() -> None --- -This object becomes invalid. - - - - - - Test for a certain father class. -isObjectTypeOf(type) -> Bool - - - - - - Name of the selected object - - - - - - Name of the selected sub-element if any - - - - - - Name of the selected object - - - - - - Type name of the selected object - - - - - - Name of the document of the selected object - - - - - - Document of the selected object - - - - - - Selected object - - - - - - Selected sub-element, if any - - - - - - Picked points for selection - - - - - - Selected sub-element, if any - - - - - - diff --git a/src/Gui/Selection/SoFCSelectionContext.h b/src/Gui/Selection/SoFCSelectionContext.h index 3d92e00b1a..d9cb345bbf 100644 --- a/src/Gui/Selection/SoFCSelectionContext.h +++ b/src/Gui/Selection/SoFCSelectionContext.h @@ -23,7 +23,6 @@ #ifndef GUI_SOFCSELECTIONCONTEXT_H #define GUI_SOFCSELECTIONCONTEXT_H -#include #include #include #include @@ -79,11 +78,11 @@ struct GuiExport SoFCSelectionContext : SoFCSelectionContextBase } bool isHighlightAll() const{ - return highlightIndex==INT_MAX && (selectionIndex.empty() || isSelectAll()); + return highlightIndex == std::numeric_limits::max() && (selectionIndex.empty() || isSelectAll()); } void highlightAll() { - highlightIndex = INT_MAX; + highlightIndex = std::numeric_limits::max(); } void removeHighlight() { diff --git a/src/Gui/ShortcutManager.cpp b/src/Gui/ShortcutManager.cpp index c7103f7c7a..2f49e8fd7d 100644 --- a/src/Gui/ShortcutManager.cpp +++ b/src/Gui/ShortcutManager.cpp @@ -409,7 +409,7 @@ void ShortcutManager::onTimer() timer.stop(); QAction *found = nullptr; - int priority = -INT_MAX; + int priority = -std::numeric_limits::max(); int seq_length = 0; for (const auto &info : pendingActions) { if (info.action) { diff --git a/src/Gui/SoDatumLabel.cpp b/src/Gui/SoDatumLabel.cpp index fff3978b6f..978a7721a1 100644 --- a/src/Gui/SoDatumLabel.cpp +++ b/src/Gui/SoDatumLabel.cpp @@ -34,8 +34,8 @@ # endif # include -# include # include +# include # include # include @@ -78,11 +78,12 @@ void glDrawLine(const SbVec3f& p1, const SbVec3f& p2){ glEnd(); } -void glDrawArc(const SbVec3f& center, float radius, float startAngle=0., float endAngle=2.0*M_PI, int countSegments=0){ +void glDrawArc(const SbVec3f& center, float radius, float startAngle=0., + float endAngle=2.0*std::numbers::pi, int countSegments=0){ float range = endAngle - startAngle; if (countSegments == 0){ - countSegments = std::max(6, abs(int(25.0 * range / M_PI))); + countSegments = std::max(6, abs(int(25.0 * range / std::numbers::pi))); } float segment = range / (countSegments-1); @@ -238,11 +239,12 @@ public: private: void getBBox(const std::vector& corners, SbBox3f& box, SbVec3f& center) const { + constexpr float floatMax = std::numeric_limits::max(); if (corners.size() > 1) { - float minX = FLT_MAX; - float minY = FLT_MAX; - float maxX = -FLT_MAX; - float maxY = -FLT_MAX; + float minX = floatMax; + float minY = floatMax; + float maxX = -floatMax; + float maxY = -floatMax; for (SbVec3f it : corners) { minX = (it[0] < minX) ? it[0] : minX; minY = (it[1] < minY) ? it[1] : minY; @@ -288,14 +290,15 @@ private: SbVec3f dir; SbVec3f normal; + constexpr float floatEpsilon = std::numeric_limits::epsilon(); if (label->datumtype.getValue() == SoDatumLabel::DISTANCE) { dir = (p2-p1); } else if (label->datumtype.getValue() == SoDatumLabel::DISTANCEX) { - dir = SbVec3f( (p2[0] - p1[0] >= FLT_EPSILON) ? 1 : -1, 0, 0); + dir = SbVec3f( (p2[0] - p1[0] >= floatEpsilon) ? 1 : -1, 0, 0); } else if (label->datumtype.getValue() == SoDatumLabel::DISTANCEY) { - dir = SbVec3f(0, (p2[1] - p1[1] >= FLT_EPSILON) ? 1 : -1, 0); + dir = SbVec3f(0, (p2[1] - p1[1] >= floatEpsilon) ? 1 : -1, 0); } dir.normalize(); @@ -546,11 +549,11 @@ private: float startangle = atan2f(vc1[1], vc1[0]); float endangle = atan2f(vc2[1], vc2[0]); if (endangle < startangle) { - endangle += 2. * M_PI; + endangle += 2. * std::numbers::pi; } SbVec3f textCenter; - if (endangle - startangle <= M_PI) { + if (endangle - startangle <= std::numbers::pi) { textCenter = ctr + vm * (length + imgHeight); } else { textCenter = ctr - vm * (length + 2. * imgHeight); @@ -628,14 +631,16 @@ SbVec3f SoDatumLabel::getLabelTextCenterDistance(const SbVec3f& p1, const SbVec3 SbVec3f dir; SbVec3f normal; + + constexpr float floatEpsilon = std::numeric_limits::epsilon(); if (datumtype.getValue() == SoDatumLabel::DISTANCE) { dir = (p2 - p1); } else if (datumtype.getValue() == SoDatumLabel::DISTANCEX) { - dir = SbVec3f((p2[0] - p1[0] >= FLT_EPSILON) ? 1 : -1, 0, 0); + dir = SbVec3f((p2[0] - p1[0] >= floatEpsilon) ? 1 : -1, 0, 0); } else if (datumtype.getValue() == SoDatumLabel::DISTANCEY) { - dir = SbVec3f(0, (p2[1] - p1[1] >= FLT_EPSILON) ? 1 : -1, 0); + dir = SbVec3f(0, (p2[1] - p1[1] >= floatEpsilon) ? 1 : -1, 0); } dir.normalize(); @@ -689,7 +694,7 @@ SbVec3f SoDatumLabel::getLabelTextCenterArcLength(const SbVec3f& ctr, const SbVe float endangle = atan2f(vc2[1], vc2[0]); if (endangle < startangle) { - endangle += 2. * M_PI; + endangle += 2. * std::numbers::pi; } // Text location @@ -697,7 +702,7 @@ SbVec3f SoDatumLabel::getLabelTextCenterArcLength(const SbVec3f& ctr, const SbVe vm.normalize(); SbVec3f textCenter; - if (endangle - startangle <= M_PI) { + if (endangle - startangle <= std::numbers::pi) { textCenter = ctr + vm * (length + this->imgHeight); } else { textCenter = ctr - vm * (length + 2. * this->imgHeight); @@ -709,12 +714,13 @@ SbVec3f SoDatumLabel::getLabelTextCenterArcLength(const SbVec3f& ctr, const SbVe void SoDatumLabel::generateDistancePrimitives(SoAction * action, const SbVec3f& p1, const SbVec3f& p2) { SbVec3f dir; + constexpr float floatEpsilon = std::numeric_limits::epsilon(); if (this->datumtype.getValue() == DISTANCE) { dir = (p2-p1); } else if (this->datumtype.getValue() == DISTANCEX) { - dir = SbVec3f( (p2[0] - p1[0] >= FLT_EPSILON) ? 1 : -1, 0, 0); + dir = SbVec3f( (p2[0] - p1[0] >= floatEpsilon) ? 1 : -1, 0, 0); } else if (this->datumtype.getValue() == DISTANCEY) { - dir = SbVec3f(0, (p2[1] - p1[1] >= FLT_EPSILON) ? 1 : -1, 0); + dir = SbVec3f(0, (p2[1] - p1[1] >= floatEpsilon) ? 1 : -1, 0); } dir.normalize(); @@ -957,7 +963,8 @@ void SoDatumLabel::generateArcLengthPrimitives(SoAction * action, const SbVec3f& void SoDatumLabel::generatePrimitives(SoAction * action) { // Initialisation check (needs something more sensible) prevents an infinite loop bug - if (this->imgHeight <= FLT_EPSILON || this->imgWidth <= FLT_EPSILON) { + constexpr float floatEpsilon = std::numeric_limits::epsilon(); + if (this->imgHeight <= floatEpsilon | this->imgWidth <= floatEpsilon) { return; } @@ -1161,6 +1168,8 @@ void SoDatumLabel::getDimension(float scale, int& srcw, int& srch) void SoDatumLabel::drawDistance(const SbVec3f* points, float scale, int srch, float& angle, SbVec3f& textOffset) { + using std::numbers::pi; + float length = this->param1.getValue(); float length2 = this->param2.getValue(); @@ -1168,12 +1177,13 @@ void SoDatumLabel::drawDistance(const SbVec3f* points, float scale, int srch, fl SbVec3f p2 = points[1]; SbVec3f dir; + constexpr float floatEpsilon = std::numeric_limits::epsilon(); if (this->datumtype.getValue() == DISTANCE) { dir = (p2-p1); } else if (this->datumtype.getValue() == DISTANCEX) { - dir = SbVec3f( (p2[0] - p1[0] >= FLT_EPSILON) ? 1 : -1, 0, 0); + dir = SbVec3f( (p2[0] - p1[0] >= floatEpsilon) ? 1 : -1, 0, 0); } else if (this->datumtype.getValue() == DISTANCEY) { - dir = SbVec3f(0, (p2[1] - p1[1] >= FLT_EPSILON) ? 1 : -1, 0); + dir = SbVec3f(0, (p2[1] - p1[1] >= floatEpsilon) ? 1 : -1, 0); } dir.normalize(); @@ -1192,10 +1202,10 @@ void SoDatumLabel::drawDistance(const SbVec3f* points, float scale, int srch, fl // Get magnitude of angle between horizontal angle = atan2f(dir[1],dir[0]); - if (angle > M_PI_2+M_PI/12) { - angle -= (float)M_PI; - } else if (angle <= -M_PI_2+M_PI/12) { - angle += (float)M_PI; + if (angle > pi/2 + pi/12) { + angle -= (float)pi; + } else if (angle <= -pi/2 + pi/12) { + angle += (float)pi; } textOffset = midpos + normal * length + dir * length2; @@ -1291,7 +1301,7 @@ void SoDatumLabel::drawDistance(const SbVec3f* points) float startangle1 = this->param3.getValue(); float radius1 = this->param5.getValue(); SbVec3f center = points[2]; - int countSegments = std::max(6, abs(int(50.0 * range1 / (2 * M_PI)))); + int countSegments = std::max(6, abs(int(50.0 * range1 / (2 * std::numbers::pi)))); double segment = range1 / (countSegments - 1); glBegin(GL_LINE_STRIP); @@ -1307,7 +1317,7 @@ void SoDatumLabel::drawDistance(const SbVec3f* points) float startangle2 = this->param6.getValue(); float radius2 = this->param8.getValue(); SbVec3f center = points[3]; - int countSegments = std::max(6, abs(int(50.0 * range2 / (2 * M_PI)))); + int countSegments = std::max(6, abs(int(50.0 * range2 / (2 * std::numbers::pi)))); double segment = range2 / (countSegments - 1); glBegin(GL_LINE_STRIP); @@ -1342,10 +1352,10 @@ void SoDatumLabel::drawRadiusOrDiameter(const SbVec3f* points, float& angle, SbV // Get magnitude of angle between horizontal angle = atan2f(dir[1],dir[0]); - if (angle > M_PI_2+M_PI/12) { - angle -= (float)M_PI; - } else if (angle <= -M_PI_2+M_PI/12) { - angle += (float)M_PI; + if (angle > std::numbers::pi/2 + std::numbers::pi/12) { + angle -= (float)std::numbers::pi; + } else if (angle <= -std::numbers::pi/2 + std::numbers::pi/12) { + angle += (float)std::numbers::pi; } textOffset = pos; @@ -1401,7 +1411,7 @@ void SoDatumLabel::drawRadiusOrDiameter(const SbVec3f* points, float& angle, SbV float startangle = this->param3.getValue(); float range = this->param4.getValue(); if (range != 0.0) { - int countSegments = std::max(6, abs(int(50.0 * range / (2 * M_PI)))); + int countSegments = std::max(6, abs(int(50.0 * range / (2 * std::numbers::pi)))); double segment = range / (countSegments - 1); glBegin(GL_LINE_STRIP); @@ -1521,6 +1531,8 @@ void SoDatumLabel::drawSymmetric(const SbVec3f* points) void SoDatumLabel::drawArcLength(const SbVec3f* points, float& angle, SbVec3f& textOffset) { + using std::numbers::pi; + SbVec3f ctr = points[0]; SbVec3f p1 = points[1]; SbVec3f p2 = points[2]; @@ -1535,7 +1547,7 @@ void SoDatumLabel::drawArcLength(const SbVec3f* points, float& angle, SbVec3f& t float startangle = atan2f(vc1[1], vc1[0]); float endangle = atan2f(vc2[1], vc2[0]); if (endangle < startangle) { - endangle += 2.0F * (float)M_PI; + endangle += 2.0F * (float)pi; } float range = endangle - startangle; @@ -1547,10 +1559,10 @@ void SoDatumLabel::drawArcLength(const SbVec3f* points, float& angle, SbVec3f& t dir.normalize(); // Get magnitude of angle between horizontal angle = atan2f(dir[1],dir[0]); - if (angle > M_PI_2+M_PI/12) { - angle -= (float)M_PI; - } else if (angle <= -M_PI_2+M_PI/12) { - angle += (float)M_PI; + if (angle > pi/2 + pi/12) { + angle -= (float)pi; + } else if (angle <= -pi/2 + pi/12) { + angle += (float)pi; } // Text location textOffset = getLabelTextCenterArcLength(ctr, p1, p2); @@ -1566,7 +1578,7 @@ void SoDatumLabel::drawArcLength(const SbVec3f* points, float& angle, SbVec3f& t SbVec3f pnt4 = p2 + (length-radius) * vm; // Draw arc - if (range <= M_PI) { + if (range <= pi) { glDrawArc(ctr + (length-radius)*vm, radius, startangle, endangle); } else { @@ -1606,7 +1618,7 @@ void SoDatumLabel::drawText(SoState *state, int srcw, int srch, float angle, con const SbViewVolume & vv = SoViewVolumeElement::get(state); SbVec3f z = vv.zVector(); - bool flip = norm.getValue().dot(z) > FLT_EPSILON; + bool flip = norm.getValue().dot(z) > std::numeric_limits::epsilon(); static bool init = false; static bool npot = false; @@ -1678,7 +1690,7 @@ void SoDatumLabel::drawText(SoState *state, int srcw, int srch, float angle, con // Apply a rotation and translation matrix glTranslatef(textOffset[0], textOffset[1], textOffset[2]); - glRotatef((GLfloat) angle * 180 / M_PI, 0,0,1); + glRotatef((GLfloat) angle * 180 / std::numbers::pi, 0,0,1); glBegin(GL_QUADS); glColor3f(1.F, 1.F, 1.F); diff --git a/src/Gui/SoFCCSysDragger.cpp b/src/Gui/SoFCCSysDragger.cpp index 39130d3b9e..21142b5def 100644 --- a/src/Gui/SoFCCSysDragger.cpp +++ b/src/Gui/SoFCCSysDragger.cpp @@ -23,6 +23,7 @@ #include "PreCompiled.h" #ifndef _PreComp_ #include +#include #include #include @@ -743,7 +744,7 @@ RDragger::RDragger() } SO_KIT_ADD_FIELD(rotation, (SbVec3f(0.0, 0.0, 1.0), 0.0)); - SO_KIT_ADD_FIELD(rotationIncrement, (M_PI / 8.0)); + SO_KIT_ADD_FIELD(rotationIncrement, (std::numbers::pi / 8.0)); SO_KIT_ADD_FIELD(rotationIncrementCount, (0)); SO_KIT_INIT_INSTANCE(); @@ -808,7 +809,7 @@ SoGroup* RDragger::buildGeometry() unsigned int segments = 15; - float angleIncrement = static_cast(M_PI / 2.0) / static_cast(segments); + float angleIncrement = (std::numbers::pi_v / 2.f) / static_cast(segments); SbRotation rotation(SbVec3f(0.0, 0.0, 1.0), angleIncrement); SbVec3f point(arcRadius, 0.0, 0.0); for (unsigned int index = 0; index <= segments; ++index) { @@ -965,9 +966,10 @@ void RDragger::drag() appendRotation(getStartMotionMatrix(), localRotation, SbVec3f(0.0, 0.0, 0.0))); } - Base::Quantity quantity(static_cast(rotationIncrementCount.getValue()) * (180.0 / M_PI) - * rotationIncrement.getValue(), - Base::Unit::Angle); + Base::Quantity quantity( + static_cast(rotationIncrementCount.getValue()) + * (180.0 / std::numbers::pi)* rotationIncrement.getValue(), + Base::Unit::Angle); QString message = QStringLiteral("%1 %2").arg(QObject::tr("Rotation:"), QString::fromStdString(quantity.getUserString())); @@ -1179,7 +1181,7 @@ SoFCCSysDragger::SoFCCSysDragger() SO_KIT_ADD_FIELD(translationIncrementCountZ, (0)); SO_KIT_ADD_FIELD(rotation, (SbVec3f(0.0, 0.0, 1.0), 0.0)); - SO_KIT_ADD_FIELD(rotationIncrement, (M_PI / 8.0)); + SO_KIT_ADD_FIELD(rotationIncrement, (std::numbers::pi / 8.0)); SO_KIT_ADD_FIELD(rotationIncrementCountX, (0)); SO_KIT_ADD_FIELD(rotationIncrementCountY, (0)); SO_KIT_ADD_FIELD(rotationIncrementCountZ, (0)); @@ -1272,7 +1274,7 @@ SoFCCSysDragger::SoFCCSysDragger() SoRotation* localRotation; SbRotation tempRotation; - auto angle = static_cast(M_PI / 2.0); + auto angle = static_cast(std::numbers::pi / 2.0); // Translator localRotation = SO_GET_ANY_PART(this, "xTranslatorRotation", SoRotation); localRotation->rotation.setValue(SbVec3f(0.0, 0.0, -1.0), angle); diff --git a/src/Gui/SoTextLabel.cpp b/src/Gui/SoTextLabel.cpp index 6e533bea98..037f75b6f4 100644 --- a/src/Gui/SoTextLabel.cpp +++ b/src/Gui/SoTextLabel.cpp @@ -31,7 +31,6 @@ # else # include # endif -# include # include # include # include @@ -51,12 +50,7 @@ #include #include #include - -#if COIN_MAJOR_VERSION > 3 #include -#else -#include -#endif #include "SoTextLabel.h" #include "SoFCInteractiveElement.h" @@ -65,31 +59,6 @@ using namespace Gui; -/*! -\code - -s=""" - #Inventor V2.1 ascii - - Annotation { - Translation { translation 4 0 0 } - FontStyle { - size 20 - style BOLD - } - BaseColor { - rgb 0.0 0.0 0.0 - } - - - SoTextLabel { string ["Text label", "Second line"] backgroundColor 1.0 0.447059 0.337255} - } -""" - -App.ActiveDocument.addObject("App::InventorObject","iv").Buffer=s - -\endcode -*/ SO_NODE_SOURCE(SoTextLabel) @@ -215,11 +184,7 @@ void SoTextLabel::GLRender(SoGLRenderAction *action) // disable textures for all units SoGLTextureEnabledElement::set(state, this, false); -#if COIN_MAJOR_VERSION > 3 SoMultiTextureEnabledElement::set(state, this, false); -#else - SoGLTexture3EnabledElement::set(state, this, false); -#endif glPushAttrib(GL_ENABLE_BIT | GL_PIXEL_MODE_BIT | GL_COLOR_BUFFER_BIT); glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT); diff --git a/src/Gui/SoTouchEvents.cpp b/src/Gui/SoTouchEvents.cpp index 628fb089c3..8b99c606e8 100644 --- a/src/Gui/SoTouchEvents.cpp +++ b/src/Gui/SoTouchEvents.cpp @@ -22,6 +22,8 @@ #include "PreCompiled.h" +#include + #include #include #include @@ -86,8 +88,8 @@ SoGesturePinchEvent::SoGesturePinchEvent(QPinchGesture* qpinch, QWidget *widget) deltaZoom = qpinch->scaleFactor(); totalZoom = qpinch->totalScaleFactor(); - deltaAngle = -unbranchAngle((qpinch->rotationAngle()-qpinch->lastRotationAngle()) / 180.0 * M_PI); - totalAngle = -qpinch->totalRotationAngle() / 180 * M_PI; + deltaAngle = -unbranchAngle((qpinch->rotationAngle()-qpinch->lastRotationAngle()) / 180.0 * std::numbers::pi); + totalAngle = -qpinch->totalRotationAngle() / 180 * std::numbers::pi; state = SbGestureState(qpinch->state()); @@ -111,7 +113,9 @@ SbBool SoGesturePinchEvent::isSoGesturePinchEvent(const SoEvent *ev) const */ double SoGesturePinchEvent::unbranchAngle(double ang) { - return ang - 2.0 * M_PI * floor((ang + M_PI) / (2.0 * M_PI)); + using std::numbers::pi; + + return ang - 2.0 * pi * floor((ang + pi) / (2.0 * pi)); } diff --git a/src/Gui/SpinBox.cpp b/src/Gui/SpinBox.cpp index 207ec81335..a4fdb99add 100644 --- a/src/Gui/SpinBox.cpp +++ b/src/Gui/SpinBox.cpp @@ -238,7 +238,7 @@ UnsignedValidator::UnsignedValidator( QObject * parent ) : QValidator( parent ) { b = 0; - t = UINT_MAX; + t = std::numeric_limits::max(); } UnsignedValidator::UnsignedValidator( uint minimum, uint maximum, QObject * parent ) @@ -295,27 +295,31 @@ public: uint mapToUInt( int v ) const { uint ui; - if ( v == INT_MIN ) { + if ( v == std::numeric_limits::min() ) { ui = 0; - } else if ( v == INT_MAX ) { - ui = UINT_MAX; + } else if ( v == std::numeric_limits::max() ) { + ui = std::numeric_limits::max(); } else if ( v < 0 ) { - v -= INT_MIN; ui = (uint)v; + v -= std::numeric_limits::min(); + ui = static_cast(v); } else { - ui = (uint)v; ui -= INT_MIN; + ui = static_cast(v); + ui -= std::numeric_limits::min(); } return ui; } int mapToInt( uint v ) const { int in; - if ( v == UINT_MAX ) { - in = INT_MAX; + if ( v == std::numeric_limits::max() ) { + in = std::numeric_limits::max(); } else if ( v == 0 ) { - in = INT_MIN; - } else if ( v > INT_MAX ) { - v += INT_MIN; in = (int)v; + in = std::numeric_limits::min(); + } else if ( v > std::numeric_limits::max() ) { + v += std::numeric_limits::min(); + in = static_cast(v); } else { - in = v; in += INT_MIN; + in = v; + in += std::numeric_limits::min(); } return in; } }; diff --git a/src/Gui/Stylesheets/FreeCAD Dark.qss b/src/Gui/Stylesheets/FreeCAD Dark.qss index 2d84f81a3a..94e789eef8 100644 --- a/src/Gui/Stylesheets/FreeCAD Dark.qss +++ b/src/Gui/Stylesheets/FreeCAD Dark.qss @@ -1287,11 +1287,6 @@ QPushButton::menu-indicator { bottom: 4px; } -QDialogButtonBox QPushButton { - /* Issue # 194 # 248 - Special case of QPushButton inside dialogs, for better UI */ - min-width: -1; -} - /* QToolButton ------------------------------------------------------------ https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qtoolbutton diff --git a/src/Gui/Stylesheets/FreeCAD Light.qss b/src/Gui/Stylesheets/FreeCAD Light.qss index 6e502a628b..596c0b722c 100644 --- a/src/Gui/Stylesheets/FreeCAD Light.qss +++ b/src/Gui/Stylesheets/FreeCAD Light.qss @@ -1284,11 +1284,6 @@ QPushButton::menu-indicator { bottom: 4px; } -QDialogButtonBox QPushButton { - /* Issue # 194 # 248 - Special case of QPushButton inside dialogs, for better UI */ - min-width: -1; -} - /* QToolButton ------------------------------------------------------------ https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qtoolbutton diff --git a/src/Gui/Transform.cpp b/src/Gui/Transform.cpp index e1d22d1105..b6b0e68b81 100644 --- a/src/Gui/Transform.cpp +++ b/src/Gui/Transform.cpp @@ -378,6 +378,7 @@ Base::Vector3d Transform::getDirection() const Base::Placement Transform::getPlacementData() const { + using std::numbers::pi; int index = ui->rotationInput->currentIndex(); Base::Rotation rot; Base::Vector3d pos; @@ -388,7 +389,7 @@ Base::Placement Transform::getPlacementData() const if (index == 0) { Base::Vector3d dir = getDirection(); - rot.setValue(Base::Vector3d(dir.x,dir.y,dir.z),ui->angle->value().getValue()*D_PI/180.0); + rot.setValue(Base::Vector3d(dir.x,dir.y,dir.z),ui->angle->value().getValue()*pi/180.0); } else if (index == 1) { rot.setYawPitchRoll( diff --git a/src/Gui/VectorListEditor.cpp b/src/Gui/VectorListEditor.cpp index 9450145b7f..0e432920d1 100644 --- a/src/Gui/VectorListEditor.cpp +++ b/src/Gui/VectorListEditor.cpp @@ -255,8 +255,8 @@ QWidget *VectorTableDelegate::createEditor(QWidget *parent, const QStyleOptionVi { auto editor = new QDoubleSpinBox(parent); editor->setDecimals(decimals); - editor->setMinimum(INT_MIN); - editor->setMaximum(INT_MAX); + editor->setMinimum(std::numeric_limits::min()); + editor->setMaximum(std::numeric_limits::max()); editor->setSingleStep(0.1); return editor; @@ -299,11 +299,14 @@ VectorListEditor::VectorListEditor(int decimals, QWidget* parent) ui->tableWidget->setModel(model); ui->widget->hide(); - ui->coordX->setRange(INT_MIN, INT_MAX); + ui->coordX->setRange(std::numeric_limits::min(), + std::numeric_limits::max()); ui->coordX->setDecimals(decimals); - ui->coordY->setRange(INT_MIN, INT_MAX); + ui->coordY->setRange(std::numeric_limits::min(), + std::numeric_limits::max()); ui->coordY->setDecimals(decimals); - ui->coordZ->setRange(INT_MIN, INT_MAX); + ui->coordZ->setRange(std::numeric_limits::min(), + std::numeric_limits::max()); ui->coordZ->setDecimals(decimals); ui->toolButtonMouse->setDisabled(true); diff --git a/src/Gui/View3DInventorRiftViewer.cpp b/src/Gui/View3DInventorRiftViewer.cpp index 7dc8c49a62..3dd1a92647 100644 --- a/src/Gui/View3DInventorRiftViewer.cpp +++ b/src/Gui/View3DInventorRiftViewer.cpp @@ -43,7 +43,7 @@ View3DInventorRiftViewer::View3DInventorRiftViewer() : CoinRiftWidget() rotation1 = new SoRotationXYZ ; rotation1->axis.setValue(SoRotationXYZ::X); - rotation1->angle.setValue(-M_PI/2); + rotation1->angle.setValue(-std::numbers::pi/2); workplace->addChild(rotation1); rotation2 = new SoRotationXYZ ; @@ -104,7 +104,7 @@ void View3DInventorRiftViewer::setSceneGraph(SoNode *sceneGraph) void View3DInventorRiftViewer::keyPressEvent(QKeyEvent *event) { static const float increment = 0.02; // move two centimeter per key - static const float rotIncrement = M_PI/4; // move two 90° per key + static const float rotIncrement = std::numbers::pi / 4; // move two 90° per key if (event->key() == Qt::Key_Plus) { diff --git a/src/Gui/View3DInventorViewer.cpp b/src/Gui/View3DInventorViewer.cpp index 3103b1afde..078abfa336 100644 --- a/src/Gui/View3DInventorViewer.cpp +++ b/src/Gui/View3DInventorViewer.cpp @@ -23,7 +23,6 @@ #include "PreCompiled.h" #ifndef _PreComp_ -# include # ifdef FC_OS_WIN32 # include # endif @@ -1707,16 +1706,11 @@ void View3DInventorViewer::savePicture(int width, int height, int sample, const auto root = new SoSeparator; root->ref(); -#if (COIN_MAJOR_VERSION >= 4) - // The behaviour in Coin4 has changed so that when using the same instance of 'SoFCOffscreenRenderer' - // multiple times internally the biggest viewport size is stored and set to the SoGLRenderAction. - // The trick is to add a callback node and override the viewport size with what we want. if (useCoinOffscreenRenderer) { auto cbvp = new SoCallback; cbvp->setCallback(setViewportCB); root->addChild(cbvp); } -#endif SoCamera* camera = getSoRenderManager()->getCamera(); @@ -3267,7 +3261,7 @@ void View3DInventorViewer::setCameraType(SoType type) // heightAngle. Setting it to 45 deg also causes an issue with a too // close camera but we don't have this other ugly effect. - static_cast(cam)->heightAngle = (float)(M_PI / 4.0); // NOLINT + static_cast(cam)->heightAngle = (float)(std::numbers::pi / 4.0); // NOLINT } lightRotation->rotation.connectFrom(&cam->orientation); @@ -3426,7 +3420,7 @@ void View3DInventorViewer::viewAll() SoCamera* cam = this->getSoRenderManager()->getCamera(); if (cam && cam->getTypeId().isDerivedFrom(SoPerspectiveCamera::getClassTypeId())) { - static_cast(cam)->heightAngle = (float)(M_PI / 4.0); // NOLINT + static_cast(cam)->heightAngle = (float)(std::numbers::pi / 4.0); // NOLINT } if (isAnimationEnabled()) { @@ -3524,7 +3518,6 @@ void View3DInventorViewer::viewSelection() float(bbox.MaxX), float(bbox.MaxY), float(bbox.MaxZ)); -#if (COIN_MAJOR_VERSION >= 4) float aspectratio = getSoRenderManager()->getViewportRegion().getViewportAspectRatio(); switch (cam->viewportMapping.getValue()) { case SoCamera::CROP_VIEWPORT_FILL_FRAME: @@ -3536,27 +3529,6 @@ void View3DInventorViewer::viewSelection() break; } cam->viewBoundingBox(box,aspectratio,1.0); -#else - SoTempPath path(2); - path.ref(); - auto pcGroup = new SoGroup; - pcGroup->ref(); - auto pcTransform = new SoTransform; - pcGroup->addChild(pcTransform); - pcTransform->translation = box.getCenter(); - auto *pcCube = new SoCube; - pcGroup->addChild(pcCube); - float sizeX,sizeY,sizeZ; - box.getSize(sizeX,sizeY,sizeZ); - pcCube->width = sizeX; - pcCube->height = sizeY; - pcCube->depth = sizeZ; - path.append(pcGroup); - path.append(pcCube); - cam->viewAll(&path,getSoRenderManager()->getViewportRegion()); - path.unrefNoDelete(); - pcGroup->unref(); -#endif } } @@ -3632,26 +3604,28 @@ void View3DInventorViewer::alignToSelection() angle *= -1; } + using std::numbers::pi; + // Make angle positive if (angle < 0) { - angle += 2 * M_PI; + angle += 2 * pi; } // Find the angle to rotate to the nearest horizontal or vertical alignment with directionX. // f is a small value used to get more deterministic behavior when the camera is at directionX +- 45 degrees. const float f = 0.00001F; - if (angle <= M_PI_4 + f) { + if (angle <= pi/4 + f) { angle = 0; } - else if (angle <= 3 * M_PI_4 + f) { - angle = M_PI_2; + else if (angle <= 3 * pi/4 + f) { + angle = pi/2; } - else if (angle < M_PI + M_PI_4 - f) { - angle = M_PI; + else if (angle < pi + pi/4 - f) { + angle = pi; } - else if (angle < M_PI + 3 * M_PI_4 - f) { - angle = M_PI + M_PI_2; + else if (angle < pi + 3 * pi/4 - f) { + angle = pi + pi/2; } else { angle = 0; @@ -3960,7 +3934,7 @@ void View3DInventorViewer::drawAxisCross() const float NEARVAL = 0.1F; const float FARVAL = 10.0F; - const float dim = NEARVAL * float(tan(M_PI / 8.0)); // FOV is 45 deg (45/360 = 1/8) + const float dim = NEARVAL * float(tan(std::numbers::pi / 8.0)); // FOV is 45 deg (45/360 = 1/8) glFrustum(-dim, dim, -dim, dim, NEARVAL, FARVAL); diff --git a/src/Gui/View3DPy.cpp b/src/Gui/View3DPy.cpp index eb8b946289..dab66a874b 100644 --- a/src/Gui/View3DPy.cpp +++ b/src/Gui/View3DPy.cpp @@ -553,7 +553,7 @@ Py::Object View3DInventorPy::viewDefaultOrientation(const Py::Tuple& args) { char* view = nullptr; double scale = -1.0; - if (!PyArg_ParseTuple(args.ptr(), "|sd", &view, &scale)) + if (!PyArg_ParseTuple(args.ptr(), "|zd", &view, &scale)) throw Py::Exception(); try { @@ -658,7 +658,7 @@ Py::Object View3DInventorPy::viewRotateLeft() SbRotation rot = cam->orientation.getValue(); SbVec3f vdir(0, 0, -1); rot.multVec(vdir, vdir); - SbRotation nrot(vdir, (float)M_PI/2); + SbRotation nrot(vdir, (float)std::numbers::pi/2); cam->orientation.setValue(rot*nrot); } catch (const Base::Exception& e) { @@ -681,7 +681,7 @@ Py::Object View3DInventorPy::viewRotateRight() SbRotation rot = cam->orientation.getValue(); SbVec3f vdir(0, 0, -1); rot.multVec(vdir, vdir); - SbRotation nrot(vdir, (float)-M_PI/2); + SbRotation nrot(vdir, (float)-std::numbers::pi/2); cam->orientation.setValue(rot*nrot); } catch (const Base::Exception& e) { diff --git a/src/Gui/View3DSettings.cpp b/src/Gui/View3DSettings.cpp index b625158f0d..9bd0669627 100644 --- a/src/Gui/View3DSettings.cpp +++ b/src/Gui/View3DSettings.cpp @@ -45,22 +45,27 @@ using namespace Gui; View3DSettings::View3DSettings(ParameterGrp::handle hGrp, View3DInventorViewer* view) : hGrp(hGrp) + , hLightSourcesGrp(hGrp->GetGroup("LightSources")) , _viewers{view} { hGrp->Attach(this); + hLightSourcesGrp->Attach(this); } View3DSettings::View3DSettings(ParameterGrp::handle hGrp, const std::vector& view) : hGrp(hGrp) + , hLightSourcesGrp(hGrp->GetGroup("LightSources")) , _viewers(view) { hGrp->Attach(this); + hLightSourcesGrp->Attach(this); } View3DSettings::~View3DSettings() { hGrp->Detach(this); + hLightSourcesGrp->Detach(this); } int View3DSettings::stopAnimatingIfDeactivated() const diff --git a/src/Gui/View3DSettings.h b/src/Gui/View3DSettings.h index 0bc7f3af6d..ccef176218 100644 --- a/src/Gui/View3DSettings.h +++ b/src/Gui/View3DSettings.h @@ -54,6 +54,8 @@ public: private: ParameterGrp::handle hGrp; + ParameterGrp::handle hLightSourcesGrp; + std::vector _viewers; }; diff --git a/src/Gui/ViewProvider.pyi b/src/Gui/ViewProvider.pyi new file mode 100644 index 0000000000..5d651e9893 --- /dev/null +++ b/src/Gui/ViewProvider.pyi @@ -0,0 +1,369 @@ +from Base.Metadata import constmethod +from Base.BoundBox import BoundBox +from App.ExtensionContainer import ExtensionContainer +from typing import Any, Final, List, Optional + + +class ViewProvider(ExtensionContainer): + """ + This is the ViewProvider base class + + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + def addProperty( + self, + type: str, + name: str, + group: str, + doc: str, + attr: int = 0, + ro: bool = False, + hd: bool = False, + ) -> "ViewProvider": + """ + addProperty(type, name, group, doc, attr=0, ro=False, hd=False) -> ViewProvider + + Add a generic property. + + type : str + Property type. + name : str + Property name. Optional. + group : str + Property group. Optional. + attr : int + Property attributes. + ro : bool + Read only property. + hd : bool + Hidden property. + """ + ... + + def removeProperty(self, name: str) -> bool: + """ + removeProperty(name) -> bool + + Remove a generic property. + Only user-defined properties can be removed, not built-in ones. + + name : str + Property name. + """ + ... + + def supportedProperties(self) -> list: + """ + supportedProperties() -> list + + A list of supported property types. + """ + ... + + def show(self) -> None: + """ + show() -> None + + Show the object. + """ + ... + + def hide(self) -> None: + """ + hide() -> None + + Hide the object. + """ + ... + + def isVisible(self) -> bool: + """ + isVisible() -> bool + + Check if the object is visible. + """ + ... + + def canDragObject(self, obj: Any = None) -> bool: + """ + canDragObject(obj=None) -> bool + + Check whether the child object can be removed by dragging. + If 'obj' is not given, check without filter by any particular object. + + obj : App.DocumentObject + Object to be dragged. + """ + ... + + def dragObject(self, obj: Any) -> None: + """ + dragObject(obj) -> None + + Remove a child object by dropping. + + obj : App.DocumentObject + Object to be dragged. + """ + ... + + def canDropObject( + self, + *, + obj: Any = None, + owner: Any = None, + subname: str, + elem: Optional[List[str]] = None + ) -> bool: + """ + canDropObject(obj=None, owner=None, subname, elem=None) -> bool + + Check whether the child object can be added by dropping. + If 'obj' is not given, check without filter by any particular object. + + obj : App.DocumentObject + Object to be dropped. + owner : App.DocumentObject + Parent object of the dropping object. + subname : str + Subname reference to the dropping object. Optional. + elem : sequence of str + Non-objects subelements selected when the object is + being dropped. + """ + ... + + def dropObject( + self, + *, + obj: Any, + owner: Any = None, + subname: str, + elem: Optional[List[str]] = None + ) -> str: + """ + dropObject(obj, owner=None, subname, elem=None) -> str + + Add a child object by dropping. + + obj : App.DocumentObject + Object to be dropped. + owner : App.DocumentObject + Parent object of the dropping object. + subname : str + Subname reference to the dropping object. Optional. + elem : sequence of str + Non-objects subelements selected when the object is + being dropped. + """ + ... + + def canDragAndDropObject(self, obj: Any) -> bool: + """ + canDragAndDropObject(obj) -> bool + + Check whether the child object can be removed from + other parent and added here by drag and drop. + + obj : App.DocumentObject + Object to be dragged and dropped. + """ + ... + + def replaceObject(self, oldObj: Any, newObj: Any) -> int: + """ + replaceObject(oldObj, newObj) -> int + + Replace a child object. + Returns 1 if succeeded, 0 if not found, -1 if not supported. + + oldObj : App.DocumentObject + Old object. + newObj : App.DocumentObject + New object. + """ + ... + + def doubleClicked(self) -> bool: + """ + doubleClicked() -> bool + + Trigger double clicking the corresponding tree item of this view object. + """ + ... + + def addDisplayMode(self, obj: Any, mode: str) -> None: + """ + addDisplayMode(obj, mode) -> None + + Add a new display mode to the view provider. + + obj : coin.SoNode + Display mode. + mode : str + Name of the display mode. + """ + ... + + def listDisplayModes(self) -> list: + """ + listDisplayModes() -> list + + Show a list of all display modes. + """ + ... + + def toString(self) -> str: + """ + toString() -> str + + Return a string representation of the Inventor node. + """ + ... + + def setTransformation(self, trans: Any) -> None: + """ + setTransformation(trans) -> None + + Set a transformation on the Inventor node. + + trans : Base.Placement, Base.Matrix + """ + ... + + @constmethod + def claimChildren(self) -> list: + """ + claimChildren() -> list + + Returns list of objects that are to be grouped in tree under this object. + """ + ... + + @constmethod + def claimChildrenRecursive(self) -> list: + """ + claimChildrenRecursive() -> list + + Returns list of objects that are to be grouped in tree under this object recursively. + """ + ... + + def partialRender(self, sub: Any = None, clear: bool = False) -> int: + """ + partialRender(sub=None, clear=False) -> int + + Render only part of the object. + + sub: None, str, sequence of str + Refer to the subelement. If it is None then reset the partial rendering. + clear: bool + True to add, or False to remove the subelement(s) for rendering. + """ + ... + + def getElementColors(self, elementName: Optional[str] = None) -> dict: + """ + getElementColors(elementName) -> dict + + Get a dictionary of the form {elementName : (r,g,b,a)}. + If no element name is given a dictionary with all the elements is returned. + + elementName : str + Name of the element. Optional. + """ + ... + + def setElementColors(self, colors: dict) -> None: + """ + setElementColors(colors) -> None + + Set element colors. + + colors: dict + Color dictionary of the form {elementName:(r,g,b,a)}. + """ + ... + + @constmethod + def getElementPicked(self, pickPoint: Any) -> str: + """ + getElementPicked(pickPoint) -> str + + Return the picked subelement. + + pickPoint : coin.SoPickedPoint + """ + ... + + @constmethod + def getDetailPath(self, subelement: str, path: Any, append: bool = True) -> Any: + """ + getDetailPath(subelement, path, append=True) -> coin.SoDetail or None + + Return Coin detail and path of an subelement. + + subname: str + Dot separated string reference to the sub element. + pPath: coin.SoPath + Output coin path leading to the returned element detail. + append: bool + If True, path will be first appended with the root node and the mode + switch node of this view provider. + """ + ... + + @constmethod + def signalChangeIcon(self) -> None: + """ + signalChangeIcon() -> None + + Trigger icon changed signal. + """ + ... + + def getBoundingBox( + self, subName: Optional[str] = None, transform: bool = True, view: Any = None + ) -> BoundBox: + """ + getBoundingBox(subName, transform=True, view) -> Base.BoundBox + + Obtain the bounding box of this view object. + + subName : str + Name referring a sub-object. Optional. + transform: bool + Whether to apply the transformation matrix of this view provider. + view: View3DInventorPy + Default to active view. Optional. + """ + ... + + Annotation: Any = ... + """A pivy Separator to add a custom scenegraph to this ViewProvider.""" + + Icon: Final[Any] = ... + """The icon of this ViewProvider.""" + + RootNode: Any = ... + """A pivy Separator with the root of this ViewProvider.""" + + SwitchNode: Any = ... + """A pivy SoSwitch for the display mode switch of this ViewProvider.""" + + DefaultMode: int = 0 + """Get/Set the default display mode in turns of coin node index.""" + + IV: Final[str] = "" + """Represents the whole ViewProvider as an Inventor string.""" + + CanRemoveChildrenFromRoot: Final[bool] = False + """Tells the tree view whether to remove the children item from root or not.""" + + LinkVisibility: bool = False + """Get/set visibilities of all links to this view object.""" + + DropPrefix: Final[str] = "" + """Subname referencing the sub-object for holding dropped object.""" diff --git a/src/Gui/ViewProviderAnnotation.cpp b/src/Gui/ViewProviderAnnotation.cpp index c1cb67e3ce..5e110f90c6 100644 --- a/src/Gui/ViewProviderAnnotation.cpp +++ b/src/Gui/ViewProviderAnnotation.cpp @@ -155,7 +155,7 @@ void ViewProviderAnnotation::onChanged(const App::Property* prop) } } else if (prop == &Rotation) { - pRotationXYZ->angle = (Rotation.getValue()/360)*(2*M_PI); + pRotationXYZ->angle = (Rotation.getValue()/360)*(2*std::numbers::pi); } else { ViewProviderDocumentObject::onChanged(prop); @@ -248,15 +248,8 @@ void ViewProviderAnnotation::updateData(const App::Property* prop) const char* cs = line.c_str(); if (line.empty()) cs = " "; // empty lines make coin crash, we use a space instead -#if (COIN_MAJOR_VERSION <= 3) - QByteArray latin1str; - latin1str = (QString::fromUtf8(cs)).toLatin1(); - pLabel->string.set1Value(index, SbString(latin1str.constData())); - pLabel3d->string.set1Value(index, SbString(latin1str.constData())); -#else pLabel->string.set1Value(index, SbString(cs)); pLabel3d->string.set1Value(index, SbString(cs)); -#endif index++; } } diff --git a/src/Gui/ViewProviderDocumentObject.pyi b/src/Gui/ViewProviderDocumentObject.pyi new file mode 100644 index 0000000000..2057c2bd14 --- /dev/null +++ b/src/Gui/ViewProviderDocumentObject.pyi @@ -0,0 +1,26 @@ +from ViewProvider import ViewProvider +from typing import Any, Final + + +class ViewProviderDocumentObject(ViewProvider): + """ + This is the ViewProvider base class + + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + def update(self) -> None: + """ + Update the view representation of the object + """ + ... + + Object: Any = ... + """Set/Get the associated data object""" + + ForceUpdate: bool = False + """Reference count to force update visual""" + + Document: Final[Any] = ... + """Return the document the view provider is part of""" diff --git a/src/Gui/ViewProviderDocumentObjectPy.xml b/src/Gui/ViewProviderDocumentObjectPy.xml deleted file mode 100644 index c2fa0dc08d..0000000000 --- a/src/Gui/ViewProviderDocumentObjectPy.xml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - This is the ViewProvider base class - - - - Update the view representation of the object - - - - - Set/Get the associated data object - - - - - - Reference count to force update visual - - - - - - Return the document the view provider is part of - - - - - diff --git a/src/Gui/ViewProviderExtension.pyi b/src/Gui/ViewProviderExtension.pyi new file mode 100644 index 0000000000..66df2b2695 --- /dev/null +++ b/src/Gui/ViewProviderExtension.pyi @@ -0,0 +1,24 @@ +from Base.Metadata import constmethod +from App.Extension import Extension + + +class ViewProviderExtension(Extension): + """ + Base class for all view provider extensions + + Author: Werner Mayer (wmayer[at]users.sourceforge.net) + Licence: LGPL + """ + + def setIgnoreOverlayIcon(self) -> None: + """ + Ignore the overlay icon of an extension + """ + ... + + @constmethod + def ignoreOverlayIcon(self) -> None: + """ + Ignore the overlay icon of an extension + """ + ... diff --git a/src/Gui/ViewProviderExtensionPy.xml b/src/Gui/ViewProviderExtensionPy.xml deleted file mode 100644 index f5eb37fc61..0000000000 --- a/src/Gui/ViewProviderExtensionPy.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - Base class for all view provider extensions - - - - Ignore the overlay icon of an extension - - - - - Ignore the overlay icon of an extension - - - - - diff --git a/src/Gui/ViewProviderGeometryObject.pyi b/src/Gui/ViewProviderGeometryObject.pyi new file mode 100644 index 0000000000..6034c942ff --- /dev/null +++ b/src/Gui/ViewProviderGeometryObject.pyi @@ -0,0 +1,21 @@ +from Base.Metadata import no_args +from ViewProviderDocumentObject import ViewProviderDocumentObject + + +class ViewProviderGeometryObject(ViewProviderDocumentObject): + """ + This is the ViewProvider geometry class + + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + @staticmethod + @no_args + def getUserDefinedMaterial() -> object: + """ + getUserDefinedMaterial() -> object + + Get a material object with the user-defined colors. + """ + ... diff --git a/src/Gui/ViewProviderGeometryObjectPy.xml b/src/Gui/ViewProviderGeometryObjectPy.xml deleted file mode 100644 index a33a742568..0000000000 --- a/src/Gui/ViewProviderGeometryObjectPy.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - This is the ViewProvider geometry class - - - - getUserDefinedMaterial() -> object - -Get a material object with the user-defined colors. - - - - - diff --git a/src/Gui/ViewProviderLink.cpp b/src/Gui/ViewProviderLink.cpp index a706205798..9338ff37ad 100644 --- a/src/Gui/ViewProviderLink.cpp +++ b/src/Gui/ViewProviderLink.cpp @@ -1534,8 +1534,9 @@ bool LinkView::linkGetDetailPath(const char *subname, SoFullPath *path, SoDetail return true; if(info.isLinked()) { - info.linkInfo->getDetail(false,childType,subname,det,path); - return true; + if (info.linkInfo->getDetail(false,childType,subname,det,path)) { + return true; + } } } if(isLinked()) { @@ -2389,7 +2390,7 @@ bool ViewProviderLink::onDelete(const std::vector &) { } auto link = getObject(); - if (link->ElementCount.getValue() != 0) { + if (link && link->ElementCount.getValue() != 0) { auto doc = link->getDocument(); auto elements = link->ElementList.getValues(); for (auto element : elements) { diff --git a/src/Gui/ViewProviderLink.pyi b/src/Gui/ViewProviderLink.pyi new file mode 100644 index 0000000000..b9a3ebf84a --- /dev/null +++ b/src/Gui/ViewProviderLink.pyi @@ -0,0 +1,17 @@ +from ViewProviderDocumentObject import ViewProviderDocumentObject +from typing import Any, Final + + +class ViewProviderLink(ViewProviderDocumentObject): + """ + This is the ViewProviderLink class + + Author: Zheng, Lei (realthunder.dev@gmail.com) + Licence: LGPL + """ + + DraggingPlacement: Any = ... + """Get/set dragger placement during dragging""" + + LinkView: Final[Any] = ... + """Get the associated LinkView object""" diff --git a/src/Gui/ViewProviderLinkPy.xml b/src/Gui/ViewProviderLinkPy.xml deleted file mode 100644 index 09641c2cf8..0000000000 --- a/src/Gui/ViewProviderLinkPy.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - This is the ViewProviderLink class - - - - Get/set dragger placement during dragging - - - - - - Get the associated LinkView object - - - - - diff --git a/src/Gui/ViewProviderPy.xml b/src/Gui/ViewProviderPy.xml deleted file mode 100644 index ad5c5c6365..0000000000 --- a/src/Gui/ViewProviderPy.xml +++ /dev/null @@ -1,344 +0,0 @@ - - - - - - This is the ViewProvider base class - - - - addProperty(type, name, group, doc, attr=0, ro=False, hd=False) -> ViewProvider - -Add a generic property. - -type : str - Property type. -name : str - Property name. Optional. -group : str - Property group. Optional. -attr : int - Property attributes. -ro : bool - Read only property. -hd : bool - Hidden property. - - - - - removeProperty(name) -> bool - -Remove a generic property. -Only user-defined properties can be removed, not built-in ones. - -name : str - Property name. - - - - - supportedProperties() -> list - -A list of supported property types. - - - - - show() -> None - -Show the object. - - - - - hide() -> None - -Hide the object. - - - - - isVisible() -> bool - -Check if the object is visible. - - - - - canDragObject(obj=None) -> bool - -Check whether the child object can be removed by dragging. -If 'obj' is not given, check without filter by any particular object. - -obj : App.DocumentObject - Object to be dragged. - - - - - dragObject(obj) -> None - -Remove a child object by dropping. - -obj : App.DocumentObject - Object to be dragged. - - - - - canDropObject(obj=None, owner=None, subname, elem=None) -> bool - -Check whether the child object can be added by dropping. -If 'obj' is not given, check without filter by any particular object. - -obj : App.DocumentObject - Object to be dropped. -owner : App.DocumentObject - Parent object of the dropping object. -subname : str - Subname reference to the dropping object. Optional. -elem : sequence of str - Non-objects subelements selected when the object is - being dropped. - - - - - dropObject(obj, owner=None, subname, elem=None) -> str - -Add a child object by dropping. - -obj : App.DocumentObject - Object to be dropped. -owner : App.DocumentObject - Parent object of the dropping object. -subname : str - Subname reference to the dropping object. Optional. -elem : sequence of str - Non-objects subelements selected when the object is - being dropped. - - - - - canDragAndDropObject(obj) -> bool - -Check whether the child object can be removed from -other parent and added here by drag and drop. - -obj : App.DocumentObject - Object to be dragged and dropped. - - - - - replaceObject(oldObj, newObj) -> int - -Replace a child object. -Returns 1 if succeeded, 0 if not found, -1 if not supported. - -oldObj : App.DocumentObject - Old object. -newObj : App.DocumentObject - New object. - - - - - doubleClicked() -> bool - -Trigger double clicking the corresponding tree item of this view object. - - - - - addDisplayMode(obj, mode) -> None - -Add a new display mode to the view provider. - -obj : coin.SoNode - Display mode. -mode : str - Name of the display mode. - - - - - listDisplayModes() -> list - -Show a list of all display modes. - - - - - toString() -> str - -Return a string representation of the Inventor node. - - - - - setTransformation(trans) -> None - -Set a transformation on the Inventor node. - -trans : Base.Placement, Base.Matrix - - - - - claimChildren() -> list - -Returns list of objects that are to be grouped in tree under this object. - - - - - claimChildrenRecursive() -> list - -Returns list of objects that are to be grouped in tree under this object recursively. - - - - - partialRender(sub=None, clear=False) -> int - -Render only part of the object. - -sub: None, str, sequence of str - Refer to the subelement. If it is None then reset the partial rendering. -clear: bool - True to add, or False to remove the subelement(s) for rendering. - - - - - getElementColors(elementName) -> dict - -Get a dictionary of the form {elementName : (r,g,b,a)}. -If no element name is given a dictionary with all the elements is returned. - -elementName : str - Name of the element. Optional. - - - - - setElementColors(colors) -> None - -Set element colors. - -colors: dict - Color dictionary of the form {elementName:(r,g,b,a)}. - - - - - getElementPicked(pickPoint) -> str - -Return the picked subelement. - -pickPoint : coin.SoPickedPoint - - - - - getDetailPath(subelement, path, append=True) -> coin.SoDetail or None - -Return Coin detail and path of an subelement. - -subname: str - Dot separated string reference to the sub element. -pPath: coin.SoPath - Output coin path leading to the returned element detail. -append: bool - If True, path will be first appended with the root node and the mode - switch node of this view provider. - - - - - signalChangeIcon() -> None - -Trigger icon changed signal. - - - - - getBoundingBox(subName, transform=True, view) -> Base.BoundBox - -Obtain the bounding box of this view object. - -subName : str - Name referring a sub-object. Optional. -transform: bool - Whether to apply the transformation matrix of this view provider. -view: View3DInventorPy - Default to active view. Optional. - - - - - A pivy Separator to add a custom scenegraph to this ViewProvider. - - - - - - The icon of this ViewProvider. - - - - - - A pivy Separator with the root of this ViewProvider. - - - - - - A pivy SoSwitch for the display mode switch of this ViewProvider. - - - - - - Get/Set the default display mode in turns of coin node index. - - - - - - Represents the whole ViewProvider as an Inventor string. - - - - - - Tells the tree view whether to remove the children item from root or not. - - - - - - Get/set visibilities of all links to this view object. - - - - - - Subname referencing the sub-object for holding dropped object. - - - - - diff --git a/src/Gui/Workbench.pyi b/src/Gui/Workbench.pyi new file mode 100644 index 0000000000..0c88fb61d8 --- /dev/null +++ b/src/Gui/Workbench.pyi @@ -0,0 +1,58 @@ +from Base.Metadata import export +from Base.BaseClass import BaseClass +from typing import Any, List, Dict + + +@export( + Include="Gui/Workbench.h", +) +class Workbench(BaseClass): + """ + This is the base class for workbenches + + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + def name(self) -> str: + """ + Return the workbench name + """ + ... + + def activate(self) -> None: + """ + Activate this workbench + """ + ... + + def listToolbars(self) -> List[Any]: + """ + Show a list of all toolbars + """ + ... + + def getToolbarItems(self) -> Dict[Any, Any]: + """ + Show a dict of all toolbars and their commands + """ + ... + + def listCommandbars(self) -> List[Any]: + """ + Show a list of all command bars + """ + ... + + def listMenus(self) -> List[Any]: + """ + Show a list of all menus + """ + ... + + @staticmethod + def reloadActive() -> None: + """ + Reload the active workbench after changing menus or toolbars + """ + ... diff --git a/src/Gui/WorkbenchPy.xml b/src/Gui/WorkbenchPy.xml deleted file mode 100644 index af5b8d9285..0000000000 --- a/src/Gui/WorkbenchPy.xml +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - This is the base class for workbenches - - - - Return the workbench name - - - - - Activate this workbench - - - - - Show a list of all toolbars - - - - - Show a dict of all toolbars and their commands - - - - - Show a list of all command bars - - - - - Show a list of all menus - - - - - Reload the active workbench after changing menus or toolbars - - - - - diff --git a/src/Gui/propertyeditor/PropertyEditor.h b/src/Gui/propertyeditor/PropertyEditor.h index 40fb89482e..4badc264ac 100644 --- a/src/Gui/propertyeditor/PropertyEditor.h +++ b/src/Gui/propertyeditor/PropertyEditor.h @@ -60,7 +60,7 @@ class PropertyModel; See also: https://man42.net/blog/2011/09/qt-4-7-modify-a-custom-q_property-with-a-qt-style-sheet/ */ -class PropertyEditor: public QTreeView +class GuiExport PropertyEditor: public QTreeView { // clang-format off Q_OBJECT diff --git a/src/Gui/propertyeditor/PropertyItem.cpp b/src/Gui/propertyeditor/PropertyItem.cpp index e07d3fdf20..94bbd659f7 100644 --- a/src/Gui/propertyeditor/PropertyItem.cpp +++ b/src/Gui/propertyeditor/PropertyItem.cpp @@ -968,7 +968,8 @@ QWidget* PropertyIntegerItem::createEditor(QWidget* parent, void PropertyIntegerItem::setEditorData(QWidget* editor, const QVariant& data) const { auto sb = qobject_cast(editor); - sb->setRange(INT_MIN, INT_MAX); + sb->setRange(std::numeric_limits::min(), + std::numeric_limits::max()); sb->setValue(data.toInt()); } @@ -1128,7 +1129,8 @@ QWidget* PropertyFloatItem::createEditor(QWidget* parent, const std::function(editor); - sb->setRange((double)INT_MIN, (double)INT_MAX); + sb->setRange(static_cast(std::numeric_limits::min()), + static_cast(std::numeric_limits::max())); sb->setValue(data.toDouble()); } diff --git a/src/Gui/propertyeditor/PropertyItem.h b/src/Gui/propertyeditor/PropertyItem.h index c27300cadb..b8182c4fd8 100644 --- a/src/Gui/propertyeditor/PropertyItem.h +++ b/src/Gui/propertyeditor/PropertyItem.h @@ -360,8 +360,8 @@ protected: PropertyIntegerConstraintItem(); private: - int min = INT_MIN; - int max = INT_MAX; + int min = std::numeric_limits::min(); + int max = std::numeric_limits::max(); int steps = 1; }; @@ -434,8 +434,8 @@ protected: PropertyUnitConstraintItem(); private: - double min = double(INT_MIN); - double max = double(INT_MAX); + double min = static_cast(std::numeric_limits::min()); + double max = static_cast(std::numeric_limits::max()); double steps = 0.1; }; @@ -472,8 +472,8 @@ protected: PropertyFloatConstraintItem(); private: - double min = double(INT_MIN); - double max = double(INT_MAX); + double min = static_cast(std::numeric_limits::min()); + double max = static_cast(std::numeric_limits::max()); double steps = 0.1; }; diff --git a/src/Gui/propertyeditor/PropertyModel.cpp b/src/Gui/propertyeditor/PropertyModel.cpp index bb5e565f8d..aa93d2563a 100644 --- a/src/Gui/propertyeditor/PropertyModel.cpp +++ b/src/Gui/propertyeditor/PropertyModel.cpp @@ -96,7 +96,7 @@ bool PropertyModel::setData(const QModelIndex& index, const QVariant& value, int // now? double d = data.toDouble(); double v = value.toDouble(); - if (fabs(d - v) > DBL_EPSILON) { + if (fabs(d - v) > std::numeric_limits::epsilon()) { return item->setData(value); } } diff --git a/src/Gui/propertyeditor/PropertyModel.h b/src/Gui/propertyeditor/PropertyModel.h index 34c4620312..0a6288f3c9 100644 --- a/src/Gui/propertyeditor/PropertyModel.h +++ b/src/Gui/propertyeditor/PropertyModel.h @@ -38,7 +38,7 @@ class Property; namespace Gui { namespace PropertyEditor { -class PropertyModel : public QAbstractItemModel +class GuiExport PropertyModel : public QAbstractItemModel { Q_OBJECT diff --git a/src/Main/MainCmd.cpp b/src/Main/MainCmd.cpp index 8dba8a5ef2..01ffa7bd78 100644 --- a/src/Main/MainCmd.cpp +++ b/src/Main/MainCmd.cpp @@ -100,7 +100,7 @@ int main(int argc, char** argv) msg << "While initializing " << appName << " the following exception occurred: '" << e.what() << "'\n\n"; msg << "Python is searching for its runtime files in the following directories:\n" - << Py_EncodeLocale(Py_GetPath(), nullptr) << "\n\n"; + << Base::Interpreter().getPythonPath() << "\n\n"; msg << "Python version information:\n" << Py_GetVersion() << "\n"; const char* pythonhome = getenv("PYTHONHOME"); if (pythonhome) { diff --git a/src/Main/MainGui.cpp b/src/Main/MainGui.cpp index bcb07ffd19..1066298cab 100644 --- a/src/Main/MainGui.cpp +++ b/src/Main/MainGui.cpp @@ -266,7 +266,7 @@ int main(int argc, char** argv) "Python version information:\n%4\n") .arg(appName, QString::fromUtf8(e.what()), - QString::fromUtf8(Py_EncodeLocale(Py_GetPath(), nullptr)), + QString::fromStdString(Base::Interpreter().getPythonPath()), QString::fromLatin1(Py_GetVersion())); const char* pythonhome = getenv("PYTHONHOME"); if (pythonhome) { diff --git a/src/Mod/AddonManager/AddonCatalog.py b/src/Mod/AddonManager/AddonCatalog.py new file mode 100644 index 0000000000..9140c48e94 --- /dev/null +++ b/src/Mod/AddonManager/AddonCatalog.py @@ -0,0 +1,143 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# *************************************************************************** +# * * +# * Copyright (c) 2025 The FreeCAD project association AISBL * +# * * +# * This file is part of FreeCAD. * +# * * +# * FreeCAD is free software: you can redistribute it and/or modify it * +# * under the terms of the GNU Lesser General Public License as * +# * published by the Free Software Foundation, either version 2.1 of the * +# * License, or (at your option) any later version. * +# * * +# * FreeCAD 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 * +# * Lesser General Public License for more details. * +# * * +# * You should have received a copy of the GNU Lesser General Public * +# * License along with FreeCAD. If not, see * +# * . * +# * * +# *************************************************************************** + +"""The Addon Catalog is the main list of all Addons along with their various +sources and compatible versions. Added in FreeCAD 1.1 to replace .gitmodules.""" + +from dataclasses import dataclass +from hashlib import sha256 +from typing import Any, Dict, List, Optional, Tuple +from addonmanager_metadata import Version +from Addon import Addon + +import addonmanager_freecad_interface as fci + + +@dataclass +class AddonCatalogEntry: + """Each individual entry in the catalog, storing data about a particular version of an + Addon. Note that this class needs to be identical to the one that is used in the remote cache + generation, so don't make changes here without ensuring that the classes are synchronized.""" + + freecad_min: Optional[Version] = None + freecad_max: Optional[Version] = None + repository: Optional[str] = None + git_ref: Optional[str] = None + zip_url: Optional[str] = None + note: Optional[str] = None + branch_display_name: Optional[str] = None + + def __init__(self, raw_data: Dict[str, str]) -> None: + """Create an AddonDictionaryEntry from the raw JSON data""" + super().__init__() + for key, value in raw_data.items(): + if hasattr(self, key): + if key in ("freecad_min", "freecad_max"): + value = Version(from_string=value) + setattr(self, key, value) + + def is_compatible(self) -> bool: + """Check whether this AddonCatalogEntry is compatible with the current version of FreeCAD""" + if self.freecad_min is None and self.freecad_max is None: + return True + current_version = Version(from_list=fci.Version()) + if self.freecad_min is None: + return current_version <= self.freecad_max + if self.freecad_max is None: + return current_version >= self.freecad_min + return self.freecad_min <= current_version <= self.freecad_max + + def unique_identifier(self) -> str: + """Return a unique identifier of the AddonCatalogEntry, guaranteed to be repeatable: when + given the same basic information, the same ID is created. Used as the key when storing + the metadata for a given AddonCatalogEntry.""" + sha256_hash = sha256() + sha256_hash.update(str(self).encode("utf-8")) + return sha256_hash.hexdigest() + + +class AddonCatalog: + """A catalog of addons grouped together into sets representing versions that are + compatible with different versions of FreeCAD and/or represent different available branches + of a given addon (e.g. a Development branch that users are presented).""" + + def __init__(self, data: Dict[str, Any]): + self._original_data = data + self._dictionary = {} + self._parse_raw_data() + + def _parse_raw_data(self): + self._dictionary = {} # Clear pre-existing contents + for key, value in self._original_data.items(): + if key == "_meta": # Don't add the documentation object to the tree + continue + self._dictionary[key] = [] + for entry in value: + self._dictionary[key].append(AddonCatalogEntry(entry)) + + def load_metadata_cache(self, cache: Dict[str, Any]): + """Given the raw dictionary, couple that with the remote metadata cache to create the + final working addon dictionary. Only create Addons that are compatible with the current + version of FreeCAD.""" + for value in self._dictionary.values(): + for entry in value: + sha256_hash = entry.unique_identifier() + print(sha256_hash) + if sha256_hash in cache and entry.is_compatible(): + entry.addon = Addon.from_cache(cache[sha256_hash]) + + def get_available_addon_ids(self) -> List[str]: + """Get a list of IDs that have at least one entry compatible with the current version of + FreeCAD""" + id_list = [] + for key, value in self._dictionary.items(): + for entry in value: + if entry.is_compatible(): + id_list.append(key) + break + return id_list + + def get_available_branches(self, addon_id: str) -> List[Tuple[str, str]]: + """For a given ID, get the list of available branches compatible with this version of + FreeCAD along with the branch display name. Either field may be empty, but not both. The + first entry in the list is expected to be the "primary".""" + if addon_id not in self._dictionary: + return [] + result = [] + for entry in self._dictionary[addon_id]: + if entry.is_compatible(): + result.append((entry.git_ref, entry.branch_display_name)) + return result + + def get_addon_from_id(self, addon_id: str, branch: Optional[Tuple[str, str]] = None) -> Addon: + """Get the instantiated Addon object for the given ID and optionally branch. If no + branch is provided, whichever branch is the "primary" branch will be returned (i.e. the + first branch that matches). Raises a ValueError if no addon matches the request.""" + if addon_id not in self._dictionary: + raise ValueError(f"Addon '{addon_id}' not found") + for entry in self._dictionary[addon_id]: + if not entry.is_compatible(): + continue + if not branch or entry.branch_display_name == branch: + return entry.addon + raise ValueError(f"Addon '{addon_id}' has no compatible branches named '{branch}'") diff --git a/src/Mod/AddonManager/AddonManagerTest/app/test_addoncatalog.py b/src/Mod/AddonManager/AddonManagerTest/app/test_addoncatalog.py new file mode 100644 index 0000000000..5b058c53c2 --- /dev/null +++ b/src/Mod/AddonManager/AddonManagerTest/app/test_addoncatalog.py @@ -0,0 +1,213 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +# pylint: disable=global-at-module-level,global-statement,import-outside-toplevel, + +"""Tests for the AddonCatalog and AddonCatalogEntry classes.""" + +import unittest +from unittest import mock +from unittest.mock import patch + +global AddonCatalogEntry +global AddonCatalog +global Version + + +class TestAddonCatalogEntry(unittest.TestCase): + """Tests for the AddonCatalogEntry class.""" + + def setUp(self): + """Start mock for addonmanager_licenses class.""" + global AddonCatalogEntry + global AddonCatalog + global Version + self.addon_patch = mock.patch.dict("sys.modules", {"addonmanager_licenses": mock.Mock()}) + self.mock_addon_module = self.addon_patch.start() + from AddonCatalog import AddonCatalogEntry, AddonCatalog + from addonmanager_metadata import Version + + def tearDown(self): + """Stop patching the addonmanager_licenses class""" + self.addon_patch.stop() + + def test_version_match_without_restrictions(self): + """Given an AddonCatalogEntry that has no version restrictions, a fixed version matches.""" + with patch("addonmanager_freecad_interface.Version") as mock_freecad: + mock_freecad.Version = lambda: (1, 2, 3, "dev") + ac = AddonCatalogEntry({}) + self.assertTrue(ac.is_compatible()) + + def test_version_match_with_min_no_max_good_match(self): + """Given an AddonCatalogEntry with a minimum FreeCAD version, a version smaller than that + does not match.""" + with patch("addonmanager_freecad_interface.Version", return_value=(1, 2, 3, "dev")): + ac = AddonCatalogEntry({"freecad_min": Version(from_string="1.2")}) + self.assertTrue(ac.is_compatible()) + + def test_version_match_with_max_no_min_good_match(self): + """Given an AddonCatalogEntry with a maximum FreeCAD version, a version larger than that + does not match.""" + with patch("addonmanager_freecad_interface.Version", return_value=(1, 2, 3, "dev")): + ac = AddonCatalogEntry({"freecad_max": Version(from_string="1.3")}) + self.assertTrue(ac.is_compatible()) + + def test_version_match_with_min_and_max_good_match(self): + """Given an AddonCatalogEntry with both a minimum and maximum FreeCAD version, a version + between the two matches.""" + with patch("addonmanager_freecad_interface.Version", return_value=(1, 2, 3, "dev")): + ac = AddonCatalogEntry( + { + "freecad_min": Version(from_string="1.1"), + "freecad_max": Version(from_string="1.3"), + } + ) + self.assertTrue(ac.is_compatible()) + + def test_version_match_with_min_and_max_bad_match_high(self): + """Given an AddonCatalogEntry with both a minimum and maximum FreeCAD version, a version + higher than the maximum does not match.""" + with patch("addonmanager_freecad_interface.Version", return_value=(1, 3, 3, "dev")): + ac = AddonCatalogEntry( + { + "freecad_min": Version(from_string="1.1"), + "freecad_max": Version(from_string="1.3"), + } + ) + self.assertFalse(ac.is_compatible()) + + def test_version_match_with_min_and_max_bad_match_low(self): + """Given an AddonCatalogEntry with both a minimum and maximum FreeCAD version, a version + lower than the minimum does not match.""" + with patch("addonmanager_freecad_interface.Version", return_value=(1, 0, 3, "dev")): + ac = AddonCatalogEntry( + { + "freecad_min": Version(from_string="1.1"), + "freecad_max": Version(from_string="1.3"), + } + ) + self.assertFalse(ac.is_compatible()) + + +class TestAddonCatalog(unittest.TestCase): + """Tests for the AddonCatalog class.""" + + def setUp(self): + """Start mock for addonmanager_licenses class.""" + global AddonCatalog + global Version + self.addon_patch = mock.patch.dict("sys.modules", {"addonmanager_licenses": mock.Mock()}) + self.mock_addon_module = self.addon_patch.start() + from AddonCatalog import AddonCatalog + from addonmanager_metadata import Version + + def tearDown(self): + """Stop patching the addonmanager_licenses class""" + self.addon_patch.stop() + + def test_single_addon_simple_entry(self): + """Test that an addon entry for an addon with only a git ref is accepted and added, and + appears as an available addon.""" + data = {"AnAddon": [{"git_ref": "main"}]} + catalog = AddonCatalog(data) + ids = catalog.get_available_addon_ids() + self.assertEqual(len(ids), 1) + self.assertIn("AnAddon", ids) + + def test_single_addon_max_single_entry(self): + """Test that an addon with the maximum possible data load is accepted.""" + data = { + "AnAddon": [ + { + "freecad_min": "0.21.0", + "freecad_max": "1.99.99", + "repository": "https://github.com/FreeCAD/FreeCAD", + "git_ref": "main", + "zip_url": "https://github.com/FreeCAD/FreeCAD/archive/main.zip", + "note": "This is a fake repo, don't use it", + "branch_display_name": "main", + } + ] + } + catalog = AddonCatalog(data) + ids = catalog.get_available_addon_ids() + self.assertEqual(len(ids), 1) + self.assertIn("AnAddon", ids) + + def test_single_addon_multiple_entries(self): + """Test that an addon with multiple entries is accepted and only appears as a single + addon.""" + data = { + "AnAddon": [ + { + "freecad_min": "1.0.0", + "repository": "https://github.com/FreeCAD/FreeCAD", + "git_ref": "main", + }, + { + "freecad_min": "0.21.0", + "freecad_max": "0.21.99", + "repository": "https://github.com/FreeCAD/FreeCAD", + "git_ref": "0_21_compatibility_branch", + "branch_display_name": "FreeCAD 0.21 Compatibility Branch", + }, + ] + } + catalog = AddonCatalog(data) + ids = catalog.get_available_addon_ids() + self.assertEqual(len(ids), 1) + self.assertIn("AnAddon", ids) + + def test_multiple_addon_entries(self): + """Test that multiple distinct addon entries are added as distinct addons""" + data = { + "AnAddon": [{"git_ref": "main"}], + "AnotherAddon": [{"git_ref": "main"}], + "YetAnotherAddon": [{"git_ref": "main"}], + } + catalog = AddonCatalog(data) + ids = catalog.get_available_addon_ids() + self.assertEqual(len(ids), 3) + self.assertIn("AnAddon", ids) + self.assertIn("AnotherAddon", ids) + self.assertIn("YetAnotherAddon", ids) + + def test_multiple_branches_single_match(self): + """Test that an addon with multiple branches representing different configurations of + min and max FreeCAD versions returns only the appropriate match.""" + data = { + "AnAddon": [ + { + "freecad_min": "1.0.0", + "repository": "https://github.com/FreeCAD/FreeCAD", + "git_ref": "main", + }, + { + "freecad_min": "0.21.0", + "freecad_max": "0.21.99", + "repository": "https://github.com/FreeCAD/FreeCAD", + "git_ref": "0_21_compatibility_branch", + "branch_display_name": "FreeCAD 0.21 Compatibility Branch", + }, + { + "freecad_min": "0.19.0", + "freecad_max": "0.20.99", + "repository": "https://github.com/FreeCAD/FreeCAD", + "git_ref": "0_19_compatibility_branch", + "branch_display_name": "FreeCAD 0.19 Compatibility Branch", + }, + ] + } + with patch("addonmanager_freecad_interface.Version", return_value=(1, 0, 3, "dev")): + catalog = AddonCatalog(data) + branches = catalog.get_available_branches("AnAddon") + self.assertEqual(len(branches), 1) + + def test_load_metadata_cache(self): + """Test that an addon with a known hash is correctly loaded (e.g. no exception is raised)""" + data = {"AnAddon": [{"git_ref": "main"}]} + catalog = AddonCatalog(data) + sha = "cbce6737d7d058dca2b5ae3f2fdb8cc45b0c02bf711e75bdf5f12fb71ce87790" + cache = {sha: "CacheData"} + with patch("addonmanager_freecad_interface.Version", return_value=cache): + with patch("Addon.Addon") as addon_mock: + catalog.load_metadata_cache(cache) diff --git a/src/Mod/AddonManager/CMakeLists.txt b/src/Mod/AddonManager/CMakeLists.txt index 8efcf67a0e..895b5e3bd2 100644 --- a/src/Mod/AddonManager/CMakeLists.txt +++ b/src/Mod/AddonManager/CMakeLists.txt @@ -92,6 +92,7 @@ SET(AddonManagerTestsApp_SRCS AddonManagerTest/app/__init__.py AddonManagerTest/app/mocks.py AddonManagerTest/app/test_addon.py + AddonManagerTest/app/test_addoncatalog.py AddonManagerTest/app/test_cache.py AddonManagerTest/app/test_dependency_installer.py AddonManagerTest/app/test_freecad_interface.py @@ -100,8 +101,8 @@ SET(AddonManagerTestsApp_SRCS AddonManagerTest/app/test_macro.py AddonManagerTest/app/test_macro_parser.py AddonManagerTest/app/test_metadata.py - AddonManagerTest/app/test_utilities.py AddonManagerTest/app/test_uninstaller.py + AddonManagerTest/app/test_utilities.py ) SET(AddonManagerTestsGui_SRCS @@ -109,8 +110,10 @@ SET(AddonManagerTestsGui_SRCS AddonManagerTest/gui/gui_mocks.py AddonManagerTest/gui/test_gui.py AddonManagerTest/gui/test_installer_gui.py - AddonManagerTest/gui/test_update_all_gui.py + AddonManagerTest/gui/test_python_deps_gui.py AddonManagerTest/gui/test_uninstaller_gui.py + AddonManagerTest/gui/test_update_all_gui.py + AddonManagerTest/gui/test_widget_progress_bar.py AddonManagerTest/gui/test_workers_startup.py AddonManagerTest/gui/test_workers_utility.py ) diff --git a/src/Mod/Assembly/App/AssemblyLink.cpp b/src/Mod/Assembly/App/AssemblyLink.cpp index 52607d3180..7df49b12e7 100644 --- a/src/Mod/Assembly/App/AssemblyLink.cpp +++ b/src/Mod/Assembly/App/AssemblyLink.cpp @@ -159,7 +159,6 @@ void AssemblyLink::onChanged(const App::Property* prop) propPlc->setValue(movePlc); } } - return; } App::Part::onChanged(prop); @@ -175,7 +174,6 @@ void AssemblyLink::updateContents() else { synchronizeJoints(); } - purgeTouched(); } @@ -220,7 +218,6 @@ void AssemblyLink::synchronizeComponents() continue; } - if (linkedObj == obj) { found = true; link = obj2; @@ -272,24 +269,6 @@ void AssemblyLink::synchronizeComponents() if (objLinkMap.find(link) != objLinkMap.end()) { doc->removeObject(link->getNameInDocument()); } - - /*if (!link->isDerivedFrom() && !link->isDerivedFrom()) { - // AssemblyLink should contain only Links or assembly links. - continue; - } - - auto* linkedObj = link->getLinkedObject(false); // not recursive - - bool found = false; - for (auto* obj2 : assemblyGroup) { - if (obj2 == linkedObj) { - found = true; - break; - } - } - if (!found) { - doc->removeObject(link->getNameInDocument()); - }*/ } } @@ -567,7 +546,6 @@ bool AssemblyLink::isRigid() if (!prop) { return true; } - return prop->getValue(); } @@ -578,6 +556,5 @@ std::vector AssemblyLink::getJoints() if (!jointGroup) { return {}; } - return jointGroup->getJoints(); } diff --git a/src/Mod/Assembly/App/AssemblyObject.cpp b/src/Mod/Assembly/App/AssemblyObject.cpp index a7164bad36..495536f6c8 100644 --- a/src/Mod/Assembly/App/AssemblyObject.cpp +++ b/src/Mod/Assembly/App/AssemblyObject.cpp @@ -158,7 +158,6 @@ int AssemblyObject::solve(bool enableRedo, bool updateJCS) } try { - // mbdAssembly->runPreDrag(); // solve() is causing some issues with limits. mbdAssembly->runKINEMATIC(); } catch (const std::exception& e) { @@ -198,7 +197,6 @@ int AssemblyObject::generateSimulation(App::DocumentObject* sim) create_mbdSimulationParameters(sim); - try { mbdAssembly->runKINEMATIC(); } @@ -384,7 +382,6 @@ bool AssemblyObject::validateNewPlacements() // TODO: We could do further tests // For example check if the joints connectors are correctly aligned. - return true; } @@ -593,7 +590,6 @@ App::DocumentObject* AssemblyObject::getJointOfPartConnectingToGround(App::Docum return joint; } } - return nullptr; } @@ -753,7 +749,6 @@ std::vector AssemblyObject::getJointsOfPart(App::DocumentO jointsOf.push_back(joint); } } - return jointsOf; } @@ -1125,7 +1120,7 @@ std::shared_ptr AssemblyObject::makeMbdJointOfType(App::DocumentObjec } else if (type == JointType::Angle) { double angle = fabs(Base::toRadians(getJointDistance(joint))); - if (fmod(angle, 2 * M_PI) < Precision::Confusion()) { + if (fmod(angle, 2 * std::numbers::pi) < Precision::Confusion()) { return CREATE::With(); } else { @@ -1804,7 +1799,6 @@ AssemblyObject::MbDPartData AssemblyObject::getMbDData(App::DocumentObject* part addConnectedFixedParts(part, addConnectedFixedParts); } - return data; } @@ -1813,7 +1807,6 @@ std::shared_ptr AssemblyObject::getMbDPart(App::DocumentObject* part) if (!part) { return nullptr; } - return getMbDData(part).part; } @@ -1831,7 +1824,6 @@ AssemblyObject::makeMbdPart(std::string& name, Base::Placement plc, double mass) Base::Vector3d pos = plc.getPosition(); mbdPart->setPosition3D(pos.x, pos.y, pos.z); - // Base::Console().Warning("MbD Part placement : (%f, %f, %f)\n", pos.x, pos.y, pos.z); // TODO : replace with quaternion to simplify Base::Rotation rot = plc.getRotation(); diff --git a/src/Mod/Assembly/App/AssemblyObject.h b/src/Mod/Assembly/App/AssemblyObject.h index b8ea59049c..61ceaff796 100644 --- a/src/Mod/Assembly/App/AssemblyObject.h +++ b/src/Mod/Assembly/App/AssemblyObject.h @@ -200,12 +200,8 @@ private: std::vector> previousPositions; bool bundleFixed; - // void handleChangedPropertyType(Base::XMLReader &reader, const char *TypeName, App::Property - // *prop) override; }; -// using AssemblyObjectPython = App::FeaturePythonT; - } // namespace Assembly diff --git a/src/Mod/Assembly/App/AssemblyUtils.cpp b/src/Mod/Assembly/App/AssemblyUtils.cpp index f48eb4dbd7..f665b25613 100644 --- a/src/Mod/Assembly/App/AssemblyUtils.cpp +++ b/src/Mod/Assembly/App/AssemblyUtils.cpp @@ -37,9 +37,8 @@ #include #include #include -// #include #include -// #include + #include #include #include @@ -695,7 +694,6 @@ App::DocumentObject* getMovingPartFromRef(const AssemblyObject* assemblyObject, return obj; } - return nullptr; } @@ -715,7 +713,6 @@ App::DocumentObject* getMovingPartFromRef(const AssemblyObject* assemblyObject, if (subs.empty()) { return nullptr; } - return getMovingPartFromRef(assemblyObject, obj, subs[0]); } @@ -731,102 +728,5 @@ App::DocumentObject* getMovingPartFromRef(const AssemblyObject* assemblyObject, return getMovingPartFromRef(assemblyObject, prop); } -/* -Base::Placement getPlacementFromProp(App::DocumentObject* obj, const char* propName) -{ - Base::Placement plc = Base::Placement(); - auto* propPlacement = dynamic_cast(obj->getPropertyByName(propName)); - if (propPlacement) { - plc = propPlacement->getValue(); - } - return plc; -} - -// Currently unused -Base::Placement* getTargetPlacementRelativeTo( - App::DocumentObject* targetObj, App::DocumentObject* part, App::DocumentObject* container, - bool inContainerBranch, bool ignorePlacement = false) -{ - inContainerBranch = inContainerBranch || (!ignorePlacement && part == container); - - Base::Console().Warning("sub --------------\n"); - if (targetObj == part && inContainerBranch && !ignorePlacement) { - Base::Console().Warning("found0\n"); - return &getPlacementFromProp(targetObj, "Placement"); - } - - if (auto group = dynamic_cast(part)) { - for (auto& obj : group->getOutList()) { - auto foundPlacement = getTargetPlacementRelativeTo( - targetObj, obj, container, inContainerBranch, ignorePlacement - ); - if (foundPlacement != nullptr) { - return foundPlacement; - } - } - } - else if (auto assembly = dynamic_cast(part)) { - Base::Console().Warning("h3\n"); - for (auto& obj : assembly->getOutList()) { - auto foundPlacement = getTargetPlacementRelativeTo( - targetObj, obj, container, inContainerBranch - ); - if (foundPlacement == nullptr) { - continue; - } - - if (!ignorePlacement) { - *foundPlacement = getPlacementFromProp(part, "Placement") * *foundPlacement; - } - - Base::Console().Warning("found\n"); - return foundPlacement; - } - } - else if (auto link = dynamic_cast(part)) { - Base::Console().Warning("h4\n"); - auto linked_obj = link->getLinkedObject(); - - if (dynamic_cast(linked_obj) || dynamic_cast(linked_obj)) { - for (auto& obj : linked_obj->getOutList()) { - auto foundPlacement = getTargetPlacementRelativeTo( - targetObj, obj, container, inContainerBranch - ); - if (foundPlacement == nullptr) { - continue; - } - - *foundPlacement = getPlacementFromProp(link, "Placement") * *foundPlacement; - return foundPlacement; - } - } - - auto foundPlacement = getTargetPlacementRelativeTo( - targetObj, linked_obj, container, inContainerBranch, true - ); - - if (foundPlacement != nullptr && !ignorePlacement) { - *foundPlacement = getPlacementFromProp(link, "Placement") * *foundPlacement; - } - - Base::Console().Warning("found2\n"); - return foundPlacement; - } - - return nullptr; -} - -Base::Placement getGlobalPlacement(App::DocumentObject* targetObj, App::DocumentObject* container = -nullptr) { bool inContainerBranch = container == nullptr; auto rootObjects = -App::GetApplication().getActiveDocument()->getRootObjects(); for (auto& part : rootObjects) { auto -foundPlacement = getTargetPlacementRelativeTo(targetObj, part, container, inContainerBranch); if -(foundPlacement != nullptr) { Base::Placement plc(foundPlacement->toMatrix()); return plc; - } - } - - return Base::Placement(); -} -*/ - } // namespace Assembly diff --git a/src/Mod/Assembly/App/AssemblyUtils.h b/src/Mod/Assembly/App/AssemblyUtils.h index b04145d26b..6fe1e22912 100644 --- a/src/Mod/Assembly/App/AssemblyUtils.h +++ b/src/Mod/Assembly/App/AssemblyUtils.h @@ -25,7 +25,6 @@ #ifndef ASSEMBLY_AssemblyUtils_H #define ASSEMBLY_AssemblyUtils_H - #include #include diff --git a/src/Mod/BIM/CMakeLists.txt b/src/Mod/BIM/CMakeLists.txt index 98756b73b6..895f30a8a0 100644 --- a/src/Mod/BIM/CMakeLists.txt +++ b/src/Mod/BIM/CMakeLists.txt @@ -26,7 +26,6 @@ SET(Arch_SRCS ArchStairs.py ArchSpace.py ArchRebar.py - TestArch.py ArchFrame.py ArchPanel.py ArchEquipment.py @@ -46,6 +45,8 @@ SET(Arch_SRCS ArchSketchObject.py BimSelect.py BimStatus.py + TestArch.py + TestArchGui.py ) SET(importers_SRCS @@ -196,6 +197,13 @@ SET(nativeifc_SRCS nativeifc/ifc_classification.py ) +SET(bimtests_SRCS + bimtests/TestArchBase.py + bimtests/TestArchRoof.py + bimtests/TestArchSpace.py + bimtests/TestArchWall.py +) + SOURCE_GROUP("" FILES ${Arch_SRCS}) SET(BIMGuiIcon_SVG @@ -214,6 +222,7 @@ ADD_CUSTOM_TARGET(BIM ALL ${ArchGuiIcon_SVG} ${importers_SRCS} ${bimcommands_SRCS} + ${bimtests_SRCS} ${nativeifc_SRCS} ${BIMGuiIcon_SVG} ) @@ -226,6 +235,7 @@ fc_copy_sources(BIM "${CMAKE_BINARY_DIR}/Mod/BIM" ${Arch_SRCS}) fc_copy_sources(BIM "${CMAKE_BINARY_DIR}/Mod/BIM" ${Dice3DS_SRCS}) fc_copy_sources(BIM "${CMAKE_BINARY_DIR}/Mod/BIM" ${importers_SRCS}) fc_copy_sources(BIM "${CMAKE_BINARY_DIR}/Mod/BIM" ${bimcommands_SRCS}) +fc_copy_sources(BIM "${CMAKE_BINARY_DIR}/Mod/BIM" ${bimtests_SRCS}) fc_copy_sources(BIM "${CMAKE_BINARY_DIR}/Mod/BIM" ${nativeifc_SRCS}) fc_copy_sources(BIM "${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_DATADIR}/Mod/BIM" ${BIMGuiIcon_SVG}) @@ -273,6 +283,12 @@ INSTALL( DESTINATION Mod/BIM/bimcommands ) +INSTALL( + FILES + ${bimtests_SRCS} + DESTINATION Mod/BIM/bimtests +) + INSTALL( FILES ${nativeifc_SRCS} diff --git a/src/Mod/BIM/Init.py b/src/Mod/BIM/Init.py index ee23f69fdf..b8ac919cf5 100644 --- a/src/Mod/BIM/Init.py +++ b/src/Mod/BIM/Init.py @@ -35,3 +35,5 @@ FreeCAD.addExportType("Collada (*.dae)","importers.importDAE") FreeCAD.addImportType("3D Studio mesh (*.3ds *.3DS)","importers.import3DS") FreeCAD.addImportType("SweetHome3D (*.sh3d)","importers.importSH3D") FreeCAD.addImportType("Shapefile (*.shp *.SHP)","importers.importSHP") + +FreeCAD.__unit_test__ += ["TestArch"] \ No newline at end of file diff --git a/src/Mod/BIM/InitGui.py b/src/Mod/BIM/InitGui.py index d07c2296c2..2972f610b0 100644 --- a/src/Mod/BIM/InitGui.py +++ b/src/Mod/BIM/InitGui.py @@ -686,6 +686,6 @@ FreeCADGui.addPreferencePage(":/ui/preferences-dae.ui", t) FreeCADGui.addPreferencePage(":/ui/preferences-sh3d-import.ui", t) # Add unit tests -FreeCAD.__unit_test__ += ["TestArch"] +FreeCAD.__unit_test__ += ["TestArchGui"] # The NativeIFC tests require internet connection and file download # FreeCAD.__unit_test__ += ["nativeifc.ifc_selftest"] diff --git a/src/Mod/BIM/TestArch.py b/src/Mod/BIM/TestArch.py index 9057faee09..6fca718b9a 100644 --- a/src/Mod/BIM/TestArch.py +++ b/src/Mod/BIM/TestArch.py @@ -34,352 +34,12 @@ import Sketcher import TechDraw import WorkingPlane -from FreeCAD import Units +from bimtests.TestArchRoof import TestArchRoof +from bimtests.TestArchSpace import TestArchSpace +from bimtests.TestArchWall import TestArchWall + from draftutils.messages import _msg -if App.GuiUp: - import FreeCADGui - -brepArchiCAD = """ -DBRep_DrawableShape - -CASCADE Topology V1, (c) Matra-Datavision -Locations 3 -1 - 1 0 0 0 - 0 1 0 0 - 0 0 1 0 -1 - 0 1 0 0 - -1 0 0 0 - 0 0 1 0 -2 2 -1 0 -Curve2ds 0 -Curves 12 -1 0 0 0 1 0 0 -1 3000 0 0 0 0 1 -1 3000 0 3000 -1 0 0 -1 0 0 3000 0 0 -1 -1 0 0 0 0 1 0 -1 0 5000 0 1 0 0 -1 3000 5000 0 0 -1 0 -1 3000 5000 0 0 0 1 -1 3000 5000 3000 0 -1 0 -1 3000 5000 3000 -1 0 0 -1 0 5000 3000 0 -1 0 -1 0 5000 3000 0 0 -1 -Polygon3D 0 -PolygonOnTriangulations 24 -2 1 2 -p 18.3333333333333 1 0 3000 -2 1 4 -p 18.3333333333333 1 0 3000 -2 2 3 -p 18.3333333333333 1 0 3000 -2 2 4 -p 18.3333333333333 1 0 3000 -2 3 4 -p 18.3333333333333 1 0 3000 -2 1 2 -p 18.3333333333333 1 0 3000 -2 4 1 -p 18.3333333333333 1 0 3000 -2 3 1 -p 18.3333333333333 1 0 3000 -2 1 2 -p 18.3333333333333 1 0 5000 -2 1 2 -p 18.3333333333333 1 0 5000 -2 2 3 -p 18.3333333333333 1 0 3000 -2 1 2 -p 18.3333333333333 1 0 3000 -2 3 4 -p 18.3333333333333 1 0 5000 -2 1 2 -p 18.3333333333333 1 0 5000 -2 1 3 -p 18.3333333333333 1 0 3000 -2 2 4 -p 18.3333333333333 1 0 3000 -2 3 4 -p 18.3333333333333 1 0 5000 -2 3 1 -p 18.3333333333333 1 0 5000 -2 3 4 -p 18.3333333333333 1 0 3000 -2 4 3 -p 18.3333333333333 1 0 3000 -2 4 2 -p 18.3333333333333 1 0 5000 -2 4 3 -p 18.3333333333333 1 0 5000 -2 4 2 -p 18.3333333333333 1 0 3000 -2 3 1 -p 18.3333333333333 1 0 3000 -Surfaces 6 -1 1500 0 1500 -0 -1 -0 0 0 -1 1 0 0 -1 1500 2500 0 -0 -0 -1 -1 0 0 0 1 0 -1 3000 2500 1500 1 0 0 0 0 1 0 -1 0 -1 1500 2500 3000 0 0 1 1 0 0 0 1 0 -1 0 2500 1500 -1 -0 -0 0 0 -1 0 -1 0 -1 1500 5000 1500 0 1 0 0 0 1 1 0 0 -Triangulations 6 -4 2 1 18.3333333333333 -0 0 0 3000 0 0 3000 0 3000 0 0 3000 1500 -1500 1500 1500 -1500 1500 -1500 -1500 3 4 1 2 3 1 -4 2 1 18.3333333333333 -0 0 0 0 5000 0 3000 5000 0 3000 0 0 1500 -2500 1500 2500 -1500 2500 -1500 -2500 2 3 4 2 4 1 -4 2 1 18.3333333333333 -3000 5000 0 3000 0 0 3000 5000 3000 3000 0 3000 -1500 -2500 -1500 2500 1500 -2500 1500 2500 4 2 1 4 1 3 -4 2 1 18.3333333333333 -3000 0 3000 0 0 3000 3000 5000 3000 0 5000 3000 1500 -2500 -1500 -2500 1500 2500 -1500 2500 3 2 1 3 4 2 -4 2 1 18.3333333333333 -0 0 0 0 5000 0 0 0 3000 0 5000 3000 1500 2500 1500 -2500 -1500 2500 -1500 -2500 1 3 4 1 4 2 -4 2 1 18.3333333333333 -0 5000 0 3000 5000 0 0 5000 3000 3000 5000 3000 -1500 -1500 -1500 1500 1500 -1500 1500 1500 3 2 1 4 2 3 - -TShapes 35 -Ve -0.1 -0 0 0 -0 0 - -0101101 -* -Ve -0.1 -0 -3000 0 -0 0 - -0101101 -* -Ed - 0.0001 1 1 0 -1 1 0 0 3000 -6 1 1 0 -6 2 2 0 -0 - -0101000 -+35 3 -34 3 * -Ve -0.1 -0 -3000 3000 -0 0 - -0101101 -* -Ed - 0.0001 1 1 0 -1 2 0 0 3000 -6 3 1 0 -6 4 3 0 -0 - -0101000 -+34 3 -32 3 * -Ve -0.1 -0 0 3000 -0 0 - -0101101 -* -Ed - 0.0001 1 1 0 -1 3 0 0 3000 -6 5 1 0 -6 6 4 0 -0 - -0101000 -+32 3 -30 3 * -Ed - 0.0001 1 1 0 -1 4 0 0 3000 -6 7 1 0 -6 8 5 0 -0 - -0101000 -+30 3 -35 3 * -Wi - -0101100 -+33 0 +31 0 +29 0 +28 0 * -Fa -0 0.1 1 0 -2 1 -0111000 -+27 0 * -Ve -0.1 -5000 0 0 -0 0 - -0101101 -* -Ed - 0.0001 1 1 0 -1 5 0 0 5000 -6 9 2 0 -6 10 5 0 -0 - -0101000 -+35 3 -25 3 * -Ve -0.1 -5000 -3000 0 -0 0 - -0101101 -* -Ed - 0.0001 1 1 0 -1 6 0 0 3000 -6 11 2 0 -6 12 6 0 -0 - -0101000 -+25 3 -23 3 * -Ed - 0.0001 1 1 0 -1 7 0 0 5000 -6 13 2 0 -6 14 3 0 -0 - -0101000 -+23 3 -34 3 * -Wi - -0101100 -+24 0 +22 0 +21 0 -33 0 * -Fa -0 0.1 2 0 -2 2 -0111000 -+20 0 * -Ve -0.1 -5000 -3000 3000 -0 0 - -0101101 -* -Ed - 0.0001 1 1 0 -1 8 0 0 3000 -6 15 3 0 -6 16 6 0 -0 - -0101000 -+23 3 -18 3 * -Ed - 0.0001 1 1 0 -1 9 0 0 5000 -6 17 3 0 -6 18 4 0 -0 - -0101000 -+18 3 -32 3 * -Wi - -0101100 --21 0 +17 0 +16 0 -31 0 * -Fa -0 0.1 3 0 -2 3 -0111000 -+15 0 * -Ve -0.1 -5000 0 3000 -0 0 - -0101101 -* -Ed - 0.0001 1 1 0 -1 10 0 0 3000 -6 19 4 0 -6 20 6 0 -0 - -0101000 -+18 3 -13 3 * -Ed - 0.0001 1 1 0 -1 11 0 0 5000 -6 21 4 0 -6 22 5 0 -0 - -0101000 -+13 3 -30 3 * -Wi - -0101100 --29 0 -16 0 +12 0 +11 0 * -Fa -0 0.1 4 0 -2 4 -0111000 -+10 0 * -Ed - 0.0001 1 1 0 -1 12 0 0 3000 -6 23 5 0 -6 24 6 0 -0 - -0101000 -+13 3 -25 3 * -Wi - -0101100 --24 0 -28 0 -11 0 +8 0 * -Fa -0 0.1 5 0 -2 5 -0111000 -+7 0 * -Wi - -0101100 --22 0 -8 0 -12 0 -17 0 * -Fa -0 0.1 6 0 -2 6 -0111000 -+5 0 * -Sh - -0101100 -+26 0 +19 0 +14 0 +9 0 +6 0 +4 0 * -So - -0100000 -+3 0 * -Co - -1100000 -+2 2 * - -+1 1 -""" - -def like(a, b): - return abs(a-b) < 0.001 - -def checkBB(a, b): - return like(a.XMin, b.XMin) and like(a.YMin, b.YMin) and like(a.ZMin, b.ZMin) and like(a.XMax, b.XMax) and like(a.YMax, b.YMax) and like(a.ZMax, b.ZMax) - class ArchTest(unittest.TestCase): @@ -392,72 +52,11 @@ class ArchTest(unittest.TestCase): App.newDocument("ArchTest") App.setActiveDocument("ArchTest") - def testWall(self): - App.Console.PrintLog ('Checking Arch Wall...\n') - l=Draft.makeLine(App.Vector(0,0,0),App.Vector(-2,0,0)) - w = Arch.makeWall(l) - self.assertTrue(w,"Arch Wall failed") - - def testWallMultiMatAlign(self): - App.Console.PrintLog ('Checking Arch Wall with MultiMaterial and 3 alignments...\n') - matA = Arch.makeMaterial() - matB = Arch.makeMaterial() - matMulti = Arch.makeMultiMaterial() - matMulti.Materials = [matA, matB] - matMulti.Thicknesses = [100, 200] # total width different from default 200 - pts = [App.Vector( 0, 0, 0), - App.Vector(1000, 0, 0), - App.Vector(1000, 1000, 0), - App.Vector(2000, 1000, 0)] - # wall based on wire: - wire = Draft.makeWire(pts) - wallWire = Arch.makeWall(wire) - wallWire.Material = matMulti - # wall based on sketch: - sketch = App.activeDocument().addObject('Sketcher::SketchObject','Sketch') - sketch.addGeometry([Part.LineSegment(pts[0], pts[1]), - Part.LineSegment(pts[1], pts[2]), - Part.LineSegment(pts[2], pts[3])]) - wallSketch = Arch.makeWall(sketch) - wallSketch.Material = matMulti - - alignLst = ["Left", "Center", "Right"] - checkLst = [[App.Vector(0, -300, 0), App.Vector(2000, 1000, 0)], - [App.Vector(0, -150, 0), App.Vector(2000, 1150, 0)], - [App.Vector(0, 0, 0), App.Vector(2000, 1300, 0)]] - for i in range(3): - wallWire.Align = alignLst[i] - wallSketch.Align = alignLst[i] - App.ActiveDocument.recompute() - for box in [wallWire.Shape.BoundBox, wallSketch.Shape.BoundBox]: - ptMin = App.Vector(box.XMin, box.YMin, 0) - self.assertTrue(ptMin.isEqual(checkLst[i][0], 1e-8), - "Arch Wall with MultiMaterial and 3 alignments failed") - ptMax = App.Vector(box.XMax, box.YMax, 0) - self.assertTrue(ptMax.isEqual(checkLst[i][1], 1e-8), - "Arch Wall with MultiMaterial and 3 alignments failed") - def testStructure(self): App.Console.PrintLog ('Checking BIM Structure...\n') s = Arch.makeStructure(length=2,width=3,height=5) self.assertTrue(s,"BIM Structure failed") - def testRebar(self): - App.Console.PrintLog ('Checking Arch Rebar...\n') - s = Arch.makeStructure(length=2,width=3,height=5) - sk = App.ActiveDocument.addObject('Sketcher::SketchObject','Sketch') - sk.AttachmentSupport = (s,["Face6"]) - sk.addGeometry(Part.LineSegment(App.Vector(-0.85,1.25,0),App.Vector(0.75,1.25,0))) - sk.addGeometry(Part.LineSegment(App.Vector(0.75,1.25,0),App.Vector(0.75,-1.20,0))) - sk.addGeometry(Part.LineSegment(App.Vector(0.75,-1.20,0),App.Vector(-0.85,-1.20,0))) - sk.addGeometry(Part.LineSegment(App.Vector(-0.85,-1.20,0),App.Vector(-0.85,1.25,0))) - sk.addConstraint(Sketcher.Constraint('Coincident',0,2,1,1)) - sk.addConstraint(Sketcher.Constraint('Coincident',1,2,2,1)) - sk.addConstraint(Sketcher.Constraint('Coincident',2,2,3,1)) - sk.addConstraint(Sketcher.Constraint('Coincident',3,2,0,1)) - r = Arch.makeRebar(s,sk,diameter=.1,amount=2) - self.assertTrue(r,"Arch Rebar failed") - def testFloor(self): App.Console.PrintLog ('Checking Arch Floor...\n') s = Arch.makeStructure(length=2,width=3,height=5) @@ -500,113 +99,6 @@ class ArchTest(unittest.TestCase): App.ActiveDocument.recompute() self.assertTrue(win, "'{}' failed".format(operation)) - def testRoof(self): - App.Console.PrintLog ('Checking Arch Roof...\n') - r = Draft.makeRectangle(length=2,height=-1) - r.recompute() # required before calling Arch.makeRoof - ro = Arch.makeRoof(r) - self.assertTrue(ro,"Arch Roof failed") - - def testRoof81Permutations(self): - """Create 81 roofs using a range of arguments. - """ - operation = "Arch Roof testRoof81Permutations" - _msg(" Test '{}'".format(operation)) - pts = [App.Vector( 0, 0, 0), - App.Vector(2000, 0, 0), - App.Vector(4000, 0, 0), - App.Vector(4000, 4000, 0), - App.Vector( 0, 4000, 0)] - ptsMod = [App.Vector(2000, 0, 0), - App.Vector(2000, -1000, 0), - App.Vector(2000, 1000, 0)] - angsMod = [[60, 60], [30, 60], [60, 30]] - runsMod = [[500, 500], [400, 500], [500, 400]] - overhangsMod = [[100, 100], [100, 200], [200, 100]] - delta = 6000 - pla = App.Placement() - for iY in range(9): - for iX in range(9): - pts[1] = ptsMod[iY % 3] # to get different edge angles - angsLst = angsMod[iY // 3] + [90, 90, 90] - runsLst = runsMod[iX % 3] + [0, 0, 0] - overhangsLst = overhangsMod[iX // 3] + [0, 0, 0] - pla.Base = App.Vector(iX * delta, iY * delta, 0) - wire = Draft.makeWire(pts, closed = True) - wire.MakeFace = False - wire.Placement = pla - wire.recompute() # required before calling Arch.makeRoof - roof = Arch.makeRoof(wire, - angles = angsLst, - run = runsLst, - overhang = overhangsLst) - roof.recompute() - self.assertFalse(roof.Shape.isNull(), - "'{}' failed".format(operation)) - self.assertTrue(roof.Shape.isValid(), - "'{}' failed".format(operation)) - - def testRoofAllAngles90(self): - """Create a roof with the angles of all segments set at 90 degrees. - This corner case results in a flat roof. - """ - operation = "Arch Roof testRoofAllAngles90" - _msg(" Test '{}'".format(operation)) - pts = [App.Vector( 0, 0, 0), - App.Vector(2000, 0, 0), - App.Vector(2000, 2000, 0), - App.Vector( 0, 2000, 0)] - - wire = Draft.makeWire(pts, closed = True) - wire.MakeFace = False - wire.recompute() # required before calling Arch.makeRoof - roof = Arch.makeRoof(wire, - angles = [90, 90, 90, 90]) - roof.recompute() - self.assertFalse(roof.Shape.isNull(), "'{}' failed".format(operation)) - self.assertTrue(roof.Shape.isValid(), "'{}' failed".format(operation)) - - def testRoofApex(self): - """Create a hipped roof that relies on apex calculation. The roof has - 2 triangular segments with a single apex point. - """ - operation = "Arch Roof testRoofApex" - _msg(" Test '{}'".format(operation)) - rec = Draft.makeRectangle(length = 4000, - height = 3000, - face = False) - rec.recompute() # required before calling Arch.makeRoof - roof = Arch.makeRoof(rec, - angles = [30, 40, 50, 60], - run = [2000, 0, 2000, 0], - idrel = [-1, 0, -1, 0], - thickness = [50.0], - overhang = [100.0]) - roof.recompute() - self.assertFalse(roof.Shape.isNull(), "'{}' failed".format(operation)) - self.assertTrue(roof.Shape.isValid(), "'{}' failed".format(operation)) - - def testRoofSingleEavePoint(self): - """Create a roof with a single triangular segment that has a single - eave point. - """ - operation = "Arch Roof testRoofSingleEavePoint" - _msg(" Test '{}'".format(operation)) - pts = [App.Vector( 0, 0, 0), - App.Vector( 2000, 0, 0), - App.Vector( 4000, 2000, 0), - App.Vector(-2000, 2000, 0)] - wire = Draft.makeWire(pts, closed = True) - wire.MakeFace = False - wire.recompute() # required before calling Arch.makeRoof - roof = Arch.makeRoof(wire, - angles = [45, 90, 90, 90], - run = [1000, 0, 0, 0], - overhang = [1000, 0, 0, 0]) - roof.recompute() - self.assertFalse(roof.Shape.isNull(), "'{}' failed".format(operation)) - self.assertTrue(roof.Shape.isValid(), "'{}' failed".format(operation)) - def testAxis(self): App.Console.PrintLog ('Checking Arch Axis...\n') a = Arch.makeAxis() @@ -617,27 +109,6 @@ class ArchTest(unittest.TestCase): s = Arch.makeSectionPlane([]) self.assertTrue(s,"Arch Section failed") - def testSpace(self): - App.Console.PrintLog ('Checking Arch Space...\n') - sb = Part.makeBox(1,1,1) - b = App.ActiveDocument.addObject('Part::Feature','Box') - b.Shape = sb - s = Arch.makeSpace([b]) - self.assertTrue(s,"Arch Space failed") - - def testSpaceBBox(self): - shape = Part.Shape() - shape.importBrepFromString(brepArchiCAD) - bborig = shape.BoundBox - App.Console.PrintLog ("Original BB: "+str(bborig)) - baseobj = App.ActiveDocument.addObject("Part::Feature","brepArchiCAD_body") - baseobj.Shape = shape - space = Arch.makeSpace(baseobj) - space.recompute() - bbnew = space.Shape.BoundBox - App.Console.PrintLog ("New BB: "+str(bbnew)) - self.assertTrue(checkBB(bborig,bbnew),"Arch Space has wrong Placement") - def testStairs(self): App.Console.PrintLog ('Checking Arch Stairs...\n') s = Arch.makeStairs() @@ -690,89 +161,6 @@ class ArchTest(unittest.TestCase): r = (w.Shape.Volume < 0.75) self.assertTrue(r,"Arch Remove failed") - def testBuildingPart(self): - """Create a BuildingPart from a wall with a window and check its shape. - """ - # Also regression test for: - # https://github.com/FreeCAD/FreeCAD/issues/6178 - operation = "Arch BuildingPart" - _msg(" Test '{}'".format(operation)) - # Most of the code below taken from testWindow function. - line = Draft.makeLine(App.Vector(0, 0, 0), App.Vector(3000, 0, 0)) - wall = Arch.makeWall(line) - sk = App.ActiveDocument.addObject("Sketcher::SketchObject", "Sketch001") - sk.Placement.Rotation = App.Rotation(App.Vector(1, 0, 0), 90) - sk.addGeometry(Part.LineSegment(App.Vector( 500, 800, 0), App.Vector(1500, 800, 0))) - sk.addGeometry(Part.LineSegment(App.Vector(1500, 800, 0), App.Vector(1500, 2000, 0))) - sk.addGeometry(Part.LineSegment(App.Vector(1500, 2000, 0), App.Vector( 500, 2000, 0))) - sk.addGeometry(Part.LineSegment(App.Vector( 500, 2000, 0), App.Vector( 500, 800, 0))) - sk.addConstraint(Sketcher.Constraint('Coincident', 0, 2, 1, 1)) - sk.addConstraint(Sketcher.Constraint('Coincident', 1, 2, 2, 1)) - sk.addConstraint(Sketcher.Constraint('Coincident', 2, 2, 3, 1)) - sk.addConstraint(Sketcher.Constraint('Coincident', 3, 2, 0, 1)) - App.ActiveDocument.recompute() - win = Arch.makeWindow(sk) - Arch.removeComponents(win, host=wall) - App.ActiveDocument.recompute() - bp = Arch.makeBuildingPart() - - # Wall visibility works when standalone - FreeCADGui.Selection.clearSelection() - FreeCADGui.Selection.addSelection('ArchTest',wall.Name) - assert wall.Visibility - FreeCADGui.runCommand('Std_ToggleVisibility',0) - App.ActiveDocument.recompute() - assert not wall.Visibility - FreeCADGui.runCommand('Std_ToggleVisibility',0) - assert wall.Visibility - - bp.Group = [wall] - App.ActiveDocument.recompute() - # Fails with OCC 7.5 - # self.assertTrue(len(bp.Shape.Faces) == 16, "'{}' failed".format(operation)) - - # Wall visibility works when inside a BuildingPart - FreeCADGui.runCommand('Std_ToggleVisibility',0) - App.ActiveDocument.recompute() - assert not wall.Visibility - FreeCADGui.runCommand('Std_ToggleVisibility',0) - assert wall.Visibility - - # Wall visibility works when BuildingPart Toggled - FreeCADGui.Selection.clearSelection() - FreeCADGui.Selection.addSelection('ArchTest',bp.Name) - FreeCADGui.runCommand('Std_ToggleVisibility',0) - assert not wall.Visibility - FreeCADGui.runCommand('Std_ToggleVisibility',0) - assert wall.Visibility - - # Wall visibiity works inside group inside BuildingPart Toggled - grp = App.ActiveDocument.addObject("App::DocumentObjectGroup","Group") - grp.Label="Group" - grp.Group = [wall] - bp.Group = [grp] - App.ActiveDocument.recompute() - assert wall.Visibility - FreeCADGui.runCommand('Std_ToggleVisibility',0) - App.ActiveDocument.recompute() - assert not wall.Visibility - FreeCADGui.runCommand('Std_ToggleVisibility',0) - App.ActiveDocument.recompute() - assert wall.Visibility - - def testImportSH3D(self): - """Import a SweetHome 3D file - """ - operation = "importers.importSH3D" - _msg(" Test '{}'".format(operation)) - import BIM.importers.importSH3DHelper - importer = BIM.importers.importSH3DHelper.SH3DImporter(None) - importer.import_sh3d_from_string(SH3D_HOME) - assert App.ActiveDocument.Site - assert App.ActiveDocument.BuildingPart.Label == "Building" - assert App.ActiveDocument.BuildingPart001.Label == "Level" - assert App.ActiveDocument.Wall - def testViewGeneration(self): """Tests the whole TD view generation workflow""" @@ -819,173 +207,11 @@ class ArchTest(unittest.TestCase): App.ActiveDocument.recompute() assert True - def test_addSpaceBoundaries(self): - """Test the Arch.addSpaceBoundaries method. - Create a space and a wall that intersects it. Add the wall as a boundary to the space, - and check if the resulting space area is as expected. - """ - operation = " Add a wall face as a boundary to a space" - _msg(" Test '{}'".format(operation)) - - # Create the space - pl = App.Placement() - pl.Rotation.Q = (0.0, 0.0, 0.0, 1.0) - pl.Base = App.Vector(-2000.0, -2000.0, 0.0) - rectangleBase = Draft.make_rectangle( - length=4000.0, height=4000.0, placement=pl, face=True, support=None) - App.ActiveDocument.recompute() - extr = rectangleBase.Shape.extrude(App.Vector(0,0,2000)) - Part.show(extr, 'Extrusion') - space = Arch.makeSpace(App.activeDocument().getObject('Extrusion')) - App.ActiveDocument.recompute() # To calculate area - - # Create the wall - trace = Part.LineSegment(App.Vector (3000.0, 1000.0, 0.0), - App.Vector (-3000.0, 1000.0, 0.0)) - wp = WorkingPlane.get_working_plane() - base = App.ActiveDocument.addObject("Sketcher::SketchObject","WallTrace") - base.Placement = wp.get_placement() - base.addGeometry(trace) - wall = Arch.makeWall(base,width=200.0,height=3000.0,align="Left") - wall.Normal = wp.axis - - # Add the boundary - wallBoundary = [(wall, ["Face1"])] - Arch.addSpaceBoundaries(App.ActiveDocument.Space, wallBoundary) - App.ActiveDocument.recompute() # To recalculate area - - # Assert if area is as expected - expectedArea = Units.parseQuantity('12 m^2') - actualArea = Units.parseQuantity(str(space.Area)) - - self.assertAlmostEqual( - expectedArea.Value, - actualArea.Value, - msg = (f"Invalid area value. " + - f"Expected: {expectedArea.UserString}, actual: {actualArea.UserString}")) - - def test_SpaceFromSingleWall(self): - """Create a space from boundaries of a single wall. - """ - from FreeCAD import Units - - operation = "Arch Space from single wall" - _msg(f"\n Test '{operation}'") - - # Create a wall - wallInnerLength = 4000.0 - wallHeight = 3000.0 - wallInnerFaceArea = wallInnerLength * wallHeight - pl = App.Placement() - pl.Rotation.Q = (0.0, 0.0, 0.0, 1.0) - pl.Base = App.Vector(0.0, 0.0, 0.0) - rectangleBase = Draft.make_rectangle( - length=wallInnerLength, height=wallInnerLength, placement=pl, face=True, support=None) - App.ActiveDocument.recompute() # To calculate rectangle area - rectangleArea = rectangleBase.Area - App.ActiveDocument.getObject(rectangleBase.Name).MakeFace = False - wall = Arch.makeWall(baseobj=rectangleBase, height=wallHeight, align="Left") - App.ActiveDocument.recompute() # To calculate face areas - - # Create a space from the wall's inner faces - boundaries = [f"Face{ind+1}" for ind, face in enumerate(wall.Shape.Faces) - if round(face.Area) == round(wallInnerFaceArea)] - - if App.GuiUp: - FreeCADGui.Selection.clearSelection() - FreeCADGui.Selection.addSelection(wall, boundaries) - - space = Arch.makeSpace(FreeCADGui.Selection.getSelectionEx()) - # Alternative, but test takes longer to run (~10x) - # FreeCADGui.activateWorkbench("BIMWorkbench") - # FreeCADGui.runCommand('Arch_Space', 0) - # space = App.ActiveDocument.Space - else: - # Also tests the alternative way of specifying the boundaries - # [ (, ["Face1", ...]), ... ] - space = Arch.makeSpace([(wall, boundaries)]) - - App.ActiveDocument.recompute() # To calculate space area - - # Assert if area is as expected - expectedArea = Units.parseQuantity(str(rectangleArea)) - actualArea = Units.parseQuantity(str(space.Area)) - - self.assertAlmostEqual( - expectedArea.Value, - actualArea.Value, - msg = f"Invalid area value. Expected: {expectedArea.UserString}, actual: {actualArea.UserString}") - def tearDown(self): App.closeDocument("ArchTest") pass - -SH3D_HOME = """ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -""" +# Use the modules so that code checkers don't complain (flake8) +True if TestArchSpace else False +True if TestArchRoof else False +True if TestArchWall else False \ No newline at end of file diff --git a/src/Mod/BIM/TestArchGui.py b/src/Mod/BIM/TestArchGui.py new file mode 100644 index 0000000000..a0a971901b --- /dev/null +++ b/src/Mod/BIM/TestArchGui.py @@ -0,0 +1,226 @@ +#*************************************************************************** +#* Copyright (c) 2013 Yorik van Havre * +#* * +#* This file is part of the FreeCAD CAx development system. * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* FreeCAD 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 FreeCAD; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#***************************************************************************/ + +# Unit test for the Arch module + +import os +import unittest + +import FreeCAD as App +from FreeCAD import Units + +import Arch +import Draft +import Part +import Sketcher +import TechDraw +import WorkingPlane + +from draftutils.messages import _msg + +if App.GuiUp: + import FreeCADGui + +class ArchTest(unittest.TestCase): + + def setUp(self): + # setting a new document to hold the tests + if App.ActiveDocument: + if App.ActiveDocument.Name != "ArchTest": + App.newDocument("ArchTest") + else: + App.newDocument("ArchTest") + App.setActiveDocument("ArchTest") + + def testRebar(self): + App.Console.PrintLog ('Checking Arch Rebar...\n') + s = Arch.makeStructure(length=2,width=3,height=5) + sk = App.ActiveDocument.addObject('Sketcher::SketchObject','Sketch') + sk.AttachmentSupport = (s,["Face6"]) + sk.addGeometry(Part.LineSegment(App.Vector(-0.85,1.25,0),App.Vector(0.75,1.25,0))) + sk.addGeometry(Part.LineSegment(App.Vector(0.75,1.25,0),App.Vector(0.75,-1.20,0))) + sk.addGeometry(Part.LineSegment(App.Vector(0.75,-1.20,0),App.Vector(-0.85,-1.20,0))) + sk.addGeometry(Part.LineSegment(App.Vector(-0.85,-1.20,0),App.Vector(-0.85,1.25,0))) + sk.addConstraint(Sketcher.Constraint('Coincident',0,2,1,1)) + sk.addConstraint(Sketcher.Constraint('Coincident',1,2,2,1)) + sk.addConstraint(Sketcher.Constraint('Coincident',2,2,3,1)) + sk.addConstraint(Sketcher.Constraint('Coincident',3,2,0,1)) + r = Arch.makeRebar(s,sk,diameter=.1,amount=2) + self.assertTrue(r,"Arch Rebar failed") + + def testBuildingPart(self): + """Create a BuildingPart from a wall with a window and check its shape. + """ + # Also regression test for: + # https://github.com/FreeCAD/FreeCAD/issues/6178 + operation = "Arch BuildingPart" + _msg(" Test '{}'".format(operation)) + # Most of the code below taken from testWindow function. + line = Draft.makeLine(App.Vector(0, 0, 0), App.Vector(3000, 0, 0)) + wall = Arch.makeWall(line) + sk = App.ActiveDocument.addObject("Sketcher::SketchObject", "Sketch001") + sk.Placement.Rotation = App.Rotation(App.Vector(1, 0, 0), 90) + sk.addGeometry(Part.LineSegment(App.Vector( 500, 800, 0), App.Vector(1500, 800, 0))) + sk.addGeometry(Part.LineSegment(App.Vector(1500, 800, 0), App.Vector(1500, 2000, 0))) + sk.addGeometry(Part.LineSegment(App.Vector(1500, 2000, 0), App.Vector( 500, 2000, 0))) + sk.addGeometry(Part.LineSegment(App.Vector( 500, 2000, 0), App.Vector( 500, 800, 0))) + sk.addConstraint(Sketcher.Constraint('Coincident', 0, 2, 1, 1)) + sk.addConstraint(Sketcher.Constraint('Coincident', 1, 2, 2, 1)) + sk.addConstraint(Sketcher.Constraint('Coincident', 2, 2, 3, 1)) + sk.addConstraint(Sketcher.Constraint('Coincident', 3, 2, 0, 1)) + App.ActiveDocument.recompute() + win = Arch.makeWindow(sk) + Arch.removeComponents(win, host=wall) + App.ActiveDocument.recompute() + bp = Arch.makeBuildingPart() + + # Wall visibility works when standalone + FreeCADGui.Selection.clearSelection() + FreeCADGui.Selection.addSelection('ArchTest',wall.Name) + assert wall.Visibility + FreeCADGui.runCommand('Std_ToggleVisibility',0) + App.ActiveDocument.recompute() + assert not wall.Visibility + FreeCADGui.runCommand('Std_ToggleVisibility',0) + assert wall.Visibility + + bp.Group = [wall] + App.ActiveDocument.recompute() + # Fails with OCC 7.5 + # self.assertTrue(len(bp.Shape.Faces) == 16, "'{}' failed".format(operation)) + + # Wall visibility works when inside a BuildingPart + FreeCADGui.runCommand('Std_ToggleVisibility',0) + App.ActiveDocument.recompute() + assert not wall.Visibility + FreeCADGui.runCommand('Std_ToggleVisibility',0) + assert wall.Visibility + + # Wall visibility works when BuildingPart Toggled + FreeCADGui.Selection.clearSelection() + FreeCADGui.Selection.addSelection('ArchTest',bp.Name) + FreeCADGui.runCommand('Std_ToggleVisibility',0) + assert not wall.Visibility + FreeCADGui.runCommand('Std_ToggleVisibility',0) + assert wall.Visibility + + # Wall visibiity works inside group inside BuildingPart Toggled + grp = App.ActiveDocument.addObject("App::DocumentObjectGroup","Group") + grp.Label="Group" + grp.Group = [wall] + bp.Group = [grp] + App.ActiveDocument.recompute() + assert wall.Visibility + FreeCADGui.runCommand('Std_ToggleVisibility',0) + App.ActiveDocument.recompute() + assert not wall.Visibility + FreeCADGui.runCommand('Std_ToggleVisibility',0) + App.ActiveDocument.recompute() + assert wall.Visibility + + def testImportSH3D(self): + """Import a SweetHome 3D file + """ + operation = "importers.importSH3D" + _msg(" Test '{}'".format(operation)) + import BIM.importers.importSH3DHelper + importer = BIM.importers.importSH3DHelper.SH3DImporter(None) + importer.import_sh3d_from_string(SH3D_HOME) + assert App.ActiveDocument.Site + assert App.ActiveDocument.BuildingPart.Label == "Building" + assert App.ActiveDocument.BuildingPart001.Label == "Level" + assert App.ActiveDocument.Wall + + def tearDown(self): + App.closeDocument("ArchTest") + pass + + +SH3D_HOME = """ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +""" diff --git a/src/Mod/BIM/bimcommands/BimMaterial.py b/src/Mod/BIM/bimcommands/BimMaterial.py index 581c4852b7..221678568e 100644 --- a/src/Mod/BIM/bimcommands/BimMaterial.py +++ b/src/Mod/BIM/bimcommands/BimMaterial.py @@ -150,7 +150,10 @@ class BIM_Material: ) buttonMergeDupes.setIcon(QtGui.QIcon(":/icons/view-refresh.svg")) createButtonsLayout.addWidget(buttonMergeDupes, 1, 0) + self.dlg.buttonMergeDupes = buttonMergeDupes buttonMergeDupes.clicked.connect(self.onMergeDupes) + if len(self.dlg.materials) < 2: + buttonMergeDupes.setEnabled(False) # delete unused buttonDeleteUnused = QtGui.QPushButton( @@ -208,20 +211,15 @@ class BIM_Material: first = True for mat in self.dlg.materials: orig = None - for om in mats: - if om.Label == mat.Label: - orig = om - break - else: - if ( - mat.Label[-1].isdigit() - and mat.Label[-2].isdigit() - and mat.Label[-3].isdigit() - ): - for om in self.dlg.materials: - if om.Label == mat.Label[:-3].strip(): - orig = om - break + if ( + mat.Label[-1].isdigit() + and mat.Label[-2].isdigit() + and mat.Label[-3].isdigit() + ): + for om in self.dlg.materials: + if om.Label == mat.Label[:-3].strip(): + orig = om + break if orig: for par in mat.InList: for prop in par.PropertiesList: @@ -522,6 +520,9 @@ class BIM_Material: i.setFlags(i.flags() | QtCore.Qt.ItemIsEditable) if o.Name == name: self.dlg.matList.setCurrentItem(i) + if hasattr(self.dlg, "buttonMergeDupes"): + hasMultipleMaterials = len(self.dlg.materials) > 1 + self.dlg.buttonMergeDupes.setEnabled(hasMultipleMaterials) def createIcon(self, obj): from PySide import QtCore, QtGui diff --git a/src/Mod/BIM/bimcommands/BimProjectManager.py b/src/Mod/BIM/bimcommands/BimProjectManager.py index 96041080c6..a22e349a8d 100644 --- a/src/Mod/BIM/bimcommands/BimProjectManager.py +++ b/src/Mod/BIM/bimcommands/BimProjectManager.py @@ -144,6 +144,7 @@ class BIM_ProjectManager: import Draft import FreeCADGui import Part + from draftutils import params vaxes = [] haxes = [] @@ -306,8 +307,8 @@ class BIM_ProjectManager: outtext = Draft.make_text( [buildingname], FreeCAD.Vector( - Draft.getParam("textheight", 0.20) * 0.3, - -Draft.getParam("textheight", 0.20) * 1.43, + params.get_param("textheight") * 0.3, + -params.get_param("textheight") * 1.43, 0, ), ) @@ -324,8 +325,8 @@ class BIM_ProjectManager: axisV.Label = translate("BIM", "Vertical Axes") axisV.ViewObject.BubblePosition = "Both" axisV.ViewObject.LineWidth = self.form.lineWidth.value() - axisV.ViewObject.FontSize = Draft.getParam("textheight", 0.20) - axisV.ViewObject.BubbleSize = Draft.getParam("textheight", 0.20) * 1.43 + axisV.ViewObject.FontSize = params.get_param("textheight") + axisV.ViewObject.BubbleSize = params.get_param("textheight") * 1.43 axisV.ViewObject.LineColor = color axisH = None if self.form.countHAxes.value() and distHAxes: @@ -336,8 +337,8 @@ class BIM_ProjectManager: axisH.ViewObject.BubblePosition = "Both" axisH.ViewObject.NumberingStyle = "A,B,C" axisH.ViewObject.LineWidth = self.form.lineWidth.value() - axisH.ViewObject.FontSize = Draft.getParam("textheight", 0.20) - axisH.ViewObject.BubbleSize = Draft.getParam("textheight", 0.20) * 1.43 + axisH.ViewObject.FontSize = params.get_param("textheight") + axisH.ViewObject.BubbleSize = params.get_param("textheight") * 1.43 axisH.Placement.Rotation = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), 90) axisH.ViewObject.LineColor = color if axisV and axisH: diff --git a/src/Mod/BIM/bimtests/TestArchBase.py b/src/Mod/BIM/bimtests/TestArchBase.py new file mode 100644 index 0000000000..4394d90f29 --- /dev/null +++ b/src/Mod/BIM/bimtests/TestArchBase.py @@ -0,0 +1,43 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# *************************************************************************** +# * * +# * Copyright (c) 2025 Furgo * +# * * +# * This file is part of FreeCAD. * +# * * +# * FreeCAD is free software: you can redistribute it and/or modify it * +# * under the terms of the GNU Lesser General Public License as * +# * published by the Free Software Foundation, either version 2.1 of the * +# * License, or (at your option) any later version. * +# * * +# * FreeCAD 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 * +# * Lesser General Public License for more details. * +# * * +# * You should have received a copy of the GNU Lesser General Public * +# * License along with FreeCAD. If not, see * +# * . * +# * * +# *************************************************************************** + +"""Defines the base class for Arch module unit tests.""" + +import unittest +import FreeCAD + +class TestArchBase(unittest.TestCase): + + def setUp(self): + self.document = FreeCAD.newDocument(self.__class__.__name__) + + def tearDown(self): + FreeCAD.closeDocument(self.document.Name) + + def printTestMessage(self, text, prepend_text="Test ", end="\n"): + """Write messages to the console including the line ending. + + Messages will be prepended with "Test ", unless an empty string is + passed as the prepend_text argument + """ + FreeCAD.Console.PrintMessage(prepend_text + text + end) diff --git a/src/Mod/BIM/bimtests/TestArchRoof.py b/src/Mod/BIM/bimtests/TestArchRoof.py new file mode 100644 index 0000000000..ded401712a --- /dev/null +++ b/src/Mod/BIM/bimtests/TestArchRoof.py @@ -0,0 +1,150 @@ +#*************************************************************************** +#* Copyright (c) 2013 Yorik van Havre * +#* * +#* This file is part of the FreeCAD CAx development system. * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* FreeCAD 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 FreeCAD; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#***************************************************************************/ + +# Unit tests for the Arch wall module + +import os +import unittest +import Arch +import Draft +import Part +import FreeCAD as App +from bimtests import TestArchBase +from draftutils.messages import _msg + +class TestArchRoof(TestArchBase.TestArchBase): + + def testRoof(self): + operation = "Checking Arch Roof..." + self.printTestMessage(operation) + + r = Draft.makeRectangle(length=2,height=-1) + r.recompute() # required before calling Arch.makeRoof + ro = Arch.makeRoof(r) + self.assertTrue(ro,"Arch Roof failed") + + def testRoof81Permutations(self): + """Create 81 roofs using a range of arguments. + """ + operation = "Arch Roof testRoof81Permutations" + self.printTestMessage(operation) + + pts = [App.Vector( 0, 0, 0), + App.Vector(2000, 0, 0), + App.Vector(4000, 0, 0), + App.Vector(4000, 4000, 0), + App.Vector( 0, 4000, 0)] + ptsMod = [App.Vector(2000, 0, 0), + App.Vector(2000, -1000, 0), + App.Vector(2000, 1000, 0)] + angsMod = [[60, 60], [30, 60], [60, 30]] + runsMod = [[500, 500], [400, 500], [500, 400]] + overhangsMod = [[100, 100], [100, 200], [200, 100]] + delta = 6000 + pla = App.Placement() + for iY in range(9): + for iX in range(9): + pts[1] = ptsMod[iY % 3] # to get different edge angles + angsLst = angsMod[iY // 3] + [90, 90, 90] + runsLst = runsMod[iX % 3] + [0, 0, 0] + overhangsLst = overhangsMod[iX // 3] + [0, 0, 0] + pla.Base = App.Vector(iX * delta, iY * delta, 0) + wire = Draft.makeWire(pts, closed = True) + wire.MakeFace = False + wire.Placement = pla + wire.recompute() # required before calling Arch.makeRoof + roof = Arch.makeRoof(wire, + angles = angsLst, + run = runsLst, + overhang = overhangsLst) + roof.recompute() + self.assertFalse(roof.Shape.isNull(), + "'{}' failed".format(operation)) + self.assertTrue(roof.Shape.isValid(), + "'{}' failed".format(operation)) + + def testRoofAllAngles90(self): + """Create a roof with the angles of all segments set at 90 degrees. + This corner case results in a flat roof. + """ + operation = "Arch Roof testRoofAllAngles90" + self.printTestMessage(operation) + + pts = [App.Vector( 0, 0, 0), + App.Vector(2000, 0, 0), + App.Vector(2000, 2000, 0), + App.Vector( 0, 2000, 0)] + + wire = Draft.makeWire(pts, closed = True) + wire.MakeFace = False + wire.recompute() # required before calling Arch.makeRoof + roof = Arch.makeRoof(wire, + angles = [90, 90, 90, 90]) + roof.recompute() + self.assertFalse(roof.Shape.isNull(), "'{}' failed".format(operation)) + self.assertTrue(roof.Shape.isValid(), "'{}' failed".format(operation)) + + def testRoofApex(self): + """Create a hipped roof that relies on apex calculation. The roof has + 2 triangular segments with a single apex point. + """ + operation = "Arch Roof testRoofApex" + self.printTestMessage(operation) + + rec = Draft.makeRectangle(length = 4000, + height = 3000, + face = False) + rec.recompute() # required before calling Arch.makeRoof + roof = Arch.makeRoof(rec, + angles = [30, 40, 50, 60], + run = [2000, 0, 2000, 0], + idrel = [-1, 0, -1, 0], + thickness = [50.0], + overhang = [100.0]) + roof.recompute() + self.assertFalse(roof.Shape.isNull(), "'{}' failed".format(operation)) + self.assertTrue(roof.Shape.isValid(), "'{}' failed".format(operation)) + + def testRoofSingleEavePoint(self): + """Create a roof with a single triangular segment that has a single + eave point. + """ + operation = "Arch Roof testRoofSingleEavePoint" + self.printTestMessage(operation) + + pts = [App.Vector( 0, 0, 0), + App.Vector( 2000, 0, 0), + App.Vector( 4000, 2000, 0), + App.Vector(-2000, 2000, 0)] + wire = Draft.makeWire(pts, closed = True) + wire.MakeFace = False + wire.recompute() # required before calling Arch.makeRoof + roof = Arch.makeRoof(wire, + angles = [45, 90, 90, 90], + run = [1000, 0, 0, 0], + overhang = [1000, 0, 0, 0]) + roof.recompute() + self.assertFalse(roof.Shape.isNull(), "'{}' failed".format(operation)) + self.assertTrue(roof.Shape.isValid(), "'{}' failed".format(operation)) + + diff --git a/src/Mod/BIM/bimtests/TestArchSpace.py b/src/Mod/BIM/bimtests/TestArchSpace.py new file mode 100644 index 0000000000..a542fb9129 --- /dev/null +++ b/src/Mod/BIM/bimtests/TestArchSpace.py @@ -0,0 +1,500 @@ +#*************************************************************************** +#* Copyright (c) 2013 Yorik van Havre * +#* * +#* This file is part of the FreeCAD CAx development system. * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* FreeCAD 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 FreeCAD; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#***************************************************************************/ + +# Unit tests for the Arch space module + +import os +import unittest +import Arch +import Draft +import Part +import FreeCAD as App +from FreeCAD import Units +from bimtests import TestArchBase +import WorkingPlane + +def like(a, b): + return abs(a-b) < 0.001 + +def checkBB(a, b): + return like(a.XMin, b.XMin) and like(a.YMin, b.YMin) and like(a.ZMin, b.ZMin) and like(a.XMax, b.XMax) and like(a.YMax, b.YMax) and like(a.ZMax, b.ZMax) + +class TestArchSpace(TestArchBase.TestArchBase): + + def testSpace(self): + operation = "Checking Arch Space..." + self.printTestMessage(operation) + + sb = Part.makeBox(1,1,1) + b = App.ActiveDocument.addObject('Part::Feature','Box') + b.Shape = sb + s = Arch.makeSpace([b]) + self.assertTrue(s,"Arch Space failed") + + def testSpaceBBox(self): + operation = "Checking Arch Space bound box..." + self.printTestMessage(operation) + + shape = Part.Shape() + shape.importBrepFromString(brepArchiCAD) + bborig = shape.BoundBox + App.Console.PrintLog ("Original BB: "+str(bborig)) + baseobj = App.ActiveDocument.addObject("Part::Feature","brepArchiCAD_body") + baseobj.Shape = shape + space = Arch.makeSpace(baseobj) + space.recompute() + bbnew = space.Shape.BoundBox + App.Console.PrintLog ("New BB: "+str(bbnew)) + self.assertTrue(checkBB(bborig,bbnew),"Arch Space has wrong Placement") + + + def test_addSpaceBoundaries(self): + """Test the Arch.addSpaceBoundaries method. + Create a space and a wall that intersects it. Add the wall as a boundary to the space, + and check if the resulting space area is as expected. + """ + operation = "Add a wall face as a boundary to a space" + self.printTestMessage(operation) + + # Create the space + pl = App.Placement() + pl.Rotation.Q = (0.0, 0.0, 0.0, 1.0) + pl.Base = App.Vector(-2000.0, -2000.0, 0.0) + rectangleBase = Draft.make_rectangle( + length=4000.0, height=4000.0, placement=pl, face=True, support=None) + App.ActiveDocument.recompute() + extr = rectangleBase.Shape.extrude(App.Vector(0,0,2000)) + Part.show(extr, 'Extrusion') + space = Arch.makeSpace(App.activeDocument().getObject('Extrusion')) + App.ActiveDocument.recompute() # To calculate area + + # Create the wall + trace = Part.LineSegment(App.Vector (3000.0, 1000.0, 0.0), + App.Vector (-3000.0, 1000.0, 0.0)) + wp = WorkingPlane.get_working_plane() + base = App.ActiveDocument.addObject("Sketcher::SketchObject","WallTrace") + base.Placement = wp.get_placement() + base.addGeometry(trace) + wall = Arch.makeWall(base,width=200.0,height=3000.0,align="Left") + wall.Normal = wp.axis + + # Add the boundary + wallBoundary = [(wall, ["Face1"])] + Arch.addSpaceBoundaries(App.ActiveDocument.Space, wallBoundary) + App.ActiveDocument.recompute() # To recalculate area + + # Assert if area is as expected + expectedArea = Units.parseQuantity('12 m^2') + actualArea = Units.parseQuantity(str(space.Area)) + + self.assertAlmostEqual( + expectedArea.Value, + actualArea.Value, + msg = (f"Invalid area value. " + + f"Expected: {expectedArea.UserString}, actual: {actualArea.UserString}")) + + def test_SpaceFromSingleWall(self): + """Create a space from boundaries of a single wall. + """ + operation = "Arch Space from single wall" + self.printTestMessage(operation) + + # Create a wall + wallInnerLength = 4000.0 + wallHeight = 3000.0 + wallInnerFaceArea = wallInnerLength * wallHeight + pl = App.Placement() + pl.Rotation.Q = (0.0, 0.0, 0.0, 1.0) + pl.Base = App.Vector(0.0, 0.0, 0.0) + rectangleBase = Draft.make_rectangle( + length=wallInnerLength, height=wallInnerLength, placement=pl, face=True, support=None) + App.ActiveDocument.recompute() # To calculate rectangle area + rectangleArea = rectangleBase.Area + App.ActiveDocument.getObject(rectangleBase.Name).MakeFace = False + wall = Arch.makeWall(baseobj=rectangleBase, height=wallHeight, align="Left") + App.ActiveDocument.recompute() # To calculate face areas + + # Create a space from the wall's inner faces + boundaries = [f"Face{ind+1}" for ind, face in enumerate(wall.Shape.Faces) + if round(face.Area) == round(wallInnerFaceArea)] + + if App.GuiUp: + import FreeCADGui + FreeCADGui.Selection.clearSelection() + FreeCADGui.Selection.addSelection(wall, boundaries) + + space = Arch.makeSpace(FreeCADGui.Selection.getSelectionEx()) + # Alternative, but test takes longer to run (~10x) + # FreeCADGui.activateWorkbench("BIMWorkbench") + # FreeCADGui.runCommand('Arch_Space', 0) + # space = App.ActiveDocument.Space + else: + # Also tests the alternative way of specifying the boundaries + # [ (, ["Face1", ...]), ... ] + space = Arch.makeSpace([(wall, boundaries)]) + + App.ActiveDocument.recompute() # To calculate space area + + # Assert if area is as expected + expectedArea = Units.parseQuantity(str(rectangleArea)) + actualArea = Units.parseQuantity(str(space.Area)) + + self.assertAlmostEqual( + expectedArea.Value, + actualArea.Value, + msg = f"Invalid area value. Expected: {expectedArea.UserString}, actual: {actualArea.UserString}") + + +brepArchiCAD = """ +DBRep_DrawableShape + +CASCADE Topology V1, (c) Matra-Datavision +Locations 3 +1 + 1 0 0 0 + 0 1 0 0 + 0 0 1 0 +1 + 0 1 0 0 + -1 0 0 0 + 0 0 1 0 +2 2 -1 0 +Curve2ds 0 +Curves 12 +1 0 0 0 1 0 0 +1 3000 0 0 0 0 1 +1 3000 0 3000 -1 0 0 +1 0 0 3000 0 0 -1 +1 0 0 0 0 1 0 +1 0 5000 0 1 0 0 +1 3000 5000 0 0 -1 0 +1 3000 5000 0 0 0 1 +1 3000 5000 3000 0 -1 0 +1 3000 5000 3000 -1 0 0 +1 0 5000 3000 0 -1 0 +1 0 5000 3000 0 0 -1 +Polygon3D 0 +PolygonOnTriangulations 24 +2 1 2 +p 18.3333333333333 1 0 3000 +2 1 4 +p 18.3333333333333 1 0 3000 +2 2 3 +p 18.3333333333333 1 0 3000 +2 2 4 +p 18.3333333333333 1 0 3000 +2 3 4 +p 18.3333333333333 1 0 3000 +2 1 2 +p 18.3333333333333 1 0 3000 +2 4 1 +p 18.3333333333333 1 0 3000 +2 3 1 +p 18.3333333333333 1 0 3000 +2 1 2 +p 18.3333333333333 1 0 5000 +2 1 2 +p 18.3333333333333 1 0 5000 +2 2 3 +p 18.3333333333333 1 0 3000 +2 1 2 +p 18.3333333333333 1 0 3000 +2 3 4 +p 18.3333333333333 1 0 5000 +2 1 2 +p 18.3333333333333 1 0 5000 +2 1 3 +p 18.3333333333333 1 0 3000 +2 2 4 +p 18.3333333333333 1 0 3000 +2 3 4 +p 18.3333333333333 1 0 5000 +2 3 1 +p 18.3333333333333 1 0 5000 +2 3 4 +p 18.3333333333333 1 0 3000 +2 4 3 +p 18.3333333333333 1 0 3000 +2 4 2 +p 18.3333333333333 1 0 5000 +2 4 3 +p 18.3333333333333 1 0 5000 +2 4 2 +p 18.3333333333333 1 0 3000 +2 3 1 +p 18.3333333333333 1 0 3000 +Surfaces 6 +1 1500 0 1500 -0 -1 -0 0 0 -1 1 0 0 +1 1500 2500 0 -0 -0 -1 -1 0 0 0 1 0 +1 3000 2500 1500 1 0 0 0 0 1 0 -1 0 +1 1500 2500 3000 0 0 1 1 0 0 0 1 0 +1 0 2500 1500 -1 -0 -0 0 0 -1 0 -1 0 +1 1500 5000 1500 0 1 0 0 0 1 1 0 0 +Triangulations 6 +4 2 1 18.3333333333333 +0 0 0 3000 0 0 3000 0 3000 0 0 3000 1500 -1500 1500 1500 -1500 1500 -1500 -1500 3 4 1 2 3 1 +4 2 1 18.3333333333333 +0 0 0 0 5000 0 3000 5000 0 3000 0 0 1500 -2500 1500 2500 -1500 2500 -1500 -2500 2 3 4 2 4 1 +4 2 1 18.3333333333333 +3000 5000 0 3000 0 0 3000 5000 3000 3000 0 3000 -1500 -2500 -1500 2500 1500 -2500 1500 2500 4 2 1 4 1 3 +4 2 1 18.3333333333333 +3000 0 3000 0 0 3000 3000 5000 3000 0 5000 3000 1500 -2500 -1500 -2500 1500 2500 -1500 2500 3 2 1 3 4 2 +4 2 1 18.3333333333333 +0 0 0 0 5000 0 0 0 3000 0 5000 3000 1500 2500 1500 -2500 -1500 2500 -1500 -2500 1 3 4 1 4 2 +4 2 1 18.3333333333333 +0 5000 0 3000 5000 0 0 5000 3000 3000 5000 3000 -1500 -1500 -1500 1500 1500 -1500 1500 1500 3 2 1 4 2 3 + +TShapes 35 +Ve +0.1 +0 0 0 +0 0 + +0101101 +* +Ve +0.1 +0 -3000 0 +0 0 + +0101101 +* +Ed + 0.0001 1 1 0 +1 1 0 0 3000 +6 1 1 0 +6 2 2 0 +0 + +0101000 ++35 3 -34 3 * +Ve +0.1 +0 -3000 3000 +0 0 + +0101101 +* +Ed + 0.0001 1 1 0 +1 2 0 0 3000 +6 3 1 0 +6 4 3 0 +0 + +0101000 ++34 3 -32 3 * +Ve +0.1 +0 0 3000 +0 0 + +0101101 +* +Ed + 0.0001 1 1 0 +1 3 0 0 3000 +6 5 1 0 +6 6 4 0 +0 + +0101000 ++32 3 -30 3 * +Ed + 0.0001 1 1 0 +1 4 0 0 3000 +6 7 1 0 +6 8 5 0 +0 + +0101000 ++30 3 -35 3 * +Wi + +0101100 ++33 0 +31 0 +29 0 +28 0 * +Fa +0 0.1 1 0 +2 1 +0111000 ++27 0 * +Ve +0.1 +5000 0 0 +0 0 + +0101101 +* +Ed + 0.0001 1 1 0 +1 5 0 0 5000 +6 9 2 0 +6 10 5 0 +0 + +0101000 ++35 3 -25 3 * +Ve +0.1 +5000 -3000 0 +0 0 + +0101101 +* +Ed + 0.0001 1 1 0 +1 6 0 0 3000 +6 11 2 0 +6 12 6 0 +0 + +0101000 ++25 3 -23 3 * +Ed + 0.0001 1 1 0 +1 7 0 0 5000 +6 13 2 0 +6 14 3 0 +0 + +0101000 ++23 3 -34 3 * +Wi + +0101100 ++24 0 +22 0 +21 0 -33 0 * +Fa +0 0.1 2 0 +2 2 +0111000 ++20 0 * +Ve +0.1 +5000 -3000 3000 +0 0 + +0101101 +* +Ed + 0.0001 1 1 0 +1 8 0 0 3000 +6 15 3 0 +6 16 6 0 +0 + +0101000 ++23 3 -18 3 * +Ed + 0.0001 1 1 0 +1 9 0 0 5000 +6 17 3 0 +6 18 4 0 +0 + +0101000 ++18 3 -32 3 * +Wi + +0101100 +-21 0 +17 0 +16 0 -31 0 * +Fa +0 0.1 3 0 +2 3 +0111000 ++15 0 * +Ve +0.1 +5000 0 3000 +0 0 + +0101101 +* +Ed + 0.0001 1 1 0 +1 10 0 0 3000 +6 19 4 0 +6 20 6 0 +0 + +0101000 ++18 3 -13 3 * +Ed + 0.0001 1 1 0 +1 11 0 0 5000 +6 21 4 0 +6 22 5 0 +0 + +0101000 ++13 3 -30 3 * +Wi + +0101100 +-29 0 -16 0 +12 0 +11 0 * +Fa +0 0.1 4 0 +2 4 +0111000 ++10 0 * +Ed + 0.0001 1 1 0 +1 12 0 0 3000 +6 23 5 0 +6 24 6 0 +0 + +0101000 ++13 3 -25 3 * +Wi + +0101100 +-24 0 -28 0 -11 0 +8 0 * +Fa +0 0.1 5 0 +2 5 +0111000 ++7 0 * +Wi + +0101100 +-22 0 -8 0 -12 0 -17 0 * +Fa +0 0.1 6 0 +2 6 +0111000 ++5 0 * +Sh + +0101100 ++26 0 +19 0 +14 0 +9 0 +6 0 +4 0 * +So + +0100000 ++3 0 * +Co + +1100000 ++2 2 * + ++1 1 +""" diff --git a/src/Mod/BIM/bimtests/TestArchWall.py b/src/Mod/BIM/bimtests/TestArchWall.py new file mode 100644 index 0000000000..64c3676e85 --- /dev/null +++ b/src/Mod/BIM/bimtests/TestArchWall.py @@ -0,0 +1,85 @@ +#*************************************************************************** +#* Copyright (c) 2013 Yorik van Havre * +#* * +#* This file is part of the FreeCAD CAx development system. * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* FreeCAD 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 FreeCAD; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#***************************************************************************/ + +# Unit tests for the Arch wall module + +import os +import unittest +import Arch +import Draft +import Part +import FreeCAD as App +from bimtests import TestArchBase + + +class TestArchWall(TestArchBase.TestArchBase): + + def testWall(self): + operation = "Checking Arch Wall..." + self.printTestMessage(operation) + + l=Draft.makeLine(App.Vector(0,0,0),App.Vector(-2,0,0)) + w = Arch.makeWall(l) + self.assertTrue(w,"Arch Wall failed") + + def testWallMultiMatAlign(self): + operation = "Checking Arch Wall with MultiMaterial and 3 alignments..." + self.printTestMessage(operation) + + matA = Arch.makeMaterial() + matB = Arch.makeMaterial() + matMulti = Arch.makeMultiMaterial() + matMulti.Materials = [matA, matB] + matMulti.Thicknesses = [100, 200] # total width different from default 200 + pts = [App.Vector( 0, 0, 0), + App.Vector(1000, 0, 0), + App.Vector(1000, 1000, 0), + App.Vector(2000, 1000, 0)] + # wall based on wire: + wire = Draft.makeWire(pts) + wallWire = Arch.makeWall(wire) + wallWire.Material = matMulti + # wall based on sketch: + sketch = App.activeDocument().addObject('Sketcher::SketchObject','Sketch') + sketch.addGeometry([Part.LineSegment(pts[0], pts[1]), + Part.LineSegment(pts[1], pts[2]), + Part.LineSegment(pts[2], pts[3])]) + wallSketch = Arch.makeWall(sketch) + wallSketch.Material = matMulti + + alignLst = ["Left", "Center", "Right"] + checkLst = [[App.Vector(0, -300, 0), App.Vector(2000, 1000, 0)], + [App.Vector(0, -150, 0), App.Vector(2000, 1150, 0)], + [App.Vector(0, 0, 0), App.Vector(2000, 1300, 0)]] + for i in range(3): + wallWire.Align = alignLst[i] + wallSketch.Align = alignLst[i] + App.ActiveDocument.recompute() + for box in [wallWire.Shape.BoundBox, wallSketch.Shape.BoundBox]: + ptMin = App.Vector(box.XMin, box.YMin, 0) + self.assertTrue(ptMin.isEqual(checkLst[i][0], 1e-8), + "Arch Wall with MultiMaterial and 3 alignments failed") + ptMax = App.Vector(box.XMax, box.YMax, 0) + self.assertTrue(ptMax.isEqual(checkLst[i][1], 1e-8), + "Arch Wall with MultiMaterial and 3 alignments failed") + diff --git a/tests/src/Mod/Material/App/Model.cpp b/src/Mod/BIM/bimtests/__init__.py similarity index 100% rename from tests/src/Mod/Material/App/Model.cpp rename to src/Mod/BIM/bimtests/__init__.py diff --git a/src/Mod/CAM/App/Area.cpp b/src/Mod/CAM/App/Area.cpp index df87a8f527..82892eef4d 100644 --- a/src/Mod/CAM/App/Area.cpp +++ b/src/Mod/CAM/App/Area.cpp @@ -26,7 +26,6 @@ #define BOOST_GEOMETRY_DISABLE_DEPRECATED_03_WARNING #ifndef _PreComp_ -#include #include #include @@ -452,7 +451,7 @@ void Area::addWire(CArea& area, if (reversed) { type = -type; } - if (fabs(first - last) > M_PI) { + if (fabs(first - last) > std::numbers::pi) { // Split arc(circle) larger than half circle. Because gcode // can't handle full circle? gp_Pnt mid = curve.Value((last - first) * 0.5 + first); @@ -1221,7 +1220,8 @@ struct WireJoiner info.iEnd[i] = info.iStart[i] = (int)adjacentList.size(); // populate adjacent list - for (auto vit = vmap.qbegin(bgi::nearest(pt[i], INT_MAX)); vit != vmap.qend(); + constexpr int intMax = std::numeric_limits::max(); + for (auto vit = vmap.qbegin(bgi::nearest(pt[i], intMax)); vit != vmap.qend(); ++vit) { ++rcount; if (vit->pt().SquareDistance(pt[i]) > tol) { @@ -2631,7 +2631,7 @@ TopoDS_Shape Area::makePocket(int index, PARAM_ARGS(PARAM_FARG, AREA_PARAMS_POCK for (int j = 0; j < steps; ++j, offset += stepover) { Point p1(-r, offset), p2(r, offset); if (a > Precision::Confusion()) { - double r = a * M_PI / 180.0; + double r = a * std::numbers::pi / 180.0; p1.Rotate(r); p2.Rotate(r); } @@ -3703,7 +3703,7 @@ std::list Area::sortWires(const std::list& shapes, double max_dist = sort_mode == SortModeGreedy ? threshold * threshold : 0; while (!shape_list.empty()) { AREA_TRACE("sorting " << shape_list.size() << ' ' << AREA_XYZ(pstart)); - double best_d = DBL_MAX; + double best_d = std::numeric_limits::max(); auto best_it = shape_list.begin(); for (auto it = best_it; it != shape_list.end(); ++it) { double d; @@ -4155,7 +4155,7 @@ void Area::toPath(Toolpath& path, } } - if (fabs(first - last) > M_PI) { + if (fabs(first - last) > std::numbers::pi) { // Split arc(circle) larger than half circle. gp_Pnt mid = curve.Value((last - first) * 0.5 + first); addGArc(verbose, diff --git a/src/Mod/CAM/App/ParamsHelper.h b/src/Mod/CAM/App/ParamsHelper.h index 32ecb7237b..95f4d23243 100644 --- a/src/Mod/CAM/App/ParamsHelper.h +++ b/src/Mod/CAM/App/ParamsHelper.h @@ -27,7 +27,7 @@ * \ingroup PATH * Collections of macros for managing groups of parameters. * - * \section Motivation + * \section motivation_groups_params Motivation * * For an application like FreeCAD, there are often cases where the same set of * parameters are referred in dozons of different places. The macros here is @@ -61,7 +61,7 @@ * /src/Mod/CAM/App.CMakeFiles/Path.dir/Area.cpp.i * \endcode * - * \section Introduction of Boost.Preprocessor + * \section intro_boost_preproc Introduction of Boost.Preprocessor * * The macros here make heavy use of the awesome * [Boost.Preprocessor](http://www.boost.org/libs/preprocessor/) (short for @@ -1017,6 +1017,7 @@ * if(name1.isTouched()) return 1; * if(name2.isTouched()) return 1; * ... + * \endcode * \ingroup ParamProperty */ #define PARAM_PROP_TOUCHED(_seq) PARAM_FOREACH(PARAM_PROP_TOUCHED_, _seq) diff --git a/src/Mod/CAM/App/PathSegmentWalker.cpp b/src/Mod/CAM/App/PathSegmentWalker.cpp index 2cd883ef09..5b9a54b6cb 100644 --- a/src/Mod/CAM/App/PathSegmentWalker.cpp +++ b/src/Mod/CAM/App/PathSegmentWalker.cpp @@ -31,14 +31,6 @@ #define ARC_MIN_SEGMENTS 20.0 // minimum # segments to interpolate an arc -#ifndef M_PI -#define M_PI 3.14159265358979323846 /* pi */ -#endif - -#ifndef M_PI_2 -#define M_PI_2 1.57079632679489661923 /* pi/2 */ -#endif - namespace Path { @@ -195,7 +187,7 @@ void PathSegmentWalker::walk(PathSegmentVisitor& cb, const Base::Vector3d& start if (nrot != lrot) { double amax = std::max(fmod(fabs(a - A), 360), std::max(fmod(fabs(b - B), 360), fmod(fabs(c - C), 360))); - double angle = amax / 180 * M_PI; + double angle = amax / 180 * std::numbers::pi; int segments = std::max(ARC_MIN_SEGMENTS, 3.0 / (deviation / angle)); double da = (a - A) / segments; @@ -257,16 +249,16 @@ void PathSegmentWalker::walk(PathSegmentVisitor& cb, const Base::Vector3d& start Base::Vector3d anorm = (last0 - center0) % (next0 - center0); if (anorm.*pz < 0) { if (name == "G3" || name == "G03") { - angle = M_PI * 2 - angle; + angle = std::numbers::pi * 2 - angle; } } else if (anorm.*pz > 0) { if (name == "G2" || name == "G02") { - angle = M_PI * 2 - angle; + angle = std::numbers::pi * 2 - angle; } } else if (angle == 0) { - angle = M_PI * 2; + angle = std::numbers::pi * 2; } double amax = std::max(fmod(fabs(a - A), 360), @@ -337,7 +329,7 @@ void PathSegmentWalker::walk(PathSegmentVisitor& cb, const Base::Vector3d& start if (nrot != lrot) { double amax = std::max(fmod(fabs(a - A), 360), std::max(fmod(fabs(b - B), 360), fmod(fabs(c - C), 360))); - double angle = amax / 180 * M_PI; + double angle = amax / 180 * std::numbers::pi; int segments = std::max(ARC_MIN_SEGMENTS, 3.0 / (deviation / angle)); double da = (a - A) / segments; diff --git a/src/Mod/CAM/App/Voronoi.cpp b/src/Mod/CAM/App/Voronoi.cpp index 55aeb601c3..0ccad2e191 100644 --- a/src/Mod/CAM/App/Voronoi.cpp +++ b/src/Mod/CAM/App/Voronoi.cpp @@ -22,8 +22,6 @@ #include "PreCompiled.h" #ifndef _PreComp_ -#define _USE_MATH_DEFINES -#include #endif #include @@ -260,10 +258,10 @@ double Voronoi::diagram_type::angleOfSegment(int i, Voronoi::diagram_type::angle double ang = 0; if (p0.x() == p1.x()) { if (p0.y() < p1.y()) { - ang = M_PI_2; + ang = std::numbers::pi / 2; } else { - ang = -M_PI_2; + ang = -std::numbers::pi / 2; } } else { @@ -292,7 +290,8 @@ bool Voronoi::diagram_type::segmentsAreConnected(int i, int j) const void Voronoi::colorColinear(Voronoi::color_type color, double degree) { - double rad = degree * M_PI / 180; + using std::numbers::pi; + double rad = degree * pi / 180; Voronoi::diagram_type::angle_map_t angle; int psize = vd->points.size(); @@ -306,11 +305,11 @@ void Voronoi::colorColinear(Voronoi::color_type color, double degree) double a0 = vd->angleOfSegment(i0, &angle); double a1 = vd->angleOfSegment(i1, &angle); double a = a0 - a1; - if (a > M_PI_2) { - a -= M_PI; + if (a > pi / 2) { + a -= pi; } - else if (a < -M_PI_2) { - a += M_PI; + else if (a < -pi / 2) { + a += pi; } if (fabs(a) < rad) { it->color(color); diff --git a/src/Mod/CAM/App/Voronoi.h b/src/Mod/CAM/App/Voronoi.h index d7a4ac5c8c..ff77698bfb 100644 --- a/src/Mod/CAM/App/Voronoi.h +++ b/src/Mod/CAM/App/Voronoi.h @@ -22,7 +22,6 @@ #ifndef PATH_VORONOI_H #define PATH_VORONOI_H -#include #include #include #include @@ -33,11 +32,6 @@ #include #include -#if (SIZE_MAX == UINT_MAX) -#define PATH_VORONOI_COLOR_MASK 0x07FFFFFFul -#else -#define PATH_VORONOI_COLOR_MASK 0x07FFFFFFFFFFFFFFul -#endif namespace Path { @@ -51,8 +45,8 @@ public: ~Voronoi() override; using color_type = std::size_t; - static const int InvalidIndex = INT_MAX; - static const color_type ColorMask = PATH_VORONOI_COLOR_MASK; + static const int InvalidIndex = std::numeric_limits::max(); + static const color_type ColorMask = std::numeric_limits::max() >> 5; // types using coordinate_type = double; diff --git a/src/Mod/CAM/App/VoronoiEdgePyImp.cpp b/src/Mod/CAM/App/VoronoiEdgePyImp.cpp index 9015e49d52..bf47258083 100644 --- a/src/Mod/CAM/App/VoronoiEdgePyImp.cpp +++ b/src/Mod/CAM/App/VoronoiEdgePyImp.cpp @@ -466,12 +466,12 @@ PyObject* VoronoiEdgePy::isBorderline(PyObject* args) PyObject* VoronoiEdgePy::toShape(PyObject* args) { double z0 = 0.0; - double z1 = DBL_MAX; + double z1 = std::numeric_limits::max(); int dbg = 0; if (!PyArg_ParseTuple(args, "|ddp", &z0, &z1, &dbg)) { throw Py::RuntimeError("no, one or two arguments of type double accepted"); } - if (z1 == DBL_MAX) { + if (z1 == std::numeric_limits::max()) { z1 = z0; } VoronoiEdge* e = getVoronoiEdgePtr(); @@ -688,6 +688,8 @@ PyObject* VoronoiEdgePy::getDistances(PyObject* args) PyObject* VoronoiEdgePy::getSegmentAngle(PyObject* args) { + using std::numbers::pi; + VoronoiEdge* e = getVoronoiEdgeFromPy(this, args); if (e->ptr->cell()->contains_segment() && e->ptr->twin()->cell()->contains_segment()) { @@ -697,11 +699,11 @@ PyObject* VoronoiEdgePy::getSegmentAngle(PyObject* args) double a0 = e->dia->angleOfSegment(i0); double a1 = e->dia->angleOfSegment(i1); double a = a0 - a1; - if (a > M_PI_2) { - a -= M_PI; + if (a > pi / 2) { + a -= pi; } - else if (a < -M_PI_2) { - a += M_PI; + else if (a < -pi / 2) { + a += pi; } return Py::new_reference_to(Py::Float(a)); } diff --git a/src/Mod/CAM/CAMTests/TestRefactoredGrblPost.py b/src/Mod/CAM/CAMTests/TestRefactoredGrblPost.py index a942985053..eed8364a92 100644 --- a/src/Mod/CAM/CAMTests/TestRefactoredGrblPost.py +++ b/src/Mod/CAM/CAMTests/TestRefactoredGrblPost.py @@ -332,3 +332,22 @@ M2 result = gcode.splitlines()[15] expected = "(comment)" self.assertEqual(result, expected) + + def test100(self): + """ + Test if coolant is enabled. + """ + nl = "\n" + + c = Path.Command("M7") + c1 = Path.Command("M8") + c2 = Path.Command("M9") + + self.profile_op.Path = Path.Path([c, c1, c2]) + + self.job.PostProcessorArgs = "--no-header --no-show-editor" + gcode = self.post.export()[0][1] + # print(f"--------{nl}{gcode}--------{nl}") + self.assertEqual(gcode.splitlines()[15], "M7") + self.assertEqual(gcode.splitlines()[16], "M8") + self.assertEqual(gcode.splitlines()[17], "M9") diff --git a/src/Mod/CAM/CAMTests/TestRefactoredMassoG3Post.py b/src/Mod/CAM/CAMTests/TestRefactoredMassoG3Post.py index 479d781407..29b24e1ad0 100644 --- a/src/Mod/CAM/CAMTests/TestRefactoredMassoG3Post.py +++ b/src/Mod/CAM/CAMTests/TestRefactoredMassoG3Post.py @@ -35,7 +35,7 @@ import FreeCAD import Path import CAMTests.PathTestUtils as PathTestUtils -from Path.Post.scripts import refactored_masso_g3_post as postprocessor +from Path.Post.Processor import PostProcessorFactory Path.Log.setLevel(Path.Log.Level.DEBUG, Path.Log.thisModule()) @@ -54,8 +54,16 @@ class TestRefactoredMassoG3Post(PathTestUtils.PathTestBase): is able to call static methods within this same class. """ - # Open existing FreeCAD document with test geometry - FreeCAD.newDocument("Unnamed") + FreeCAD.ConfigSet("SuppressRecomputeRequiredDialog", "True") + cls.doc = FreeCAD.open(FreeCAD.getHomePath() + "/Mod/CAM/CAMTests/boxtest.fcstd") + cls.job = cls.doc.getObject("Job") + cls.post = PostProcessorFactory.get_post_processor(cls.job, "refactored_masso_g3") + # locate the operation named "Profile" + for op in cls.job.Operations.Group: + if op.Label == "Profile": + # remember the "Profile" operation + cls.profile_op = op + return @classmethod def tearDownClass(cls): @@ -66,8 +74,8 @@ class TestRefactoredMassoG3Post(PathTestUtils.PathTestBase): have access to the class `self` reference. This method is able to call static methods within this same class. """ - # Close geometry document without saving - FreeCAD.closeDocument(FreeCAD.ActiveDocument.Name) + FreeCAD.closeDocument(cls.doc.Name) + FreeCAD.ConfigSet("SuppressRecomputeRequiredDialog", "") # Setup and tear down methods called before and after each unit test def setUp(self): @@ -75,87 +83,106 @@ class TestRefactoredMassoG3Post(PathTestUtils.PathTestBase): This method is called prior to each `test()` method. Add code and objects here that are needed for multiple `test()` methods. """ - self.doc = FreeCAD.ActiveDocument - self.con = FreeCAD.Console - self.docobj = FreeCAD.ActiveDocument.addObject("Path::Feature", "testpath") - reload( - postprocessor - ) # technical debt. This shouldn't be necessary but here to bypass a bug + # allow a full length "diff" if an error occurs + self.maxDiff = None + # reinitialize the postprocessor data structures between tests + self.post.reinitialize() def tearDown(self): """tearDown()... This method is called after each test() method. Add cleanup instructions here. Such cleanup instructions will likely undo those in the setUp() method. """ - FreeCAD.ActiveDocument.removeObject("testpath") + pass def test000(self): """Test Output Generation. Empty path. Produces only the preamble and postable. """ + nl = "\n" - self.docobj.Path = Path.Path([]) - postables = [self.docobj] + self.profile_op.Path = Path.Path([]) # Test generating with header # Header contains a time stamp that messes up unit testing. # Only test length of result. - args = "--no-show-editor" - gcode = postprocessor.export(postables, "-", args) - self.assertTrue(len(gcode.splitlines()) == 14) + self.job.PostProcessorArgs = "--no-show-editor" + gcode = self.post.export()[0][1] + # print(f"--------{nl}{gcode}--------{nl}") + self.assertTrue(len(gcode.splitlines()) == 26) # Test without header expected = """(Begin preamble) G17 G54 G40 G49 G80 G90 G21 -(Begin operation: testpath) +(Begin operation: Fixture) (Machine units: mm/min) -(Finish operation: testpath) +G54 +(Finish operation: Fixture) +(Begin operation: TC: Default Tool) +(Machine units: mm/min) +(TC: Default Tool) +(Begin toolchange) +M5 +T1 M6 +G43 H1 +(Finish operation: TC: Default Tool) +(Begin operation: Profile) +(Machine units: mm/min) +(Finish operation: Profile) (Begin postamble) M05 G17 G54 G90 G80 G40 M2 """ - self.docobj.Path = Path.Path([]) - postables = [self.docobj] + self.profile_op.Path = Path.Path([]) - args = "--no-header --no-show-editor" + self.job.PostProcessorArgs = "--no-header --no-show-editor" # args = ("--no-header --no-comments --no-show-editor --precision=2") - gcode = postprocessor.export(postables, "-", args) + gcode = self.post.export()[0][1] + # print(f"--------{nl}{gcode}--------{nl}") self.assertEqual(gcode, expected) # test without comments expected = """G17 G54 G40 G49 G80 G90 G21 +G54 +M5 +T1 M6 +G43 H1 M05 G17 G54 G90 G80 G40 M2 """ - args = "--no-header --no-comments --no-show-editor" + self.job.PostProcessorArgs = "--no-header --no-comments --no-show-editor" # args = ("--no-header --no-comments --no-show-editor --precision=2") - gcode = postprocessor.export(postables, "-", args) + gcode = self.post.export()[0][1] + # print(f"--------{nl}{gcode}--------{nl}") self.assertEqual(gcode, expected) def test010(self): """Test command Generation. Test Precision """ + nl = "\n" + c = Path.Command("G0 X10 Y20 Z30") - self.docobj.Path = Path.Path([c]) - postables = [self.docobj] + self.profile_op.Path = Path.Path([c]) - args = "--no-header --no-show-editor" - gcode = postprocessor.export(postables, "-", args) - result = gcode.splitlines()[5] + self.job.PostProcessorArgs = "--no-header --no-show-editor" + gcode = self.post.export()[0][1] + # print(f"--------{nl}{gcode}--------{nl}") + result = gcode.splitlines()[17] expected = "G0 X10.000 Y20.000 Z30.000" self.assertEqual(result, expected) - args = "--no-header --precision=2 --no-show-editor" - gcode = postprocessor.export(postables, "-", args) - result = gcode.splitlines()[5] + self.job.PostProcessorArgs = "--no-header --precision=2 --no-show-editor" + gcode = self.post.export()[0][1] + # print(f"--------{nl}{gcode}--------{nl}") + result = gcode.splitlines()[17] expected = "G0 X10.00 Y20.00 Z30.00" self.assertEqual(result, expected) @@ -163,27 +190,32 @@ M2 """ Test Line Numbers """ + nl = "\n" + c = Path.Command("G0 X10 Y20 Z30") - self.docobj.Path = Path.Path([c]) - postables = [self.docobj] + self.profile_op.Path = Path.Path([c]) - args = "--no-header --line-numbers --no-show-editor" - gcode = postprocessor.export(postables, "-", args) - result = gcode.splitlines()[5] - expected = "N150 G0 X10.000 Y20.000 Z30.000" + self.job.PostProcessorArgs = "--no-header --line-numbers --no-show-editor" + gcode = self.post.export()[0][1] + # print(f"--------{nl}{gcode}--------{nl}") + result = gcode.splitlines()[17] + expected = "N270 G0 X10.000 Y20.000 Z30.000" self.assertEqual(result, expected) def test030(self): """ Test Pre-amble """ + nl = "\n" - self.docobj.Path = Path.Path([]) - postables = [self.docobj] + self.profile_op.Path = Path.Path([]) - args = "--no-header --no-comments --preamble='G18 G55' --no-show-editor" - gcode = postprocessor.export(postables, "-", args) + self.job.PostProcessorArgs = ( + "--no-header --no-comments --preamble='G18 G55' --no-show-editor" + ) + gcode = self.post.export()[0][1] + # print(f"--------{nl}{gcode}--------{nl}") result = gcode.splitlines()[0] self.assertEqual(result, "G18 G55") @@ -191,10 +223,15 @@ M2 """ Test Post-amble """ - self.docobj.Path = Path.Path([]) - postables = [self.docobj] - args = "--no-header --no-comments --postamble='G0 Z50\nM2' --no-show-editor" - gcode = postprocessor.export(postables, "-", args) + nl = "\n" + + self.profile_op.Path = Path.Path([]) + + self.job.PostProcessorArgs = ( + "--no-header --no-comments --postamble='G0 Z50\nM2' --no-show-editor" + ) + gcode = self.post.export()[0][1] + # print(f"--------{nl}{gcode}--------{nl}") result = gcode.splitlines()[-2] self.assertEqual(result, "G0 Z50") self.assertEqual(gcode.splitlines()[-1], "M2") @@ -203,22 +240,25 @@ M2 """ Test inches """ + nl = "\n" c = Path.Command("G0 X10 Y20 Z30") - self.docobj.Path = Path.Path([c]) - postables = [self.docobj] - args = "--no-header --inches --no-show-editor" - gcode = postprocessor.export(postables, "-", args) + self.profile_op.Path = Path.Path([c]) + + self.job.PostProcessorArgs = "--no-header --inches --no-show-editor" + gcode = self.post.export()[0][1] + # print(f"--------{nl}{gcode}--------{nl}") self.assertEqual(gcode.splitlines()[2], "G20") - result = gcode.splitlines()[5] + result = gcode.splitlines()[17] expected = "G0 X0.3937 Y0.7874 Z1.1811" self.assertEqual(result, expected) - args = "--no-header --inches --precision=2 --no-show-editor" - gcode = postprocessor.export(postables, "-", args) - result = gcode.splitlines()[5] + self.job.PostProcessorArgs = "--no-header --inches --precision=2 --no-show-editor" + gcode = self.post.export()[0][1] + # print(f"--------{nl}{gcode}--------{nl}") + result = gcode.splitlines()[17] expected = "G0 X0.39 Y0.79 Z1.18" self.assertEqual(result, expected) @@ -227,15 +267,17 @@ M2 Test test modal Suppress the command name if the same as previous """ + nl = "\n" + c = Path.Command("G0 X10 Y20 Z30") c1 = Path.Command("G0 X10 Y30 Z30") - self.docobj.Path = Path.Path([c, c1]) - postables = [self.docobj] + self.profile_op.Path = Path.Path([c, c1]) - args = "--no-header --modal --no-show-editor" - gcode = postprocessor.export(postables, "-", args) - result = gcode.splitlines()[6] + self.job.PostProcessorArgs = "--no-header --modal --no-show-editor" + gcode = self.post.export()[0][1] + # print(f"--------{nl}{gcode}--------{nl}") + result = gcode.splitlines()[18] expected = "X10.000 Y30.000 Z30.000" self.assertEqual(result, expected) @@ -244,15 +286,17 @@ M2 Test axis modal Suppress the axis coordinate if the same as previous """ + nl = "\n" + c = Path.Command("G0 X10 Y20 Z30") c1 = Path.Command("G0 X10 Y30 Z30") - self.docobj.Path = Path.Path([c, c1]) - postables = [self.docobj] + self.profile_op.Path = Path.Path([c, c1]) - args = "--no-header --axis-modal --no-show-editor" - gcode = postprocessor.export(postables, "-", args) - result = gcode.splitlines()[6] + self.job.PostProcessorArgs = "--no-header --axis-modal --no-show-editor" + gcode = self.post.export()[0][1] + # print(f"--------{nl}{gcode}--------{nl}") + result = gcode.splitlines()[18] expected = "G0 Y30.000" self.assertEqual(result, expected) @@ -260,35 +304,40 @@ M2 """ Test tool change """ + nl = "\n" + c = Path.Command("M6 T2") c2 = Path.Command("M3 S3000") - self.docobj.Path = Path.Path([c, c2]) - postables = [self.docobj] - args = "--no-header --no-show-editor" - gcode = postprocessor.export(postables, "-", args) - self.assertEqual(gcode.splitlines()[6], "M5") - self.assertEqual(gcode.splitlines()[7], "T2 M6") - self.assertEqual(gcode.splitlines()[8], "G43 H2") - self.assertEqual(gcode.splitlines()[9], "M3 S3000") + self.profile_op.Path = Path.Path([c, c2]) + + self.job.PostProcessorArgs = "--no-header --no-show-editor" + gcode = self.post.export()[0][1] + # print(f"--------{nl}{gcode}--------{nl}") + self.assertEqual(gcode.splitlines()[18], "M5") + self.assertEqual(gcode.splitlines()[19], "T2 M6") + self.assertEqual(gcode.splitlines()[20], "G43 H2") + self.assertEqual(gcode.splitlines()[21], "M3 S3000") # suppress TLO - args = "--no-header --no-tlo --no-show-editor" - gcode = postprocessor.export(postables, "-", args) - self.assertEqual(gcode.splitlines()[8], "M3 S3000") + self.job.PostProcessorArgs = "--no-header --no-tlo --no-show-editor" + gcode = self.post.export()[0][1] + # print(f"--------{nl}{gcode}--------{nl}") + self.assertEqual(gcode.splitlines()[19], "M3 S3000") def test090(self): """ Test comment """ + nl = "\n" c = Path.Command("(comment)") - self.docobj.Path = Path.Path([c]) - postables = [self.docobj] + self.profile_op.Path = Path.Path([c]) - args = "--no-header --no-show-editor" - gcode = postprocessor.export(postables, "-", args) - result = gcode.splitlines()[5] + self.job.PostProcessorArgs = "--no-header --no-show-editor" + gcode = self.post.export()[0][1] + # print(f"--------{nl}{gcode}--------{nl}") + result = gcode.splitlines()[17] expected = "(comment)" self.assertEqual(result, expected) diff --git a/src/Mod/CAM/Gui/ViewProviderPath.cpp b/src/Mod/CAM/Gui/ViewProviderPath.cpp index 25276bdacb..44698df05a 100644 --- a/src/Mod/CAM/Gui/ViewProviderPath.cpp +++ b/src/Mod/CAM/Gui/ViewProviderPath.cpp @@ -183,11 +183,11 @@ ViewProviderPath::ViewProviderPath() ShowCountConstraints.LowerBound = 0; - ShowCountConstraints.UpperBound = INT_MAX; + ShowCountConstraints.UpperBound = std::numeric_limits::max(); ShowCountConstraints.StepSize = 1; ShowCount.setConstraints(&ShowCountConstraints); StartIndexConstraints.LowerBound = 0; - StartIndexConstraints.UpperBound = INT_MAX; + StartIndexConstraints.UpperBound = std::numeric_limits::max(); StartIndexConstraints.StepSize = 1; StartIndex.setConstraints(&StartIndexConstraints); ADD_PROPERTY_TYPE(StartPosition, diff --git a/src/Mod/CAM/Path/Post/Processor.py b/src/Mod/CAM/Path/Post/Processor.py index 9d52b5a0ed..9dbecca733 100644 --- a/src/Mod/CAM/Path/Post/Processor.py +++ b/src/Mod/CAM/Path/Post/Processor.py @@ -1,7 +1,11 @@ # -*- coding: utf-8 -*- # *************************************************************************** # * Copyright (c) 2014 Yorik van Havre * -# * Copyright (c) 2024 Larry Woestman * +# * Copyright (c) 2014 sliptonic * +# * Copyright (c) 2022 - 2025 Larry Woestman * +# * Copyright (c) 2024 Ondsel * +# * * +# * This file is part of the FreeCAD CAx development system. * # * * # * This program is free software; you can redistribute it and/or modify * # * it under the terms of the GNU Lesser General Public License (LGPL) * @@ -21,17 +25,24 @@ # * * # *************************************************************************** """ -The base classes for post processors in CAM workbench. +The base classes for post processors in the CAM workbench. """ -from PySide import QtCore, QtGui -from importlib import reload -import FreeCAD -import Path -import Path.Base.Util as PathUtil +import argparse import importlib.util import os -import sys +from PySide import QtCore, QtGui import re +import sys +from typing import Any, Dict, List, Optional, Tuple, Union + +import Path.Base.Util as PathUtil +import Path.Post.UtilsArguments as PostUtilsArguments +import Path.Post.UtilsExport as PostUtilsExport + +import FreeCAD +import Path + +translate = FreeCAD.Qt.translate Path.Log.setLevel(Path.Log.Level.INFO, Path.Log.thisModule()) @@ -50,6 +61,23 @@ class _TempObject: Label = "Fixture" +# +# Define some types that are used throughout this file. +# +Defaults = Dict[str, bool] +FormatHelp = str +GCodeOrNone = Optional[str] +GCodeSections = List[Tuple[str, GCodeOrNone]] +Parser = argparse.ArgumentParser +ParserArgs = Union[None, str, argparse.Namespace] +Postables = Union[List, List[Tuple[str, List]]] +Section = Tuple[str, List] +Sublist = List +Units = str +Values = Dict[str, Any] +Visible = Dict[str, bool] + + class PostProcessorFactory: """Factory class for creating post processors.""" @@ -89,7 +117,7 @@ class PostProcessorFactory: class PostProcessor: - """Base Class. All postprocessors should inherit from this class.""" + """Base Class. All non-legacy postprocessors should inherit from this class.""" def __init__(self, job, tooltip, tooltipargs, units, *args, **kwargs): self._tooltip = tooltip @@ -98,14 +126,12 @@ class PostProcessor: self._job = job self._args = args self._kwargs = kwargs + self.reinitialize() @classmethod def exists(cls, processor): return processor in Path.Preferences.allAvailablePostProcessors() - def export(self): - raise NotImplementedError("Subclass must implement abstract method") - @property def tooltip(self): """Get the tooltip text for the post processor.""" @@ -113,10 +139,8 @@ class PostProcessor: # return self._tooltip @property - def tooltipArgs(self): - """Get the tooltip arguments for the post processor.""" - raise NotImplementedError("Subclass must implement abstract method") - # return self._tooltipargs + def tooltipArgs(self) -> FormatHelp: + return self.parser.format_help() @property def units(self): @@ -125,8 +149,8 @@ class PostProcessor: def _buildPostList(self): """ - determines the specific objects and order to - postprocess Returns a list of objects which can be passed to + determines the specific objects and order to postprocess + Returns a list of objects which can be passed to exportObjectsWith() for final posting.""" def __fixtureSetup(order, fixture, job): @@ -140,7 +164,7 @@ class PostProcessor: fobj.Path = Path.Path([c1]) # Avoid any tool move after G49 in preamble and before tool change # and G43 in case tool height compensation is in use, to avoid - # dangerous move without toolgg compesation. + # dangerous move without tool compensation. if order != 0: c2 = Path.Command( "G0 Z" @@ -279,6 +303,161 @@ class PostProcessor: Path.Log.debug(f"Postlist: {postlist}") return finalpostlist + def export(self) -> Union[None, GCodeSections]: + """Process the parser arguments, then postprocess the 'postables'.""" + args: ParserArgs + flag: bool + + Path.Log.debug("Exporting the job") + + (flag, args) = self.process_arguments() + # + # If the flag is True, then continue postprocessing the 'postables'. + # + if flag: + return self.process_postables() + # + # The flag is False meaning something unusual happened. + # + # If args is None then there was an error during argument processing. + # + if args is None: + return None + # + # Otherwise args will contain the argument list formatted for output + # instead of the "usual" gcode. + # + return [("allitems", args)] # type: ignore + + def init_arguments( + self, + values: Values, + argument_defaults: Defaults, + arguments_visible: Visible, + ) -> Parser: + """Initialize the shared argument definitions.""" + _parser: Parser = PostUtilsArguments.init_shared_arguments( + values, argument_defaults, arguments_visible + ) + # + # Add any argument definitions that are not shared with other postprocessors here. + # + return _parser + + def init_argument_defaults(self, argument_defaults: Defaults) -> None: + """Initialize which arguments (in a pair) are shown as the default argument.""" + PostUtilsArguments.init_argument_defaults(argument_defaults) + # + # Modify which argument to show as the default in flag-type arguments here. + # If the value is True, the first argument will be shown as the default. + # If the value is False, the second argument will be shown as the default. + # + # For example, if you want to show Metric mode as the default, use: + # argument_defaults["metric_inch"] = True + # + # If you want to show that "Don't pop up editor for writing output" is + # the default, use: + # argument_defaults["show-editor"] = False. + # + # Note: You also need to modify the corresponding entries in the "values" hash + # to actually make the default value(s) change to match. + # + + def init_arguments_visible(self, arguments_visible: Visible) -> None: + """Initialize which argument pairs are visible in TOOLTIP_ARGS.""" + PostUtilsArguments.init_arguments_visible(arguments_visible) + # + # Modify the visibility of any arguments from the defaults here. + # + + def init_values(self, values: Values) -> None: + """Initialize values that are used throughout the postprocessor.""" + # + PostUtilsArguments.init_shared_values(values) + # + # Set any values here that need to override the default values set + # in the init_shared_values routine. + # + values["UNITS"] = self._units + + def process_arguments(self) -> Tuple[bool, ParserArgs]: + """Process any arguments to the postprocessor.""" + # + # This function is separated out to make it easier to inherit from this class. + # + args: ParserArgs + flag: bool + + (flag, args) = PostUtilsArguments.process_shared_arguments( + self.values, self.parser, self._job.PostProcessorArgs, self.all_visible, "-" + ) + # + # If the flag is True, then all of the arguments should be processed normally. + # + if flag: + # + # Process any additional arguments here. + # + # + # Update any variables that might have been modified while processing the arguments. + # + self._units = self.values["UNITS"] + # + # If the flag is False, then args is either None (indicating an error while + # processing the arguments) or a string containing the argument list formatted + # for output. Either way the calling routine will need to handle the args value. + # + return (flag, args) + + def process_postables(self) -> GCodeSections: + """Postprocess the 'postables' in the job to g code sections.""" + # + # This function is separated out to make it easier to inherit from this class. + # + gcode: GCodeOrNone + g_code_sections: GCodeSections + partname: str + postables: Postables + section: Section + sublist: Sublist + + postables = self._buildPostList() + + Path.Log.debug(f"postables count: {len(postables)}") + + g_code_sections = [] + for _, section in enumerate(postables): + partname, sublist = section + gcode = PostUtilsExport.export_common(self.values, sublist, "-") + g_code_sections.append((partname, gcode)) + + return g_code_sections + + def reinitialize(self) -> None: + """Initialize or reinitialize the 'core' data structures for the postprocessor.""" + # + # This is also used to reinitialize the data structures between tests. + # + self.values: Values = {} + self.init_values(self.values) + self.argument_defaults: Defaults = {} + self.init_argument_defaults(self.argument_defaults) + self.arguments_visible: Visible = {} + self.init_arguments_visible(self.arguments_visible) + self.parser: Parser = self.init_arguments( + self.values, self.argument_defaults, self.arguments_visible + ) + # + # Create another parser just to get a list of all possible arguments + # that may be output using --output_all_arguments. + # + self.all_arguments_visible: Visible = {} + for k in iter(self.arguments_visible): + self.all_arguments_visible[k] = True + self.all_visible: Parser = self.init_arguments( + self.values, self.argument_defaults, self.all_arguments_visible + ) + class WrapperPost(PostProcessor): """Wrapper class for old post processors that are scripts.""" diff --git a/src/Mod/CAM/Path/Post/scripts/estlcam_post.py b/src/Mod/CAM/Path/Post/scripts/estlcam_post.py index de508aaa16..ee47e9cc9d 100644 --- a/src/Mod/CAM/Path/Post/scripts/estlcam_post.py +++ b/src/Mod/CAM/Path/Post/scripts/estlcam_post.py @@ -327,10 +327,11 @@ def export(objectslist, filename, argstring): print("Done postprocessing.") - # write the file - gfile = pyopen(filename, "w") - gfile.write(final) - gfile.close() + if filename != "-": + with pyopen(filename, "w") as gfile: + gfile.write(final) + + return final def linenumber(): diff --git a/src/Mod/CAM/Path/Post/scripts/refactored_centroid_post.py b/src/Mod/CAM/Path/Post/scripts/refactored_centroid_post.py index d1a0e4ef1a..e1f8370341 100644 --- a/src/Mod/CAM/Path/Post/scripts/refactored_centroid_post.py +++ b/src/Mod/CAM/Path/Post/scripts/refactored_centroid_post.py @@ -23,13 +23,9 @@ # * * # *************************************************************************** -import argparse - -from typing import Any, Dict, List, Optional, Tuple, Union +from typing import Any, Dict from Path.Post.Processor import PostProcessor -import Path.Post.UtilsArguments as PostUtilsArguments -import Path.Post.UtilsExport as PostUtilsExport import Path import FreeCAD @@ -46,16 +42,6 @@ else: # # Define some types that are used throughout this file. # -Defaults = Dict[str, bool] -FormatHelp = str -GCodeOrNone = Optional[str] -GCodeSections = List[Tuple[str, GCodeOrNone]] -Parser = argparse.ArgumentParser -ParserArgs = Union[None, str, argparse.Namespace] -Postables = Union[List, List[Tuple[str, List]]] -Section = Tuple[str, List] -Sublist = List -Units = str Values = Dict[str, Any] Visible = Dict[str, bool] @@ -63,48 +49,28 @@ Visible = Dict[str, bool] class Refactored_Centroid(PostProcessor): """The Refactored Centroid post processor class.""" - def __init__(self, job) -> None: + def __init__( + self, + job, + tooltip=translate("CAM", "Refactored Centroid post processor"), + tooltipargs=[""], + units="Metric", + ) -> None: super().__init__( job=job, - tooltip=translate("CAM", "Refactored Centroid post processor"), - tooltipargs=[""], - units="Metric", + tooltip=tooltip, + tooltipargs=tooltipargs, + units=units, ) - self.reinitialize() Path.Log.debug("Refactored Centroid post processor initialized.") - def reinitialize(self) -> None: - """Initialize or reinitialize the 'core' data structures for the postprocessor.""" - # - # This is also used to reinitialize the data structures between tests. - # - self.values: Values = {} - self.init_values(self.values) - self.argument_defaults: Defaults = {} - self.init_argument_defaults(self.argument_defaults) - self.arguments_visible: Visible = {} - self.init_arguments_visible(self.arguments_visible) - self.parser: Parser = self.init_arguments( - self.values, self.argument_defaults, self.arguments_visible - ) - # - # Create another parser just to get a list of all possible arguments - # that may be output using --output_all_arguments. - # - self.all_arguments_visible: Visible = {} - for k in iter(self.arguments_visible): - self.all_arguments_visible[k] = True - self.all_visible: Parser = self.init_arguments( - self.values, self.argument_defaults, self.all_arguments_visible - ) - def init_values(self, values: Values) -> None: """Initialize values that are used throughout the postprocessor.""" # - PostUtilsArguments.init_shared_values(values) + super().init_values(values) # # Set any values here that need to override the default values set - # in the init_shared_values routine. + # in the parent routine. # # Use 4 digits for axis precision by default. # @@ -197,7 +163,6 @@ class Refactored_Centroid(PostProcessor): ] = """M5 M25 G49 H0""" - values["UNITS"] = self._units # # Default to not outputting a G43 following tool changes # @@ -209,28 +174,9 @@ G49 H0""" # ZAXISRETURN = """G91 G28 X0 Z0 G90""" # - def init_argument_defaults(self, argument_defaults: Defaults) -> None: - """Initialize which arguments (in a pair) are shown as the default argument.""" - PostUtilsArguments.init_argument_defaults(argument_defaults) - # - # Modify which argument to show as the default in flag-type arguments here. - # If the value is True, the first argument will be shown as the default. - # If the value is False, the second argument will be shown as the default. - # - # For example, if you want to show Metric mode as the default, use: - # argument_defaults["metric_inch"] = True - # - # If you want to show that "Don't pop up editor for writing output" is - # the default, use: - # argument_defaults["show-editor"] = False. - # - # Note: You also need to modify the corresponding entries in the "values" hash - # to actually make the default value(s) change to match. - # - def init_arguments_visible(self, arguments_visible: Visible) -> None: """Initialize which argument pairs are visible in TOOLTIP_ARGS.""" - PostUtilsArguments.init_arguments_visible(arguments_visible) + super().init_arguments_visible(arguments_visible) # # Modify the visibility of any arguments from the defaults here. # @@ -238,100 +184,6 @@ G49 H0""" arguments_visible["precision"] = False arguments_visible["tlo"] = False - def init_arguments( - self, - values: Values, - argument_defaults: Defaults, - arguments_visible: Visible, - ) -> Parser: - """Initialize the shared argument definitions.""" - _parser: Parser = PostUtilsArguments.init_shared_arguments( - values, argument_defaults, arguments_visible - ) - # - # Add any argument definitions that are not shared with other postprocessors here. - # - return _parser - - def process_arguments(self) -> Tuple[bool, ParserArgs]: - """Process any arguments to the postprocessor.""" - # - # This function is separated out to make it easier to inherit from this postprocessor. - # - args: ParserArgs - flag: bool - - (flag, args) = PostUtilsArguments.process_shared_arguments( - self.values, self.parser, self._job.PostProcessorArgs, self.all_visible, "-" - ) - # - # If the flag is True, then all of the arguments should be processed normally. - # - if flag: - # - # Process any additional arguments here. - # - # - # Update any variables that might have been modified while processing the arguments. - # - self._units = self.values["UNITS"] - # - # If the flag is False, then args is either None (indicating an error while - # processing the arguments) or a string containing the argument list formatted - # for output. Either way the calling routine will need to handle the args value. - # - return (flag, args) - - def process_postables(self) -> GCodeSections: - """Postprocess the 'postables' in the job to g code sections.""" - # - # This function is separated out to make it easier to inherit from this postprocessor. - # - gcode: GCodeOrNone - g_code_sections: GCodeSections - partname: str - postables: Postables - section: Section - sublist: Sublist - - postables = self._buildPostList() - - Path.Log.debug(f"postables count: {len(postables)}") - - g_code_sections = [] - for _, section in enumerate(postables): - partname, sublist = section - gcode = PostUtilsExport.export_common(self.values, sublist, "-") - g_code_sections.append((partname, gcode)) - - return g_code_sections - - def export(self) -> GCodeSections: - """Process the parser arguments, then postprocess the 'postables'.""" - args: ParserArgs - flag: bool - - Path.Log.debug("Exporting the job") - - (flag, args) = self.process_arguments() - # - # If the flag is True, then continue postprocessing the 'postables' - # - if flag: - return self.process_postables() - # - # The flag is False meaning something unusual happened. - # - # If args is None then there was an error during argument processing. - # - if args is None: - return None - # - # Otherwise args will contain the argument list formatted for output - # instead of the "usual" gcode. - # - return [("allitems", args)] # type: ignore - @property def tooltip(self): tooltip: str = """ @@ -340,11 +192,3 @@ G49 H0""" and output 'real' GCode suitable for a centroid 3 axis mill. """ return tooltip - - @property - def tooltipArgs(self) -> FormatHelp: - return self.parser.format_help() - - @property - def units(self) -> Units: - return self._units diff --git a/src/Mod/CAM/Path/Post/scripts/refactored_grbl_post.py b/src/Mod/CAM/Path/Post/scripts/refactored_grbl_post.py index f7910af47c..ea47280a8b 100644 --- a/src/Mod/CAM/Path/Post/scripts/refactored_grbl_post.py +++ b/src/Mod/CAM/Path/Post/scripts/refactored_grbl_post.py @@ -25,11 +25,9 @@ import argparse -from typing import Any, Dict, List, Optional, Tuple, Union +from typing import Any, Dict from Path.Post.Processor import PostProcessor -import Path.Post.UtilsArguments as PostUtilsArguments -import Path.Post.UtilsExport as PostUtilsExport import Path import FreeCAD @@ -47,15 +45,6 @@ else: # Define some types that are used throughout this file. # Defaults = Dict[str, bool] -FormatHelp = str -GCodeOrNone = Optional[str] -GCodeSections = List[Tuple[str, GCodeOrNone]] -Parser = argparse.ArgumentParser -ParserArgs = Union[None, str, argparse.Namespace] -Postables = Union[List, List[Tuple[str, List]]] -Section = Tuple[str, List] -Sublist = List -Units = str Values = Dict[str, Any] Visible = Dict[str, bool] @@ -63,49 +52,30 @@ Visible = Dict[str, bool] class Refactored_Grbl(PostProcessor): """The Refactored Grbl post processor class.""" - def __init__(self, job) -> None: + def __init__( + self, + job, + tooltip=translate("CAM", "Refactored Grbl post processor"), + tooltipargs=[""], + units="Metric", + ) -> None: super().__init__( job=job, - tooltip=translate("CAM", "Refactored Grbl post processor"), - tooltipargs=[""], - units="Metric", + tooltip=tooltip, + tooltipargs=tooltipargs, + units=units, ) - self.reinitialize() Path.Log.debug("Refactored Grbl post processor initialized.") - def reinitialize(self) -> None: - """Initialize or reinitialize the 'core' data structures for the postprocessor.""" - # - # This is also used to reinitialize the data structures between tests. - # - self.values: Values = {} - self.init_values(self.values) - self.argument_defaults: Defaults = {} - self.init_argument_defaults(self.argument_defaults) - self.arguments_visible: Visible = {} - self.init_arguments_visible(self.arguments_visible) - self.parser: Parser = self.init_arguments( - self.values, self.argument_defaults, self.arguments_visible - ) - # - # Create another parser just to get a list of all possible arguments - # that may be output using --output_all_arguments. - # - self.all_arguments_visible: Visible = {} - for k in iter(self.arguments_visible): - self.all_arguments_visible[k] = True - self.all_visible: Parser = self.init_arguments( - self.values, self.argument_defaults, self.all_arguments_visible - ) - def init_values(self, values: Values) -> None: """Initialize values that are used throughout the postprocessor.""" # - PostUtilsArguments.init_shared_values(values) + super().init_values(values) # # Set any values here that need to override the default values set - # in the init_shared_values routine. + # in the parent routine. # + values["ENABLE_COOLANT"] = True # # If this is set to True, then commands that are placed in # comments that look like (MC_RUN_COMMAND: blah) will be output. @@ -168,7 +138,6 @@ M2""" # Do not show the current machine units just before the PRE_OPERATION. # values["SHOW_MACHINE_UNITS"] = False - values["UNITS"] = self._units # # Default to not outputting a G43 following tool changes # @@ -176,7 +145,7 @@ M2""" def init_argument_defaults(self, argument_defaults: Defaults) -> None: """Initialize which arguments (in a pair) are shown as the default argument.""" - PostUtilsArguments.init_argument_defaults(argument_defaults) + super().init_argument_defaults(argument_defaults) # # Modify which argument to show as the default in flag-type arguments here. # If the value is True, the first argument will be shown as the default. @@ -197,7 +166,7 @@ M2""" def init_arguments_visible(self, arguments_visible: Visible) -> None: """Initialize which argument pairs are visible in TOOLTIP_ARGS.""" - PostUtilsArguments.init_arguments_visible(arguments_visible) + super().init_arguments_visible(arguments_visible) # # Modify the visibility of any arguments from the defaults here. # @@ -209,100 +178,6 @@ M2""" arguments_visible["translate_drill"] = True arguments_visible["wait-for-spindle"] = True - def init_arguments( - self, - values: Values, - argument_defaults: Defaults, - arguments_visible: Visible, - ) -> Parser: - """Initialize the shared argument definitions.""" - _parser: Parser = PostUtilsArguments.init_shared_arguments( - values, argument_defaults, arguments_visible - ) - # - # Add any argument definitions that are not shared with other postprocessors here. - # - return _parser - - def process_arguments(self) -> Tuple[bool, ParserArgs]: - """Process any arguments to the postprocessor.""" - # - # This function is separated out to make it easier to inherit from this postprocessor. - # - args: ParserArgs - flag: bool - - (flag, args) = PostUtilsArguments.process_shared_arguments( - self.values, self.parser, self._job.PostProcessorArgs, self.all_visible, "-" - ) - # - # If the flag is True, then all of the arguments should be processed normally. - # - if flag: - # - # Process any additional arguments here. - # - # - # Update any variables that might have been modified while processing the arguments. - # - self._units = self.values["UNITS"] - # - # If the flag is False, then args is either None (indicating an error while - # processing the arguments) or a string containing the argument list formatted - # for output. Either way the calling routine will need to handle the args value. - # - return (flag, args) - - def process_postables(self) -> GCodeSections: - """Postprocess the 'postables' in the job to g code sections.""" - # - # This function is separated out to make it easier to inherit from this postprocessor. - # - gcode: GCodeOrNone - g_code_sections: GCodeSections - partname: str - postables: Postables - section: Section - sublist: Sublist - - postables = self._buildPostList() - - Path.Log.debug(f"postables count: {len(postables)}") - - g_code_sections = [] - for _, section in enumerate(postables): - partname, sublist = section - gcode = PostUtilsExport.export_common(self.values, sublist, "-") - g_code_sections.append((partname, gcode)) - - return g_code_sections - - def export(self) -> Union[None, GCodeSections]: - """Process the parser arguments, then postprocess the 'postables'.""" - args: ParserArgs - flag: bool - - Path.Log.debug("Exporting the job") - - (flag, args) = self.process_arguments() - # - # If the flag is True, then continue postprocessing the 'postables'. - # - if flag: - return self.process_postables() - # - # The flag is False meaning something unusual happened. - # - # If args is None then there was an error during argument processing. - # - if args is None: - return None - # - # Otherwise args will contain the argument list formatted for output - # instead of the "usual" gcode. - # - return [("allitems", args)] # type: ignore - @property def tooltip(self): tooltip: str = """ @@ -311,11 +186,3 @@ M2""" and output 'real' GCode suitable for a Grbl 3 axis mill. """ return tooltip - - @property - def tooltipArgs(self) -> FormatHelp: - return self.parser.format_help() - - @property - def units(self) -> Units: - return self._units diff --git a/src/Mod/CAM/Path/Post/scripts/refactored_linuxcnc_post.py b/src/Mod/CAM/Path/Post/scripts/refactored_linuxcnc_post.py index 0450d556bc..eb8334fdce 100644 --- a/src/Mod/CAM/Path/Post/scripts/refactored_linuxcnc_post.py +++ b/src/Mod/CAM/Path/Post/scripts/refactored_linuxcnc_post.py @@ -23,19 +23,10 @@ # * USA * # * * # *************************************************************************** -# *************************************************************************** -# * Note: refactored_masso_g3_Post.py is a modified clone of this file * -# * any changes to this file should be applied to the other * -# * * -# *************************************************************************** -import argparse - -from typing import Any, Dict, List, Optional, Tuple, Union +from typing import Any, Dict from Path.Post.Processor import PostProcessor -import Path.Post.UtilsArguments as PostUtilsArguments -import Path.Post.UtilsExport as PostUtilsExport import Path import FreeCAD @@ -52,65 +43,34 @@ else: # # Define some types that are used throughout this file. # -Defaults = Dict[str, bool] -FormatHelp = str -GCodeOrNone = Optional[str] -GCodeSections = List[Tuple[str, GCodeOrNone]] -Parser = argparse.ArgumentParser -ParserArgs = Union[None, str, argparse.Namespace] -Postables = Union[List, List[Tuple[str, List]]] -Section = Tuple[str, List] -Sublist = List -Units = str Values = Dict[str, Any] -Visible = Dict[str, bool] class Refactored_Linuxcnc(PostProcessor): """The Refactored LinuxCNC post processor class.""" - def __init__(self, job) -> None: + def __init__( + self, + job, + tooltip=translate("CAM", "Refactored LinuxCNC post processor"), + tooltipargs=[""], + units="Metric", + ) -> None: super().__init__( job=job, - tooltip=translate("CAM", "Refactored LinuxCNC post processor"), - tooltipargs=[""], - units="Metric", + tooltip=tooltip, + tooltipargs=tooltipargs, + units=units, ) - self.reinitialize() Path.Log.debug("Refactored LinuxCNC post processor initialized.") - def reinitialize(self) -> None: - """Initialize or reinitialize the 'core' data structures for the postprocessor.""" - # - # This is also used to reinitialize the data structures between tests. - # - self.values: Values = {} - self.init_values(self.values) - self.argument_defaults: Defaults = {} - self.init_argument_defaults(self.argument_defaults) - self.arguments_visible: Visible = {} - self.init_arguments_visible(self.arguments_visible) - self.parser: Parser = self.init_arguments( - self.values, self.argument_defaults, self.arguments_visible - ) - # - # Create another parser just to get a list of all possible arguments - # that may be output using --output_all_arguments. - # - self.all_arguments_visible: Visible = {} - for k in iter(self.arguments_visible): - self.all_arguments_visible[k] = True - self.all_visible: Parser = self.init_arguments( - self.values, self.argument_defaults, self.all_arguments_visible - ) - def init_values(self, values: Values) -> None: """Initialize values that are used throughout the postprocessor.""" # - PostUtilsArguments.init_shared_values(values) + super().init_values(values) # # Set any values here that need to override the default values set - # in the init_shared_values routine. + # in the parent routine. # values["ENABLE_COOLANT"] = True # @@ -140,7 +100,7 @@ class Refactored_Linuxcnc(PostProcessor): # # Used in the argparser code as the "name" of the postprocessor program. # - values["MACHINE_NAME"] = "Refactored_LinuxCNC" + values["MACHINE_NAME"] = "LinuxCNC" # # Any commands in this value will be output as the last commands # in the G-code file. @@ -156,128 +116,6 @@ M2""" # safety block at the beginning of the G-code file. # values["PREAMBLE"] = """G17 G54 G40 G49 G80 G90""" - values["UNITS"] = self._units - - def init_argument_defaults(self, argument_defaults: Defaults) -> None: - """Initialize which arguments (in a pair) are shown as the default argument.""" - PostUtilsArguments.init_argument_defaults(argument_defaults) - # - # Modify which argument to show as the default in flag-type arguments here. - # If the value is True, the first argument will be shown as the default. - # If the value is False, the second argument will be shown as the default. - # - # For example, if you want to show Metric mode as the default, use: - # argument_defaults["metric_inch"] = True - # - # If you want to show that "Don't pop up editor for writing output" is - # the default, use: - # argument_defaults["show-editor"] = False. - # - # Note: You also need to modify the corresponding entries in the "values" hash - # to actually make the default value(s) change to match. - # - - def init_arguments_visible(self, arguments_visible: Visible) -> None: - """Initialize which argument pairs are visible in TOOLTIP_ARGS.""" - PostUtilsArguments.init_arguments_visible(arguments_visible) - # - # Modify the visibility of any arguments from the defaults here. - # - - def init_arguments( - self, - values: Values, - argument_defaults: Defaults, - arguments_visible: Visible, - ) -> Parser: - """Initialize the shared argument definitions.""" - _parser: Parser = PostUtilsArguments.init_shared_arguments( - values, argument_defaults, arguments_visible - ) - # - # Add any argument definitions that are not shared with all other - # postprocessors here. - # - return _parser - - def process_arguments(self) -> Tuple[bool, ParserArgs]: - """Process any arguments to the postprocessor.""" - # - # This function is separated out to make it easier to inherit from this postprocessor - # - args: ParserArgs - flag: bool - - (flag, args) = PostUtilsArguments.process_shared_arguments( - self.values, self.parser, self._job.PostProcessorArgs, self.all_visible, "-" - ) - # - # If the flag is True, then all of the arguments should be processed normally. - # - if flag: - # - # Process any additional arguments here. - # - # - # Update any variables that might have been modified while processing the arguments. - # - self._units = self.values["UNITS"] - # - # If the flag is False, then args is either None (indicating an error while - # processing the arguments) or a string containing the argument list formatted - # for output. Either way the calling routine will need to handle the args value. - # - return (flag, args) - - def process_postables(self) -> GCodeSections: - """Postprocess the 'postables' in the job to g code sections.""" - # - # This function is separated out to make it easier to inherit from this postprocessor. - # - gcode: GCodeOrNone - g_code_sections: GCodeSections - partname: str - postables: Postables - section: Section - sublist: Sublist - - postables = self._buildPostList() - - Path.Log.debug(f"postables count: {len(postables)}") - - g_code_sections = [] - for _, section in enumerate(postables): - partname, sublist = section - gcode = PostUtilsExport.export_common(self.values, sublist, "-") - g_code_sections.append((partname, gcode)) - - return g_code_sections - - def export(self) -> GCodeSections: - """Process the parser arguments, then postprocess the 'postables'.""" - args: ParserArgs - flag: bool - - Path.Log.debug("Exporting the job") - - (flag, args) = self.process_arguments() - # - # If the flag is True, then continue postprocessing the 'postables' - # - if flag: - return self.process_postables() - # - # The flag is False meaning something unusual happened. - # - # If args is None then there was an error during argument processing. - # - if args is None: - return None - # - # Otherwise args will contain the argument list formatted for output - # instead of the "usual" gcode. - # - return [("allitems", args)] # type: ignore @property def tooltip(self): @@ -287,11 +125,3 @@ M2""" and output 'real' GCode suitable for a linuxcnc 3 axis mill. """ return tooltip - - @property - def tooltipArgs(self) -> FormatHelp: - return self.parser.format_help() - - @property - def units(self) -> Units: - return self._units diff --git a/src/Mod/CAM/Path/Post/scripts/refactored_mach3_mach4_post.py b/src/Mod/CAM/Path/Post/scripts/refactored_mach3_mach4_post.py index baba1cbd4a..bff92e81aa 100644 --- a/src/Mod/CAM/Path/Post/scripts/refactored_mach3_mach4_post.py +++ b/src/Mod/CAM/Path/Post/scripts/refactored_mach3_mach4_post.py @@ -23,13 +23,9 @@ # * * # *************************************************************************** -import argparse - -from typing import Any, Dict, List, Optional, Tuple, Union +from typing import Any, Dict from Path.Post.Processor import PostProcessor -import Path.Post.UtilsArguments as PostUtilsArguments -import Path.Post.UtilsExport as PostUtilsExport import Path import FreeCAD @@ -46,16 +42,6 @@ else: # # Define some types that are used throughout this file. # -Defaults = Dict[str, bool] -FormatHelp = str -GCodeOrNone = Optional[str] -GCodeSections = List[Tuple[str, GCodeOrNone]] -Parser = argparse.ArgumentParser -ParserArgs = Union[None, str, argparse.Namespace] -Postables = Union[List, List[Tuple[str, List]]] -Section = Tuple[str, List] -Sublist = List -Units = str Values = Dict[str, Any] Visible = Dict[str, bool] @@ -63,48 +49,28 @@ Visible = Dict[str, bool] class Refactored_Mach3_Mach4(PostProcessor): """The Refactored Mach3_Mach4 post processor class.""" - def __init__(self, job) -> None: + def __init__( + self, + job, + tooltip=translate("CAM", "Refactored Mach3_Mach4 post processor"), + tooltipargs=[""], + units="Metric", + ) -> None: super().__init__( job=job, - tooltip=translate("CAM", "Refactored Mach3_Mach4 post processor"), - tooltipargs=[""], - units="Metric", + tooltip=tooltip, + tooltipargs=tooltipargs, + units=units, ) - self.reinitialize() Path.Log.debug("Refactored Mach3_Mach4 post processor initialized.") - def reinitialize(self) -> None: - """Initialize or reinitialize the 'core' data structures for the postprocessor.""" - # - # This is also used to reinitialize the data structures between tests. - # - self.values: Values = {} - self.init_values(self.values) - self.argument_defaults: Defaults = {} - self.init_argument_defaults(self.argument_defaults) - self.arguments_visible: Visible = {} - self.init_arguments_visible(self.arguments_visible) - self.parser: Parser = self.init_arguments( - self.values, self.argument_defaults, self.arguments_visible - ) - # - # Create another parser just to get a list of all possible arguments - # that may be output using --output_all_arguments. - # - self.all_arguments_visible: Visible = {} - for k in iter(self.arguments_visible): - self.all_arguments_visible[k] = True - self.all_visible: Parser = self.init_arguments( - self.values, self.argument_defaults, self.all_arguments_visible - ) - def init_values(self, values: Values) -> None: """Initialize values that are used throughout the postprocessor.""" # - PostUtilsArguments.init_shared_values(values) + super().init_values(values) # # Set any values here that need to override the default values set - # in the init_shared_values routine. + # in the parent routine. # values["ENABLE_COOLANT"] = True # @@ -163,129 +129,15 @@ M2""" # Output the machine name for mach3_mach4 instead of the machine units alone. # values["SHOW_MACHINE_UNITS"] = False - values["UNITS"] = self._units - - def init_argument_defaults(self, argument_defaults: Defaults) -> None: - """Initialize which arguments (in a pair) are shown as the default argument.""" - PostUtilsArguments.init_argument_defaults(argument_defaults) - # - # Modify which argument to show as the default in flag-type arguments here. - # If the value is True, the first argument will be shown as the default. - # If the value is False, the second argument will be shown as the default. - # - # For example, if you want to show Metric mode as the default, use: - # argument_defaults["metric_inch"] = True - # - # If you want to show that "Don't pop up editor for writing output" is - # the default, use: - # argument_defaults["show-editor"] = False. - # - # Note: You also need to modify the corresponding entries in the "values" hash - # to actually make the default value(s) change to match. - # def init_arguments_visible(self, arguments_visible: Visible) -> None: """Initialize which argument pairs are visible in TOOLTIP_ARGS.""" - PostUtilsArguments.init_arguments_visible(arguments_visible) + super().init_arguments_visible(arguments_visible) # # Modify the visibility of any arguments from the defaults here. # arguments_visible["axis-modal"] = True - def init_arguments( - self, - values: Values, - argument_defaults: Defaults, - arguments_visible: Visible, - ) -> Parser: - """Initialize the shared argument definitions.""" - _parser: Parser = PostUtilsArguments.init_shared_arguments( - values, argument_defaults, arguments_visible - ) - # - # Add any argument definitions that are not shared with other postprocessors here. - # - return _parser - - def process_arguments(self) -> Tuple[bool, ParserArgs]: - """Process any arguments to the postprocessor.""" - # - # This function is separated out to make it easier to inherit from this postprocessor. - # - args: ParserArgs - flag: bool - - (flag, args) = PostUtilsArguments.process_shared_arguments( - self.values, self.parser, self._job.PostProcessorArgs, self.all_visible, "-" - ) - # - # If the flag is True, then all of the arguments should be processed normally. - # - if flag: - # - # Process any additional arguments here. - # - # - # Update any variables that might have been modified while processing the arguments. - # - self._units = self.values["UNITS"] - # - # If the flag is False, then args is either None (indicating an error while - # processing the arguments) or a string containing the argument list formatted - # for output. Either way the calling routine will need to handle the args value. - # - return (flag, args) - - def process_postables(self) -> GCodeSections: - """Postprocess the 'postables' in the job to g code sections.""" - # - # This function is separated out to make it easier to inherit from this postprocessor. - # - gcode: GCodeOrNone - g_code_sections: GCodeSections - partname: str - postables: Postables - section: Section - sublist: Sublist - - postables = self._buildPostList() - - Path.Log.debug(f"postables count: {len(postables)}") - - g_code_sections = [] - for _, section in enumerate(postables): - partname, sublist = section - gcode = PostUtilsExport.export_common(self.values, sublist, "-") - g_code_sections.append((partname, gcode)) - - return g_code_sections - - def export(self) -> GCodeSections: - """Process the parser arguments, then postprocess the 'postables'.""" - args: ParserArgs - flag: bool - - Path.Log.debug("Exporting the job") - - (flag, args) = self.process_arguments() - # - # If the flag is True, then continue postprocessing the 'postables'. - # - if flag: - return self.process_postables() - # - # The flag is False meaning something unusual happened. - # - # If args is None then there was an error during argument processing. - # - if args is None: - return None - # - # Otherwise args will contain the argument list formatted for output - # instead of the "usual" gcode. - # - return [("allitems", args)] # type: ignore - @property def tooltip(self): tooltip: str = """ @@ -294,11 +146,3 @@ M2""" and output 'real' GCode suitable for a Mach3_4 3 axis mill. """ return tooltip - - @property - def tooltipArgs(self) -> FormatHelp: - return self.parser.format_help() - - @property - def units(self) -> Units: - return self._units diff --git a/src/Mod/CAM/Path/Post/scripts/refactored_masso_g3_post.py b/src/Mod/CAM/Path/Post/scripts/refactored_masso_g3_post.py index 3ff1bd8e35..9f479fdddc 100644 --- a/src/Mod/CAM/Path/Post/scripts/refactored_masso_g3_post.py +++ b/src/Mod/CAM/Path/Post/scripts/refactored_masso_g3_post.py @@ -1,6 +1,7 @@ # *************************************************************************** # * Copyright (c) 2014 sliptonic * -# * Copyright (c) 2022 Larry Woestman * +# * Copyright (c) 2022 - 2025 Larry Woestman * +# * Copyright (c) 2024 Ondsel * # * Copyright (c) 2024 Carl Slater * # * * # * This file is part of the FreeCAD CAx development system. * @@ -23,205 +24,70 @@ # * * # *************************************************************************** -# *************************************************************************** -# * Note: this is copy & mod of refactored_linuxcnc_post.py * -# * * -# * * -# *************************************************************************** +from typing import Any, Dict -import argparse +from Path.Post.scripts.refactored_linuxcnc_post import Refactored_Linuxcnc -from typing import Any, Dict, Union +import Path +import FreeCAD -import Path.Post.UtilsArguments as PostUtilsArguments -import Path.Post.UtilsExport as PostUtilsExport +translate = FreeCAD.Qt.translate -# Define some types that are used throughout this file -Parser = argparse.ArgumentParser +DEBUG = False +if DEBUG: + Path.Log.setLevel(Path.Log.Level.DEBUG, Path.Log.thisModule()) + Path.Log.trackModule(Path.Log.thisModule()) +else: + Path.Log.setLevel(Path.Log.Level.INFO, Path.Log.thisModule()) + +# +# Define some types that are used throughout this file. +# Values = Dict[str, Any] -# -# The following variables need to be global variables -# to keep the PathPostProcessor.load method happy: -# -# TOOLTIP -# TOOLTIP_ARGS -# UNITS -# -# The "argument_defaults", "arguments_visible", and the "values" hashes -# need to be defined before the "init_shared_arguments" routine can be -# called to create TOOLTIP_ARGS, so they also end up having to be globals. -# -TOOLTIP: str = """This is a postprocessor file for the Path workbench. It is used to -take a pseudo-gcode fragment outputted by a Path object, and output -real GCode suitable for a Masso G3 3 axis mill. This postprocessor, once placed -in the appropriate PathScripts folder, can be used directly from inside -FreeCAD, via the GUI importer or via python scripts with: -import refactored_masso_g3_post -refactored_masso_g3_post.export(object,"/path/to/file.ncc","") -""" -# -# Default to metric mode -# -UNITS: str = "G21" +class Refactored_Masso_G3(Refactored_Linuxcnc): + """The Refactored Masso G3 post processor class.""" + def __init__( + self, + job, + tooltip=translate("CAM", "Refactored Masso G3 post processor"), + tooltipargs=[""], + units="Metric", + ) -> None: + super().__init__( + job=job, + tooltip=tooltip, + tooltipargs=tooltipargs, + units=units, + ) + Path.Log.debug("Refactored Masso G3 post processor initialized.") -def init_values(values: Values) -> None: - """Initialize values that are used throughout the postprocessor.""" - # - PostUtilsArguments.init_shared_values(values) - # - # Set any values here that need to override the default values set - # in the init_shared_values routine. - # - values["ENABLE_COOLANT"] = True - # the order of parameters - # Masso G3 doesn't want K properties on XY plane; Arcs need work. - values["PARAMETER_ORDER"] = [ - "X", - "Y", - "Z", - "A", - "B", - "C", - "I", - "J", - "F", - "S", - "T", - "Q", - "R", - "L", - "H", - "D", - "P", - ] - # - # Used in the argparser code as the "name" of the postprocessor program. - # This would normally show up in the usage message in the TOOLTIP_ARGS, - # but we are suppressing the usage message, so it doesn't show up after all. - # - values["MACHINE_NAME"] = "Masso G3" - # - # Any commands in this value will be output as the last commands - # in the G-code file. - # - values[ - "POSTAMBLE" - ] = """M05 -G17 G54 G90 G80 G40 -M2""" - values["POSTPROCESSOR_FILE_NAME"] = __name__ - # - # Any commands in this value will be output after the header and - # safety block at the beginning of the G-code file. - # - values["PREAMBLE"] = """G17 G54 G40 G49 G80 G90""" + def init_values(self, values: Values) -> None: + """Initialize values that are used throughout the postprocessor.""" + # + super().init_values(values) + # + # Set any values here that need to override the default values set + # in the parent routine. + # + # + # Used in the argparser code as the "name" of the postprocessor program. + # + values["MACHINE_NAME"] = "Masso G3" + values["POSTPROCESSOR_FILE_NAME"] = __name__ + # + # setting TOOL_BEFORE_CHANGE to True will output T# M6 before each tool change + # rather than M6 T#. + # + values["TOOL_BEFORE_CHANGE"] = True - # - # setting TOOL_BEFORE_CHANGE to True will output T# M6 before each tool change - # rather than M6 T#. - # - values["TOOL_BEFORE_CHANGE"] = type(True) - values["UNITS"] = UNITS - - -def init_argument_defaults(argument_defaults: Dict[str, bool]) -> None: - """Initialize which arguments (in a pair) are shown as the default argument.""" - PostUtilsArguments.init_argument_defaults(argument_defaults) - # - # Modify which argument to show as the default in flag-type arguments here. - # If the value is True, the first argument will be shown as the default. - # If the value is False, the second argument will be shown as the default. - # - # For example, if you want to show Metric mode as the default, use: - # argument_defaults["metric_inch"] = True - # - # If you want to show that "Don't pop up editor for writing output" is - # the default, use: - # argument_defaults["show-editor"] = False. - # - # Note: You also need to modify the corresponding entries in the "values" hash - # to actually make the default value(s) change to match. - # - - -def init_arguments_visible(arguments_visible: Dict[str, bool]) -> None: - """Initialize which argument pairs are visible in TOOLTIP_ARGS.""" - PostUtilsArguments.init_arguments_visible(arguments_visible) - # - # Modify the visibility of any arguments from the defaults here. - # - - -def init_arguments( - values: Values, - argument_defaults: Dict[str, bool], - arguments_visible: Dict[str, bool], -) -> Parser: - """Initialize the shared argument definitions.""" - parser: Parser = PostUtilsArguments.init_shared_arguments( - values, argument_defaults, arguments_visible - ) - # - # Add any argument definitions that are not shared with all other - # postprocessors here. - # - return parser - - -# -# Creating global variables and using functions to modify them -# is useful for being able to test things later. -# -global_values: Values = {} -init_values(global_values) -global_argument_defaults: Dict[str, bool] = {} -init_argument_defaults(global_argument_defaults) -global_arguments_visible: Dict[str, bool] = {} -init_arguments_visible(global_arguments_visible) -global_parser: Parser = init_arguments( - global_values, global_argument_defaults, global_arguments_visible -) -# -# The TOOLTIP_ARGS value is created from the help information about the arguments. -# -TOOLTIP_ARGS: str = global_parser.format_help() -# -# Create another parser just to get a list of all possible arguments -# that may be output using --output_all_arguments. -# -global_all_arguments_visible: Dict[str, bool] = {} -for k in iter(global_arguments_visible): - global_all_arguments_visible[k] = True -global_all_visible: Parser = init_arguments( - global_values, global_argument_defaults, global_all_arguments_visible -) - - -def export(objectslist, filename: str, argstring: str) -> str: - """Postprocess the objects in objectslist to filename.""" - args: Union[str, argparse.Namespace] - flag: bool - - global UNITS # pylint: disable=global-statement - - # print(parser.format_help()) - - (flag, args) = PostUtilsArguments.process_shared_arguments( - global_values, global_parser, argstring, global_all_visible, filename - ) - if not flag: - return args # type: ignore - # - # Process any additional arguments here - # - - # - # Update the global variables that might have been modified - # while processing the arguments. - # - UNITS = global_values["UNITS"] - - return PostUtilsExport.export_common(global_values, objectslist, filename) + @property + def tooltip(self): + tooltip: str = """ + This is a postprocessor file for the CAM workbench. + It is used to take a pseudo-gcode fragment from a CAM object + and output 'real' GCode suitable for a Masso G3 3 axis mill. + """ + return tooltip diff --git a/src/Mod/CAM/Path/Post/scripts/refactored_test_post.py b/src/Mod/CAM/Path/Post/scripts/refactored_test_post.py index 247359ecde..0e7469e49c 100644 --- a/src/Mod/CAM/Path/Post/scripts/refactored_test_post.py +++ b/src/Mod/CAM/Path/Post/scripts/refactored_test_post.py @@ -23,13 +23,9 @@ # * * # *************************************************************************** -import argparse - -from typing import Any, Dict, List, Optional, Tuple, Union +from typing import Any, Dict from Path.Post.Processor import PostProcessor -import Path.Post.UtilsArguments as PostUtilsArguments -import Path.Post.UtilsExport as PostUtilsExport import Path import FreeCAD @@ -46,16 +42,6 @@ else: # # Define some types that are used throughout this file. # -Defaults = Dict[str, bool] -FormatHelp = str -GCodeOrNone = Optional[str] -GCodeSections = List[Tuple[str, GCodeOrNone]] -Parser = argparse.ArgumentParser -ParserArgs = Union[None, str, argparse.Namespace] -Postables = Union[List, List[Tuple[str, List]]] -Section = Tuple[str, List] -Sublist = List -Units = str Values = Dict[str, Any] Visible = Dict[str, bool] @@ -63,48 +49,28 @@ Visible = Dict[str, bool] class Refactored_Test(PostProcessor): """The Refactored Test post processor class.""" - def __init__(self, job) -> None: + def __init__( + self, + job, + tooltip=translate("CAM", "Refactored Test post processor"), + tooltipargs=[""], + units="Metric", + ) -> None: super().__init__( job=job, - tooltip=translate("CAM", "Refactored Test post processor"), - tooltipargs=[""], - units="Metric", + tooltip=tooltip, + tooltipargs=tooltipargs, + units=units, ) - self.reinitialize() Path.Log.debug("Refactored Test post processor initialized") - def reinitialize(self) -> None: - """Initialize or reinitialize the 'core' data structures for the postprocessor.""" - # - # This is also used to reinitialize the data structures between tests. - # - self.values: Values = {} - self.init_values(self.values) - self.argument_defaults: Defaults = {} - self.init_argument_defaults(self.argument_defaults) - self.arguments_visible: Visible = {} - self.init_arguments_visible(self.arguments_visible) - self.parser: Parser = self.init_arguments( - self.values, self.argument_defaults, self.arguments_visible - ) - # - # Create another parser just to get a list of all possible arguments - # that may be output using --output_all_arguments. - # - self.all_arguments_visible: Visible = {} - for k in iter(self.arguments_visible): - self.all_arguments_visible[k] = True - self.all_visible: Parser = self.init_arguments( - self.values, self.argument_defaults, self.all_arguments_visible - ) - def init_values(self, values: Values) -> None: """Initialize values that are used throughout the postprocessor.""" # - PostUtilsArguments.init_shared_values(values) + super().init_values(values) # # Set any values here that need to override the default values set - # in the init_shared_values routine. + # in the parent routine. # # Used in the argparser code as the "name" of the postprocessor program. # @@ -143,30 +109,10 @@ class Refactored_Test(PostProcessor): # Don't output a G43 tool length command following tool changes by default. # values["USE_TLO"] = False - values["UNITS"] = self._units - - def init_argument_defaults(self, argument_defaults: Defaults) -> None: - """Initialize which arguments (in a pair) are shown as the default argument.""" - PostUtilsArguments.init_argument_defaults(argument_defaults) - # - # Modify which argument to show as the default in flag-type arguments here. - # If the value is True, the first argument will be shown as the default. - # If the value is False, the second argument will be shown as the default. - # - # For example, if you want to show Metric mode as the default, use: - # argument_defaults["metric_inch"] = True - # - # If you want to show that "Don't pop up editor for writing output" is - # the default, use: - # argument_defaults["show-editor"] = False. - # - # Note: You also need to modify the corresponding entries in the "values" hash - # to actually make the default value(s) change to match. - # def init_arguments_visible(self, arguments_visible: Visible) -> None: """Initialize which argument pairs are visible in TOOLTIP_ARGS.""" - PostUtilsArguments.init_arguments_visible(arguments_visible) + super().init_arguments_visible(arguments_visible) # # Modify the visibility of any arguments from the defaults here. # @@ -175,100 +121,6 @@ class Refactored_Test(PostProcessor): for key in iter(arguments_visible): arguments_visible[key] = False - def init_arguments( - self, - values: Values, - argument_defaults: Defaults, - arguments_visible: Visible, - ) -> Parser: - """Initialize the shared argument definitions.""" - _parser: Parser = PostUtilsArguments.init_shared_arguments( - values, argument_defaults, arguments_visible - ) - # - # Add any argument definitions that are not shared with other postprocessors here. - # - return _parser - - def process_arguments(self) -> Tuple[bool, ParserArgs]: - """Process any arguments to the postprocessor.""" - # - # This function is separated out to make it easier to inherit from this postprocessor. - # - args: ParserArgs - flag: bool - - (flag, args) = PostUtilsArguments.process_shared_arguments( - self.values, self.parser, self._job.PostProcessorArgs, self.all_visible, "-" - ) - # - # If the flag is True, then all of the arguments should be processed normally. - # - if flag: - # - # Process any additional arguments here. - # - # - # Update any variables that might have been modified while processing the arguments. - # - self._units = self.values["UNITS"] - # - # If the flag is False, then args is either None (indicating an error while - # processing the arguments) or a string containing the argument list formatted - # for output. Either way the calling routine will need to handle the args value. - # - return (flag, args) - - def process_postables(self) -> GCodeSections: - """Postprocess the 'postables' in the job to g code sections.""" - # - # This function is separated out to make it easier to inherit from this postprocessor. - # - gcode: GCodeOrNone - g_code_sections: GCodeSections - partname: str - postables: Postables - section: Section - sublist: Sublist - - postables = self._buildPostList() - - Path.Log.debug(f"postables count: {len(postables)}") - - g_code_sections = [] - for _, section in enumerate(postables): - partname, sublist = section - gcode = PostUtilsExport.export_common(self.values, sublist, "-") - g_code_sections.append((partname, gcode)) - - return g_code_sections - - def export(self) -> Union[None, GCodeSections]: - """Process the parser arguments, then postprocess the 'postables'.""" - args: ParserArgs - flag: bool - - Path.Log.debug("Exporting the job") - - (flag, args) = self.process_arguments() - # - # If the flag is True, then continue postprocessing the 'postables'. - # - if flag: - return self.process_postables() - # - # The flag is False meaning something unusual happened. - # - # If args is None then there was an error during argument processing. - # - if args is None: - return None - # - # Otherwise args will contain the argument list formatted for output - # instead of the "usual" gcode. - # - return [("allitems", args)] # type: ignore - @property def tooltip(self): tooltip: str = """ @@ -276,11 +128,3 @@ class Refactored_Test(PostProcessor): to test the postprocessor code. It probably isn't useful for "real" gcode. """ return tooltip - - @property - def tooltipArgs(self) -> FormatHelp: - return self.parser.format_help() - - @property - def units(self) -> Units: - return self._units diff --git a/src/Mod/CAM/Path/Post/scripts/snapmaker_post.py b/src/Mod/CAM/Path/Post/scripts/snapmaker_post.py index 80338513fb..7606c18276 100644 --- a/src/Mod/CAM/Path/Post/scripts/snapmaker_post.py +++ b/src/Mod/CAM/Path/Post/scripts/snapmaker_post.py @@ -116,22 +116,24 @@ class Snapmaker(Path.Post.Processor.PostProcessor): self.arguments_visible: dict[str, bool] = dict() self.parser = argparse.ArgumentParser() - self.init_values() - self.init_argument_defaults() - self.init_arguments_visible() - self.parser = self.init_parser(self.values, self.argument_defaults, self.arguments_visible) + self.snapmaker_init_values() + self.snapmaker_init_argument_defaults() + self.snapmaker_init_arguments_visible() + self.parser = self.snapmaker_init_parser( + self.values, self.argument_defaults, self.arguments_visible + ) # create another parser with all visible arguments all_arguments_visible = dict() for key in iter(self.arguments_visible): all_arguments_visible[key] = True - self.visible_parser = self.init_parser( + self.visible_parser = self.snapmaker_init_parser( self.values, self.argument_defaults, all_arguments_visible ) FreeCAD.Console.PrintLog(f'{self.values["POSTPROCESSOR_FILE_NAME"]}: initialized.\n') - def init_values(self): + def snapmaker_init_values(self): """Initialize values that are used throughout the postprocessor.""" Path.Post.UtilsArguments.init_shared_values(self.values) @@ -197,7 +199,7 @@ class Snapmaker(Path.Post.Processor.PostProcessor): "percent" ] - def init_argument_defaults(self) -> None: + def snapmaker_init_argument_defaults(self) -> None: """Initialize which arguments (in a pair) are shown as the default argument.""" Path.Post.UtilsArguments.init_argument_defaults(self.argument_defaults) @@ -210,7 +212,7 @@ class Snapmaker(Path.Post.Processor.PostProcessor): self.argument_defaults["boundaries-check"] = True self.argument_defaults["spindle-percent"] = True - def init_arguments_visible(self) -> None: + def snapmaker_init_arguments_visible(self) -> None: """Initialize which argument pairs are visible in TOOLTIP_ARGS.""" Path.Post.UtilsArguments.init_arguments_visible(self.arguments_visible) @@ -232,7 +234,9 @@ class Snapmaker(Path.Post.Processor.PostProcessor): self.arguments_visible["line-increment"] = True self.arguments_visible["spindle-speeds"] = True - def init_parser(self, values, argument_defaults, arguments_visible) -> argparse.ArgumentParser: + def snapmaker_init_parser( + self, values, argument_defaults, arguments_visible + ) -> argparse.ArgumentParser: """Initialize the postprocessor arguments parser""" parser = Path.Post.UtilsArguments.init_shared_arguments( values, argument_defaults, arguments_visible @@ -335,7 +339,7 @@ class Snapmaker(Path.Post.Processor.PostProcessor): return parser - def process_arguments(self, filename: str = "-") -> (bool, str | argparse.Namespace): + def snapmaker_process_arguments(self, filename: str = "-") -> (bool, str | argparse.Namespace): """Process any arguments to the postprocessor.""" (flag, args) = Path.Post.UtilsArguments.process_shared_arguments( self.values, self.parser, self._job.PostProcessorArgs, self.visible_parser, filename @@ -389,7 +393,7 @@ class Snapmaker(Path.Post.Processor.PostProcessor): return flag, args - def process_postables(self, filename: str = "-") -> [(str, str)]: + def snapmaker_process_postables(self, filename: str = "-") -> [(str, str)]: """process job sections to gcode""" sections: [(str, str)] = list() @@ -622,11 +626,12 @@ class Snapmaker(Path.Post.Processor.PostProcessor): def export(self, filename: str | pathlib.Path = "-"): """process gcode and export""" - (flag, args) = self.process_arguments() + (flag, args) = self.snapmaker_process_arguments() if flag: - return self.process_postables(filename) - else: - return [("allitems", args)] + return self.snapmaker_process_postables(filename) + if args is None: + return None + return [("allitems", args)] @property def tooltip(self) -> str: diff --git a/src/Mod/CAM/PathSimulator/AppGL/linmath.h b/src/Mod/CAM/PathSimulator/AppGL/linmath.h index 7ebdc2cfba..c0feb2caa0 100644 --- a/src/Mod/CAM/PathSimulator/AppGL/linmath.h +++ b/src/Mod/CAM/PathSimulator/AppGL/linmath.h @@ -5,7 +5,7 @@ #define LINMATH_H #include -#include +#include #ifdef LINMATH_NO_INLINE #define LINMATH_H_FUNC static diff --git a/src/Mod/CAM/libarea/Adaptive.cpp b/src/Mod/CAM/libarea/Adaptive.cpp index 3b45c3254a..535d25efe4 100644 --- a/src/Mod/CAM/libarea/Adaptive.cpp +++ b/src/Mod/CAM/libarea/Adaptive.cpp @@ -26,6 +26,7 @@ #include #include #include +#include namespace ClipperLib { @@ -116,7 +117,7 @@ inline double Angle3Points(const DoublePoint& p1, const DoublePoint& p2, const D double t1 = atan2(p2.Y - p1.Y, p2.X - p1.X); double t2 = atan2(p3.Y - p2.Y, p3.X - p2.X); double a = fabs(t2 - t1); - return min(a, 2 * M_PI - a); + return min(a, 2 * std::numbers::pi - a); } inline DoublePoint DirectionV(const IntPoint& pt1, const IntPoint& pt2) @@ -1096,8 +1097,8 @@ private: class Interpolation { public: - const double MIN_ANGLE = -M_PI / 4; - const double MAX_ANGLE = M_PI / 4; + const double MIN_ANGLE = -std::numbers::pi / 4; + const double MAX_ANGLE = std::numbers::pi / 4; void clear() { @@ -1542,7 +1543,7 @@ double Adaptive2d::CalcCutArea(Clipper& clip, double minFi = fi1; double maxFi = fi2; if (maxFi < minFi) { - maxFi += 2 * M_PI; + maxFi += 2 * std::numbers::pi; } if (preventConventional && interPathLen >= RESOLUTION_FACTOR) { @@ -2359,7 +2360,7 @@ bool Adaptive2d::MakeLeadPath(bool leadIn, IntPoint(currentPoint.X + nextDir.X * stepSize, currentPoint.Y + nextDir.Y * stepSize); Path checkPath; double adaptFactor = 0.4; - double alfa = M_PI / 64; + double alfa = std::numbers::pi / 64; double pathLen = 0; checkPath.push_back(nextPoint); for (int i = 0; i < 10000; i++) { @@ -2802,7 +2803,7 @@ void Adaptive2d::ProcessPolyNode(Paths boundPaths, Paths toolBoundPaths) IntPoint clp; // to store closest point vector gyro; // used to average tool direction vector angleHistory; // use to predict deflection angle - double angle = M_PI; + double angle = std::numbers::pi; engagePoint = toolPos; Interpolation interp; // interpolation instance @@ -2846,7 +2847,7 @@ void Adaptive2d::ProcessPolyNode(Paths boundPaths, Paths toolBoundPaths) } } - angle = M_PI / 4; // initial pass angle + angle = std::numbers::pi / 4; // initial pass angle bool recalcArea = false; double cumulativeCutArea = 0; // init gyro @@ -2991,7 +2992,7 @@ void Adaptive2d::ProcessPolyNode(Paths boundPaths, Paths toolBoundPaths) rotateStep++; // if new tool pos. outside boundary rotate until back in recalcArea = true; - newToolDir = rotate(newToolDir, M_PI / 90); + newToolDir = rotate(newToolDir, std::numbers::pi / 90); newToolPos = IntPoint(long(toolPos.X + newToolDir.X * stepScaled), long(toolPos.Y + newToolDir.Y * stepScaled)); } diff --git a/src/Mod/CAM/libarea/Adaptive.hpp b/src/Mod/CAM/libarea/Adaptive.hpp index 497aa2792a..9f57b5407f 100644 --- a/src/Mod/CAM/libarea/Adaptive.hpp +++ b/src/Mod/CAM/libarea/Adaptive.hpp @@ -36,10 +36,6 @@ #define __LONG_MAX__ 2147483647 #endif -#ifndef M_PI -#define M_PI 3.141592653589793238 -#endif - // #define DEV_MODE #define NTOL 1.0e-7 // numeric tolerance diff --git a/src/Mod/CAM/libarea/Box2D.h b/src/Mod/CAM/libarea/Box2D.h index 23ee202ac7..b0c02d9e3f 100644 --- a/src/Mod/CAM/libarea/Box2D.h +++ b/src/Mod/CAM/libarea/Box2D.h @@ -29,7 +29,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #pragma once #include // for memcpy() prototype -#include // for sqrt() prototype +#include // for sqrt() prototype class CBox2D { diff --git a/src/Mod/CAM/libarea/Curve.h b/src/Mod/CAM/libarea/Curve.h index f6f62eee14..35ea485a52 100644 --- a/src/Mod/CAM/libarea/Curve.h +++ b/src/Mod/CAM/libarea/Curve.h @@ -31,7 +31,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include -#include +#include #include "Point.h" #include "Box2D.h" diff --git a/src/Mod/CAM/libarea/kurve/geometry.h b/src/Mod/CAM/libarea/kurve/geometry.h index 88cbbf5b1a..8c8dc5a6ca 100644 --- a/src/Mod/CAM/libarea/kurve/geometry.h +++ b/src/Mod/CAM/libarea/kurve/geometry.h @@ -17,7 +17,7 @@ #endif #endif -#include +#include #include #include #include diff --git a/src/Mod/Draft/Draft.py b/src/Mod/Draft/Draft.py index 78b0444586..1d05ec41fc 100644 --- a/src/Mod/Draft/Draft.py +++ b/src/Mod/Draft/Draft.py @@ -54,12 +54,6 @@ from draftutils.utils import ARROW_TYPES as arrowtypes from draftutils.utils import (type_check, typecheck, - get_param_type, - getParamType, - get_param, - getParam, - set_param, - setParam, precision, tolerance) diff --git a/src/Mod/Draft/Resources/ui/preferences-drafttexts.ui b/src/Mod/Draft/Resources/ui/preferences-drafttexts.ui index 2191e02e2c..0dfb1dfcfc 100644 --- a/src/Mod/Draft/Resources/ui/preferences-drafttexts.ui +++ b/src/Mod/Draft/Resources/ui/preferences-drafttexts.ui @@ -75,22 +75,20 @@ in the Annotation scale widget. If the scale is 1:100 the multiplier is 100. - Font name or family + Font name - + + + + 280 + 0 + + - The default font for texts, dimensions and labels. It can be a font name such -as "Arial", a style such as "sans", "serif" or "mono", or a family such as -"Arial,Helvetica,sans", or a name with a style such as "Arial:Bold". - - - - - - Internal font + The default font for texts, dimensions and labels textfont @@ -100,6 +98,13 @@ as "Arial", a style such as "sans", "serif" or &qu + + + + Qt::Horizontal + + + @@ -138,13 +143,6 @@ as "Arial", a style such as "sans", "serif" or &qu - - - - Qt::Horizontal - - - @@ -695,6 +693,11 @@ used for linear dimensions.
    QComboBox
    Gui/PrefWidgets.h
    + + Gui::PrefFontBox + QFontComboBox +
    Gui/PrefWidgets.h
    +
    Gui::PrefSpinBox QSpinBox diff --git a/src/Mod/Draft/draftguitools/gui_trackers.py b/src/Mod/Draft/draftguitools/gui_trackers.py index 1ce39fd681..1c067781f6 100644 --- a/src/Mod/Draft/draftguitools/gui_trackers.py +++ b/src/Mod/Draft/draftguitools/gui_trackers.py @@ -43,6 +43,7 @@ import FreeCADGui import Draft import DraftVecUtils from FreeCAD import Vector +from draftutils import grid_observer from draftutils import gui_utils from draftutils import params from draftutils import utils @@ -1350,6 +1351,16 @@ class gridTracker(Tracker): self.setAxesColor(wp) self.on() + def on(self): + """Set the visibility to True and update the checked state of the grid button.""" + super().on() + grid_observer._update_grid_gui() + + def off(self): + """Set the visibility to False and update the checked state of the grid button.""" + super().off() + grid_observer._update_grid_gui() + def getClosestNode(self, point): """Return the closest node from the given point.""" wp = self._get_wp() diff --git a/src/Mod/Draft/draftutils/init_draft_statusbar.py b/src/Mod/Draft/draftutils/init_draft_statusbar.py index 43ef0b63fd..80e048f6e4 100644 --- a/src/Mod/Draft/draftutils/init_draft_statusbar.py +++ b/src/Mod/Draft/draftutils/init_draft_statusbar.py @@ -287,6 +287,46 @@ def init_draft_statusbar_snap(): snap_action.addAction(Gui.Command.get(cmd).getAction()[0]) +def show_draft_statusbar_scale(): + """ + shows draft statusbar scale widget + """ + mw = Gui.getMainWindow() + sb = mw.statusBar() + + scale_widget = sb.findChild(QtWidgets.QToolBar, "draft_scale_widget") + if scale_widget: + scale_widget.show() + else: + scale_widget = mw.findChild(QtWidgets.QToolBar, "draft_scale_widget") + if scale_widget: + sb.insertPermanentWidget(3, scale_widget) + scale_widget.show() + else: + init_draft_statusbar_scale() + + +def show_draft_statusbar_snap(): + """ + shows draft statusbar snap widget + """ + mw = Gui.getMainWindow() + sb = mw.statusBar() + + snap_widget = sb.findChild(QtWidgets.QToolBar, "draft_snap_widget") + if snap_widget: + snap_widget.setOrientation(QtCore.Qt.Orientation.Horizontal) + snap_widget.show() + else: + snap_widget = mw.findChild(QtWidgets.QToolBar, "draft_snap_widget") + if snap_widget: + sb.insertPermanentWidget(2, snap_widget) + snap_widget.setOrientation(QtCore.Qt.Orientation.Horizontal) + snap_widget.show() + else: + init_draft_statusbar_snap() + + def hide_draft_statusbar_scale(): """ hides draft statusbar scale widget @@ -323,36 +363,10 @@ def show_draft_statusbar(): """ shows draft statusbar if present or initializes it """ - mw = Gui.getMainWindow() - sb = mw.statusBar() - if params.get_param("DisplayStatusbarScaleWidget"): - scale_widget = sb.findChild(QtWidgets.QToolBar, "draft_scale_widget") - if scale_widget: - scale_widget.show() - else: - scale_widget = mw.findChild(QtWidgets.QToolBar, "draft_scale_widget") - if scale_widget: - sb.insertPermanentWidget(3, scale_widget) - scale_widget.show() - else: - t = QtCore.QTimer() - t.singleShot(500, init_draft_statusbar_scale) - + QtCore.QTimer().singleShot(500, show_draft_statusbar_scale) if params.get_param("DisplayStatusbarSnapWidget"): - snap_widget = sb.findChild(QtWidgets.QToolBar, "draft_snap_widget") - if snap_widget: - snap_widget.setOrientation(QtCore.Qt.Orientation.Horizontal) - snap_widget.show() - else: - snap_widget = mw.findChild(QtWidgets.QToolBar, "draft_snap_widget") - if snap_widget: - sb.insertPermanentWidget(2, snap_widget) - snap_widget.setOrientation(QtCore.Qt.Orientation.Horizontal) - snap_widget.show() - else: - t = QtCore.QTimer() - t.singleShot(500, init_draft_statusbar_snap) + QtCore.QTimer().singleShot(500, show_draft_statusbar_snap) def hide_draft_statusbar(): @@ -361,8 +375,7 @@ def hide_draft_statusbar(): """ # Delay required in case the Draft WB is autoloaded, # else show_draft_statusbar will not yet be done. - t = QtCore.QTimer() - t.singleShot(500, hide_draft_statusbar_scale) - t.singleShot(500, hide_draft_statusbar_snap) + QtCore.QTimer().singleShot(500, hide_draft_statusbar_scale) + QtCore.QTimer().singleShot(500, hide_draft_statusbar_snap) ## @} diff --git a/src/Mod/Draft/draftutils/params.py b/src/Mod/Draft/draftutils/params.py index 70d86a19fb..a08f2bec32 100644 --- a/src/Mod/Draft/draftutils/params.py +++ b/src/Mod/Draft/draftutils/params.py @@ -367,6 +367,29 @@ def _param_from_PrefFileChooser(widget): return path, entry, "" +def _param_from_PrefFontBox(widget): + if App.GuiUp: + from PySide import QtGui + font = QtGui.QFont() + font.setStyleHint(QtGui.QFont.StyleHint.SansSerif) + value = font.defaultFamily() + else: + value = "" + for elem in list(widget): + if "name" in elem.keys(): + att_name = elem.attrib["name"] + if att_name == "prefEntry": + entry = elem.find("cstring").text + elif att_name == "prefPath": + path = elem.find("cstring").text + # We must set the parameter if it does not exist, else + # the Gui::PrefFontBox will show the wrong value. + param_grp = App.ParamGet("User parameter:BaseApp/Preferences/" + path) + if entry not in param_grp.GetStrings(): + param_grp.SetString(entry, value) + return path, entry, value + + def _get_param_dictionary(): # print("Creating preferences dictionary...") @@ -595,6 +618,9 @@ def _get_param_dictionary(): elif att_class == "Gui::PrefFileChooser": path, entry, value = _param_from_PrefFileChooser(widget) typ = "string" + elif att_class == "Gui::PrefFontBox": + path, entry, value = _param_from_PrefFontBox(widget) + typ = "string" if path is not None: if path in param_dict: diff --git a/src/Mod/Draft/draftutils/utils.py b/src/Mod/Draft/draftutils/utils.py index a3b864bc31..cb97c82441 100644 --- a/src/Mod/Draft/draftutils/utils.py +++ b/src/Mod/Draft/draftutils/utils.py @@ -183,178 +183,6 @@ def type_check(args_and_types, name="?"): typecheck = type_check -def get_param_type(param): - """Return the type of the parameter entered. - - Parameters - ---------- - param : str - A string that indicates a parameter in the parameter database. - - Returns - ------- - str or None - The returned string could be `'int'`, `'string'`, `'float'`, - `'bool'`, `'unsigned'`, depending on the parameter. - It returns `None` for unhandled situations. - """ - if param in ("dimsymbol", "dimPrecision", - "precision", "defaultWP", "snapRange", "gridEvery", - "linewidth", "modconstrain", "modsnap", - "maxSnapEdges", "modalt", "HatchPatternResolution", - "snapStyle", "DefaultAnnoDisplayMode", "DefaultAnnoLineWidth", - "DefaultDrawStyle", "DefaultDisplayMode", - "gridSize", "gridTransparency"): - return "int" - elif param in ("constructiongroupname", "textfont", - "patternFile", "snapModes", - "FontFile", "ClonePrefix", "overrideUnit", - "labeltype", "gridSpacing") or "inCommandShortcut" in param: - return "string" - elif param in ("textheight", "arrowsize", "extlines", "dimspacing", - "dimovershoot", "extovershoot", "HatchPatternSize", - "LineSpacing", "DefaultAnnoScaleMultiplier"): - return "float" - elif param in ("selectBaseObjects", "alwaysSnap", "grid", - "MakeFaceMode", "DimShowLine", - "SvgLinesBlack", "dxfStdSize", "SnapBarShowOnlyDuringCommands", - "alwaysShowGrid", "renderPolylineWidth", - "showPlaneTracker", "UsePartPrimitives", - "DiscretizeEllipses", "showUnit", "coloredGridAxes", - "Draft_array_fuse", "Draft_array_Link", "gridBorder"): - return "bool" - elif param in ("color", "constructioncolor", "snapcolor", - "gridColor", "DefaultTextColor", "DefaultAnnoLineColor"): - return "unsigned" - else: - return None - - -getParamType = get_param_type - - -def get_param(param, default=None): - """Return a parameter value from the current parameter database. - - The parameter database is located in the tree - :: - 'User parameter:BaseApp/Preferences/Mod/Draft' - - In the case that `param` is `'linewidth'` or `'color'` it will get - the values from the View parameters - :: - 'User parameter:BaseApp/Preferences/View/DefaultShapeLineWidth' - 'User parameter:BaseApp/Preferences/View/DefaultShapeLineColor' - - Parameters - ---------- - param : str - A string that indicates a parameter in the parameter database. - - default : optional - It indicates the default value of the given parameter. - It defaults to `None`, in which case it will use a specific - value depending on the type of parameter determined - with `get_param_type`. - - Returns - ------- - int, or str, or float, or bool - Depending on `param` and its type, by returning `ParameterGrp.GetInt`, - `ParameterGrp.GetString`, `ParameterGrp.GetFloat`, - `ParameterGrp.GetBool`, or `ParameterGrp.GetUnsinged`. - """ - draft_params = "User parameter:BaseApp/Preferences/Mod/Draft" - view_params = "User parameter:BaseApp/Preferences/View" - - p = App.ParamGet(draft_params) - v = App.ParamGet(view_params) - t = get_param_type(param) - # print("getting param ",param, " of type ",t, " default: ",str(default)) - if t == "int": - if default is None: - default = 0 - if param == "linewidth": - return v.GetInt("DefaultShapeLineWidth", default) - return p.GetInt(param, default) - elif t == "string": - if default is None: - default = "" - return p.GetString(param, default) - elif t == "float": - if default is None: - default = 0 - return p.GetFloat(param, default) - elif t == "bool": - if default is None: - default = False - return p.GetBool(param, default) - elif t == "unsigned": - if default is None: - default = 0 - if param == "color": - return v.GetUnsigned("DefaultShapeLineColor", default) - return p.GetUnsigned(param, default) - else: - return None - - -getParam = get_param - - -def set_param(param, value): - """Set a Draft parameter with the given value. - - The parameter database is located in the tree - :: - 'User parameter:BaseApp/Preferences/Mod/Draft' - - In the case that `param` is `'linewidth'` or `'color'` it will set - the View parameters - :: - 'User parameter:BaseApp/Preferences/View/DefaultShapeLineWidth' - 'User parameter:BaseApp/Preferences/View/DefaultShapeLineColor' - - Parameters - ---------- - param : str - A string that indicates a parameter in the parameter database. - - value : int, or str, or float, or bool - The appropriate value of the parameter. - Depending on `param` and its type, determined with `get_param_type`, - it sets the appropriate value by calling `ParameterGrp.SetInt`, - `ParameterGrp.SetString`, `ParameterGrp.SetFloat`, - `ParameterGrp.SetBool`, or `ParameterGrp.SetUnsinged`. - """ - draft_params = "User parameter:BaseApp/Preferences/Mod/Draft" - view_params = "User parameter:BaseApp/Preferences/View" - - p = App.ParamGet(draft_params) - v = App.ParamGet(view_params) - t = get_param_type(param) - - if t == "int": - if param == "linewidth": - v.SetInt("DefaultShapeLineWidth", value) - else: - p.SetInt(param, value) - elif t == "string": - p.SetString(param, value) - elif t == "float": - p.SetFloat(param, value) - elif t == "bool": - p.SetBool(param, value) - elif t == "unsigned": - if param == "color": - v.SetUnsigned("DefaultShapeLineColor", value) - else: - p.SetUnsigned(param, value) - - -setParam = set_param - - def precision(): """Return the precision value from the parameter database. diff --git a/src/Mod/Drawing/App/DrawingExport.cpp b/src/Mod/Drawing/App/DrawingExport.cpp index 1f654caf60..516458e917 100644 --- a/src/Mod/Drawing/App/DrawingExport.cpp +++ b/src/Mod/Drawing/App/DrawingExport.cpp @@ -207,9 +207,10 @@ void SVGOutput::printCircle(const BRepAdaptor_Curve& c, std::ostream& out) } // arc of circle else { + using std::numbers::pi; // See also https://developer.mozilla.org/en/SVG/Tutorial/Paths - char xar = '0'; // x-axis-rotation - char las = (l - f > D_PI) ? '1' : '0'; // large-arc-flag + char xar = '0'; // x-axis-rotation + char las = (l - f > pi) ? '1' : '0'; // large-arc-flag char swp = (a < 0) ? '1' : '0'; // sweep-flag, i.e. clockwise (0) or counter-clockwise (1) out << ""; @@ -255,7 +256,8 @@ void SVGOutput::printEllipse(const BRepAdaptor_Curve& c, int id, std::ostream& o } // arc of ellipse else { - char las = (l - f > D_PI) ? '1' : '0'; // large-arc-flag + using std::numbers::pi; + char las = (l - f > pi) ? '1' : '0'; // large-arc-flag char swp = (a < 0) ? '1' : '0'; // sweep-flag, i.e. clockwise (0) or counter-clockwise (1) out << "" << std::endl; @@ -460,6 +462,8 @@ void DXFOutput::printHeader(std::ostream& out) void DXFOutput::printCircle(const BRepAdaptor_Curve& c, std::ostream& out) { + using std::numbers::pi; + gp_Circ circ = c.Circle(); const gp_Pnt& p = circ.Location(); double r = circ.Radius(); @@ -502,8 +506,8 @@ void DXFOutput::printCircle(const BRepAdaptor_Curve& c, std::ostream& out) double bx = e.X() - p.X(); double by = e.Y() - p.Y(); - double start_angle = atan2(ay, ax) * 180 / D_PI; - double end_angle = atan2(by, bx) * 180 / D_PI; + double start_angle = atan2(ay, ax) * 180 / pi; + double end_angle = atan2(by, bx) * 180 / pi; if (a > 0) { double temp = start_angle; diff --git a/src/Mod/Drawing/Gui/TaskOrthoViews.cpp b/src/Mod/Drawing/Gui/TaskOrthoViews.cpp index 98d8a2897f..319dbcb186 100644 --- a/src/Mod/Drawing/Gui/TaskOrthoViews.cpp +++ b/src/Mod/Drawing/Gui/TaskOrthoViews.cpp @@ -256,7 +256,7 @@ void orthoview::set_projection(const gp_Ax2& cs) // angle between desired projection and actual projection float rotation = X_dir.Angle(actual_X); - if (rotation != 0 && abs(M_PI - rotation) > 0.05) { + if (rotation != 0 && abs(std::numbers::pi - rotation) > 0.05) { if (!Z_dir.IsEqual(actual_X.Crossed(X_dir), 0.05)) { rotation = -rotation; } @@ -266,7 +266,7 @@ void orthoview::set_projection(const gp_Ax2& cs) // this_view->Direction.setValue(Z_dir.X(), Z_dir.Y(), Z_dir.Z()); this_view->Direction.setValue(x, y, z); - this_view->Rotation.setValue(180 * rotation / M_PI); + this_view->Rotation.setValue(180 * rotation / std::numbers::pi); } /////////////////////////////////////////////////////////////////////////////////////////////// @@ -613,8 +613,8 @@ void OrthoViews::set_orientation(int index) // set orientation of single view dir = primary.XDirection(); n = -views[index]->rel_y; } - - rotation = n * rotate_coeff * M_PI / 2; // rotate_coeff is -1 or 1 for 1st or 3rd angle + // rotate_coeff is -1 or 1 for 1st or 3rd angle + rotation = n * rotate_coeff * std::numbers::pi / 2; cs = primary.Rotated(gp_Ax1(gp_Pnt(0, 0, 0), dir), rotation); views[index]->set_projection(cs); } @@ -780,7 +780,7 @@ void OrthoViews::set_Axo(int rel_x, rotations[1] = -0.6156624905260762; } else { - rotations[0] = 1.3088876392502007 - M_PI / 2; + rotations[0] = 1.3088876392502007 - std::numbers::pi / 2; rotations[1] = -0.6156624905260762; } diff --git a/src/Mod/Fem/App/AppFem.cpp b/src/Mod/Fem/App/AppFem.cpp index b8a8cb6d17..df13a6cf96 100644 --- a/src/Mod/Fem/App/AppFem.cpp +++ b/src/Mod/Fem/App/AppFem.cpp @@ -189,6 +189,7 @@ PyMOD_INIT_FUNC(Fem) Fem::FemPostPipeline ::init(); Fem::FemPostFilter ::init(); Fem::FemPostBranchFilter ::init(); + Fem::FemPostCalculatorFilter ::init(); Fem::FemPostClipFilter ::init(); Fem::FemPostContoursFilter ::init(); Fem::FemPostCutFilter ::init(); diff --git a/src/Mod/Fem/App/FemConstraint.cpp b/src/Mod/Fem/App/FemConstraint.cpp index 6e48bdc599..322e824335 100644 --- a/src/Mod/Fem/App/FemConstraint.cpp +++ b/src/Mod/Fem/App/FemConstraint.cpp @@ -75,7 +75,10 @@ using Adaptor3d_HSurface = Adaptor3d_Surface; using BRepAdaptor_HSurface = BRepAdaptor_Surface; #endif -static const App::PropertyFloatConstraint::Constraints scaleConstraint = {0.0, DBL_MAX, 0.1}; +static const App::PropertyFloatConstraint::Constraints scaleConstraint = { + 0.0, + std::numeric_limits::max(), + 0.1}; PROPERTY_SOURCE(Fem::Constraint, App::DocumentObject) diff --git a/src/Mod/Fem/App/FemConstraintTransform.cpp b/src/Mod/Fem/App/FemConstraintTransform.cpp index b1947866ab..aaa7299009 100644 --- a/src/Mod/Fem/App/FemConstraintTransform.cpp +++ b/src/Mod/Fem/App/FemConstraintTransform.cpp @@ -116,12 +116,14 @@ namespace Base::Rotation anglesToRotation(double xAngle, double yAngle, double zAngle) { + using std::numbers::pi; + static Base::Vector3d a(1, 0, 0); static Base::Vector3d b(0, 1, 0); static int count = 0; - double xRad = xAngle * D_PI / 180.0; - double yRad = yAngle * D_PI / 180.0; - double zRad = zAngle * D_PI / 180.0; + double xRad = xAngle * pi / 180.0; + double yRad = yAngle * pi / 180.0; + double zRad = zAngle * pi / 180.0; if (xAngle != 0) { a[1] = 0; a[2] = 0; diff --git a/src/Mod/Fem/App/FemPostBranchFilter.h b/src/Mod/Fem/App/FemPostBranchFilter.h index 7d483ff0a5..f07f0dd3f8 100644 --- a/src/Mod/Fem/App/FemPostBranchFilter.h +++ b/src/Mod/Fem/App/FemPostBranchFilter.h @@ -64,7 +64,6 @@ protected: private: static const char* OutputEnums[]; - bool m_transform_used = false; void setupPipeline(); vtkSmartPointer m_append; diff --git a/src/Mod/Fem/App/FemPostFilter.cpp b/src/Mod/Fem/App/FemPostFilter.cpp index dc46efa3e8..ddf41382d1 100644 --- a/src/Mod/Fem/App/FemPostFilter.cpp +++ b/src/Mod/Fem/App/FemPostFilter.cpp @@ -1297,3 +1297,132 @@ short int FemPostWarpVectorFilter::mustExecute() const return App::DocumentObject::mustExecute(); } } + + +// *************************************************************************** +// calculator filter +PROPERTY_SOURCE(Fem::FemPostCalculatorFilter, Fem::FemPostFilter) + +FemPostCalculatorFilter::FemPostCalculatorFilter() + : FemPostFilter() +{ + ADD_PROPERTY_TYPE(FieldName, + ("Calculator"), + "Calculator", + App::Prop_None, + "Name of the calculated field"); + ADD_PROPERTY_TYPE(Function, + (""), + "Calculator", + App::Prop_None, + "Expression of the unction to evaluate"); + ADD_PROPERTY_TYPE(ReplacementValue, + (0.0f), + "Calculator", + App::Prop_None, + "Value used to replace invalid operations"); + ADD_PROPERTY_TYPE(ReplaceInvalid, + (false), + "Calculator", + App::Prop_None, + "Replace invalid values"); + + FilterPipeline calculator; + m_calculator = vtkSmartPointer::New(); + m_calculator->SetResultArrayName(FieldName.getValue()); + calculator.source = m_calculator; + calculator.target = m_calculator; + addFilterPipeline(calculator, "calculator"); + setActiveFilterPipeline("calculator"); +} + +FemPostCalculatorFilter::~FemPostCalculatorFilter() = default; + +DocumentObjectExecReturn* FemPostCalculatorFilter::execute() +{ + updateAvailableFields(); + + return FemPostFilter::execute(); +} + +void FemPostCalculatorFilter::onChanged(const Property* prop) +{ + if (prop == &Function) { + m_calculator->SetFunction(Function.getValue()); + } + else if (prop == &FieldName) { + m_calculator->SetResultArrayName(FieldName.getValue()); + } + else if (prop == &ReplaceInvalid) { + m_calculator->SetReplaceInvalidValues(ReplaceInvalid.getValue()); + } + else if (prop == &ReplacementValue) { + m_calculator->SetReplacementValue(ReplacementValue.getValue()); + } + else if (prop == &Data) { + updateAvailableFields(); + } + Fem::FemPostFilter::onChanged(prop); +} + +short int FemPostCalculatorFilter::mustExecute() const +{ + if (Function.isTouched() || FieldName.isTouched()) { + return 1; + } + else { + return FemPostFilter::mustExecute(); + } +} + +void FemPostCalculatorFilter::updateAvailableFields() +{ + // clear all variables + m_calculator->RemoveAllVariables(); + m_calculator->AddCoordinateScalarVariable("coordsX", 0); + m_calculator->AddCoordinateScalarVariable("coordsY", 1); + m_calculator->AddCoordinateScalarVariable("coordsZ", 2); + m_calculator->AddCoordinateVectorVariable("coords"); + + std::vector scalars; + std::vector vectors; + // std::vector tensors; + + vtkSmartPointer data = getInputData(); + vtkDataSet* dset = vtkDataSet::SafeDownCast(data); + if (!dset) { + return; + } + vtkPointData* pd = dset->GetPointData(); + + // get all vector fields + for (int i = 0; i < pd->GetNumberOfArrays(); ++i) { + std::string name1 = pd->GetArrayName(i); + std::string name2 = name1; + std::replace(name2.begin(), name2.end(), ' ', '_'); + if (pd->GetArray(i)->GetNumberOfComponents() == 3) { + m_calculator->AddVectorVariable(name2.c_str(), name1.c_str()); + // add components as scalar variable + m_calculator->AddScalarVariable((name2 + "_X").c_str(), name1.c_str(), 0); + m_calculator->AddScalarVariable((name2 + "_Y").c_str(), name1.c_str(), 1); + m_calculator->AddScalarVariable((name2 + "_Z").c_str(), name1.c_str(), 2); + } + else if (pd->GetArray(i)->GetNumberOfComponents() == 1) { + m_calculator->AddScalarVariable(name2.c_str(), name1.c_str()); + } + } +} + +const std::vector FemPostCalculatorFilter::getScalarVariables() +{ + std::vector scalars = m_calculator->GetScalarVariableNames(); + scalars.insert(scalars.begin(), {"coordsX", "coordsY", "coordsZ"}); + return scalars; +} + +const std::vector FemPostCalculatorFilter::getVectorVariables() +{ + std::vector vectors = m_calculator->GetVectorVariableNames(); + vectors.insert(vectors.begin(), "coords"); + return vectors; +} diff --git a/src/Mod/Fem/App/FemPostFilter.h b/src/Mod/Fem/App/FemPostFilter.h index f748a4d914..d137b68bcf 100644 --- a/src/Mod/Fem/App/FemPostFilter.h +++ b/src/Mod/Fem/App/FemPostFilter.h @@ -23,6 +23,7 @@ #ifndef Fem_FemPostFilter_H #define Fem_FemPostFilter_H +#include #include #include #include @@ -372,6 +373,41 @@ private: App::Enumeration m_vectorFields; }; +// *************************************************************************** +// calculator filter +class FemExport FemPostCalculatorFilter: public FemPostFilter +{ + + PROPERTY_HEADER_WITH_OVERRIDE(Fem::FemPostCalculatorFilter); + +public: + FemPostCalculatorFilter(); + ~FemPostCalculatorFilter() override; + + App::PropertyString FieldName; + App::PropertyString Function; + App::PropertyFloat ReplacementValue; + App::PropertyBool ReplaceInvalid; + + const char* getViewProviderName() const override + { + return "FemGui::ViewProviderFemPostCalculator"; + } + short int mustExecute() const override; + + const std::vector getScalarVariables(); + const std::vector getVectorVariables(); + +protected: + App::DocumentObjectExecReturn* execute() override; + void onChanged(const App::Property* prop) override; + + void updateAvailableFields(); + +private: + vtkSmartPointer m_calculator; +}; + } // namespace Fem diff --git a/src/Mod/Fem/App/FemPostPipeline.h b/src/Mod/Fem/App/FemPostPipeline.h index 6c96b6dbae..c56d37ad01 100644 --- a/src/Mod/Fem/App/FemPostPipeline.h +++ b/src/Mod/Fem/App/FemPostPipeline.h @@ -133,7 +133,6 @@ private: bool m_block_property = false; bool m_data_updated = false; - bool m_use_transform = false; void updateData(); diff --git a/src/Mod/Fem/App/FemPostPipelinePyImp.cpp b/src/Mod/Fem/App/FemPostPipelinePyImp.cpp index d75f8dff41..83c282fc56 100644 --- a/src/Mod/Fem/App/FemPostPipelinePyImp.cpp +++ b/src/Mod/Fem/App/FemPostPipelinePyImp.cpp @@ -58,26 +58,22 @@ PyObject* FemPostPipelinePy::read(PyObject* args) &unitobj, &value_type)) { if (!values) { - // single argument version was called! - if (!PyUnicode_Check(files)) { PyErr_SetString(PyExc_TypeError, "argument must be file path"); return nullptr; } const char* path = PyUnicode_AsUTF8(files); getFemPostPipelinePtr()->read(Base::FileInfo(path)); + Py_Return; } else if (values && unitobj) { - // multistep version! - if (!(PyTuple_Check(files) || PyList_Check(files)) || !(PyTuple_Check(values) || PyList_Check(values))) { - - std::string error = std::string( - "Files and values must be list of strings and number respectively."); - throw Base::TypeError(error); + PyErr_SetString(PyExc_TypeError, + "Files and values must be list of strings and number respectively"); + return nullptr; } // extract the result objects @@ -89,7 +85,8 @@ PyObject* FemPostPipelinePy::read(PyObject* args) for (Py::Sequence::size_type i = 0; i < size; i++) { auto path = Py::Object(file_list[i]); if (!path.isString()) { - throw Base::TypeError("File path must be string"); + PyErr_SetString(PyExc_TypeError, "File path must be string"); + return nullptr; } file_result[i] = Base::FileInfo(path.as_string()); } @@ -103,8 +100,8 @@ PyObject* FemPostPipelinePy::read(PyObject* args) for (Py::Sequence::size_type i = 0; i < size; i++) { auto value = Py::Object(values_list[i]); if (!value.isNumeric()) { - std::string error = std::string("Values must be numbers"); - throw Base::TypeError(error); + PyErr_SetString(PyExc_TypeError, "Values must be numbers"); + return nullptr; } value_result[i] = Py::Float(value).as_double(); } diff --git a/src/Mod/Fem/App/PreCompiled.h b/src/Mod/Fem/App/PreCompiled.h index f679a40781..dc915b7df7 100644 --- a/src/Mod/Fem/App/PreCompiled.h +++ b/src/Mod/Fem/App/PreCompiled.h @@ -153,16 +153,21 @@ #include // VTK +#include #include +#include #include #include #include #include #include #include +#include #include #include #include +#include +#include #include #include #include @@ -177,18 +182,20 @@ #include #include #include -#include -#include -#include #include +#include +#include #include #include +#include #include #include #include #include #include #include +#include +#include #include #include #include diff --git a/src/Mod/Fem/CMakeLists.txt b/src/Mod/Fem/CMakeLists.txt index 44aff6f11c..aabc377464 100755 --- a/src/Mod/Fem/CMakeLists.txt +++ b/src/Mod/Fem/CMakeLists.txt @@ -180,6 +180,7 @@ SET(FemObjects_SRCS femobjects/constraint_bodyheatsource.py femobjects/constraint_centrif.py femobjects/constraint_currentdensity.py + femobjects/constraint_electricchargedensity.py femobjects/constraint_electrostaticpotential.py femobjects/constraint_flowvelocity.py femobjects/constraint_initialflowvelocity.py @@ -582,6 +583,7 @@ SET(FemGuiTaskPanels_SRCS femtaskpanels/task_constraint_bodyheatsource.py femtaskpanels/task_constraint_centrif.py femtaskpanels/task_constraint_currentdensity.py + femtaskpanels/task_constraint_electricchargedensity.py femtaskpanels/task_constraint_electrostaticpotential.py femtaskpanels/task_constraint_flowvelocity.py femtaskpanels/task_constraint_initialflowvelocity.py @@ -627,6 +629,7 @@ SET(FemGuiViewProvider_SRCS femviewprovider/view_constraint_bodyheatsource.py femviewprovider/view_constraint_centrif.py femviewprovider/view_constraint_currentdensity.py + femviewprovider/view_constraint_electricchargedensity.py femviewprovider/view_constraint_electrostaticpotential.py femviewprovider/view_constraint_flowvelocity.py femviewprovider/view_constraint_initialflowvelocity.py diff --git a/src/Mod/Fem/Gui/AppFemGui.cpp b/src/Mod/Fem/Gui/AppFemGui.cpp index cc0aa8dc7f..65364059eb 100644 --- a/src/Mod/Fem/Gui/AppFemGui.cpp +++ b/src/Mod/Fem/Gui/AppFemGui.cpp @@ -162,6 +162,7 @@ PyMOD_INIT_FUNC(FemGui) FemGui::ViewProviderFemPostObject ::init(); FemGui::ViewProviderFemPostPipeline ::init(); FemGui::ViewProviderFemPostBranchFilter ::init(); + FemGui::ViewProviderFemPostCalculator ::init(); FemGui::ViewProviderFemPostClip ::init(); FemGui::ViewProviderFemPostContours ::init(); FemGui::ViewProviderFemPostCut ::init(); diff --git a/src/Mod/Fem/Gui/CMakeLists.txt b/src/Mod/Fem/Gui/CMakeLists.txt index 6fb0572ecd..d7fdd2c4b5 100755 --- a/src/Mod/Fem/Gui/CMakeLists.txt +++ b/src/Mod/Fem/Gui/CMakeLists.txt @@ -87,6 +87,7 @@ if(BUILD_FEM_VTK) CylinderWidget.ui PlaneWidget.ui SphereWidget.ui + TaskPostCalculator.ui TaskPostClip.ui TaskPostContours.ui TaskPostCut.ui @@ -281,6 +282,7 @@ if(BUILD_FEM_VTK) SphereWidget.ui TaskPostBoxes.h TaskPostBoxes.cpp + TaskPostCalculator.ui TaskPostClip.ui TaskPostContours.ui TaskPostCut.ui @@ -369,6 +371,7 @@ SET(FemGuiIcon_SVG SET(FemGuiSymbol_IV Resources/symbols/ConstraintContact.iv Resources/symbols/ConstraintDisplacement.iv + Resources/symbols/ConstraintElectricChargeDensity.iv Resources/symbols/ConstraintElectrostaticPotential.iv Resources/symbols/ConstraintCurrentDensity.iv Resources/symbols/ConstraintFixed.iv @@ -405,6 +408,7 @@ SET(FemGuiPythonUI_SRCS Resources/ui/ConstraintSectionPrint.ui Resources/ui/CurrentDensity.ui Resources/ui/DlgSettingsNetgen.ui + Resources/ui/ElectricChargeDensity.ui Resources/ui/ElectrostaticPotential.ui Resources/ui/ElementFluid1D.ui Resources/ui/ElementGeometry1D.ui diff --git a/src/Mod/Fem/Gui/Command.cpp b/src/Mod/Fem/Gui/Command.cpp index e71185cacd..04e7e66d4d 100644 --- a/src/Mod/Fem/Gui/Command.cpp +++ b/src/Mod/Fem/Gui/Command.cpp @@ -1416,6 +1416,9 @@ void CmdFemCompEmConstraints::activated(int iMsg) else if (iMsg == 2) { rcCmdMgr.runCommandByName("FEM_ConstraintMagnetization"); } + else if (iMsg == 3) { + rcCmdMgr.runCommandByName("FEM_ConstraintElectricChargeDensity"); + } else { return; } @@ -1441,6 +1444,8 @@ Gui::Action* CmdFemCompEmConstraints::createAction() cmd1->setIcon(Gui::BitmapFactory().iconFromTheme("FEM_ConstraintCurrentDensity")); QAction* cmd2 = pcAction->addAction(QString()); cmd2->setIcon(Gui::BitmapFactory().iconFromTheme("FEM_ConstraintMagnetization")); + QAction* cmd3 = pcAction->addAction(QString()); + cmd3->setIcon(Gui::BitmapFactory().iconFromTheme("FEM_ConstraintElectricChargeDensity")); _pcAction = pcAction; languageChange(); @@ -1502,6 +1507,20 @@ void CmdFemCompEmConstraints::languageChange() cmd2->setStatusTip(QApplication::translate("FEM_ConstraintMagnetization", ConstraintMagnetization->getStatusTip())); } + + Gui::Command* ConstraintElectricChargeDensity = + rcCmdMgr.getCommandByName("FEM_ConstraintElectricChargeDensity"); + if (ConstraintElectricChargeDensity) { + QAction* cmd3 = a[3]; + cmd3->setText(QApplication::translate("FEM_ConstraintElectricChargeDensity", + ConstraintElectricChargeDensity->getMenuText())); + cmd3->setToolTip( + QApplication::translate("FEM_ConstraintElectricChargeDensity", + ConstraintElectricChargeDensity->getToolTipText())); + cmd3->setStatusTip( + QApplication::translate("FEM_ConstraintElectricChargeDensity", + ConstraintElectricChargeDensity->getStatusTip())); + } } bool CmdFemCompEmConstraints::isActive() @@ -2338,6 +2357,42 @@ bool CmdFemPostContoursFilter::isActive() } +//================================================================================================ +DEF_STD_CMD_A(CmdFemPostCalculatorFilter) + +CmdFemPostCalculatorFilter::CmdFemPostCalculatorFilter() + : Command("FEM_PostFilterCalculator") +{ + sAppModule = "Fem"; + sGroup = QT_TR_NOOP("Fem"); + sMenuText = QT_TR_NOOP("Calculator filter"); + sToolTipText = QT_TR_NOOP("Create new fields from current data"); + sWhatsThis = "FEM_PostFilterCalculator"; + sStatusTip = sToolTipText; + sPixmap = "FEM_PostFilterCalculator"; +} + +void CmdFemPostCalculatorFilter::activated(int) +{ + setupFilter(this, "Calculator"); +} + +bool CmdFemPostCalculatorFilter::isActive() +{ + // only allow one object + auto selection = getSelection().getSelection(); + if (selection.size() > 1) { + return false; + } + for (auto obj : selection) { + if (obj.pObject->isDerivedFrom()) { + return true; + } + } + return false; +} + + //================================================================================================ DEF_STD_CMD_ACL(CmdFemPostFunctions) @@ -2783,6 +2838,7 @@ void CreateFemCommands() // vtk post processing #ifdef FC_USE_VTK rcCmdMgr.addCommand(new CmdFemPostApllyChanges); + rcCmdMgr.addCommand(new CmdFemPostCalculatorFilter); rcCmdMgr.addCommand(new CmdFemPostClipFilter); rcCmdMgr.addCommand(new CmdFemPostContoursFilter); rcCmdMgr.addCommand(new CmdFemPostCutFilter); diff --git a/src/Mod/Fem/Gui/DlgSettingsFemCcxImp.cpp b/src/Mod/Fem/Gui/DlgSettingsFemCcxImp.cpp index e70b285702..cbded442f1 100644 --- a/src/Mod/Fem/Gui/DlgSettingsFemCcxImp.cpp +++ b/src/Mod/Fem/Gui/DlgSettingsFemCcxImp.cpp @@ -43,8 +43,8 @@ DlgSettingsFemCcxImp::DlgSettingsFemCcxImp(QWidget* parent) { ui->setupUi(this); // set ranges - ui->dsb_ccx_analysis_time->setMaximum(FLOAT_MAX); - ui->dsb_ccx_initial_time_step->setMaximum(FLOAT_MAX); + ui->dsb_ccx_analysis_time->setMaximum(std::numeric_limits::max()); + ui->dsb_ccx_initial_time_step->setMaximum(std::numeric_limits::max()); connect(ui->fc_ccx_binary_path, &Gui::PrefFileChooser::fileNameChanged, diff --git a/src/Mod/Fem/Gui/PreCompiled.h b/src/Mod/Fem/Gui/PreCompiled.h index ef1398cef0..41128a9151 100644 --- a/src/Mod/Fem/Gui/PreCompiled.h +++ b/src/Mod/Fem/Gui/PreCompiled.h @@ -81,6 +81,7 @@ #include #include #include +#include #include #include #include diff --git a/src/Mod/Fem/Gui/Resources/Fem.qrc b/src/Mod/Fem/Gui/Resources/Fem.qrc index dd132e54fc..2b23520d06 100755 --- a/src/Mod/Fem/Gui/Resources/Fem.qrc +++ b/src/Mod/Fem/Gui/Resources/Fem.qrc @@ -15,6 +15,7 @@ icons/FEM_ConstraintContact.svg icons/FEM_ConstraintCurrentDensity.svg icons/FEM_ConstraintDisplacement.svg + icons/FEM_ConstraintElectricChargeDensity.svg icons/FEM_ConstraintElectrostaticPotential.svg icons/FEM_ConstraintFixed.svg icons/FEM_ConstraintFlowVelocity.svg @@ -72,6 +73,7 @@ icons/FEM_MaterialSolid.svg + icons/FEM_PostFilterCalculator.svg icons/FEM_PostFilterClipRegion.svg icons/FEM_PostFilterClipScalar.svg icons/FEM_PostFilterContours.svg @@ -127,6 +129,7 @@ ui/ConstraintTie.ui ui/CurrentDensity.ui ui/DlgSettingsNetgen.ui + ui/ElectricChargeDensity.ui ui/ElectrostaticPotential.ui ui/ElementFluid1D.ui ui/ElementGeometry1D.ui diff --git a/src/Mod/Fem/Gui/Resources/icons/FEM_ConstraintElectricChargeDensity.svg b/src/Mod/Fem/Gui/Resources/icons/FEM_ConstraintElectricChargeDensity.svg new file mode 100644 index 0000000000..4e8402350a --- /dev/null +++ b/src/Mod/Fem/Gui/Resources/icons/FEM_ConstraintElectricChargeDensity.svg @@ -0,0 +1,430 @@ + + + + FEM_ConstraintElectrostaticPotential + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + FEM_ConstraintElectrostaticPotential + + + [bitacovir] + + + PartDesign_MoveTip + 12-02-2021 + https://www.freecad.org/wiki/index.php?title=Artwork + + + FreeCAD + + + + + + FreeCAD LGPL2+ + + + https://www.gnu.org/copyleft/lesser.html + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Mod/Fem/Gui/Resources/icons/FEM_PostFilterCalculator.svg b/src/Mod/Fem/Gui/Resources/icons/FEM_PostFilterCalculator.svg new file mode 100644 index 0000000000..ec2fa44b67 --- /dev/null +++ b/src/Mod/Fem/Gui/Resources/icons/FEM_PostFilterCalculator.svg @@ -0,0 +1,324 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + Jakub Steiner + + + + + + + + + + + calc + calculator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + f(x) + diff --git a/src/Mod/Fem/Gui/Resources/symbols/ConstraintElectricChargeDensity.iv b/src/Mod/Fem/Gui/Resources/symbols/ConstraintElectricChargeDensity.iv new file mode 100644 index 0000000000..1689168a72 --- /dev/null +++ b/src/Mod/Fem/Gui/Resources/symbols/ConstraintElectricChargeDensity.iv @@ -0,0 +1,74 @@ +#Inventor V2.1 ascii + +# SPDX-License-Identifier: LGPL-2.1-or-later + +#/*************************************************************************** +# * Copyright (c) 2025 Mario Passaglia * +# * * +# * This file is part of FreeCAD. * +# * * +# * FreeCAD is free software: you can redistribute it and/or modify it * +# * under the terms of the GNU Lesser General Public License as * +# * published by the Free Software Foundation, either version 2.1 of the * +# * License, or (at your option) any later version. * +# * * +# * FreeCAD 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 * +# * Lesser General Public License for more details. * +# * * +# * You should have received a copy of the GNU Lesser General Public * +# * License along with FreeCAD. If not, see * +# * . * +# * * +# ************************************************************************** + + +Separator { + + Separator { + + Translation { + translation 0 0.5 0 + + } + Sphere { + radius 0.5 + + } + Translation { + translation 0 2.0 0 + + } + BaseColor { + rgb 0.17 0.46 1.0 + + } + Sphere { + radius 0.5 + + } + Translation { + translation 0 -1.375 0 + + } + BaseColor { + rgb 1 1 1 + + } + Cylinder { + radius 0.1 + height 0.75 + + } + Translation { + translation 0 .625 0 + + } + Cone { + bottomRadius 0.25 + height 0.5 + + } + } +} diff --git a/src/Mod/Fem/Gui/Resources/ui/ElectricChargeDensity.ui b/src/Mod/Fem/Gui/Resources/ui/ElectricChargeDensity.ui new file mode 100644 index 0000000000..77def88519 --- /dev/null +++ b/src/Mod/Fem/Gui/Resources/ui/ElectricChargeDensity.ui @@ -0,0 +1,187 @@ + + + Form + + + + 0 + 0 + 350 + 40 + + + + Analysis feature properties + + + + + + + + + + + Mode: + + + + + + + + + + + + + 0 + 0 + + + + 0 + + + + + + + Free surface charge density + + + + + Density: + + + + + + + true + + + C/mm^2 + + + Qt::AlignLeft|Qt::AlignTrailing|Qt::AlignVCenter + + + true + + + 1.000000000000000 + + + 0.000000000000000 + + + + + + + + + 1 + + + + + + + Free volume charge density + + + + + Density: + + + + + + + true + + + C/mm^3 + + + Qt::AlignLeft|Qt::AlignTrailing|Qt::AlignVCenter + + + true + + + 1.000000000000000 + + + 0.000000000000000 + + + + + + + + + 2 + + + + + + + Free total charge + + + + + Total Charge: + + + + + + + true + + + C + + + Qt::AlignLeft|Qt::AlignTrailing|Qt::AlignVCenter + + + true + + + 1.000000000000000 + + + 0.000000000000000 + + + + + + + + + + + + + + + + + Gui::QuantitySpinBox + QWidget +
    Gui/QuantitySpinBox.h
    +
    +
    + + +
    diff --git a/src/Mod/Fem/Gui/Resources/ui/ElectrostaticPotential.ui b/src/Mod/Fem/Gui/Resources/ui/ElectrostaticPotential.ui index 3956a7e83d..50fc5e15c5 100644 --- a/src/Mod/Fem/Gui/Resources/ui/ElectrostaticPotential.ui +++ b/src/Mod/Fem/Gui/Resources/ui/ElectrostaticPotential.ui @@ -416,14 +416,14 @@ Note: has no effect if a solid was selected
    - Surface Charge Density: + Electric Flux Density: - + - Surface charge density + Normal component of electric displacement field C/m^2 diff --git a/src/Mod/Fem/Gui/TaskFemConstraintBearing.cpp b/src/Mod/Fem/Gui/TaskFemConstraintBearing.cpp index 72d355449c..d054494ca6 100644 --- a/src/Mod/Fem/Gui/TaskFemConstraintBearing.cpp +++ b/src/Mod/Fem/Gui/TaskFemConstraintBearing.cpp @@ -66,18 +66,19 @@ TaskFemConstraintBearing::TaskFemConstraintBearing(ViewProviderFemConstraint* Co this->groupLayout()->addWidget(proxy); // setup ranges - ui->spinDiameter->setMinimum(-FLOAT_MAX); - ui->spinDiameter->setMaximum(FLOAT_MAX); - ui->spinOtherDiameter->setMinimum(-FLOAT_MAX); - ui->spinOtherDiameter->setMaximum(FLOAT_MAX); - ui->spinCenterDistance->setMinimum(-FLOAT_MAX); - ui->spinCenterDistance->setMaximum(FLOAT_MAX); - ui->spinForce->setMinimum(-FLOAT_MAX); - ui->spinForce->setMaximum(FLOAT_MAX); - ui->spinTensionForce->setMinimum(-FLOAT_MAX); - ui->spinTensionForce->setMaximum(FLOAT_MAX); - ui->spinDistance->setMinimum(-FLOAT_MAX); - ui->spinDistance->setMaximum(FLOAT_MAX); + constexpr float max = std::numeric_limits::max(); + ui->spinDiameter->setMinimum(-max); + ui->spinDiameter->setMaximum(max); + ui->spinOtherDiameter->setMinimum(-max); + ui->spinOtherDiameter->setMaximum(max); + ui->spinCenterDistance->setMinimum(-max); + ui->spinCenterDistance->setMaximum(max); + ui->spinForce->setMinimum(-max); + ui->spinForce->setMaximum(max); + ui->spinTensionForce->setMinimum(-max); + ui->spinTensionForce->setMaximum(max); + ui->spinDistance->setMinimum(-max); + ui->spinDistance->setMaximum(max); // Get the feature data Fem::ConstraintBearing* pcConstraint = ConstraintView->getObject(); diff --git a/src/Mod/Fem/Gui/TaskFemConstraintContact.cpp b/src/Mod/Fem/Gui/TaskFemConstraintContact.cpp index 2b49edb781..65c1744127 100644 --- a/src/Mod/Fem/Gui/TaskFemConstraintContact.cpp +++ b/src/Mod/Fem/Gui/TaskFemConstraintContact.cpp @@ -96,27 +96,27 @@ TaskFemConstraintContact::TaskFemConstraintContact(ViewProviderFemConstraintCont // Fill data into dialog elements ui->spbSlope->setUnit(pcConstraint->Slope.getUnit()); ui->spbSlope->setMinimum(0); - ui->spbSlope->setMaximum(FLOAT_MAX); + ui->spbSlope->setMaximum(std::numeric_limits::max()); ui->spbSlope->setValue(pcConstraint->Slope.getQuantityValue()); ui->spbSlope->bind(pcConstraint->Slope); ui->spbAdjust->setUnit(pcConstraint->Adjust.getUnit()); ui->spbAdjust->setMinimum(0); - ui->spbAdjust->setMaximum(FLOAT_MAX); + ui->spbAdjust->setMaximum(std::numeric_limits::max()); ui->spbAdjust->setValue(pcConstraint->Adjust.getQuantityValue()); ui->spbAdjust->bind(pcConstraint->Adjust); ui->ckbFriction->setChecked(friction); ui->spbFrictionCoeff->setMinimum(0); - ui->spbFrictionCoeff->setMaximum(FLOAT_MAX); + ui->spbFrictionCoeff->setMaximum(std::numeric_limits::max()); ui->spbFrictionCoeff->setValue(pcConstraint->FrictionCoefficient.getValue()); ui->spbFrictionCoeff->setEnabled(friction); ui->spbFrictionCoeff->bind(pcConstraint->FrictionCoefficient); ui->spbStickSlope->setUnit(pcConstraint->StickSlope.getUnit()); ui->spbStickSlope->setMinimum(0); - ui->spbStickSlope->setMaximum(FLOAT_MAX); + ui->spbStickSlope->setMaximum(std::numeric_limits::max()); ui->spbStickSlope->setValue(pcConstraint->StickSlope.getQuantityValue()); ui->spbStickSlope->setEnabled(friction); ui->spbStickSlope->bind(pcConstraint->StickSlope); diff --git a/src/Mod/Fem/Gui/TaskFemConstraintDisplacement.cpp b/src/Mod/Fem/Gui/TaskFemConstraintDisplacement.cpp index e4c490860b..b760434e77 100644 --- a/src/Mod/Fem/Gui/TaskFemConstraintDisplacement.cpp +++ b/src/Mod/Fem/Gui/TaskFemConstraintDisplacement.cpp @@ -74,18 +74,19 @@ TaskFemConstraintDisplacement::TaskFemConstraintDisplacement( this->groupLayout()->addWidget(proxy); // setup ranges - ui->spinxDisplacement->setMinimum(-FLOAT_MAX); - ui->spinxDisplacement->setMaximum(FLOAT_MAX); - ui->spinyDisplacement->setMinimum(-FLOAT_MAX); - ui->spinyDisplacement->setMaximum(FLOAT_MAX); - ui->spinzDisplacement->setMinimum(-FLOAT_MAX); - ui->spinzDisplacement->setMaximum(FLOAT_MAX); - ui->spinxRotation->setMinimum(-FLOAT_MAX); - ui->spinxRotation->setMaximum(FLOAT_MAX); - ui->spinyRotation->setMinimum(-FLOAT_MAX); - ui->spinyRotation->setMaximum(FLOAT_MAX); - ui->spinzRotation->setMinimum(-FLOAT_MAX); - ui->spinzRotation->setMaximum(FLOAT_MAX); + constexpr float max = std::numeric_limits::max(); + ui->spinxDisplacement->setMinimum(-max); + ui->spinxDisplacement->setMaximum(max); + ui->spinyDisplacement->setMinimum(-max); + ui->spinyDisplacement->setMaximum(max); + ui->spinzDisplacement->setMinimum(-max); + ui->spinzDisplacement->setMaximum(max); + ui->spinxRotation->setMinimum(-max); + ui->spinxRotation->setMaximum(max); + ui->spinyRotation->setMinimum(-max); + ui->spinyRotation->setMaximum(max); + ui->spinzRotation->setMinimum(-max); + ui->spinzRotation->setMaximum(max); // Get the feature data Fem::ConstraintDisplacement* pcConstraint = diff --git a/src/Mod/Fem/Gui/TaskFemConstraintFluidBoundary.cpp b/src/Mod/Fem/Gui/TaskFemConstraintFluidBoundary.cpp index ff11970ea9..fb9a3fe912 100644 --- a/src/Mod/Fem/Gui/TaskFemConstraintFluidBoundary.cpp +++ b/src/Mod/Fem/Gui/TaskFemConstraintFluidBoundary.cpp @@ -145,18 +145,19 @@ TaskFemConstraintFluidBoundary::TaskFemConstraintFluidBoundary( &TaskFemConstraintFluidBoundary::onReferenceDeleted); // setup ranges - ui->spinBoundaryValue->setMinimum(-FLOAT_MAX); - ui->spinBoundaryValue->setMaximum(FLOAT_MAX); + constexpr float max = std::numeric_limits::max(); + ui->spinBoundaryValue->setMinimum(-max); + ui->spinBoundaryValue->setMaximum(max); ui->spinTurbulentIntensityValue->setMinimum(0.0); - ui->spinTurbulentIntensityValue->setMaximum(FLOAT_MAX); + ui->spinTurbulentIntensityValue->setMaximum(max); ui->spinTurbulentLengthValue->setMinimum(0.0); - ui->spinTurbulentLengthValue->setMaximum(FLOAT_MAX); + ui->spinTurbulentLengthValue->setMaximum(max); ui->spinTemperatureValue->setMinimum(-273.15); - ui->spinTemperatureValue->setMaximum(FLOAT_MAX); + ui->spinTemperatureValue->setMaximum(max); ui->spinHeatFluxValue->setMinimum(0.0); - ui->spinHeatFluxValue->setMaximum(FLOAT_MAX); + ui->spinHeatFluxValue->setMaximum(max); ui->spinHTCoeffValue->setMinimum(0.0); - ui->spinHTCoeffValue->setMaximum(FLOAT_MAX); + ui->spinHTCoeffValue->setMaximum(max); connect(ui->comboBoundaryType, qOverload(&QComboBox::currentIndexChanged), @@ -352,8 +353,8 @@ TaskFemConstraintFluidBoundary::TaskFemConstraintFluidBoundary( // Fill data into dialog elements double f = pcConstraint->BoundaryValue.getValue(); - ui->spinBoundaryValue->setMinimum(FLOAT_MIN); // previous set the min to ZERO is not flexible - ui->spinBoundaryValue->setMaximum(FLOAT_MAX); + ui->spinBoundaryValue->setMinimum(std::numeric_limits::min()); // ZERO is not flexible + ui->spinBoundaryValue->setMaximum(std::numeric_limits::max()); ui->spinBoundaryValue->setValue(f); ui->listReferences->clear(); for (std::size_t i = 0; i < Objects.size(); i++) { diff --git a/src/Mod/Fem/Gui/TaskFemConstraintForce.cpp b/src/Mod/Fem/Gui/TaskFemConstraintForce.cpp index 236fdef904..fb8f7f9855 100644 --- a/src/Mod/Fem/Gui/TaskFemConstraintForce.cpp +++ b/src/Mod/Fem/Gui/TaskFemConstraintForce.cpp @@ -75,7 +75,7 @@ TaskFemConstraintForce::TaskFemConstraintForce(ViewProviderFemConstraintForce* C // Fill data into dialog elements ui->spinForce->setUnit(pcConstraint->Force.getUnit()); ui->spinForce->setMinimum(0); - ui->spinForce->setMaximum(FLOAT_MAX); + ui->spinForce->setMaximum(std::numeric_limits::max()); ui->spinForce->setValue(force); ui->listReferences->clear(); for (std::size_t i = 0; i < Objects.size(); i++) { diff --git a/src/Mod/Fem/Gui/TaskFemConstraintGear.cpp b/src/Mod/Fem/Gui/TaskFemConstraintGear.cpp index 3a116dff1c..9e139f899c 100644 --- a/src/Mod/Fem/Gui/TaskFemConstraintGear.cpp +++ b/src/Mod/Fem/Gui/TaskFemConstraintGear.cpp @@ -87,10 +87,10 @@ TaskFemConstraintGear::TaskFemConstraintGear(ViewProviderFemConstraint* Constrai // Fill data into dialog elements ui->spinDiameter->setMinimum(0); - ui->spinDiameter->setMaximum(FLOAT_MAX); + ui->spinDiameter->setMaximum(std::numeric_limits::max()); ui->spinDiameter->setValue(dia); ui->spinForce->setMinimum(0); - ui->spinForce->setMaximum(FLOAT_MAX); + ui->spinForce->setMaximum(std::numeric_limits::max()); ui->spinForce->setValue(force); ui->spinForceAngle->setMinimum(-360); ui->spinForceAngle->setMaximum(360); diff --git a/src/Mod/Fem/Gui/TaskFemConstraintHeatflux.cpp b/src/Mod/Fem/Gui/TaskFemConstraintHeatflux.cpp index 41651983ef..0241563cbb 100644 --- a/src/Mod/Fem/Gui/TaskFemConstraintHeatflux.cpp +++ b/src/Mod/Fem/Gui/TaskFemConstraintHeatflux.cpp @@ -119,16 +119,16 @@ TaskFemConstraintHeatflux::TaskFemConstraintHeatflux( ui->sw_heatflux->setCurrentIndex(constrType->getValue()); ui->qsb_ambienttemp_conv->setMinimum(0); - ui->qsb_ambienttemp_conv->setMaximum(FLOAT_MAX); + ui->qsb_ambienttemp_conv->setMaximum(std::numeric_limits::max()); ui->qsb_film_coef->setMinimum(0); - ui->qsb_film_coef->setMaximum(FLOAT_MAX); + ui->qsb_film_coef->setMaximum(std::numeric_limits::max()); ui->dsb_emissivity->setMinimum(0); - ui->dsb_emissivity->setMaximum(FLOAT_MAX); + ui->dsb_emissivity->setMaximum(std::numeric_limits::max()); ui->qsb_ambienttemp_rad->setMinimum(0); - ui->qsb_ambienttemp_rad->setMaximum(FLOAT_MAX); + ui->qsb_ambienttemp_rad->setMaximum(std::numeric_limits::max()); ui->qsb_ambienttemp_conv->setValue(pcConstraint->AmbientTemp.getQuantityValue()); ui->qsb_film_coef->setValue(pcConstraint->FilmCoef.getQuantityValue()); diff --git a/src/Mod/Fem/Gui/TaskFemConstraintPressure.cpp b/src/Mod/Fem/Gui/TaskFemConstraintPressure.cpp index 030cfc9c1c..41406cc1a2 100644 --- a/src/Mod/Fem/Gui/TaskFemConstraintPressure.cpp +++ b/src/Mod/Fem/Gui/TaskFemConstraintPressure.cpp @@ -64,7 +64,7 @@ TaskFemConstraintPressure::TaskFemConstraintPressure( // Fill data into dialog elements ui->if_pressure->setUnit(pcConstraint->Pressure.getUnit()); ui->if_pressure->setMinimum(0); - ui->if_pressure->setMaximum(FLOAT_MAX); + ui->if_pressure->setMaximum(std::numeric_limits::max()); ui->if_pressure->setValue(pcConstraint->Pressure.getQuantityValue()); ui->if_pressure->bind(pcConstraint->Pressure); diff --git a/src/Mod/Fem/Gui/TaskFemConstraintPulley.cpp b/src/Mod/Fem/Gui/TaskFemConstraintPulley.cpp index 710b041a61..ebc8ac670e 100644 --- a/src/Mod/Fem/Gui/TaskFemConstraintPulley.cpp +++ b/src/Mod/Fem/Gui/TaskFemConstraintPulley.cpp @@ -76,15 +76,15 @@ TaskFemConstraintPulley::TaskFemConstraintPulley(ViewProviderFemConstraintPulley // Fill data into dialog elements ui->spinOtherDiameter->setMinimum(0); - ui->spinOtherDiameter->setMaximum(FLOAT_MAX); + ui->spinOtherDiameter->setMaximum(std::numeric_limits::max()); ui->spinOtherDiameter->setValue(otherdia); ui->spinCenterDistance->setMinimum(0); - ui->spinCenterDistance->setMaximum(FLOAT_MAX); + ui->spinCenterDistance->setMaximum(std::numeric_limits::max()); ui->spinCenterDistance->setValue(centerdist); ui->checkIsDriven->setChecked(isdriven); - ui->spinForce->setMinimum(-FLOAT_MAX); + ui->spinForce->setMinimum(-std::numeric_limits::max()); ui->spinTensionForce->setMinimum(0); - ui->spinTensionForce->setMaximum(FLOAT_MAX); + ui->spinTensionForce->setMaximum(std::numeric_limits::max()); ui->spinTensionForce->setValue(tensionforce); // Adjust ui diff --git a/src/Mod/Fem/Gui/TaskFemConstraintRigidBody.cpp b/src/Mod/Fem/Gui/TaskFemConstraintRigidBody.cpp index 70da10d875..c1f5e17ee9 100644 --- a/src/Mod/Fem/Gui/TaskFemConstraintRigidBody.cpp +++ b/src/Mod/Fem/Gui/TaskFemConstraintRigidBody.cpp @@ -48,6 +48,7 @@ TaskFemConstraintRigidBody::TaskFemConstraintRigidBody( QWidget* parent) : TaskFemConstraintOnBoundary(ConstraintView, parent, "FEM_ConstraintRigidBody") { // Note change "RigidBody" in line above to new constraint name + constexpr float floatMax = std::numeric_limits::max(); proxy = new QWidget(this); ui = new Ui_TaskFemConstraintRigidBody(); ui->setupUi(proxy); @@ -137,12 +138,12 @@ TaskFemConstraintRigidBody::TaskFemConstraintRigidBody( App::ObjectIdentifier::parse(pcConstraint, std::string("ReferenceNode.y"))); ui->qsb_ref_node_z->bind( App::ObjectIdentifier::parse(pcConstraint, std::string("ReferenceNode.z"))); - ui->qsb_ref_node_x->setMinimum(-FLOAT_MAX); - ui->qsb_ref_node_x->setMaximum(FLOAT_MAX); - ui->qsb_ref_node_y->setMinimum(-FLOAT_MAX); - ui->qsb_ref_node_y->setMaximum(FLOAT_MAX); - ui->qsb_ref_node_z->setMinimum(-FLOAT_MAX); - ui->qsb_ref_node_z->setMaximum(FLOAT_MAX); + ui->qsb_ref_node_x->setMinimum(-floatMax); + ui->qsb_ref_node_x->setMaximum(floatMax); + ui->qsb_ref_node_y->setMinimum(-floatMax); + ui->qsb_ref_node_y->setMaximum(floatMax); + ui->qsb_ref_node_z->setMinimum(-floatMax); + ui->qsb_ref_node_z->setMaximum(floatMax); ui->qsb_disp_x->setValue(disp.x); ui->qsb_disp_y->setValue(disp.y); @@ -150,12 +151,12 @@ TaskFemConstraintRigidBody::TaskFemConstraintRigidBody( ui->qsb_disp_x->bind(App::ObjectIdentifier::parse(pcConstraint, std::string("Displacement.x"))); ui->qsb_disp_y->bind(App::ObjectIdentifier::parse(pcConstraint, std::string("Displacement.y"))); ui->qsb_disp_z->bind(App::ObjectIdentifier::parse(pcConstraint, std::string("Displacement.z"))); - ui->qsb_disp_x->setMinimum(-FLOAT_MAX); - ui->qsb_disp_x->setMaximum(FLOAT_MAX); - ui->qsb_disp_y->setMinimum(-FLOAT_MAX); - ui->qsb_disp_y->setMaximum(FLOAT_MAX); - ui->qsb_disp_z->setMinimum(-FLOAT_MAX); - ui->qsb_disp_z->setMaximum(FLOAT_MAX); + ui->qsb_disp_x->setMinimum(-floatMax); + ui->qsb_disp_x->setMaximum(floatMax); + ui->qsb_disp_y->setMinimum(-floatMax); + ui->qsb_disp_y->setMaximum(floatMax); + ui->qsb_disp_z->setMinimum(-floatMax); + ui->qsb_disp_z->setMaximum(floatMax); ui->spb_rot_axis_x->setValue(rotDir.x); ui->spb_rot_axis_y->setValue(rotDir.y); @@ -169,14 +170,14 @@ TaskFemConstraintRigidBody::TaskFemConstraintRigidBody( App::ObjectIdentifier::parse(pcConstraint, std::string("Rotation.Axis.z"))); ui->qsb_rot_angle->bind( App::ObjectIdentifier::parse(pcConstraint, std::string("Rotation.Angle"))); - ui->spb_rot_axis_x->setMinimum(-FLOAT_MAX); - ui->spb_rot_axis_x->setMaximum(FLOAT_MAX); - ui->spb_rot_axis_y->setMinimum(-FLOAT_MAX); - ui->spb_rot_axis_y->setMaximum(FLOAT_MAX); - ui->spb_rot_axis_z->setMinimum(-FLOAT_MAX); - ui->spb_rot_axis_z->setMaximum(FLOAT_MAX); - ui->qsb_rot_angle->setMinimum(-FLOAT_MAX); - ui->qsb_rot_angle->setMaximum(FLOAT_MAX); + ui->spb_rot_axis_x->setMinimum(-floatMax); + ui->spb_rot_axis_x->setMaximum(floatMax); + ui->spb_rot_axis_y->setMinimum(-floatMax); + ui->spb_rot_axis_y->setMaximum(floatMax); + ui->spb_rot_axis_z->setMinimum(-floatMax); + ui->spb_rot_axis_z->setMaximum(floatMax); + ui->qsb_rot_angle->setMinimum(-floatMax); + ui->qsb_rot_angle->setMaximum(floatMax); ui->qsb_force_x->setValue(forceX); ui->qsb_force_y->setValue(forceY); @@ -184,12 +185,12 @@ TaskFemConstraintRigidBody::TaskFemConstraintRigidBody( ui->qsb_force_x->bind(pcConstraint->ForceX); ui->qsb_force_y->bind(pcConstraint->ForceY); ui->qsb_force_z->bind(pcConstraint->ForceZ); - ui->qsb_force_x->setMinimum(-FLOAT_MAX); - ui->qsb_force_x->setMaximum(FLOAT_MAX); - ui->qsb_force_y->setMinimum(-FLOAT_MAX); - ui->qsb_force_y->setMaximum(FLOAT_MAX); - ui->qsb_force_z->setMinimum(-FLOAT_MAX); - ui->qsb_force_z->setMaximum(FLOAT_MAX); + ui->qsb_force_x->setMinimum(-floatMax); + ui->qsb_force_x->setMaximum(floatMax); + ui->qsb_force_y->setMinimum(-floatMax); + ui->qsb_force_y->setMaximum(floatMax); + ui->qsb_force_z->setMinimum(-floatMax); + ui->qsb_force_z->setMaximum(floatMax); ui->qsb_moment_x->setValue(momentX); ui->qsb_moment_y->setValue(momentY); @@ -197,12 +198,12 @@ TaskFemConstraintRigidBody::TaskFemConstraintRigidBody( ui->qsb_moment_x->bind(pcConstraint->MomentX); ui->qsb_moment_y->bind(pcConstraint->MomentY); ui->qsb_moment_z->bind(pcConstraint->MomentZ); - ui->qsb_moment_x->setMinimum(-FLOAT_MAX); - ui->qsb_moment_x->setMaximum(FLOAT_MAX); - ui->qsb_moment_y->setMinimum(-FLOAT_MAX); - ui->qsb_moment_y->setMaximum(FLOAT_MAX); - ui->qsb_moment_z->setMinimum(-FLOAT_MAX); - ui->qsb_moment_z->setMaximum(FLOAT_MAX); + ui->qsb_moment_x->setMinimum(-floatMax); + ui->qsb_moment_x->setMaximum(floatMax); + ui->qsb_moment_y->setMinimum(-floatMax); + ui->qsb_moment_y->setMaximum(floatMax); + ui->qsb_moment_z->setMinimum(-floatMax); + ui->qsb_moment_z->setMaximum(floatMax); QStringList modeList; diff --git a/src/Mod/Fem/Gui/TaskFemConstraintSpring.cpp b/src/Mod/Fem/Gui/TaskFemConstraintSpring.cpp index 4f09a745b8..e21fb5b299 100644 --- a/src/Mod/Fem/Gui/TaskFemConstraintSpring.cpp +++ b/src/Mod/Fem/Gui/TaskFemConstraintSpring.cpp @@ -76,11 +76,11 @@ TaskFemConstraintSpring::TaskFemConstraintSpring(ViewProviderFemConstraintSpring // Fill data into dialog elements ui->qsb_norm->setUnit(pcConstraint->NormalStiffness.getUnit()); - ui->qsb_norm->setMaximum(FLOAT_MAX); + ui->qsb_norm->setMaximum(std::numeric_limits::max()); ui->qsb_norm->setValue(pcConstraint->NormalStiffness.getQuantityValue()); ui->qsb_tan->setUnit(pcConstraint->TangentialStiffness.getUnit()); - ui->qsb_tan->setMaximum(FLOAT_MAX); + ui->qsb_tan->setMaximum(std::numeric_limits::max()); ui->qsb_tan->setValue(pcConstraint->TangentialStiffness.getQuantityValue()); ui->cb_elmer_stiffness->clear(); diff --git a/src/Mod/Fem/Gui/TaskFemConstraintTemperature.cpp b/src/Mod/Fem/Gui/TaskFemConstraintTemperature.cpp index c989436740..ff648c41e7 100644 --- a/src/Mod/Fem/Gui/TaskFemConstraintTemperature.cpp +++ b/src/Mod/Fem/Gui/TaskFemConstraintTemperature.cpp @@ -68,9 +68,9 @@ TaskFemConstraintTemperature::TaskFemConstraintTemperature( // Fill data into dialog elements ui->qsb_temperature->setMinimum(0); - ui->qsb_temperature->setMaximum(FLOAT_MAX); - ui->qsb_cflux->setMinimum(-FLOAT_MAX); - ui->qsb_cflux->setMaximum(FLOAT_MAX); + ui->qsb_temperature->setMaximum(std::numeric_limits::max()); + ui->qsb_cflux->setMinimum(-std::numeric_limits::max()); + ui->qsb_cflux->setMaximum(std::numeric_limits::max()); App::PropertyEnumeration* constrType = &pcConstraint->ConstraintType; QStringList qTypeList; diff --git a/src/Mod/Fem/Gui/TaskFemConstraintTransform.cpp b/src/Mod/Fem/Gui/TaskFemConstraintTransform.cpp index d53e476f37..894b27b62e 100644 --- a/src/Mod/Fem/Gui/TaskFemConstraintTransform.cpp +++ b/src/Mod/Fem/Gui/TaskFemConstraintTransform.cpp @@ -130,14 +130,15 @@ TaskFemConstraintTransform::TaskFemConstraintTransform( ui->qsb_rot_angle->bind( App::ObjectIdentifier::parse(pcConstraint, std::string("Rotation.Angle"))); - ui->spb_rot_axis_x->setMinimum(-FLOAT_MAX); - ui->spb_rot_axis_x->setMaximum(FLOAT_MAX); - ui->spb_rot_axis_y->setMinimum(-FLOAT_MAX); - ui->spb_rot_axis_y->setMaximum(FLOAT_MAX); - ui->spb_rot_axis_z->setMinimum(-FLOAT_MAX); - ui->spb_rot_axis_z->setMaximum(FLOAT_MAX); - ui->qsb_rot_angle->setMinimum(-FLOAT_MAX); - ui->qsb_rot_angle->setMaximum(FLOAT_MAX); + float max = std::numeric_limits::max(); + ui->spb_rot_axis_x->setMinimum(-max); + ui->spb_rot_axis_x->setMaximum(max); + ui->spb_rot_axis_y->setMinimum(-max); + ui->spb_rot_axis_y->setMaximum(max); + ui->spb_rot_axis_z->setMinimum(-max); + ui->spb_rot_axis_z->setMaximum(max); + ui->qsb_rot_angle->setMinimum(-max); + ui->qsb_rot_angle->setMaximum(max); std::string transform_type = pcConstraint->TransformType.getValueAsString(); if (transform_type == "Rectangular") { diff --git a/src/Mod/Fem/Gui/TaskPostBoxes.cpp b/src/Mod/Fem/Gui/TaskPostBoxes.cpp index e9e6f3c610..dbd9dacbc4 100644 --- a/src/Mod/Fem/Gui/TaskPostBoxes.cpp +++ b/src/Mod/Fem/Gui/TaskPostBoxes.cpp @@ -52,6 +52,7 @@ #include #include +#include "ui_TaskPostCalculator.h" #include "ui_TaskPostClip.h" #include "ui_TaskPostContours.h" #include "ui_TaskPostCut.h" @@ -323,6 +324,9 @@ void TaskDlgPost::open() void TaskDlgPost::clicked(int button) { if (button == QDialogButtonBox::Apply) { + for (auto box : m_boxes) { + box->apply(); + } recompute(); } } @@ -2123,4 +2127,130 @@ void TaskPostWarpVector::onMinValueChanged(double) } +// *************************************************************************** +// calculator filter +static const std::vector calculatorOperators = { + "+", "-", "*", "/", "-", "^", "abs", "cos", "sin", "tan", "exp", + "log", "pow", "sqrt", "iHat", "jHat", "kHat", "cross", "dot", "mag", "norm"}; + +TaskPostCalculator::TaskPostCalculator(ViewProviderFemPostCalculator* view, QWidget* parent) + : TaskPostBox(view, + Gui::BitmapFactory().pixmap("FEM_PostFilterCalculator"), + tr("Calculator options"), + parent) + , ui(new Ui_TaskPostCalculator) +{ + // we load the views widget + proxy = new QWidget(this); + ui->setupUi(proxy); + setupConnections(); + this->groupLayout()->addWidget(proxy); + + // load the default values + auto obj = getObject(); + ui->let_field_name->blockSignals(true); + ui->let_field_name->setText(QString::fromUtf8(obj->FieldName.getValue())); + ui->let_field_name->blockSignals(false); + + ui->let_function->blockSignals(true); + ui->let_function->setText(QString::fromUtf8(obj->Function.getValue())); + ui->let_function->blockSignals(false); + + ui->ckb_replace_invalid->setChecked(obj->ReplaceInvalid.getValue()); + ui->dsb_replacement_value->setEnabled(obj->ReplaceInvalid.getValue()); + ui->dsb_replacement_value->setValue(obj->ReplacementValue.getValue()); + ui->dsb_replacement_value->setMaximum(std::numeric_limits::max()); + ui->dsb_replacement_value->setMinimum(std::numeric_limits::lowest()); + + // fill available fields + for (const auto& f : obj->getScalarVariables()) { + ui->cb_scalars->addItem(QString::fromStdString(f)); + } + for (const auto& f : obj->getVectorVariables()) { + ui->cb_vectors->addItem(QString::fromStdString(f)); + } + + QStringList qOperators; + for (const auto& o : calculatorOperators) { + qOperators << QString::fromStdString(o); + } + ui->cb_operators->addItems(qOperators); + + ui->cb_scalars->setCurrentIndex(-1); + ui->cb_vectors->setCurrentIndex(-1); + ui->cb_operators->setCurrentIndex(-1); +} + +TaskPostCalculator::~TaskPostCalculator() = default; + +void TaskPostCalculator::setupConnections() +{ + connect(ui->dsb_replacement_value, + qOverload(&QDoubleSpinBox::valueChanged), + this, + &TaskPostCalculator::onReplacementValueChanged); + connect(ui->ckb_replace_invalid, + &QCheckBox::toggled, + this, + &TaskPostCalculator::onReplaceInvalidChanged); + connect(ui->cb_scalars, + qOverload(&QComboBox::activated), + this, + &TaskPostCalculator::onScalarsActivated); + connect(ui->cb_vectors, + qOverload(&QComboBox::activated), + this, + &TaskPostCalculator::onVectorsActivated); + connect(ui->cb_operators, + qOverload(&QComboBox::activated), + this, + &TaskPostCalculator::onOperatorsActivated); +} + +void TaskPostCalculator::onReplaceInvalidChanged(bool state) +{ + auto obj = static_cast(getObject()); + obj->ReplaceInvalid.setValue(state); + ui->dsb_replacement_value->setEnabled(state); + recompute(); +} + +void TaskPostCalculator::onReplacementValueChanged(double value) +{ + auto obj = static_cast(getObject()); + obj->ReplacementValue.setValue(value); + recompute(); +} + +void TaskPostCalculator::onScalarsActivated(int index) +{ + QString item = ui->cb_scalars->itemText(index); + ui->let_function->insert(item); +} + +void TaskPostCalculator::onVectorsActivated(int index) +{ + QString item = ui->cb_vectors->itemText(index); + ui->let_function->insert(item); +} + +void TaskPostCalculator::onOperatorsActivated(int index) +{ + QString item = ui->cb_operators->itemText(index); + ui->let_function->insert(item); +} + +void TaskPostCalculator::apply() +{ + auto obj = getObject(); + std::string function = ui->let_function->text().toStdString(); + std::string name = ui->let_field_name->text().toStdString(); + obj->Function.setValue(function); + obj->FieldName.setValue(name); + recompute(); + + auto view = getTypedView(); + view->Field.setValue(obj->FieldName.getValue()); +} + #include "moc_TaskPostBoxes.cpp" diff --git a/src/Mod/Fem/Gui/TaskPostBoxes.h b/src/Mod/Fem/Gui/TaskPostBoxes.h index b33729e03b..74842dcc5e 100644 --- a/src/Mod/Fem/Gui/TaskPostBoxes.h +++ b/src/Mod/Fem/Gui/TaskPostBoxes.h @@ -32,6 +32,7 @@ class QComboBox; class Ui_TaskPostDisplay; +class Ui_TaskPostCalculator; class Ui_TaskPostClip; class Ui_TaskPostContours; class Ui_TaskPostDataAlongLine; @@ -141,12 +142,15 @@ public: QWidget* parent = nullptr); ~TaskPostBox() override; - virtual void applyPythonCode() = 0; + virtual void applyPythonCode() {}; virtual bool isGuiTaskOnly() { return false; } // return true if only gui properties are manipulated + // executed when the apply button is pressed in the task dialog + virtual void apply() {}; + protected: App::DocumentObject* getObject() const { @@ -555,6 +559,35 @@ private: std::unique_ptr ui; }; + +// *************************************************************************** +// calculator filter +class ViewProviderFemPostCalculator; + +class TaskPostCalculator: public TaskPostBox +{ + Q_OBJECT + +public: + explicit TaskPostCalculator(ViewProviderFemPostCalculator* view, QWidget* parent = nullptr); + ~TaskPostCalculator() override; + +protected: + void apply() override; + +private: + void setupConnections(); + void onReplaceInvalidChanged(bool state); + void onReplacementValueChanged(double value); + void onScalarsActivated(int index); + void onVectorsActivated(int index); + void onOperatorsActivated(int index); + +private: + QWidget* proxy; + std::unique_ptr ui; +}; + } // namespace FemGui #endif // GUI_TASKVIEW_TaskPostDisplay_H diff --git a/src/Mod/Fem/Gui/TaskPostCalculator.ui b/src/Mod/Fem/Gui/TaskPostCalculator.ui new file mode 100644 index 0000000000..3d90b1eea0 --- /dev/null +++ b/src/Mod/Fem/Gui/TaskPostCalculator.ui @@ -0,0 +1,127 @@ + + + TaskPostCalculator + + + + 0 + 0 + 250 + 115 + + + + Form + + + + + + + + + + + + + + Field Name: + + + + + + + + + + + + + Mathematical expression + + + + + + + + + + + + + Available fields + + + + + + + + Scalars: + + + + + + + + + + Vectors: + + + + + + + + + + Operators: + + + + + + + + + + + + + + + + + + + + Replace invalid data: + + + Replacement value for invalid operations + + + + + + + + + + + + + + + + + Gui::DoubleSpinBox + QWidget +
    Gui/SpinBox.h
    +
    +
    +
    diff --git a/src/Mod/Fem/Gui/ViewProviderFemConstraintFluidBoundary.cpp b/src/Mod/Fem/Gui/ViewProviderFemConstraintFluidBoundary.cpp index 9ac1828bd9..9813172d33 100644 --- a/src/Mod/Fem/Gui/ViewProviderFemConstraintFluidBoundary.cpp +++ b/src/Mod/Fem/Gui/ViewProviderFemConstraintFluidBoundary.cpp @@ -145,8 +145,8 @@ void ViewProviderFemConstraintFluidBoundary::updateData(const App::Property* pro for (const auto& point : points) { SbVec3f base(point.x, point.y, point.z); - if (forceDirection.GetAngle(normal) - < M_PI_2) { // Move arrow so it doesn't disappear inside the solid + if (forceDirection.GetAngle(normal) < std::numbers::pi + / 2) { // Move arrow so it doesn't disappear inside the solid base = base + dir * scaledlength; // OvG: Scaling } #ifdef USE_MULTIPLE_COPY @@ -191,7 +191,7 @@ void ViewProviderFemConstraintFluidBoundary::updateData(const App::Property* pro for (const auto& point : points) { SbVec3f base(point.x, point.y, point.z); - if (forceDirection.GetAngle(normal) < M_PI_2) { + if (forceDirection.GetAngle(normal) < std::numbers::pi / 2) { base = base + dir * scaledlength; // OvG: Scaling } #ifdef USE_MULTIPLE_COPY diff --git a/src/Mod/Fem/Gui/ViewProviderFemConstraintForce.cpp b/src/Mod/Fem/Gui/ViewProviderFemConstraintForce.cpp index 2c100a3a93..20d7832d3e 100644 --- a/src/Mod/Fem/Gui/ViewProviderFemConstraintForce.cpp +++ b/src/Mod/Fem/Gui/ViewProviderFemConstraintForce.cpp @@ -88,7 +88,7 @@ void ViewProviderFemConstraintForce::transformSymbol(const Base::Vector3d& point // Place each symbol outside the boundary Base::Vector3d dir = (rev ? -1.0 : 1.0) * obj->DirectionVector.getValue(); float symTraY = dir.Dot(normal) < 0 ? -1 * symLen : 0.0f; - float rotAngle = rev ? F_PI : 0.0f; + float rotAngle = rev ? std::numbers::pi_v : 0.0f; SbMatrix mat0, mat1; mat0.setTransform(SbVec3f(0, symTraY, 0), SbRotation(SbVec3f(0, 0, 1), rotAngle), diff --git a/src/Mod/Fem/Gui/ViewProviderFemConstraintGear.cpp b/src/Mod/Fem/Gui/ViewProviderFemConstraintGear.cpp index 33cd6ecd8a..95609843a4 100644 --- a/src/Mod/Fem/Gui/ViewProviderFemConstraintGear.cpp +++ b/src/Mod/Fem/Gui/ViewProviderFemConstraintGear.cpp @@ -88,7 +88,7 @@ void ViewProviderFemConstraintGear::updateData(const App::Property* prop) if (dia < 2 * radius) { dia = 2 * radius; } - double angle = pcConstraint->ForceAngle.getValue() / 180 * M_PI; + double angle = pcConstraint->ForceAngle.getValue() / 180 * std::numbers::pi; SbVec3f b(base.x, base.y, base.z); SbVec3f ax(axis.x, axis.y, axis.z); @@ -118,7 +118,7 @@ void ViewProviderFemConstraintGear::updateData(const App::Property* prop) if (dia < 2 * radius) { dia = 2 * radius; } - double angle = pcConstraint->ForceAngle.getValue() / 180 * M_PI; + double angle = pcConstraint->ForceAngle.getValue() / 180 * std::numbers::pi; SbVec3f ax(axis.x, axis.y, axis.z); SbVec3f dir(direction.x, direction.y, direction.z); @@ -143,7 +143,7 @@ void ViewProviderFemConstraintGear::updateData(const App::Property* prop) direction = Base::Vector3d(0, 1, 0); } double dia = pcConstraint->Diameter.getValue(); - double angle = pcConstraint->ForceAngle.getValue() / 180 * M_PI; + double angle = pcConstraint->ForceAngle.getValue() / 180 * std::numbers::pi; SbVec3f ax(axis.x, axis.y, axis.z); SbVec3f dir(direction.x, direction.y, direction.z); diff --git a/src/Mod/Fem/Gui/ViewProviderFemConstraintPressure.cpp b/src/Mod/Fem/Gui/ViewProviderFemConstraintPressure.cpp index b58c9f5596..737b34994b 100644 --- a/src/Mod/Fem/Gui/ViewProviderFemConstraintPressure.cpp +++ b/src/Mod/Fem/Gui/ViewProviderFemConstraintPressure.cpp @@ -82,7 +82,7 @@ void ViewProviderFemConstraintPressure::transformSymbol(const Base::Vector3d& po SbMatrix& mat) const { auto obj = this->getObject(); - float rotAngle = obj->Reversed.getValue() ? F_PI : 0.0f; + float rotAngle = obj->Reversed.getValue() ? std::numbers::pi_v : 0.0f; float s = obj->getScaleFactor(); // Symbol length from .iv file float symLen = 4.0f; diff --git a/src/Mod/Fem/Gui/ViewProviderFemConstraintPulley.cpp b/src/Mod/Fem/Gui/ViewProviderFemConstraintPulley.cpp index 2a43326fe1..4cd2bc084f 100644 --- a/src/Mod/Fem/Gui/ViewProviderFemConstraintPulley.cpp +++ b/src/Mod/Fem/Gui/ViewProviderFemConstraintPulley.cpp @@ -66,6 +66,8 @@ bool ViewProviderFemConstraintPulley::setEdit(int ModNum) void ViewProviderFemConstraintPulley::updateData(const App::Property* prop) { + using std::numbers::pi; + // Gets called whenever a property of the attached object changes Fem::ConstraintPulley* pcConstraint = this->getObject(); @@ -82,7 +84,7 @@ void ViewProviderFemConstraintPulley::updateData(const App::Property* prop) if (dia < 2 * radius) { dia = 2 * radius; } - double forceAngle = pcConstraint->ForceAngle.getValue() / 180 * M_PI; + double forceAngle = pcConstraint->ForceAngle.getValue() / 180 * pi; double beltAngle = pcConstraint->BeltAngle.getValue(); double rat1 = 0.8, rat2 = 0.2; double f1 = pcConstraint->BeltForce1.getValue(); @@ -106,9 +108,9 @@ void ViewProviderFemConstraintPulley::updateData(const App::Property* prop) 0, dia / 2 * cos(forceAngle + beltAngle)), SbRotation(SbVec3f(0, 1, 0), - SbVec3f(sin(forceAngle + beltAngle + M_PI_2), + SbVec3f(sin(forceAngle + beltAngle + pi / 2), 0, - cos(forceAngle + beltAngle + M_PI_2)))); + cos(forceAngle + beltAngle + pi / 2)))); GuiTools::createPlacement(sep, SbVec3f(0, dia / 8 + dia / 2 * rat1, 0), SbRotation()); sep->addChild(GuiTools::createArrow(dia / 8 + dia / 2 * rat1, dia / 8)); pShapeSep->addChild(sep); // child 3 @@ -118,9 +120,9 @@ void ViewProviderFemConstraintPulley::updateData(const App::Property* prop) 0, -dia / 2 * cos(forceAngle - beltAngle)), SbRotation(SbVec3f(0, 1, 0), - SbVec3f(-sin(forceAngle - beltAngle - M_PI_2), + SbVec3f(-sin(forceAngle - beltAngle - pi / 2), 0, - -cos(forceAngle - beltAngle - M_PI_2)))); + -cos(forceAngle - beltAngle - pi / 2)))); GuiTools::createPlacement(sep, SbVec3f(0, dia / 8 + dia / 2 * rat2, 0), SbRotation()); sep->addChild(GuiTools::createArrow(dia / 8 + dia / 2 * rat2, dia / 8)); pShapeSep->addChild(sep); // child 4 @@ -134,7 +136,7 @@ void ViewProviderFemConstraintPulley::updateData(const App::Property* prop) if (dia < 2 * radius) { dia = 2 * radius; } - double forceAngle = pcConstraint->ForceAngle.getValue() / 180 * M_PI; + double forceAngle = pcConstraint->ForceAngle.getValue() / 180 * pi; double beltAngle = pcConstraint->BeltAngle.getValue(); double rat1 = 0.8, rat2 = 0.2; double f1 = pcConstraint->BeltForce1.getValue(); @@ -153,9 +155,9 @@ void ViewProviderFemConstraintPulley::updateData(const App::Property* prop) 0, dia / 2 * cos(forceAngle + beltAngle)), SbRotation(SbVec3f(0, 1, 0), - SbVec3f(sin(forceAngle + beltAngle + M_PI_2), + SbVec3f(sin(forceAngle + beltAngle + pi / 2), 0, - cos(forceAngle + beltAngle + M_PI_2)))); + cos(forceAngle + beltAngle + pi / 2)))); GuiTools::updatePlacement(sep, 2, SbVec3f(0, dia / 8 + dia / 2 * rat1, 0), @@ -169,9 +171,9 @@ void ViewProviderFemConstraintPulley::updateData(const App::Property* prop) 0, -dia / 2 * cos(forceAngle - beltAngle)), SbRotation(SbVec3f(0, 1, 0), - SbVec3f(-sin(forceAngle - beltAngle - M_PI_2), + SbVec3f(-sin(forceAngle - beltAngle - pi / 2), 0, - -cos(forceAngle - beltAngle - M_PI_2)))); + -cos(forceAngle - beltAngle - pi / 2)))); GuiTools::updatePlacement(sep, 2, SbVec3f(0, dia / 8 + dia / 2 * rat2, 0), @@ -187,7 +189,7 @@ void ViewProviderFemConstraintPulley::updateData(const App::Property* prop) if (dia < 2 * radius) { dia = 2 * radius; } - double forceAngle = pcConstraint->ForceAngle.getValue() / 180 * M_PI; + double forceAngle = pcConstraint->ForceAngle.getValue() / 180 * pi; double beltAngle = pcConstraint->BeltAngle.getValue(); const SoSeparator* sep = static_cast(pShapeSep->getChild(3)); @@ -197,9 +199,9 @@ void ViewProviderFemConstraintPulley::updateData(const App::Property* prop) 0, dia / 2 * cos(forceAngle + beltAngle)), SbRotation(SbVec3f(0, 1, 0), - SbVec3f(sin(forceAngle + beltAngle + M_PI_2), + SbVec3f(sin(forceAngle + beltAngle + pi / 2), 0, - cos(forceAngle + beltAngle + M_PI_2)))); + cos(forceAngle + beltAngle + pi / 2)))); sep = static_cast(pShapeSep->getChild(4)); GuiTools::updatePlacement(sep, 0, @@ -207,9 +209,9 @@ void ViewProviderFemConstraintPulley::updateData(const App::Property* prop) 0, -dia / 2 * cos(forceAngle - beltAngle)), SbRotation(SbVec3f(0, 1, 0), - SbVec3f(-sin(forceAngle - beltAngle - M_PI_2), + SbVec3f(-sin(forceAngle - beltAngle - pi / 2), 0, - -cos(forceAngle - beltAngle - M_PI_2)))); + -cos(forceAngle - beltAngle - pi / 2)))); } } else if ((prop == &pcConstraint->BeltForce1) || (prop == &pcConstraint->BeltForce2)) { diff --git a/src/Mod/Fem/Gui/ViewProviderFemPostFilter.cpp b/src/Mod/Fem/Gui/ViewProviderFemPostFilter.cpp index 22ce73853d..fe0ad21fcf 100644 --- a/src/Mod/Fem/Gui/ViewProviderFemPostFilter.cpp +++ b/src/Mod/Fem/Gui/ViewProviderFemPostFilter.cpp @@ -215,3 +215,38 @@ void ViewProviderFemPostWarpVector::setupTaskDialog(TaskDlgPost* dlg) // add the display options FemGui::ViewProviderFemPostObject::setupTaskDialog(dlg); } + + +// *************************************************************************** +// calculator filter +PROPERTY_SOURCE(FemGui::ViewProviderFemPostCalculator, FemGui::ViewProviderFemPostObject) + +ViewProviderFemPostCalculator::ViewProviderFemPostCalculator() +{ + sPixmap = "FEM_PostFilterCalculator"; +} + +ViewProviderFemPostCalculator::~ViewProviderFemPostCalculator() = default; + +void ViewProviderFemPostCalculator::updateData(const App::Property* prop) +{ + auto obj = getObject(); + if (prop == &obj->Data) { + // update color bar + ViewProviderFemPostObject::updateData(prop); + updateMaterial(); + } + else { + return ViewProviderFemPostObject::updateData(prop); + } +} + +void ViewProviderFemPostCalculator::setupTaskDialog(TaskDlgPost* dlg) +{ + // add the function box + assert(dlg->getView() == this); + dlg->appendBox(new TaskPostCalculator(this)); + + // add the display options + FemGui::ViewProviderFemPostObject::setupTaskDialog(dlg); +} diff --git a/src/Mod/Fem/Gui/ViewProviderFemPostFilter.h b/src/Mod/Fem/Gui/ViewProviderFemPostFilter.h index 52194b87f1..e728e5fcd0 100644 --- a/src/Mod/Fem/Gui/ViewProviderFemPostFilter.h +++ b/src/Mod/Fem/Gui/ViewProviderFemPostFilter.h @@ -154,6 +154,24 @@ protected: void setupTaskDialog(TaskDlgPost* dlg) override; }; + +// *************************************************************************** +// calculator filter +class FemGuiExport ViewProviderFemPostCalculator: public ViewProviderFemPostObject +{ + PROPERTY_HEADER_WITH_OVERRIDE(FemGui::ViewProviderFemPostCalculator); + +public: + /// constructor. + ViewProviderFemPostCalculator(); + ~ViewProviderFemPostCalculator() override; + + void updateData(const App::Property* prop) override; + +protected: + void setupTaskDialog(TaskDlgPost* dlg) override; +}; + } // namespace FemGui diff --git a/src/Mod/Fem/Gui/ViewProviderFemPostFunction.cpp b/src/Mod/Fem/Gui/ViewProviderFemPostFunction.cpp index be2daae11e..200622da31 100644 --- a/src/Mod/Fem/Gui/ViewProviderFemPostFunction.cpp +++ b/src/Mod/Fem/Gui/ViewProviderFemPostFunction.cpp @@ -756,7 +756,10 @@ void CylinderWidget::radiusChanged(double) PROPERTY_SOURCE(FemGui::ViewProviderFemPostPlaneFunction, FemGui::ViewProviderFemPostFunction) // NOTE: The technical lower limit is at 1e-4 that the Coin3D manipulator can handle -static const App::PropertyFloatConstraint::Constraints scaleConstraint = {1e-4, DBL_MAX, 1.0}; +static const App::PropertyFloatConstraint::Constraints scaleConstraint = { + 1e-4, + std::numeric_limits::max(), + 1.0}; ViewProviderFemPostPlaneFunction::ViewProviderFemPostPlaneFunction() : m_detectscale(false) @@ -1178,6 +1181,8 @@ SoGroup* postBox() SoGroup* postCylinder() { + using std::numbers::pi; + SoCoordinate3* points = new SoCoordinate3(); int nCirc = 20; const int nSide = 8; @@ -1189,8 +1194,8 @@ SoGroup* postCylinder() for (int i = 0; i < 2; ++i) { for (int j = 0; j < nCirc + 1; ++j) { points->point.set1Value(idx, - SbVec3f(std::cos(2 * M_PI / nCirc * j), - std::sin(2 * M_PI / nCirc * j), + SbVec3f(std::cos(2 * pi / nCirc * j), + std::sin(2 * pi / nCirc * j), -h / 2. + h * i)); ++idx; } @@ -1199,8 +1204,8 @@ SoGroup* postCylinder() for (int i = 0; i < nSide; ++i) { for (int j = 0; j < 2; ++j) { points->point.set1Value(idx, - SbVec3f(std::cos(2 * M_PI / nSide * i), - std::sin(2 * M_PI / nSide * i), + SbVec3f(std::cos(2 * pi / nSide * i), + std::sin(2 * pi / nSide * i), -h / 2. + h * j)); ++idx; } @@ -1243,24 +1248,26 @@ SoGroup* postPlane() SoGroup* postSphere() { + using std::numbers::pi; + SoCoordinate3* points = new SoCoordinate3(); points->point.setNum(2 * 84); int idx = 0; for (int i = 0; i < 4; i++) { for (int j = 0; j < 21; j++) { points->point.set1Value(idx, - SbVec3f(std::sin(2 * M_PI / 20 * j) * std::cos(M_PI / 4 * i), - std::sin(2 * M_PI / 20 * j) * std::sin(M_PI / 4 * i), - std::cos(2 * M_PI / 20 * j))); + SbVec3f(std::sin(2 * pi / 20 * j) * std::cos(pi / 4 * i), + std::sin(2 * pi / 20 * j) * std::sin(pi / 4 * i), + std::cos(2 * pi / 20 * j))); ++idx; } } for (int i = 0; i < 4; i++) { for (int j = 0; j < 21; j++) { points->point.set1Value(idx, - SbVec3f(std::sin(M_PI / 4 * i) * std::cos(2 * M_PI / 20 * j), - std::sin(M_PI / 4 * i) * std::sin(2 * M_PI / 20 * j), - std::cos(M_PI / 4 * i))); + SbVec3f(std::sin(pi / 4 * i) * std::cos(2 * pi / 20 * j), + std::sin(pi / 4 * i) * std::sin(2 * pi / 20 * j), + std::cos(pi / 4 * i))); ++idx; } } diff --git a/src/Mod/Fem/Gui/ViewProviderFemPostObject.cpp b/src/Mod/Fem/Gui/ViewProviderFemPostObject.cpp index ef40748888..234f68dc9f 100644 --- a/src/Mod/Fem/Gui/ViewProviderFemPostObject.cpp +++ b/src/Mod/Fem/Gui/ViewProviderFemPostObject.cpp @@ -261,27 +261,40 @@ ViewProviderFemPostObject::ViewProviderFemPostObject() ViewProviderFemPostObject::~ViewProviderFemPostObject() { - FemPostObjectSelectionObserver::instance().unregisterFemPostObject(this); - m_transpType->unref(); - m_depthBuffer->unref(); - m_shapeHints->unref(); - m_coordinates->unref(); - m_materialBinding->unref(); - m_drawStyle->unref(); - m_normalBinding->unref(); - m_normals->unref(); - m_faces->unref(); - m_triangleStrips->unref(); - m_markers->unref(); - m_lines->unref(); - m_sepMarkerLine->unref(); - m_separator->unref(); - m_material->unref(); - m_matPlainEdges->unref(); - m_switchMatEdges->unref(); - deleteColorBar(); - m_colorStyle->unref(); - m_colorRoot->unref(); + try { + FemPostObjectSelectionObserver::instance().unregisterFemPostObject(this); + m_transpType->unref(); + m_depthBuffer->unref(); + m_shapeHints->unref(); + m_coordinates->unref(); + m_materialBinding->unref(); + m_drawStyle->unref(); + m_normalBinding->unref(); + m_normals->unref(); + m_faces->unref(); + m_triangleStrips->unref(); + m_markers->unref(); + m_lines->unref(); + m_sepMarkerLine->unref(); + m_separator->unref(); + m_material->unref(); + m_matPlainEdges->unref(); + m_switchMatEdges->unref(); + m_colorStyle->unref(); + m_colorRoot->unref(); + deleteColorBar(); + } + catch (Base::Exception& e) { + Base::Console().DestructorError( + "ViewProviderFemPostObject", + "ViewProviderFemPostObject destructor threw an exception: %s\n", + e.what()); + } + catch (...) { + Base::Console().DestructorError( + "ViewProviderFemPostObject", + "ViewProviderFemPostObject destructor threw an unknown exception"); + } } void ViewProviderFemPostObject::deleteColorBar() @@ -856,12 +869,6 @@ bool ViewProviderFemPostObject::setupPipeline() if (!dset) { return false; } - std::string FieldName; - auto numFields = dset->GetPointData()->GetNumberOfArrays(); - for (int i = 0; i < numFields; ++i) { - FieldName = std::string(dset->GetPointData()->GetArrayName(i)); - addAbsoluteField(dset, FieldName); - } m_outline->SetInputData(dset); m_points->SetInputData(dset); @@ -1099,74 +1106,3 @@ void ViewProviderFemPostObject::onSelectionChanged(const Gui::SelectionChanges& } } } - -// if there is a real and an imaginary field, an absolute field is added -void ViewProviderFemPostObject::addAbsoluteField(vtkDataSet* dset, std::string FieldName) -{ - // real field names have the suffix " re", given by Elmer - // if the field does not have this suffix, we can return - auto suffix = FieldName.substr(FieldName.size() - 3, FieldName.size() - 1); - if (strcmp(suffix.c_str(), " re") != 0) { - return; - } - - // absolute fields might have already been created, then do nothing - auto strAbsoluteFieldName = FieldName.substr(0, FieldName.size() - 2) + "abs"; - vtkDataArray* testArray = dset->GetPointData()->GetArray(strAbsoluteFieldName.c_str()); - if (testArray) { - return; - } - - // safety check - vtkDataArray* realDdata = dset->GetPointData()->GetArray(FieldName.c_str()); - if (!realDdata) { - return; - } - - // now check if the imaginary counterpart exists - auto strImaginaryFieldName = FieldName.substr(0, FieldName.size() - 2) + "im"; - vtkDataArray* imagDdata = dset->GetPointData()->GetArray(strImaginaryFieldName.c_str()); - if (!imagDdata) { - return; - } - - // create a new array and copy over the real data - // since one cannot directly access the values of a vtkDataSet - // we need to copy them over in a loop - vtkSmartPointer absoluteData = vtkSmartPointer::New(); - absoluteData->SetNumberOfComponents(realDdata->GetNumberOfComponents()); - auto numTuples = realDdata->GetNumberOfTuples(); - absoluteData->SetNumberOfTuples(numTuples); - double tuple[] = {0, 0, 0}; - for (vtkIdType i = 0; i < numTuples; ++i) { - absoluteData->SetTuple(i, tuple); - } - // name the array - auto strAbsFieldName = FieldName.substr(0, FieldName.size() - 2) + "abs"; - absoluteData->SetName(strAbsFieldName.c_str()); - - // add array to data set - dset->GetPointData()->AddArray(absoluteData); - - // step through all mesh points and calculate them - double realValue = 0; - double imaginaryValue = 0; - double absoluteValue = 0; - for (int i = 0; i < dset->GetNumberOfPoints(); ++i) { - if (absoluteData->GetNumberOfComponents() == 1) { - realValue = realDdata->GetComponent(i, 0); - imaginaryValue = imagDdata->GetComponent(i, 0); - absoluteValue = sqrt(pow(realValue, 2) + pow(imaginaryValue, 2)); - absoluteData->SetComponent(i, 0, absoluteValue); - } - // if field is a vector - else { - for (int j = 0; j < absoluteData->GetNumberOfComponents(); ++j) { - realValue = realDdata->GetComponent(i, j); - imaginaryValue = imagDdata->GetComponent(i, j); - absoluteValue = sqrt(pow(realValue, 2) + pow(imaginaryValue, 2)); - absoluteData->SetComponent(i, j, absoluteValue); - } - } - } -} diff --git a/src/Mod/Fem/Gui/ViewProviderFemPostObject.h b/src/Mod/Fem/Gui/ViewProviderFemPostObject.h index 3909502d9e..19aa86be95 100644 --- a/src/Mod/Fem/Gui/ViewProviderFemPostObject.h +++ b/src/Mod/Fem/Gui/ViewProviderFemPostObject.h @@ -170,7 +170,6 @@ private: void WritePointData(vtkPoints* points, vtkDataArray* normals, vtkDataArray* tcoords); void WriteColorData(bool ResetColorBarRange); void WriteTransparency(); - void addAbsoluteField(vtkDataSet* dset, std::string FieldName); void deleteColorBar(); App::Enumeration m_coloringEnum, m_vectorEnum; diff --git a/src/Mod/Fem/Gui/Workbench.cpp b/src/Mod/Fem/Gui/Workbench.cpp index 345632754e..cbfaea76c1 100644 --- a/src/Mod/Fem/Gui/Workbench.cpp +++ b/src/Mod/Fem/Gui/Workbench.cpp @@ -209,6 +209,7 @@ Gui::ToolBarItem* Workbench::setupToolBars() const << "FEM_PostFilterDataAlongLine" << "FEM_PostFilterLinearizedStresses" << "FEM_PostFilterDataAtPoint" + << "FEM_PostFilterCalculator" << "Separator" << "FEM_PostCreateFunctions"; #endif @@ -246,7 +247,8 @@ Gui::MenuItem* Workbench::setupMenuBar() const elec->setCommand("&Electromagnetic boundary conditions"); *elec << "FEM_ConstraintElectrostaticPotential" << "FEM_ConstraintCurrentDensity" - << "FEM_ConstraintMagnetization"; + << "FEM_ConstraintMagnetization" + << "FEM_ConstraintElectricChargeDensity"; Gui::MenuItem* fluid = new Gui::MenuItem; fluid->setCommand("&Fluid boundary conditions"); @@ -356,6 +358,7 @@ Gui::MenuItem* Workbench::setupMenuBar() const << "FEM_PostFilterDataAlongLine" << "FEM_PostFilterLinearizedStresses" << "FEM_PostFilterDataAtPoint" + << "FEM_PostFilterCalculator" << "Separator" << "FEM_PostCreateFunctions"; #endif diff --git a/src/Mod/Fem/ObjectsFem.py b/src/Mod/Fem/ObjectsFem.py index b8893e5d20..17a8b436f9 100644 --- a/src/Mod/Fem/ObjectsFem.py +++ b/src/Mod/Fem/ObjectsFem.py @@ -132,6 +132,20 @@ def makeConstraintDisplacement(doc, name="ConstraintDisplacement"): return obj +def makeConstraintElectricChargeDensity(doc, name="ElectricChargeDensity"): + """makeConstraintElectricChargeDensity(document, [name]): + makes a Fem ElectricChargeDensity object""" + obj = doc.addObject("Fem::ConstraintPython", name) + from femobjects import constraint_electricchargedensity + + constraint_electricchargedensity.ConstraintElectricChargeDensity(obj) + if FreeCAD.GuiUp: + from femviewprovider import view_constraint_electricchargedensity + + view_constraint_electricchargedensity.VPConstraintElectricChargeDensity(obj.ViewObject) + return obj + + def makeConstraintElectrostaticPotential(doc, name="ConstraintElectrostaticPotential"): """makeConstraintElectrostaticPotential(document, [name]): makes a Fem ElectrostaticPotential object""" diff --git a/src/Mod/Fem/femcommands/commands.py b/src/Mod/Fem/femcommands/commands.py index 69bed9352f..89dad73b83 100644 --- a/src/Mod/Fem/femcommands/commands.py +++ b/src/Mod/Fem/femcommands/commands.py @@ -221,6 +221,22 @@ class _ConstraintCurrentDensity(CommandManager): self.do_activated = "add_obj_on_gui_set_edit" +class _ConstraintElectricChargeDensity(CommandManager): + "The FEM_ConstraintElectricChargeDensity command definition" + + def __init__(self): + super().__init__() + self.pixmap = "FEM_ConstraintElectricChargeDensity" + self.menutext = Qt.QT_TRANSLATE_NOOP( + "FEM_ConstraintElectricChargeDensity", "Electric charge density" + ) + self.tooltip = Qt.QT_TRANSLATE_NOOP( + "FEM_ConstraintElectricChargeDensity", "Creates a electric charge density" + ) + self.is_active = "with_analysis" + self.do_activated = "add_obj_on_gui_set_edit" + + class _ConstraintElectrostaticPotential(CommandManager): "The FEM_ConstraintElectrostaticPotential command definition" @@ -1171,6 +1187,7 @@ FreeCADGui.addCommand("FEM_ConstantVacuumPermittivity", _ConstantVacuumPermittiv FreeCADGui.addCommand("FEM_ConstraintBodyHeatSource", _ConstraintBodyHeatSource()) FreeCADGui.addCommand("FEM_ConstraintCentrif", _ConstraintCentrif()) FreeCADGui.addCommand("FEM_ConstraintCurrentDensity", _ConstraintCurrentDensity()) +FreeCADGui.addCommand("FEM_ConstraintElectricChargeDensity", _ConstraintElectricChargeDensity()) FreeCADGui.addCommand("FEM_ConstraintElectrostaticPotential", _ConstraintElectrostaticPotential()) FreeCADGui.addCommand("FEM_ConstraintFlowVelocity", _ConstraintFlowVelocity()) FreeCADGui.addCommand("FEM_ConstraintInitialFlowVelocity", _ConstraintInitialFlowVelocity()) diff --git a/src/Mod/Fem/feminout/convert2TetGen.py b/src/Mod/Fem/feminout/convert2TetGen.py index ef7a001f1b..423770f2d1 100644 --- a/src/Mod/Fem/feminout/convert2TetGen.py +++ b/src/Mod/Fem/feminout/convert2TetGen.py @@ -40,9 +40,6 @@ if FreeCAD.GuiUp: Gui = FreeCADGui # shortcut -## \addtogroup FEM -# @{ - def exportMeshToTetGenPoly(meshToExport, filePath, beVerbose=1): """Export mesh to TetGen *.poly file format""" diff --git a/src/Mod/Fem/femobjects/constraint_electricchargedensity.py b/src/Mod/Fem/femobjects/constraint_electricchargedensity.py new file mode 100644 index 0000000000..fb2b9a6b16 --- /dev/null +++ b/src/Mod/Fem/femobjects/constraint_electricchargedensity.py @@ -0,0 +1,87 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +# *************************************************************************** +# * Copyright (c) 2025 Mario Passaglia * +# * * +# * This file is part of FreeCAD. * +# * * +# * FreeCAD is free software: you can redistribute it and/or modify it * +# * under the terms of the GNU Lesser General Public License as * +# * published by the Free Software Foundation, either version 2.1 of the * +# * License, or (at your option) any later version. * +# * * +# * FreeCAD 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 * +# * Lesser General Public License for more details. * +# * * +# * You should have received a copy of the GNU Lesser General Public * +# * License along with FreeCAD. If not, see * +# * . * +# * * +# *************************************************************************** + +__title__ = "FreeCAD FEM constraint electric charge density document object" +__author__ = "Mario Passaglia" +__url__ = "https://www.freecad.org" + +## @package constraint_electricchargedensity +# \ingroup FEM +# \brief constraint electric charge density object + +from . import base_fempythonobject + +_PropHelper = base_fempythonobject._PropHelper + + +class ConstraintElectricChargeDensity(base_fempythonobject.BaseFemPythonObject): + + Type = "Fem::ConstraintElectricChargeDensity" + + def __init__(self, obj): + super().__init__(obj) + + for prop in self._get_properties(): + prop.add_to_object(obj) + + def _get_properties(self): + prop = [] + + prop.append( + _PropHelper( + type="App::PropertyVolumeChargeDensity", + name="SourceChargeDensity", + group="Electric Charge Density", + doc="Free electric charge per unit volume at the sources", + value="0 C/mm^3", + ) + ) + prop.append( + _PropHelper( + type="App::PropertySurfaceChargeDensity", + name="InterfaceChargeDensity", + group="Electric Charge Density", + doc="Free electric charge per unit surface at the boundaries", + value="0 C/mm^2", + ) + ) + prop.append( + _PropHelper( + type="App::PropertyElectricCharge", + name="TotalCharge", + group="Electric Charge Density", + doc="Total free electric charge", + value="0 C", + ) + ) + prop.append( + _PropHelper( + type="App::PropertyEnumeration", + name="Mode", + group="Electric Charge Density", + doc="Switch quantity input mode", + value=["Interface", "Source", "Total Interface", "Total Source"], + ) + ) + + return prop diff --git a/src/Mod/Fem/femobjects/constraint_electrostaticpotential.py b/src/Mod/Fem/femobjects/constraint_electrostaticpotential.py index 50ac56a951..0c9e37a748 100644 --- a/src/Mod/Fem/femobjects/constraint_electrostaticpotential.py +++ b/src/Mod/Fem/femobjects/constraint_electrostaticpotential.py @@ -145,9 +145,11 @@ class ConstraintElectrostaticPotential(base_fempythonobject.BaseFemPythonObject) prop.append( _PropHelper( type="App::PropertySurfaceChargeDensity", - name="SurfaceChargeDensity", + name="ElectricFluxDensity", group="Parameter", - doc="Free surface charge density", + doc="Electric displacement field D.\n" + + "For interfaces, it represents the difference\n" + + "between the normal component in the two media", value="0 C/m^2", ) ) @@ -299,3 +301,12 @@ class ConstraintElectrostaticPotential(base_fempythonobject.BaseFemPythonObject) except Base.PropertyError: pass + + # set electric flux density from old surface charge density + try: + obj.ElectricFluxDensity = obj.getPropertyByName("SurfaceChargeDensity") + obj.setPropertyStatus("SurfaceChargeDensity", "-LockDynamic") + obj.removeProperty("SurfaceChargeDensity") + + except Base.PropertyError: + pass diff --git a/src/Mod/Fem/femsolver/elmer/equations/electrostatic_writer.py b/src/Mod/Fem/femsolver/elmer/equations/electrostatic_writer.py index 6ffba76461..bc49f4ee05 100644 --- a/src/Mod/Fem/femsolver/elmer/equations/electrostatic_writer.py +++ b/src/Mod/Fem/femsolver/elmer/equations/electrostatic_writer.py @@ -30,6 +30,8 @@ __url__ = "https://www.freecad.org" ## \addtogroup FEM # @{ +from FreeCAD import Units + from .. import sifio @@ -133,8 +135,8 @@ class ESwriter: elif obj.BoundaryCondition == "Neumann": self.write.boundary( name, - "Surface Charge Density", - obj.SurfaceChargeDensity.getValueAs("C/m^2").Value, + "Electric Flux", + obj.ElectricFluxDensity.getValueAs("C/m^2").Value, ) if obj.PotentialConstant: self.write.boundary(name, "Potential Constant", True) @@ -146,5 +148,60 @@ class ESwriter: self.write.boundary(name, "Capacitance Body", obj.CapacitanceBody) self.write.handled(obj) + for obj in self.write.getMember("Fem::ConstraintElectricChargeDensity"): + if obj.Mode not in ["Interface", "Total Interface"]: + continue + + size = 0 + items = [] + for feat, sub_elem in obj.References: + for name in sub_elem: + sub = feat.getSubObject(name) + if sub.ShapeType == "Face": + size += sub.Area + items.append(name) + elif sub.ShapeType == "Edge": + size += sub.Length + items.append(name) + + if items: + if obj.Mode == "Interface": + density = obj.InterfaceChargeDensity.getValueAs("C/m^2").Value + elif obj.Mode == "Total Interface": + area = Units.Quantity(f"{size} mm^2") + density = (obj.TotalCharge / area).getValueAs("C/m^2").Value + for name in items: + self.write.boundary(name, "! FreeCAD Name", obj.Label) + self.write.boundary(name, "Surface Charge Density", round(density, 6)) + self.write.handled(obj) + + def handleElectrostaticBodyForces(self): + for obj in self.write.getMember("Fem::ConstraintElectricChargeDensity"): + if obj.Mode not in ["Source", "Total Source"]: + continue + + size = 0 + items = [] + for feat, sub_elem in obj.References: + for name in sub_elem: + sub = feat.getSubObject(name) + if sub.ShapeType == "Solid": + size += sub.Volume + items.append(name) + elif sub.ShapeType == "Face": + size += sub.Area + items.append(name) + + if items: + if obj.Mode == "Source": + density = obj.SourceChargeDensity.getValueAs("C/m^3").Value + elif obj.Mode == "Total Source": + vol = Units.Quantity(f"{size} mm^3") + density = (obj.TotalCharge / vol).getValueAs("C/m^3").Value + for name in items: + self.write.bodyForce(name, "! FreeCAD Name", obj.Label) + self.write.bodyForce(name, "Charge Density", round(density, 6)) + self.write.handled(obj) + ## @} diff --git a/src/Mod/Fem/femsolver/elmer/writer.py b/src/Mod/Fem/femsolver/elmer/writer.py index 834d87bcc1..b55833c754 100644 --- a/src/Mod/Fem/femsolver/elmer/writer.py +++ b/src/Mod/Fem/femsolver/elmer/writer.py @@ -471,6 +471,7 @@ class Writer: if activeIn: ESW.handleElectrostaticConstants() ESW.handleElectrostaticBndConditions() + ESW.handleElectrostaticBodyForces() ESW.handleElectrostaticMaterial(activeIn) # ------------------------------------------------------------------------------------------- diff --git a/src/Mod/Fem/femsolver/fenics/fenics_tools.py b/src/Mod/Fem/femsolver/fenics/fenics_tools.py index 13b09e7488..0a34c6ce43 100644 --- a/src/Mod/Fem/femsolver/fenics/fenics_tools.py +++ b/src/Mod/Fem/femsolver/fenics/fenics_tools.py @@ -257,6 +257,3 @@ class FacetFunctionFromXDMF: # TODO: write some functions to return integrals for Neumann and Robin # boundary conditions for the general case (i.e. vector, tensor) - - -## @} diff --git a/src/Mod/Fem/femsolver/mystran/solver.py b/src/Mod/Fem/femsolver/mystran/solver.py index c7fa364e97..a6573a77b3 100644 --- a/src/Mod/Fem/femsolver/mystran/solver.py +++ b/src/Mod/Fem/femsolver/mystran/solver.py @@ -92,6 +92,3 @@ class ViewProxy(solverbase.ViewProxy): def getIcon(self): return ":/icons/FEM_SolverMystran.svg" - - -## @} diff --git a/src/Mod/Fem/femsolver/z88/solver.py b/src/Mod/Fem/femsolver/z88/solver.py index 0129b35ee6..61eb94b6be 100644 --- a/src/Mod/Fem/femsolver/z88/solver.py +++ b/src/Mod/Fem/femsolver/z88/solver.py @@ -111,6 +111,3 @@ class ViewProxy(solverbase.ViewProxy): def getIcon(self): return ":/icons/FEM_SolverZ88.svg" - - -## @} diff --git a/src/Mod/Fem/femtaskpanels/task_constraint_electricchargedensity.py b/src/Mod/Fem/femtaskpanels/task_constraint_electricchargedensity.py new file mode 100644 index 0000000000..1d082454ae --- /dev/null +++ b/src/Mod/Fem/femtaskpanels/task_constraint_electricchargedensity.py @@ -0,0 +1,171 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +# *************************************************************************** +# * Copyright (c) 2025 Mario Passaglia * +# * * +# * This file is part of FreeCAD. * +# * * +# * FreeCAD is free software: you can redistribute it and/or modify it * +# * under the terms of the GNU Lesser General Public License as * +# * published by the Free Software Foundation, either version 2.1 of the * +# * License, or (at your option) any later version. * +# * * +# * FreeCAD 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 * +# * Lesser General Public License for more details. * +# * * +# * You should have received a copy of the GNU Lesser General Public * +# * License along with FreeCAD. If not, see * +# * . * +# * * +# *************************************************************************** + +__title__ = "FreeCAD FEM constraint electric charge density task panel" +__author__ = "Mario Passaglia" +__url__ = "https://www.freecad.org" + +## @package task_constraint_electricchargedensity +# \ingroup FEM +# \brief task panel for constraint electric charge density object + +from PySide import QtCore + +import FreeCAD +import FreeCADGui + +from femguiutils import selection_widgets + +from femtools import membertools +from . import base_femtaskpanel + + +class _TaskPanel(base_femtaskpanel._BaseTaskPanel): + + def __init__(self, obj): + super().__init__(obj) + + self.parameter_widget = FreeCADGui.PySideUic.loadUi( + FreeCAD.getHomePath() + "Mod/Fem/Resources/ui/ElectricChargeDensity.ui" + ) + + self.init_parameter_widget() + + QtCore.QObject.connect( + self.parameter_widget.qsb_source_charge_density, + QtCore.SIGNAL("valueChanged(Base::Quantity)"), + self.source_charge_density_changed, + ) + QtCore.QObject.connect( + self.parameter_widget.qsb_interface_charge_density, + QtCore.SIGNAL("valueChanged(Base::Quantity)"), + self.interface_charge_density_changed, + ) + QtCore.QObject.connect( + self.parameter_widget.qsb_total_charge, + QtCore.SIGNAL("valueChanged(Base::Quantity)"), + self.total_charge_changed, + ) + QtCore.QObject.connect( + self.parameter_widget.cb_mode, + QtCore.SIGNAL("currentIndexChanged(int)"), + self.mode_changed, + ) + + # geometry selection widget + # start with Solid in list! + self.selection_widget = selection_widgets.GeometryElementsSelection( + obj.References, ["Solid", "Face", "Edge"], False, False + ) + + # form made from param and selection widget + self.form = [self.parameter_widget, self.selection_widget] + + analysis = obj.getParentGroup() + self._mesh = None + self._part = None + if analysis is not None: + self._mesh = membertools.get_single_member(analysis, "Fem::FemMeshObject") + if self._mesh is not None: + self._part = self._mesh.Shape + self._partVisible = None + self._meshVisible = None + + def open(self): + if self._mesh is not None and self._part is not None: + self._meshVisible = self._mesh.ViewObject.isVisible() + self._partVisible = self._part.ViewObject.isVisible() + self._mesh.ViewObject.hide() + self._part.ViewObject.show() + + def reject(self): + self.restore_visibility() + self.selection_widget.finish_selection() + return super().reject() + + def accept(self): + self.obj.References = self.selection_widget.references + self.obj.SourceChargeDensity = self.source_charge_density + self.obj.InterfaceChargeDensity = self.interface_charge_density + self.obj.TotalCharge = self.total_charge + self.obj.Mode = self.mode + + self.selection_widget.finish_selection() + self.restore_visibility() + return super().accept() + + def restore_visibility(self): + if self._mesh is not None and self._part is not None: + if self._meshVisible: + self._mesh.ViewObject.show() + else: + self._mesh.ViewObject.hide() + if self._partVisible: + self._part.ViewObject.show() + else: + self._part.ViewObject.hide() + + def init_parameter_widget(self): + self.source_charge_density = self.obj.SourceChargeDensity + self.interface_charge_density = self.obj.InterfaceChargeDensity + self.total_charge = self.obj.TotalCharge + FreeCADGui.ExpressionBinding(self.parameter_widget.qsb_source_charge_density).bind( + self.obj, "SourceChargeDensity" + ) + self.parameter_widget.qsb_source_charge_density.setProperty( + "value", self.source_charge_density + ) + + FreeCADGui.ExpressionBinding(self.parameter_widget.qsb_interface_charge_density).bind( + self.obj, "InterfaceChargeDensity" + ) + self.parameter_widget.qsb_interface_charge_density.setProperty( + "value", self.interface_charge_density + ) + + FreeCADGui.ExpressionBinding(self.parameter_widget.qsb_total_charge).bind( + self.obj, "TotalCharge" + ) + self.parameter_widget.qsb_total_charge.setProperty("value", self.total_charge) + + self.mode = self.obj.Mode + self.mode_enum = self.obj.getEnumerationsOfProperty("Mode") + self.parameter_widget.cb_mode.addItems(self.mode_enum) + index = self.mode_enum.index(self.mode) + self.parameter_widget.cb_mode.setCurrentIndex(index) + self.mode_changed(index) + + def source_charge_density_changed(self, base_quantity_value): + self.source_charge_density = base_quantity_value + + def interface_charge_density_changed(self, base_quantity_value): + self.interface_charge_density = base_quantity_value + + def total_charge_changed(self, base_quantity_value): + self.total_charge = base_quantity_value + + def mode_changed(self, index): + self.mode = self.mode_enum[index] + if self.mode in ["Total Interface", "Total Source"]: + index = 2 + self.parameter_widget.sw_mode.setCurrentIndex(index) diff --git a/src/Mod/Fem/femtaskpanels/task_constraint_electrostaticpotential.py b/src/Mod/Fem/femtaskpanels/task_constraint_electrostaticpotential.py index 3d1826e126..e4d1b39c06 100644 --- a/src/Mod/Fem/femtaskpanels/task_constraint_electrostaticpotential.py +++ b/src/Mod/Fem/femtaskpanels/task_constraint_electrostaticpotential.py @@ -171,9 +171,9 @@ class _TaskPanel(base_femtaskpanel._BaseTaskPanel): self.electric_infinity_changed, ) QtCore.QObject.connect( - self.parameter_widget.qsb_surface_charge_density, + self.parameter_widget.qsb_electric_flux_density, QtCore.SIGNAL("valueChanged(Base::Quantity)"), - self.surface_charge_density_changed, + self.electric_flux_density_changed, ) self.init_parameter_widget() @@ -232,7 +232,7 @@ class _TaskPanel(base_femtaskpanel._BaseTaskPanel): self.electric_infinity = self.obj.ElectricInfinity self.capacitance_body_enabled = self.obj.CapacitanceBodyEnabled self.capacitance_body = self.obj.CapacitanceBody - self.surface_charge_density = self.obj.SurfaceChargeDensity + self.electric_flux_density = self.obj.ElectricFluxDensity def _set_params(self): self.obj.Potential = self.potential @@ -258,7 +258,7 @@ class _TaskPanel(base_femtaskpanel._BaseTaskPanel): self.obj.CapacitanceBodyEnabled = self.capacitance_body_enabled self.obj.CapacitanceBody = self.capacitance_body - self.obj.SurfaceChargeDensity = self.surface_charge_density + self.obj.ElectricFluxDensity = self.electric_flux_density def init_parameter_widget(self): self._get_params() @@ -313,11 +313,11 @@ class _TaskPanel(base_femtaskpanel._BaseTaskPanel): self.obj, "CapacitanceBody" ) - self.parameter_widget.qsb_surface_charge_density.setProperty( - "value", self.surface_charge_density + self.parameter_widget.qsb_electric_flux_density.setProperty( + "value", self.electric_flux_density ) - FreeCADGui.ExpressionBinding(self.parameter_widget.qsb_surface_charge_density).bind( - self.obj, "SurfaceChargeDensity" + FreeCADGui.ExpressionBinding(self.parameter_widget.qsb_electric_flux_density).bind( + self.obj, "ElectricFluxDensity" ) self.bc_enum = self.obj.getEnumerationsOfProperty("BoundaryCondition") @@ -397,8 +397,8 @@ class _TaskPanel(base_femtaskpanel._BaseTaskPanel): self.capacitance_body = value self.parameter_widget.spb_capacitance_body.setValue(value) - def surface_charge_density_changed(self, value): - self.surface_charge_density = value + def electric_flux_density_changed(self, value): + self.electric_flux_density = value def boundary_condition_changed(self, index): self.boundary_condition = self.bc_enum[index] diff --git a/src/Mod/Fem/femtest/app/test_object.py b/src/Mod/Fem/femtest/app/test_object.py index f2b154b5be..cbd67e9c84 100644 --- a/src/Mod/Fem/femtest/app/test_object.py +++ b/src/Mod/Fem/femtest/app/test_object.py @@ -159,6 +159,10 @@ class TestObjectType(unittest.TestCase): "Fem::ConstraintElectrostaticPotential", type_of_obj(ObjectsFem.makeConstraintElectrostaticPotential(doc)), ) + self.assertEqual( + "Fem::ConstraintElectricChargeDensity", + type_of_obj(ObjectsFem.makeConstraintElectricChargeDensity(doc)), + ) self.assertEqual("Fem::ConstraintFixed", type_of_obj(ObjectsFem.makeConstraintFixed(doc))) self.assertEqual( "Fem::ConstraintRigidBody", type_of_obj(ObjectsFem.makeConstraintRigidBody(doc)) @@ -334,6 +338,12 @@ class TestObjectType(unittest.TestCase): "Fem::ConstraintElectrostaticPotential", ) ) + self.assertTrue( + is_of_type( + ObjectsFem.makeConstraintElectricChargeDensity(doc), + "Fem::ConstraintElectricChargeDensity", + ) + ) self.assertTrue(is_of_type(ObjectsFem.makeConstraintFixed(doc), "Fem::ConstraintFixed")) self.assertTrue( is_of_type(ObjectsFem.makeConstraintRigidBody(doc), "Fem::ConstraintRigidBody") @@ -552,6 +562,18 @@ class TestObjectType(unittest.TestCase): ) ) + # ConstraintElectricChargeDensity + constraint_electric_charge_density = ObjectsFem.makeConstraintElectricChargeDensity(doc) + self.assertTrue(is_derived_from(constraint_electric_charge_density, "App::DocumentObject")) + self.assertTrue( + is_derived_from(constraint_electric_charge_density, "Fem::ConstraintPython") + ) + self.assertTrue( + is_derived_from( + constraint_electric_charge_density, "Fem::ConstraintElectricChargeDensity" + ) + ) + # ConstraintFixed constraint_fixed = ObjectsFem.makeConstraintFixed(doc) self.assertTrue(is_derived_from(constraint_fixed, "App::DocumentObject")) @@ -913,6 +935,11 @@ class TestObjectType(unittest.TestCase): "Fem::ConstraintPython" ) ) + self.assertTrue( + ObjectsFem.makeConstraintElectricChargeDensity(doc).isDerivedFrom( + "Fem::ConstraintPython" + ) + ) self.assertTrue(ObjectsFem.makeConstraintFixed(doc).isDerivedFrom("Fem::ConstraintFixed")) self.assertTrue( ObjectsFem.makeConstraintRigidBody(doc).isDerivedFrom("Fem::ConstraintRigidBody") @@ -1077,6 +1104,7 @@ def create_all_fem_objects_doc(doc): analysis.addObject(ObjectsFem.makeConstraintCurrentDensity(doc)) analysis.addObject(ObjectsFem.makeConstraintDisplacement(doc)) analysis.addObject(ObjectsFem.makeConstraintElectrostaticPotential(doc)) + analysis.addObject(ObjectsFem.makeConstraintElectricChargeDensity(doc)) analysis.addObject(ObjectsFem.makeConstraintFixed(doc)) analysis.addObject(ObjectsFem.makeConstraintRigidBody(doc)) analysis.addObject(ObjectsFem.makeConstraintFlowVelocity(doc)) diff --git a/src/Mod/Fem/femviewprovider/view_constraint_electricchargedensity.py b/src/Mod/Fem/femviewprovider/view_constraint_electricchargedensity.py new file mode 100644 index 0000000000..59517f2c4e --- /dev/null +++ b/src/Mod/Fem/femviewprovider/view_constraint_electricchargedensity.py @@ -0,0 +1,51 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +# *************************************************************************** +# * Copyright (c) 2025 Mario Passaglia * +# * * +# * This file is part of FreeCAD. * +# * * +# * FreeCAD is free software: you can redistribute it and/or modify it * +# * under the terms of the GNU Lesser General Public License as * +# * published by the Free Software Foundation, either version 2.1 of the * +# * License, or (at your option) any later version. * +# * * +# * FreeCAD 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 * +# * Lesser General Public License for more details. * +# * * +# * You should have received a copy of the GNU Lesser General Public * +# * License along with FreeCAD. If not, see * +# * . * +# * * +# *************************************************************************** + +__title__ = "FreeCAD FEM constraint electric charge view provider" +__author__ = "Mario Passaglia" +__url__ = "https://www.freecad.org" + +## @package view_constraint_electricchargedensity +# \ingroup FEM +# \brief view provider for the constraint electric charge density object + +from femtaskpanels import task_constraint_electricchargedensity +from . import view_base_femconstraint + + +class VPConstraintElectricChargeDensity(view_base_femconstraint.VPBaseFemConstraint): + + def __init__(self, vobj): + super().__init__(vobj) + mat = vobj.ShapeAppearance[0] + mat.DiffuseColor = (1.0, 0.0, 0.2, 0.0) + vobj.ShapeAppearance = mat + + def setEdit(self, vobj, mode=0): + return view_base_femconstraint.VPBaseFemConstraint.setEdit( + self, vobj, mode, task_constraint_electricchargedensity._TaskPanel + ) + + def attach(self, vobj): + super().attach(vobj) + vobj.loadSymbol(self.resource_symbol_dir + "ConstraintElectricChargeDensity.iv") diff --git a/src/Mod/Import/App/AppImportPy.cpp b/src/Mod/Import/App/AppImportPy.cpp index 31fd806866..7aa936f1c2 100644 --- a/src/Mod/Import/App/AppImportPy.cpp +++ b/src/Mod/Import/App/AppImportPy.cpp @@ -27,7 +27,6 @@ #ifndef _PreComp_ #include #include -#include #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wextra-semi" diff --git a/src/Mod/Import/App/ExportOCAF.cpp b/src/Mod/Import/App/ExportOCAF.cpp index b4b7ad3a14..814140ce64 100644 --- a/src/Mod/Import/App/ExportOCAF.cpp +++ b/src/Mod/Import/App/ExportOCAF.cpp @@ -36,7 +36,6 @@ #include #include #include -#include #include #include #include // for Precision::Confusion() diff --git a/src/Mod/Import/App/ExportOCAF.h b/src/Mod/Import/App/ExportOCAF.h index 847b437cda..d74ab481d9 100644 --- a/src/Mod/Import/App/ExportOCAF.h +++ b/src/Mod/Import/App/ExportOCAF.h @@ -23,7 +23,6 @@ #ifndef IMPORT_EXPORTOCAF_H #define IMPORT_EXPORTOCAF_H -#include #include #include #include diff --git a/src/Mod/Import/App/ImportOCAF.cpp b/src/Mod/Import/App/ImportOCAF.cpp index 0d155d5201..89d40ec7d7 100644 --- a/src/Mod/Import/App/ImportOCAF.cpp +++ b/src/Mod/Import/App/ImportOCAF.cpp @@ -42,7 +42,6 @@ #include #include #include -#include #include // for Precision::Confusion() #include #endif diff --git a/src/Mod/Import/App/ImportOCAF.h b/src/Mod/Import/App/ImportOCAF.h index 18ae55312a..239cdc9787 100644 --- a/src/Mod/Import/App/ImportOCAF.h +++ b/src/Mod/Import/App/ImportOCAF.h @@ -23,7 +23,6 @@ #ifndef IMPORT_IMPORTOCAF_H #define IMPORT_IMPORTOCAF_H -#include #include #include #include diff --git a/src/Mod/Import/App/ImportOCAF2.h b/src/Mod/Import/App/ImportOCAF2.h index a850e3d583..fd264eb6d5 100644 --- a/src/Mod/Import/App/ImportOCAF2.h +++ b/src/Mod/Import/App/ImportOCAF2.h @@ -23,7 +23,6 @@ #ifndef IMPORT_IMPORTOCAF2_H #define IMPORT_IMPORTOCAF2_H -#include #include #include #include diff --git a/src/Mod/Import/App/ImportOCAFAssembly.cpp b/src/Mod/Import/App/ImportOCAFAssembly.cpp index 4d60e779aa..3a904ce589 100644 --- a/src/Mod/Import/App/ImportOCAFAssembly.cpp +++ b/src/Mod/Import/App/ImportOCAFAssembly.cpp @@ -37,7 +37,6 @@ #include #include #include -#include #include #endif diff --git a/src/Mod/Import/App/ImportOCAFAssembly.h b/src/Mod/Import/App/ImportOCAFAssembly.h index ee18ab30a2..dad46e8cba 100644 --- a/src/Mod/Import/App/ImportOCAFAssembly.h +++ b/src/Mod/Import/App/ImportOCAFAssembly.h @@ -23,7 +23,6 @@ #ifndef IMPORT_ImportOCAFAssembly_H #define IMPORT_ImportOCAFAssembly_H -#include #include #include #include diff --git a/src/Mod/Import/App/Tools.h b/src/Mod/Import/App/Tools.h index 9d99cf5dae..56b704e010 100644 --- a/src/Mod/Import/App/Tools.h +++ b/src/Mod/Import/App/Tools.h @@ -41,7 +41,7 @@ struct ShapeHasher #if OCC_VERSION_HEX >= 0x070800 return std::hash {}(shape); #else - return shape.HashCode(INT_MAX); + return shape.HashCode(std::numeric_limits::max()); #endif } }; @@ -53,7 +53,7 @@ struct LabelHasher #if OCC_VERSION_HEX >= 0x070800 return std::hash {}(label); #else - return TDF_LabelMapHasher::HashCode(label, INT_MAX); + return TDF_LabelMapHasher::HashCode(label, std::numeric_limits::max()); #endif } }; diff --git a/src/Mod/Import/App/dxf/ImpExpDxf.cpp b/src/Mod/Import/App/dxf/ImpExpDxf.cpp index 13f16b3ece..913d53aae6 100644 --- a/src/Mod/Import/App/dxf/ImpExpDxf.cpp +++ b/src/Mod/Import/App/dxf/ImpExpDxf.cpp @@ -977,8 +977,8 @@ void ImpExpDxfWrite::exportEllipse(BRepAdaptor_Curve& c) // rotation appears to be the clockwise(?) angle between major & +Y?? double rotation = xaxis.AngleWithRef(gp_Dir(0, 1, 0), gp_Dir(0, 0, 1)); - // 2*M_PI = 6.28319 is invalid(doesn't display in LibreCAD), but 2PI = 6.28318 is valid! - // writeEllipse(center, major, minor, rotation, 0.0, 2 * M_PI, true ); + // 2*pi = 6.28319 is invalid(doesn't display in LibreCAD), but 2PI = 6.28318 is valid! + // writeEllipse(center, major, minor, rotation, 0.0, 2 * std::numbers::pi, true ); writeEllipse(center, major, minor, rotation, 0.0, 6.28318, true); } @@ -1036,8 +1036,8 @@ void ImpExpDxfWrite::exportEllipseArc(BRepAdaptor_Curve& c) // a > 0 ==> v2 is CCW from v1 (righthanded)? // a < 0 ==> v2 is CW from v1 (lefthanded)? - double startAngle = fmod(f, 2.0 * M_PI); // revolutions - double endAngle = fmod(l, 2.0 * M_PI); + double startAngle = fmod(f, 2.0 * std::numbers::pi); // revolutions + double endAngle = fmod(l, 2.0 * std::numbers::pi); bool endIsCW = (a < 0) ? true : false; // if !endIsCW swap(start,end) // not sure if this is a hack or not. seems to make valid arcs. if (!endIsCW) { diff --git a/src/Mod/Import/App/dxf/dxf.cpp b/src/Mod/Import/App/dxf/dxf.cpp index 54b40ad234..0799e021ba 100644 --- a/src/Mod/Import/App/dxf/dxf.cpp +++ b/src/Mod/Import/App/dxf/dxf.cpp @@ -5,8 +5,6 @@ #include "PreCompiled.h" -// required by windows for M_PI definition -#define _USE_MATH_DEFINES #include #include #include @@ -1584,10 +1582,10 @@ void CDxfWrite::writeAngularDimBlock(const double* textMidPoint, double span = fabs(endAngle - startAngle); double offset = span * 0.10; if (startAngle < 0) { - startAngle += 2 * M_PI; + startAngle += 2 * std::numbers::pi; } if (endAngle < 0) { - endAngle += 2 * M_PI; + endAngle += 2 * std::numbers::pi; } Base::Vector3d startOff(cos(startAngle + offset), sin(startAngle + offset), 0.0); Base::Vector3d endOff(cos(endAngle - offset), sin(endAngle - offset), 0.0); @@ -2130,7 +2128,7 @@ bool CDxfRead::ReadEllipse() Base::Vector3d majorAxisEnd; // relative to centre double eccentricity = 0; double startAngleRadians = 0; - double endAngleRadians = 2 * M_PI; + double endAngleRadians = 2 * std::numbers::pi; Setup3DVectorAttribute(ePrimaryPoint, centre); Setup3DVectorAttribute(ePoint2, majorAxisEnd); diff --git a/src/Mod/Import/Gui/AppImportGuiPy.cpp b/src/Mod/Import/Gui/AppImportGuiPy.cpp index 246a1dafb2..2a6143c21e 100644 --- a/src/Mod/Import/Gui/AppImportGuiPy.cpp +++ b/src/Mod/Import/Gui/AppImportGuiPy.cpp @@ -25,7 +25,6 @@ #define WNT // avoid conflict with GUID #endif #ifndef _PreComp_ -#include #include #include diff --git a/src/Mod/Inspection/App/InspectionFeature.cpp b/src/Mod/Inspection/App/InspectionFeature.cpp index bb0cdee9eb..ce6868327f 100644 --- a/src/Mod/Inspection/App/InspectionFeature.cpp +++ b/src/Mod/Inspection/App/InspectionFeature.cpp @@ -25,6 +25,7 @@ #ifndef _PreComp_ #include #include +#include #include #include @@ -316,7 +317,7 @@ InspectNominalMesh::~InspectNominalMesh() float InspectNominalMesh::getDistance(const Base::Vector3f& point) const { if (!_box.IsInBox(point)) { - return FLT_MAX; // must be inside bbox + return std::numeric_limits::max(); // must be inside bbox } std::vector indices; @@ -327,7 +328,7 @@ float InspectNominalMesh::getDistance(const Base::Vector3f& point) const indices.insert(indices.begin(), inds.begin(), inds.end()); } - float fMinDist = FLT_MAX; + float fMinDist = std::numeric_limits::max(); bool positive = true; for (unsigned long it : indices) { MeshCore::MeshGeomFacet geomFace = _mesh.GetFacet(it); @@ -393,7 +394,7 @@ InspectNominalFastMesh::~InspectNominalFastMesh() float InspectNominalFastMesh::getDistance(const Base::Vector3f& point) const { if (!_box.IsInBox(point)) { - return FLT_MAX; // must be inside bbox + return std::numeric_limits::max(); // must be inside bbox } std::set indices; @@ -413,7 +414,7 @@ float InspectNominalFastMesh::getDistance(const Base::Vector3f& point) const } #endif - float fMinDist = FLT_MAX; + float fMinDist = std::numeric_limits::max(); bool positive = true; for (unsigned long it : indices) { MeshCore::MeshGeomFacet geomFace = _mesh.GetFacet(it); @@ -457,7 +458,7 @@ float InspectNominalPoints::getDistance(const Base::Vector3f& point) const _pGrid->Position(pointd, x, y, z); _pGrid->GetElements(x, y, z, indices); - double fMinDist = DBL_MAX; + double fMinDist = std::numeric_limits::max(); for (unsigned long it : indices) { Base::Vector3d pt = _rKernel.getPoint(it); double fDist = Base::Distance(pointd, pt); @@ -501,7 +502,7 @@ float InspectNominalShape::getDistance(const Base::Vector3f& point) const BRepBuilderAPI_MakeVertex mkVert(pnt3d); distss->LoadS2(mkVert.Vertex()); - float fMinDist = FLT_MAX; + float fMinDist = std::numeric_limits::max(); if (distss->Perform() && distss->NbSolution() > 0) { fMinDist = (float)distss->Value(); // the shape is a solid, check if the vertex is inside @@ -713,7 +714,7 @@ struct DistanceInspection { Base::Vector3f pnt = actual->getPoint(index); - float fMinDist = FLT_MAX; + float fMinDist = std::numeric_limits::max(); for (auto it : nominal) { float fDist = it->getDistance(pnt); if (fabs(fDist) < fabs(fMinDist)) { @@ -722,10 +723,10 @@ struct DistanceInspection } if (fMinDist > this->radius) { - fMinDist = FLT_MAX; + fMinDist = std::numeric_limits::max(); } else if (-fMinDist > this->radius) { - fMinDist = -FLT_MAX; + fMinDist = -std::numeric_limits::max(); } return fMinDist; @@ -884,7 +885,7 @@ App::DocumentObjectExecReturn* Feature::execute() float fRMS = 0; int countRMS = 0; for (std::vector::iterator it = vals.begin(); it != vals.end(); ++it) { - if (fabs(*it) < FLT_MAX) { + if (fabs(*it) < std::numeric_limits::max()) { fRMS += (*it) * (*it); countRMS++; } @@ -904,7 +905,7 @@ App::DocumentObjectExecReturn* Feature::execute() DistanceInspectionRMS res; Base::Vector3f pnt = actual->getPoint(index); - float fMinDist = FLT_MAX; + float fMinDist = std::numeric_limits::max(); for (auto it : inspectNominal) { float fDist = it->getDistance(pnt); if (fabs(fDist) < fabs(fMinDist)) { @@ -913,10 +914,10 @@ App::DocumentObjectExecReturn* Feature::execute() } if (fMinDist > this->SearchRadius.getValue()) { - fMinDist = FLT_MAX; + fMinDist = std::numeric_limits::max(); } else if (-fMinDist > this->SearchRadius.getValue()) { - fMinDist = -FLT_MAX; + fMinDist = -std::numeric_limits::max(); } else { res.m_sumsq += fMinDist * fMinDist; diff --git a/src/Mod/Inspection/Gui/PreCompiled.h b/src/Mod/Inspection/Gui/PreCompiled.h index d6afd093c4..d304568b6a 100644 --- a/src/Mod/Inspection/Gui/PreCompiled.h +++ b/src/Mod/Inspection/Gui/PreCompiled.h @@ -35,7 +35,6 @@ #ifdef _PreComp_ // STL -#include // Inventor #include diff --git a/src/Mod/Inspection/Gui/ViewProviderInspection.cpp b/src/Mod/Inspection/Gui/ViewProviderInspection.cpp index 0ef43ac35f..243a908335 100644 --- a/src/Mod/Inspection/Gui/ViewProviderInspection.cpp +++ b/src/Mod/Inspection/Gui/ViewProviderInspection.cpp @@ -111,13 +111,26 @@ ViewProviderInspection::ViewProviderInspection() ViewProviderInspection::~ViewProviderInspection() { - pcColorRoot->unref(); - pcCoords->unref(); - pcMatBinding->unref(); - pcColorMat->unref(); - deleteColorBar(); - pcLinkRoot->unref(); - pcPointStyle->unref(); + try { + pcColorRoot->unref(); + pcCoords->unref(); + pcMatBinding->unref(); + pcColorMat->unref(); + pcLinkRoot->unref(); + pcPointStyle->unref(); + deleteColorBar(); + } + catch (Base::Exception& e) { + Base::Console().DestructorError( + "ViewProviderInspection", + "ViewProviderInspection::deleteColorBar() threw an exception: %s\n", + e.what()); + } + catch (...) { + Base::Console().DestructorError( + "ViewProviderInspection", + "ViewProviderInspection destructor threw an unknown exception"); + } } void ViewProviderInspection::onChanged(const App::Property* prop) diff --git a/src/Mod/Inspection/Gui/ViewProviderInspection.h b/src/Mod/Inspection/Gui/ViewProviderInspection.h index f311da9b45..ca3a575502 100644 --- a/src/Mod/Inspection/Gui/ViewProviderInspection.h +++ b/src/Mod/Inspection/Gui/ViewProviderInspection.h @@ -107,7 +107,7 @@ private: SoCoordinate3* pcCoords; private: - float search_radius {FLT_MAX}; + float search_radius {std::numeric_limits::max()}; static bool addflag; static App::PropertyFloatConstraint::Constraints floatRange; }; diff --git a/src/Mod/Inspection/Gui/VisualInspection.cpp b/src/Mod/Inspection/Gui/VisualInspection.cpp index afaaad9845..4170624344 100644 --- a/src/Mod/Inspection/Gui/VisualInspection.cpp +++ b/src/Mod/Inspection/Gui/VisualInspection.cpp @@ -22,7 +22,6 @@ #include "PreCompiled.h" #ifndef _PreComp_ -#include #endif #include @@ -100,9 +99,9 @@ VisualInspection::VisualInspection(QWidget* parent, Qt::WindowFlags fl) ui->textLabel2->hide(); ui->thickness->hide(); ui->searchRadius->setUnit(Base::Unit::Length); - ui->searchRadius->setRange(0, DBL_MAX); + ui->searchRadius->setRange(0, std::numeric_limits::max()); ui->thickness->setUnit(Base::Unit::Length); - ui->thickness->setRange(0, DBL_MAX); + ui->thickness->setRange(0, std::numeric_limits::max()); App::Document* doc = App::GetApplication().getActiveDocument(); // disable Ok button and enable of at least one item in each view is on diff --git a/src/Mod/Material/App/AppMaterial.cpp b/src/Mod/Material/App/AppMaterial.cpp index 556aee8b68..ddc2e16a80 100644 --- a/src/Mod/Material/App/AppMaterial.cpp +++ b/src/Mod/Material/App/AppMaterial.cpp @@ -29,15 +29,24 @@ #include -#include "MaterialFilterPy.h" #include "MaterialLoader.h" -#include "MaterialManagerPy.h" -#include "MaterialPy.h" +#include "MaterialManagerLocal.h" +#include "ModelManagerLocal.h" +#include "PropertyMaterial.h" + +#include "Array2DPy.h" +#include "Array3DPy.h" #include "ModelManagerPy.h" #include "ModelPropertyPy.h" #include "ModelPy.h" #include "UUIDsPy.h" -#include "PropertyMaterial.h" + +#include "MaterialFilterPy.h" +#include "MaterialFilterOptionsPy.h" +#include "MaterialLibraryPy.h" +#include "MaterialManagerPy.h" +#include "MaterialPropertyPy.h" +#include "MaterialPy.h" namespace Materials { @@ -74,8 +83,13 @@ PyMOD_INIT_FUNC(Materials) Base::Console().Log("Loading Material module... done\n"); - Base::Interpreter().addType(&Materials::MaterialManagerPy::Type, module, "MaterialManager"); + Base::Interpreter().addType(&Materials::Array2DPy::Type, module, "Array2D"); + Base::Interpreter().addType(&Materials::Array3DPy::Type, module, "Array3D"); Base::Interpreter().addType(&Materials::MaterialFilterPy::Type, module, "MaterialFilter"); + Base::Interpreter().addType(&Materials::MaterialFilterOptionsPy::Type, module, "MaterialFilterOptions"); + Base::Interpreter().addType(&Materials::MaterialLibraryPy::Type, module, "MaterialLibrary"); + Base::Interpreter().addType(&Materials::MaterialManagerPy::Type, module, "MaterialManager"); + Base::Interpreter().addType(&Materials::MaterialPropertyPy::Type, module, "MaterialProperty"); Base::Interpreter().addType(&Materials::MaterialPy::Type, module, "Material"); Base::Interpreter().addType(&Materials::ModelManagerPy::Type, module, "ModelManager"); Base::Interpreter().addType(&Materials::ModelPropertyPy::Type, module, "ModelProperty"); @@ -88,22 +102,25 @@ PyMOD_INIT_FUNC(Materials) Materials::Material ::init(); Materials::MaterialFilter ::init(); + Materials::MaterialFilterOptions ::init(); Materials::MaterialManager ::init(); + Materials::MaterialManagerLocal ::init(); Materials::Model ::init(); Materials::ModelManager ::init(); + Materials::ModelManagerLocal ::init(); Materials::ModelUUIDs ::init(); - Materials::LibraryBase ::init(); + Materials::Library ::init(); Materials::MaterialLibrary ::init(); + Materials::MaterialLibraryLocal ::init(); Materials::ModelLibrary ::init(); - Materials::MaterialExternalLibrary ::init(); Materials::ModelProperty ::init(); Materials::MaterialProperty ::init(); Materials::MaterialValue ::init(); - Materials::Material2DArray ::init(); - Materials::Material3DArray ::init(); + Materials::Array2D ::init(); + Materials::Array3D ::init(); Materials::PropertyMaterial ::init(); // clang-format on diff --git a/src/Mod/Material/App/Array2D.pyi b/src/Mod/Material/App/Array2D.pyi new file mode 100644 index 0000000000..7325e84490 --- /dev/null +++ b/src/Mod/Material/App/Array2D.pyi @@ -0,0 +1,53 @@ +from Base.Metadata import export +from Base.BaseClass import BaseClass +from Base.Metadata import constmethod +from typing import Final, List, Any + + +@export( + Twin="Array2D", + TwinPointer="Array2D", + Namespace="Materials", + Include="Mod/Material/App/MaterialValue.h", + Delete=True, + Constructor=True +) +class Array2D(BaseClass): + """ + 2D Array of material properties. + + Author: DavidCarter (dcarter@davidcarter.ca) + Licence: LGPL + """ + + Array: Final[List] = ... + """The 2 dimensional array.""" + + Dimensions: Final[int] = ... + """The number of dimensions in the array, in this case 2.""" + + Rows: int = ... + """The number of rows in the array.""" + + Columns: int = ... + """The number of columns in the array.""" + + @constmethod + def getRow(self, value: Any) -> Any: + """ + Get the row given the first column value + """ + ... + + @constmethod + def getValue(self, row: int, column: int) -> Any: + """ + Get the value at the given row and column + """ + ... + + def setValue(self, row: int, column: int, value: Any): + """ + Set the value at the given row and column + """ + ... diff --git a/src/Mod/Material/App/Array2DPy.xml b/src/Mod/Material/App/Array2DPy.xml deleted file mode 100644 index c9beb5580d..0000000000 --- a/src/Mod/Material/App/Array2DPy.xml +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - 2D Array of material properties. - - - - The 2 dimensional array. - - - - - - The number of rows in the array. - - - - - - The number of columns in the array. - - - - - - Get the row given the first column value - - - - - Get the value at the given row and column - - - - diff --git a/src/Mod/Material/App/Array2DPyImp.cpp b/src/Mod/Material/App/Array2DPyImp.cpp index 3b576be1ca..50d66925f4 100644 --- a/src/Mod/Material/App/Array2DPyImp.cpp +++ b/src/Mod/Material/App/Array2DPyImp.cpp @@ -44,7 +44,7 @@ using namespace Materials; std::string Array2DPy::representation() const { std::stringstream str; - str << ""; + str << ""; return str.str(); } @@ -52,7 +52,7 @@ std::string Array2DPy::representation() const PyObject* Array2DPy::PyMake(struct _typeobject*, PyObject*, PyObject*) // Python wrapper { // never create such objects with the constructor - return new Array2DPy(new Material2DArray()); + return new Array2DPy(new Array2D()); } // constructor method @@ -65,7 +65,7 @@ Py::List Array2DPy::getArray() const { Py::List list; - auto array = getMaterial2DArrayPtr()->getArray(); + auto array = getArray2DPtr()->getArray(); for (auto& row : array) { Py::List rowList; @@ -81,14 +81,29 @@ Py::List Array2DPy::getArray() const return list; } +Py::Long Array2DPy::getDimensions() const +{ + return Py::Long(2); +} + Py::Long Array2DPy::getRows() const { - return Py::Long(getMaterial2DArrayPtr()->rows()); + return Py::Long(getArray2DPtr()->rows()); +} + +void Array2DPy::setRows(Py::Long arg) +{ + getArray2DPtr()->setRows(arg); } Py::Long Array2DPy::getColumns() const { - return Py::Long(getMaterial2DArrayPtr()->columns()); + return Py::Long(getArray2DPtr()->columns()); +} + +void Array2DPy::setColumns(Py::Long arg) +{ + getArray2DPtr()->setColumns(arg); } PyObject* Array2DPy::getRow(PyObject* args) @@ -101,7 +116,7 @@ PyObject* Array2DPy::getRow(PyObject* args) try { Py::List list; - auto arrayRow = getMaterial2DArrayPtr()->getRow(row); + auto arrayRow = getArray2DPtr()->getRow(row); for (auto& column : *arrayRow) { auto quantity = new Base::QuantityPy(new Base::Quantity(column.value())); @@ -126,7 +141,7 @@ PyObject* Array2DPy::getValue(PyObject* args) } try { - auto value = getMaterial2DArrayPtr()->getValue(row, column); + auto value = getArray2DPtr()->getValue(row, column); return new Base::QuantityPy(new Base::Quantity(value.value())); } catch (const InvalidIndex&) { @@ -136,6 +151,28 @@ PyObject* Array2DPy::getValue(PyObject* args) return nullptr; } +PyObject* Array2DPy::setValue(PyObject* args) +{ + int row; + int column; + PyObject* valueObj; + if (PyArg_ParseTuple(args, "iiO!", &row, &column, &PyUnicode_Type, &valueObj)) { + Py::String item(valueObj); + try { + QVariant variant = QVariant::fromValue(Base::Quantity::parse(item.as_string())); + getArray2DPtr()->setValue(row, column, variant); + } + catch (const InvalidIndex&) { + PyErr_SetString(PyExc_IndexError, "Invalid array index"); + return nullptr; + } + Py_Return; + } + + PyErr_SetString(PyExc_TypeError, "Expected (integer, integer, string) arguments"); + return nullptr; +} + PyObject* Array2DPy::getCustomAttributes(const char* /*attr*/) const { return nullptr; diff --git a/src/Mod/Material/App/Array3D.pyi b/src/Mod/Material/App/Array3D.pyi new file mode 100644 index 0000000000..9a0d6cc5c1 --- /dev/null +++ b/src/Mod/Material/App/Array3D.pyi @@ -0,0 +1,71 @@ +from Base.Metadata import export, constmethod +from Base.BaseClass import BaseClass +from typing import Any, Final, List + + +@export( + Twin="Array3D", + TwinPointer="Array3D", + Namespace="Materials", + Include="Mod/Material/App/MaterialValue.h", + Delete=True, + Constructor=True +) +class Array3D(BaseClass): + """ + 3D Array of material properties. + + Author: DavidCarter (dcarter@davidcarter.ca) + Licence: LGPL + """ + + Array: Final[List] = ... + """The 3 dimensional array.""" + + Dimensions: Final[int] = ... + """The number of dimensions in the array, in this case 3.""" + + Columns: int = ... + """The number of columns in the array.""" + + Depth: int = ... + """The depth of the array (3rd dimension).""" + + @constmethod + def getRows(self) -> int: + """ + Get the number of rows in the array at the specified depth. + """ + ... + + @constmethod + def getValue(self) -> Any: + """ + Get the value at the given row and column + """ + ... + + @constmethod + def getDepthValue(self) -> Any: + """ + Get the column value at the given depth + """ + ... + + def setDepthValue(self, value: Any): + """ + Set the column value at the given depth + """ + ... + + def setValue(self, depth: int, row: int, column: int, value: Any): + """ + Set the value at the given depth, row, and column + """ + ... + + def setRows(self, depth: int, value: int): + """ + Set the number of rows at the given depth + """ + ... diff --git a/src/Mod/Material/App/Array3DPy.xml b/src/Mod/Material/App/Array3DPy.xml deleted file mode 100644 index 435c61e06a..0000000000 --- a/src/Mod/Material/App/Array3DPy.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - 3D Array of material properties. - - - - The 3 dimensional array. - - - - - - The number of columns in the array. - - - - - - The depth of the array (3rd dimension). - - - - - - Get the number of rows in the array at the specified depth. - - - - - Get the value at the given row and column - - - - - Get the column value at the given depth - - - - diff --git a/src/Mod/Material/App/Array3DPyImp.cpp b/src/Mod/Material/App/Array3DPyImp.cpp index bb6e57ee43..7137adbcfc 100644 --- a/src/Mod/Material/App/Array3DPyImp.cpp +++ b/src/Mod/Material/App/Array3DPyImp.cpp @@ -44,7 +44,7 @@ using namespace Materials; std::string Array3DPy::representation() const { std::stringstream str; - str << ""; + str << ""; return str.str(); } @@ -52,7 +52,7 @@ std::string Array3DPy::representation() const PyObject* Array3DPy::PyMake(struct _typeobject*, PyObject*, PyObject*) // Python wrapper { // never create such objects with the constructor - return new Array3DPy(new Material3DArray()); + return new Array3DPy(new Array3D()); } // constructor method @@ -64,7 +64,7 @@ int Array3DPy::PyInit(PyObject* /*args*/, PyObject* /*kwd*/) Py::List Array3DPy::getArray() const { Py::List list; - auto array = getMaterial3DArrayPtr()->getArray(); + auto array = getArray3DPtr()->getArray(); for (auto& depth : array) { Py::List depthList; @@ -83,24 +83,39 @@ Py::List Array3DPy::getArray() const return list; } +Py::Long Array3DPy::getDimensions() const +{ + return Py::Long(3); +} + Py::Long Array3DPy::getColumns() const { - return Py::Long(getMaterial3DArrayPtr()->columns()); + return Py::Long(getArray3DPtr()->columns()); +} + +void Array3DPy::setColumns(Py::Long arg) +{ + getArray3DPtr()->setColumns(arg); } Py::Long Array3DPy::getDepth() const { - return Py::Long(getMaterial3DArrayPtr()->depth()); + return Py::Long(getArray3DPtr()->depth()); +} + +void Array3DPy::setDepth(Py::Long arg) +{ + getArray3DPtr()->setDepth(arg); } PyObject* Array3DPy::getRows(PyObject* args) { - int depth = getMaterial3DArrayPtr()->currentDepth(); + int depth = getArray3DPtr()->currentDepth(); if (!PyArg_ParseTuple(args, "|i", &depth)) { return nullptr; } - return PyLong_FromLong(getMaterial3DArrayPtr()->rows(depth)); + return PyLong_FromLong(getArray3DPtr()->rows(depth)); } PyObject* Array3DPy::getValue(PyObject* args) @@ -113,7 +128,7 @@ PyObject* Array3DPy::getValue(PyObject* args) } try { - auto value = getMaterial3DArrayPtr()->getValue(depth, row, column); + auto value = getArray3DPtr()->getValue(depth, row, column); return new Base::QuantityPy(new Base::Quantity(value)); } catch (const InvalidIndex&) { @@ -131,7 +146,7 @@ PyObject* Array3DPy::getDepthValue(PyObject* args) } try { - auto value = getMaterial3DArrayPtr()->getDepthValue(depth); + auto value = getArray3DPtr()->getDepthValue(depth); return new Base::QuantityPy(new Base::Quantity(value)); } catch (const InvalidIndex&) { @@ -141,6 +156,60 @@ PyObject* Array3DPy::getDepthValue(PyObject* args) return nullptr; } +PyObject* Array3DPy::setDepthValue(PyObject* args) +{ + int depth; + PyObject* valueObj; + if (PyArg_ParseTuple(args, "iO!", &depth, &PyUnicode_Type, &valueObj)) { + Py::String item(valueObj); + try { + getArray3DPtr()->setDepthValue(depth, Base::Quantity::parse(item.as_string())); + } + catch (const InvalidIndex&) { + PyErr_SetString(PyExc_IndexError, "Invalid array index"); + return nullptr; + } + Py_Return; + } + + PyErr_SetString(PyExc_TypeError, "Expected (integer, string) arguments"); + return nullptr; +} + +PyObject* Array3DPy::setValue(PyObject* args) +{ + int depth; + int row; + int column; + PyObject* valueObj; + if (PyArg_ParseTuple(args, "iiiO!", &depth, &row, &column, &PyUnicode_Type, &valueObj)) { + Py::String item(valueObj); + try { + getArray3DPtr()->setValue(depth, row, column, Base::Quantity::parse(item.as_string())); + } + catch (const InvalidIndex&) { + PyErr_SetString(PyExc_IndexError, "Invalid array index"); + return nullptr; + } + Py_Return; + } + + PyErr_SetString(PyExc_TypeError, "Expected (integer, integer, integer, string) arguments"); + return nullptr; +} + +PyObject* Array3DPy::setRows(PyObject* args) +{ + int depth; + int rows; + if (!PyArg_ParseTuple(args, "ii", &depth, &rows)) { + return nullptr; + } + + getArray3DPtr()->setRows(depth, rows); + Py_Return; +} + PyObject* Array3DPy::getCustomAttributes(const char* /*attr*/) const { return nullptr; diff --git a/src/Mod/Material/App/CMakeLists.txt b/src/Mod/Material/App/CMakeLists.txt index 9775acfec5..825242fce7 100644 --- a/src/Mod/Material/App/CMakeLists.txt +++ b/src/Mod/Material/App/CMakeLists.txt @@ -42,35 +42,44 @@ else() ) endif() -generate_from_xml(Array2DPy) -generate_from_xml(Array3DPy) -generate_from_xml(MaterialFilterPy) -generate_from_xml(MaterialManagerPy) -generate_from_xml(MaterialPy) -generate_from_xml(ModelManagerPy) -generate_from_xml(ModelPropertyPy) -generate_from_xml(ModelPy) -generate_from_xml(UUIDsPy) +generate_from_py(Array2D) +generate_from_py(Array3D) +generate_from_py(MaterialFilter) +generate_from_py(MaterialFilterOptions) +generate_from_py(MaterialLibrary) +generate_from_py(MaterialManager) +generate_from_py(Material) +generate_from_py(ModelManager) +generate_from_py(ModelProperty) +generate_from_py(MaterialProperty) +generate_from_py(Model) +generate_from_py(UUIDs) SET(Python_SRCS Exceptions.h - Array2DPy.xml + Array2D.pyi Array2DPyImp.cpp - Array3DPy.xml + Array3D.pyi Array3DPyImp.cpp - MaterialManagerPy.xml - MaterialManagerPyImp.cpp - MaterialPy.xml - MaterialPyImp.cpp - MaterialFilterPy.xml + MaterialFilterOptions.pyi + MaterialFilterOptionsPyImp.cpp + MaterialFilter.pyi MaterialFilterPyImp.cpp - ModelManagerPy.xml + MaterialLibrary.pyi + MaterialLibraryPyImp.cpp + MaterialManager.pyi + MaterialManagerPyImp.cpp + MaterialProperty.pyi + MaterialPropertyPyImp.cpp + Material.pyi + MaterialPyImp.cpp + ModelManager.pyi ModelManagerPyImp.cpp - ModelPropertyPy.xml + ModelProperty.pyi ModelPropertyPyImp.cpp - ModelPy.xml + Model.pyi ModelPyImp.cpp - UUIDsPy.xml + UUIDs.pyi UUIDsPyImp.cpp ) SOURCE_GROUP("Python" FILES ${Python_SRCS}) @@ -79,6 +88,8 @@ SET(Materials_SRCS ${Python_SRCS} AppMaterial.cpp FolderTree.h + Library.cpp + Library.h MaterialConfigLoader.cpp MaterialConfigLoader.h MaterialFilter.cpp @@ -89,6 +100,8 @@ SET(Materials_SRCS MaterialLoader.h MaterialManager.cpp MaterialManager.h + MaterialManagerLocal.cpp + MaterialManagerLocal.h Materials.cpp Materials.h MaterialValue.cpp @@ -101,12 +114,16 @@ SET(Materials_SRCS ModelLoader.h ModelManager.cpp ModelManager.h + ModelManagerLocal.cpp + ModelManagerLocal.h ModelUuids.cpp ModelUuids.h PreCompiled.cpp PreCompiled.h PropertyMaterial.cpp PropertyMaterial.h + PyVariants.cpp + PyVariants.h trim.h ) diff --git a/src/Mod/Material/App/Exceptions.h b/src/Mod/Material/App/Exceptions.h index 97ca83c479..d21053b40c 100644 --- a/src/Mod/Material/App/Exceptions.h +++ b/src/Mod/Material/App/Exceptions.h @@ -34,15 +34,14 @@ class Uninitialized: public Base::Exception { public: Uninitialized() + : Base::Exception("Uninitalized") {} explicit Uninitialized(const char* msg) - { - this->setMessage(msg); - } + : Base::Exception(msg) + {} explicit Uninitialized(const QString& msg) - { - this->setMessage(msg.toStdString().c_str()); - } + : Base::Exception(msg.toStdString().c_str()) + {} ~Uninitialized() noexcept override = default; }; @@ -50,17 +49,14 @@ class ModelNotFound: public Base::Exception { public: ModelNotFound() - { - this->setMessage("Model not found"); - } + : Base::Exception("Model not found") + {} explicit ModelNotFound(const char* msg) - { - this->setMessage(msg); - } + : Base::Exception(msg) + {} explicit ModelNotFound(const QString& msg) - { - this->setMessage(msg.toStdString().c_str()); - } + : Base::Exception(msg.toStdString().c_str()) + {} ~ModelNotFound() noexcept override = default; }; @@ -68,15 +64,14 @@ class InvalidMaterialType: public Base::Exception { public: InvalidMaterialType() + : Base::Exception("Invalid material type") {} explicit InvalidMaterialType(const char* msg) - { - this->setMessage(msg); - } + : Base::Exception(msg) + {} explicit InvalidMaterialType(const QString& msg) - { - this->setMessage(msg.toStdString().c_str()); - } + : Base::Exception(msg.toStdString().c_str()) + {} ~InvalidMaterialType() noexcept override = default; }; @@ -84,17 +79,14 @@ class MaterialNotFound: public Base::Exception { public: MaterialNotFound() - { - this->setMessage("Material not found"); - } + : Base::Exception("Material not found") + {} explicit MaterialNotFound(const char* msg) - { - this->setMessage(msg); - } + : Base::Exception(msg) + {} explicit MaterialNotFound(const QString& msg) - { - this->setMessage(msg.toStdString().c_str()); - } + : Base::Exception(msg.toStdString().c_str()) + {} ~MaterialNotFound() noexcept override = default; }; @@ -102,15 +94,14 @@ class MaterialExists: public Base::Exception { public: MaterialExists() + : Base::Exception("Material already exists") {} explicit MaterialExists(const char* msg) - { - this->setMessage(msg); - } + : Base::Exception(msg) + {} explicit MaterialExists(const QString& msg) - { - this->setMessage(msg.toStdString().c_str()); - } + : Base::Exception(msg.toStdString().c_str()) + {} ~MaterialExists() noexcept override = default; }; @@ -118,15 +109,14 @@ class MaterialReadError: public Base::Exception { public: MaterialReadError() + : Base::Exception("Unable to read material") {} explicit MaterialReadError(const char* msg) - { - this->setMessage(msg); - } + : Base::Exception(msg) + {} explicit MaterialReadError(const QString& msg) - { - this->setMessage(msg.toStdString().c_str()); - } + : Base::Exception(msg.toStdString().c_str()) + {} ~MaterialReadError() noexcept override = default; }; @@ -134,17 +124,14 @@ class PropertyNotFound: public Base::Exception { public: PropertyNotFound() - { - this->setMessage("Property not found"); - } + : Base::Exception("Property not found") + {} explicit PropertyNotFound(const char* msg) - { - this->setMessage(msg); - } + : Base::Exception(msg) + {} explicit PropertyNotFound(const QString& msg) - { - this->setMessage(msg.toStdString().c_str()); - } + : Base::Exception(msg.toStdString().c_str()) + {} ~PropertyNotFound() noexcept override = default; }; @@ -152,53 +139,104 @@ class LibraryNotFound: public Base::Exception { public: LibraryNotFound() - { - this->setMessage("Library not found"); - } + : Base::Exception("Library not found") + {} explicit LibraryNotFound(const char* msg) - { - this->setMessage(msg); - } + : Base::Exception(msg) + {} explicit LibraryNotFound(const QString& msg) - { - this->setMessage(msg.toStdString().c_str()); - } + : Base::Exception(msg.toStdString().c_str()) + {} ~LibraryNotFound() noexcept override = default; }; +class CreationError: public Base::Exception +{ +public: + CreationError() + : Base::Exception("Unable to create object") + {} + explicit CreationError(const char* msg) + : Base::Exception(msg) + {} + explicit CreationError(const QString& msg) + : Base::Exception(msg.toStdString().c_str()) + {} + ~CreationError() noexcept override = default; +}; + class InvalidModel: public Base::Exception { public: InvalidModel() - { - this->setMessage("Invalid model"); - } + : Base::Exception("Invalid model") + {} explicit InvalidModel(const char* msg) - { - this->setMessage(msg); - } + : Base::Exception(msg) + {} explicit InvalidModel(const QString& msg) - { - this->setMessage(msg.toStdString().c_str()); - } + : Base::Exception(msg.toStdString().c_str()) + {} ~InvalidModel() noexcept override = default; }; +class InvalidMaterial: public Base::Exception +{ +public: + InvalidMaterial() + : Base::Exception("Invalid material") + {} + explicit InvalidMaterial(const char* msg) + : Base::Exception(msg) + {} + explicit InvalidMaterial(const QString& msg) + : Base::Exception(msg.toStdString().c_str()) + {} + ~InvalidMaterial() noexcept override = default; +}; + +class InvalidProperty: public Base::Exception +{ +public: + InvalidProperty() + : Base::Exception("Invalid property") + {} + explicit InvalidProperty(const char* msg) + : Base::Exception(msg) + {} + explicit InvalidProperty(const QString& msg) + : Base::Exception(msg.toStdString().c_str()) + {} + ~InvalidProperty() noexcept override = default; +}; + +class InvalidLibrary: public Base::Exception +{ +public: + InvalidLibrary() + : Base::Exception("Invalid library") + {} + explicit InvalidLibrary(const char* msg) + : Base::Exception(msg) + {} + explicit InvalidLibrary(const QString& msg) + : Base::Exception(msg.toStdString().c_str()) + {} + ~InvalidLibrary() noexcept override = default; +}; + class InvalidIndex: public Base::Exception { public: InvalidIndex() - { - this->setMessage("Invalid index"); - } - explicit InvalidIndex(char* msg) - { - this->setMessage(msg); - } + : Base::Exception("Invalid index") + {} + explicit InvalidIndex(const char* msg) + : Base::Exception(msg) + {} explicit InvalidIndex(const QString& msg) - { - this->setMessage(msg.toStdString().c_str()); - } + : Base::Exception(msg.toStdString().c_str()) + {} ~InvalidIndex() noexcept override = default; }; @@ -206,15 +244,14 @@ class UnknownValueType: public Base::Exception { public: UnknownValueType() + : Base::Exception("Unkown value type") + {} + explicit UnknownValueType(const char* msg) + : Base::Exception(msg) {} - explicit UnknownValueType(char* msg) - { - this->setMessage(msg); - } explicit UnknownValueType(const QString& msg) - { - this->setMessage(msg.toStdString().c_str()); - } + : Base::Exception(msg.toStdString().c_str()) + {} ~UnknownValueType() noexcept override = default; }; @@ -222,18 +259,62 @@ class DeleteError: public Base::Exception { public: DeleteError() + : Base::Exception("Unable to delete object") + {} + explicit DeleteError(const char* msg) + : Base::Exception(msg) {} - explicit DeleteError(char* msg) - { - this->setMessage(msg); - } explicit DeleteError(const QString& msg) - { - this->setMessage(msg.toStdString().c_str()); - } + : Base::Exception(msg.toStdString().c_str()) + {} ~DeleteError() noexcept override = default; }; +class RenameError: public Base::Exception +{ +public: + RenameError() + : Base::Exception("Unable to rename object") + {} + explicit RenameError(const char* msg) + : Base::Exception(msg) + {} + explicit RenameError(const QString& msg) + : Base::Exception(msg.toStdString().c_str()) + {} + ~RenameError() noexcept override = default; +}; + +class ReplacementError: public Base::Exception +{ +public: + ReplacementError() + : Base::Exception("Unable to replace object") + {} + explicit ReplacementError(const char* msg) + : Base::Exception(msg) + {} + explicit ReplacementError(const QString& msg) + : Base::Exception(msg.toStdString().c_str()) + {} + ~ReplacementError() noexcept override = default; +}; + +class ConnectionError: public Base::Exception +{ +public: + ConnectionError() + : Base::Exception("Unable to connect") + {} + explicit ConnectionError(const char* msg) + : Base::Exception(msg) + {} + explicit ConnectionError(const QString& msg) + : Base::Exception(msg.toStdString().c_str()) + {} + ~ConnectionError() noexcept override = default; +}; + } // namespace Materials #endif // MATERIAL_EXCEPTIONS_H diff --git a/src/Mod/Material/App/FolderTree.h b/src/Mod/Material/App/FolderTree.h index e2c450753c..1482016b9a 100644 --- a/src/Mod/Material/App/FolderTree.h +++ b/src/Mod/Material/App/FolderTree.h @@ -34,18 +34,21 @@ template class FolderTreeNode { public: - enum NodeType + enum class NodeType { + UnknownNode, DataNode, FolderNode }; FolderTreeNode() + : _type(NodeType::UnknownNode) {} virtual ~FolderTreeNode() = default; NodeType getType() const { + // assert(_type == NodeType::DataNode || _type == NodeType::FolderNode); return _type; } void setType(NodeType type) @@ -53,33 +56,47 @@ public: _type = type; } - const std::shared_ptr>>> getFolder() const + std::shared_ptr>>> getFolder() const { + assert(_type == NodeType::FolderNode); return _folder; } std::shared_ptr>>> getFolder() { + assert(_type == NodeType::FolderNode); return _folder; } std::shared_ptr getData() const { + assert(_type == NodeType::DataNode); return _data; } + QString getUUID() const + { + assert(_type == NodeType::DataNode); + return _uuid; + } void setFolder(std::shared_ptr>>> folder) { - setType(FolderNode); + setType(NodeType::FolderNode); _folder = folder; } void setData(std::shared_ptr data) { - setType(DataNode); + setType(NodeType::DataNode); _data = data; } + void setUUID(const QString& uuid) + { + setType(NodeType::DataNode); + _uuid = uuid; + } private: NodeType _type; std::shared_ptr>>> _folder; + QString _uuid; std::shared_ptr _data; }; diff --git a/src/Mod/Material/App/Library.cpp b/src/Mod/Material/App/Library.cpp new file mode 100644 index 0000000000..57bbd50480 --- /dev/null +++ b/src/Mod/Material/App/Library.cpp @@ -0,0 +1,141 @@ +/*************************************************************************** + * Copyright (c) 2023 David Carter * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + +#include "PreCompiled.h" +#ifndef _PreComp_ +#include +#endif + +#include + +#include "Exceptions.h" +#include "Library.h" + + +using namespace Materials; + +TYPESYSTEM_SOURCE(Materials::Library, Base::BaseClass) + +Library::Library(const QString& libraryName, const QString& icon, bool readOnly) + : _name(libraryName) + , _iconPath(icon) + , _readOnly(readOnly) +{} + +Library::Library(const QString& libraryName, const QString& dir, const QString& icon, bool readOnly) + : _name(libraryName) + , _directory(QDir::cleanPath(dir)) + , _iconPath(icon) + , _readOnly(readOnly) +{} + +bool Library::operator==(const Library& library) const +{ + return (getName() == library.getName()) && (_directory == library._directory); +} + +void Library::validate(const Library& remote) const +{ + if (getName() != remote.getName()) { + throw InvalidLibrary("Library names don't match"); + } + if (getIconPath() != remote.getIconPath()) { + Base::Console().Log("Icon path 1 '%s'\n", getIconPath().toStdString().c_str()); + Base::Console().Log("Icon path 2 '%s'\n", remote.getIconPath().toStdString().c_str()); + throw InvalidLibrary("Library icon paths don't match"); + } + + // Local and remote paths will differ + if (!remote.getDirectory().isEmpty()) { + throw InvalidLibrary("Remote library should not have a path"); + } + + if (isReadOnly() != remote.isReadOnly()) { + throw InvalidLibrary("Library readonly settings don't match"); + } +} + +QString Library::getLocalPath(const QString& path) const +{ + QString filePath = getDirectoryPath(); + if (!(filePath.endsWith(QStringLiteral("/")) || filePath.endsWith(QStringLiteral("\\")))) { + filePath += QStringLiteral("/"); + } + + QString cleanPath = QDir::cleanPath(path); + QString prefix = QStringLiteral("/") + getName(); + if (cleanPath.startsWith(prefix)) { + // Remove the library name from the path + filePath += cleanPath.right(cleanPath.length() - prefix.length()); + } + else { + filePath += cleanPath; + } + + return filePath; +} + +bool Library::isRoot(const QString& path) const +{ + QString localPath = getLocalPath(path); + QString cleanPath = getLocalPath(QStringLiteral("")); + return (cleanPath == localPath); +} + +QString Library::getRelativePath(const QString& path) const +{ + QString filePath; + QString cleanPath = QDir::cleanPath(path); + QString prefix = QStringLiteral("/") + getName(); + if (cleanPath.startsWith(prefix)) { + // Remove the library name from the path + filePath = cleanPath.right(cleanPath.length() - prefix.length()); + } + else { + filePath = cleanPath; + } + + prefix = getDirectoryPath(); + if (filePath.startsWith(prefix)) { + // Remove the library root from the path + filePath = filePath.right(filePath.length() - prefix.length()); + } + + // Remove any leading '/' + if (filePath.startsWith(QStringLiteral("/"))) { + filePath.remove(0, 1); + } + + return filePath; +} + +QString Library::getLibraryPath(const QString& path, const QString& filename) const +{ + QString filePath(path); + if (filePath.endsWith(filename)) { + filePath = filePath.left(filePath.length() - filename.length()); + } + if (filePath.endsWith(QStringLiteral("/"))) { + filePath = filePath.left(filePath.length() - 1); + } + + return filePath; +} diff --git a/src/Mod/Material/App/Library.h b/src/Mod/Material/App/Library.h new file mode 100644 index 0000000000..b2249a91a8 --- /dev/null +++ b/src/Mod/Material/App/Library.h @@ -0,0 +1,114 @@ +/*************************************************************************** + * Copyright (c) 2023 David Carter * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + +#ifndef MATERIAL_LIBRARY_H +#define MATERIAL_LIBRARY_H + +#include +#include + +#include + +#include + +namespace Materials +{ + +class MaterialsExport Library: public Base::BaseClass +{ + TYPESYSTEM_HEADER_WITH_OVERRIDE(); + +public: + Library() = default; + Library(const QString& libraryName, const QString& icon, bool readOnly = true); + Library(const QString& libraryName, + const QString& dir, + const QString& icon, + bool readOnly = true); + ~Library() override = default; + + QString getName() const + { + return _name; + } + void setName(const QString& newName) + { + _name = newName; + } + bool sameName(const QString& name) + { + return (_name == name); + } + + QString getIconPath() const + { + return _iconPath; + } + void setIconPath(const QString& icon) + { + _iconPath = icon; + } + bool isReadOnly() const + { + return _readOnly; + } + void setReadOnly(bool readOnly) + { + _readOnly = readOnly; + } + + QString getDirectory() const + { + return _directory; + } + void setDirectory(const QString& directory) + { + _directory = directory; + } + QString getDirectoryPath() const + { + return QDir(_directory).absolutePath(); + } + + bool operator==(const Library& library) const; + bool operator!=(const Library& library) const + { + return !operator==(library); + } + + QString getLocalPath(const QString& path) const; + QString getRelativePath(const QString& path) const; + QString getLibraryPath(const QString& path, const QString& filename) const; + bool isRoot(const QString& path) const; + + // Validate a remote library against this one (a local library) + void validate(const Library& remote) const; + +private: + QString _name; + QString _directory; + QString _iconPath; + bool _readOnly; +}; + +} // namespace Materials + +#endif // MATERIAL_LIBRARY_H diff --git a/src/Mod/Material/App/Material.pyi b/src/Mod/Material/App/Material.pyi new file mode 100644 index 0000000000..46c1b75c0f --- /dev/null +++ b/src/Mod/Material/App/Material.pyi @@ -0,0 +1,156 @@ +from Base.Metadata import export, no_args, sequence_protocol +from Base.BaseClass import BaseClass +from typing import Final, Dict + + +@export( + Include="Mod/Material/App/Materials.h", + Namespace="Materials", + Constructor=True, + Delete=True, +) +@sequence_protocol(sq_length=True, sq_item=True, sq_contains=True, mp_subscript=True) +class Material(BaseClass): + """ + Material descriptions. + + Author: David Carter (dcarter@davidcarter.ca) + Licence: LGPL + """ + + LibraryName: Final[str] = ... + """Model library name.""" + + LibraryRoot: Final[str] = ... + """Model library path.""" + + LibraryIcon: Final[str] = ... + """Model icon path.""" + + Name: str = ... + """Model name.""" + + Directory: str = ... + """Model directory relative to the library root.""" + + UUID: Final[str] = ... + """Unique model identifier. This is only valid after the material is saved.""" + + Description: str = ... + """Description of the material.""" + + URL: str = ... + """URL to a material reference.""" + + Reference: str = ... + """Reference for material data.""" + + Parent: str = ... + """Parent material UUID.""" + + AuthorAndLicense: Final[str] = ... + """deprecated -- Author and license information.""" + + Author: str = ... + """Author information.""" + + License: str = ... + """License information.""" + + PhysicalModels: Final[list] = ... + """List of implemented models.""" + + AppearanceModels: Final[list] = ... + """List of implemented models.""" + + Tags: Final[list] = ... + """List of searchable tags.""" + + Properties: Final[dict] = ... + """deprecated -- Dictionary of all material properties.""" + + PhysicalProperties: Final[dict] = ... + """deprecated -- Dictionary of material physical properties.""" + + AppearanceProperties: Final[dict] = ... + """deprecated -- Dictionary of material appearance properties.""" + + LegacyProperties: Final[dict] = ... + """deprecated -- Dictionary of material legacy properties.""" + + PropertyObjects: Final[dict] = ... + """Dictionary of MaterialProperty objects.""" + + def addPhysicalModel(self) -> None: + """Add the physical model with the given UUID""" + ... + + def removePhysicalModel(self) -> None: + """Remove the physical model with the given UUID""" + ... + + def hasPhysicalModel(self) -> bool: + """Check if the material implements the physical model with the given UUID""" + ... + + def addAppearanceModel(self) -> None: + """Add the appearance model with the given UUID""" + ... + + def removeAppearanceModel(self) -> None: + """Remove the appearance model with the given UUID""" + ... + + def hasAppearanceModel(self) -> bool: + """Check if the material implements the appearance model with the given UUID""" + ... + + def isPhysicalModelComplete(self) -> bool: + """Check if the material implements the physical model with the given UUID, and has values defined for each property""" + ... + + def isAppearanceModelComplete(self) -> bool: + """Check if the material implements the appearance model with the given UUID, and has values defined for each property""" + ... + + def hasPhysicalProperty(self) -> bool: + """Check if the material implements the physical property with the given name""" + ... + + def hasAppearanceProperty(self) -> bool: + """Check if the material implements the appearance property with the given name""" + ... + + def hasLegacyProperties(self) -> bool: + """Returns true of there are legacy properties""" + ... + + def getPhysicalValue(self) -> str: + """Get the value associated with the property""" + ... + + def setPhysicalValue(self) -> None: + """Set the value associated with the property""" + ... + + def getAppearanceValue(self) -> str: + """Get the value associated with the property""" + ... + + def setAppearanceValue(self) -> None: + """Set the value associated with the property""" + ... + + def setValue(self) -> None: + """Set the value associated with the property""" + ... + + @no_args + def keys(self) -> list: + """Property keys""" + ... + + @no_args + def values(self) -> list: + """Property values""" + ... diff --git a/src/Mod/Material/App/MaterialConfigLoader.cpp b/src/Mod/Material/App/MaterialConfigLoader.cpp index 0aa582e1cf..cd241acf18 100644 --- a/src/Mod/Material/App/MaterialConfigLoader.cpp +++ b/src/Mod/Material/App/MaterialConfigLoader.cpp @@ -1022,7 +1022,7 @@ void MaterialConfigLoader::addLegacy(const QMap& fcmat, { for (auto const& legacy : fcmat.keys()) { auto name = legacy; - int last = name.lastIndexOf(QLatin1String("/")); + int last = name.lastIndexOf(QStringLiteral("/")); if (last > 0) { name = name.mid(last + 1); } @@ -1034,7 +1034,7 @@ void MaterialConfigLoader::addLegacy(const QMap& fcmat, } std::shared_ptr -MaterialConfigLoader::getMaterialFromPath(const std::shared_ptr& library, +MaterialConfigLoader::getMaterialFromPath(const std::shared_ptr& library, const QString& path) { QString author = getAuthorAndLicense(path); // Place them both in the author field @@ -1056,7 +1056,10 @@ MaterialConfigLoader::getMaterialFromPath(const std::shared_ptr QString sourceReference = value(fcmat, "ReferenceSource", ""); QString sourceURL = value(fcmat, "SourceURL", ""); - std::shared_ptr finalModel = std::make_shared(library, path, uuid, name); + auto baseLibrary = + reinterpret_cast&>(library); + std::shared_ptr finalModel = + std::make_shared(baseLibrary, path, uuid, name); finalModel->setOldFormat(true); finalModel->setAuthor(author); diff --git a/src/Mod/Material/App/MaterialConfigLoader.h b/src/Mod/Material/App/MaterialConfigLoader.h index cf2b2ab3ff..171d8a36a2 100644 --- a/src/Mod/Material/App/MaterialConfigLoader.h +++ b/src/Mod/Material/App/MaterialConfigLoader.h @@ -36,6 +36,8 @@ namespace Materials { +class MaterialLibraryLocal; + class MaterialConfigLoader { public: @@ -45,7 +47,7 @@ public: static bool isConfigStyle(const QString& path); static std::shared_ptr - getMaterialFromPath(const std::shared_ptr& library, const QString& path); + getMaterialFromPath(const std::shared_ptr& library, const QString& path); private: static QString value(const QMap& fcmat, diff --git a/src/Mod/Material/App/MaterialFilter.cpp b/src/Mod/Material/App/MaterialFilter.cpp index e01654a696..a1284a7151 100644 --- a/src/Mod/Material/App/MaterialFilter.cpp +++ b/src/Mod/Material/App/MaterialFilter.cpp @@ -33,6 +33,8 @@ using namespace Materials; +TYPESYSTEM_SOURCE(Materials::MaterialFilterOptions, Base::BaseClass) + MaterialFilterOptions::MaterialFilterOptions() { auto param = App::GetApplication().GetParameterGroupByPath( @@ -94,9 +96,8 @@ bool MaterialFilter::modelIncluded(const std::shared_ptr& material) co bool MaterialFilter::modelIncluded(const QString& uuid) const { - MaterialManager manager; try { - auto material = manager.getMaterial(uuid); + auto material = MaterialManager::getManager().getMaterial(uuid); return modelIncluded(material); } catch (const MaterialNotFound&) { diff --git a/src/Mod/Material/App/MaterialFilter.h b/src/Mod/Material/App/MaterialFilter.h index 7645d75971..c25612bd2c 100644 --- a/src/Mod/Material/App/MaterialFilter.h +++ b/src/Mod/Material/App/MaterialFilter.h @@ -41,8 +41,9 @@ class Material; * This class is used to set options for a material tree search * */ -class MaterialsExport MaterialFilterOptions +class MaterialsExport MaterialFilterOptions: public Base::BaseClass { + TYPESYSTEM_HEADER_WITH_OVERRIDE(); public: MaterialFilterOptions(); diff --git a/src/Mod/Material/App/MaterialFilter.pyi b/src/Mod/Material/App/MaterialFilter.pyi new file mode 100644 index 0000000000..f131306a4c --- /dev/null +++ b/src/Mod/Material/App/MaterialFilter.pyi @@ -0,0 +1,27 @@ +from Base.Metadata import export, constmethod +from Base.BaseClass import BaseClass +from typing import List + + +@export( + Include="Mod/Material/App/MaterialFilter.h", + Namespace="Materials", + Constructor=True, + Delete=True, +) +class MaterialFilter(BaseClass): + """ + Material filters. + + Author: DavidCarter (dcarter@davidcarter.ca) + Licence: LGPL + """ + + Name: str = ... + """Name of the filter used to select a filter in a list""" + + RequiredModels: List = ... + """Materials must include the specified models.""" + + RequiredCompleteModels: List = ... + """Materials must have complete versions of the specified models.""" \ No newline at end of file diff --git a/src/Mod/Material/App/MaterialFilterOptions.pyi b/src/Mod/Material/App/MaterialFilterOptions.pyi new file mode 100644 index 0000000000..d3b6a5bd8e --- /dev/null +++ b/src/Mod/Material/App/MaterialFilterOptions.pyi @@ -0,0 +1,32 @@ +from Base.Metadata import export +from Base.BaseClass import BaseClass + + +@export( + Include="Mod/Material/App/MaterialFilter.h", + Namespace="Materials", + Constructor=True, + Delete=True +) +class MaterialFilterOptions(BaseClass): + """ + Material filtering options. + + Author: DavidCarter (dcarter@davidcarter.ca) + Licence: LGPL + """ + + IncludeFavorites: bool = ... + """Include materials marked as favorite.""" + + IncludeRecent: bool = ... + """Include recently used materials.""" + + IncludeEmptyFolders: bool = ... + """Include empty folders.""" + + IncludeEmptyLibraries: bool = ... + """Include empty libraries.""" + + IncludeLegacy: bool = ... + """Include materials using the older legacy format.""" diff --git a/src/Mod/Material/App/MaterialFilterOptionsPyImp.cpp b/src/Mod/Material/App/MaterialFilterOptionsPyImp.cpp new file mode 100644 index 0000000000..3e7f21b6d5 --- /dev/null +++ b/src/Mod/Material/App/MaterialFilterOptionsPyImp.cpp @@ -0,0 +1,122 @@ +/*************************************************************************** + * Copyright (c) 2023-2024 David Carter * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + +#include "PreCompiled.h" + +#include + +#include +#include +#include +#include + +#include "MaterialFilter.h" + +#include "MaterialFilterOptionsPy.h" + +#include "MaterialFilterOptionsPy.cpp" + +using namespace Materials; + +// Forward declaration +// static PyObject* _pyObjectFromVariant(const QVariant& value); +// static Py::List getList(const QVariant& value); + +// returns a string which represents the object e.g. when printed in python +std::string MaterialFilterOptionsPy::representation() const +{ + std::stringstream str; + str << ""; + + return str.str(); +} + +PyObject* MaterialFilterOptionsPy::PyMake(struct _typeobject*, PyObject*, PyObject*) // Python wrapper +{ + // never create such objects with the constructor + return new MaterialFilterOptionsPy(new MaterialFilterOptions()); +} + +// constructor method +int MaterialFilterOptionsPy::PyInit(PyObject* /*args*/, PyObject* /*kwd*/) +{ + return 0; +} + +Py::Boolean MaterialFilterOptionsPy::getIncludeFavorites() const +{ + return getMaterialFilterOptionsPtr()->includeFavorites(); +} + +void MaterialFilterOptionsPy::setIncludeFavorites(const Py::Boolean value) +{ + getMaterialFilterOptionsPtr()->setIncludeFavorites(value); +} + +Py::Boolean MaterialFilterOptionsPy::getIncludeRecent() const +{ + return getMaterialFilterOptionsPtr()->includeRecent(); +} + +void MaterialFilterOptionsPy::setIncludeRecent(const Py::Boolean value) +{ + getMaterialFilterOptionsPtr()->setIncludeRecent(value); +} + +Py::Boolean MaterialFilterOptionsPy::getIncludeEmptyFolders() const +{ + return getMaterialFilterOptionsPtr()->includeEmptyFolders(); +} + +void MaterialFilterOptionsPy::setIncludeEmptyFolders(const Py::Boolean value) +{ + getMaterialFilterOptionsPtr()->setIncludeEmptyFolders(value); +} + +Py::Boolean MaterialFilterOptionsPy::getIncludeEmptyLibraries() const +{ + return getMaterialFilterOptionsPtr()->includeEmptyLibraries(); +} + +void MaterialFilterOptionsPy::setIncludeEmptyLibraries(const Py::Boolean value) +{ + getMaterialFilterOptionsPtr()->setIncludeEmptyLibraries(value); +} + +Py::Boolean MaterialFilterOptionsPy::getIncludeLegacy() const +{ + return getMaterialFilterOptionsPtr()->includeLegacy(); +} + +void MaterialFilterOptionsPy::setIncludeLegacy(const Py::Boolean value) +{ + getMaterialFilterOptionsPtr()->setIncludeLegacy(value); +} + +PyObject* MaterialFilterOptionsPy::getCustomAttributes(const char* /*attr*/) const +{ + return nullptr; +} + +int MaterialFilterOptionsPy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/) +{ + return 0; +} diff --git a/src/Mod/Material/App/MaterialFilterPy.xml b/src/Mod/Material/App/MaterialFilterPy.xml deleted file mode 100644 index ac7825ff31..0000000000 --- a/src/Mod/Material/App/MaterialFilterPy.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - Material filters. - - - - Name of the filter used to select a filter in a list - - - - - - Materials must include the specified models. - - - - - - Materials must have complete versions of the specified models. - - - - - diff --git a/src/Mod/Material/App/MaterialLibrary.cpp b/src/Mod/Material/App/MaterialLibrary.cpp index 1aafe2ab00..14446afeb0 100644 --- a/src/Mod/Material/App/MaterialLibrary.cpp +++ b/src/Mod/Material/App/MaterialLibrary.cpp @@ -41,18 +41,121 @@ using namespace Materials; /* TRANSLATOR Material::Materials */ -TYPESYSTEM_SOURCE(Materials::MaterialLibrary, Materials::LibraryBase) +TYPESYSTEM_SOURCE(Materials::MaterialLibrary, Base::BaseClass) + +MaterialLibrary::MaterialLibrary(const QString& libraryName, const QString& icon, bool readOnly) + : Library(libraryName, icon, readOnly) + , _local(false) +{} MaterialLibrary::MaterialLibrary(const QString& libraryName, const QString& dir, const QString& icon, bool readOnly) - : LibraryBase(libraryName, dir, icon) - , _readOnly(readOnly) - , _materialPathMap(std::make_unique>>()) + : Library(libraryName, dir, icon, readOnly) + , _local(false) {} -void MaterialLibrary::createFolder(const QString& path) +bool MaterialLibrary::isLocal() const +{ + return _local; +} + +void MaterialLibrary::setLocal(bool local) +{ + _local = local; +} + +std::shared_ptr>> +MaterialLibrary::getMaterialTree(const std::shared_ptr& filter, + const Materials::MaterialFilterOptions& options) const +{ + std::shared_ptr>> materialTree = + std::make_shared>>(); + + auto materials = MaterialManager::getManager().libraryMaterials(getName(), filter, options); + for (auto& it : *materials) { + auto uuid = std::get<0>(it); + auto path = std::get<1>(it); + auto filename = std::get<2>(it); + + QStringList list = path.split(QStringLiteral("/")); + + // Start at the root + std::shared_ptr>> node = + materialTree; + for (auto& itp : list) { + if (!itp.isEmpty()) { + // Add the folder only if it's not already there + if (node->count(itp) == 0) { + auto mapPtr = std::make_shared< + std::map>>(); + std::shared_ptr child = + std::make_shared(); + child->setFolder(mapPtr); + (*node)[itp] = child; + node = mapPtr; + } + else { + node = (*node)[itp]->getFolder(); + } + } + } + std::shared_ptr child = std::make_shared(); + child->setUUID(uuid); + (*node)[filename] = child; + } + + // // Empty folders aren't included in _materialPathMap, so we add them by looking at the file + // // system + // if (!filter || options.includeEmptyFolders()) { + // if (isLocal()) { + // auto& materialLibrary = + // *(reinterpret_cast(this)); + // auto folderList = MaterialLoader::getMaterialFolders(materialLibrary); + // for (auto& folder : *folderList) { + // QStringList list = folder.split(QStringLiteral("/")); + + // // Start at the root + // auto node = materialTree; + // for (auto& itp : list) { + // // Add the folder only if it's not already there + // if (node->count(itp) == 0) { + // std::shared_ptr>> + // mapPtr = std::make_shared< + // std::map>>(); + // std::shared_ptr child = + // std::make_shared(); + // child->setFolder(mapPtr); + // (*node)[itp] = child; + // node = mapPtr; + // } + // else { + // node = (*node)[itp]->getFolder(); + // } + // } + // } + // } + // } + + return materialTree; +} + +/* TRANSLATOR Material::Materials */ + +TYPESYSTEM_SOURCE(Materials::MaterialLibraryLocal, Materials::MaterialLibrary) + +MaterialLibraryLocal::MaterialLibraryLocal(const QString& libraryName, + const QString& dir, + const QString& icon, + bool readOnly) + : MaterialLibrary(libraryName, dir, icon, readOnly) + , _materialPathMap(std::make_unique>>()) +{ + setLocal(true); +} + +void MaterialLibraryLocal::createFolder(const QString& path) { QString filePath = getLocalPath(path); @@ -65,8 +168,42 @@ void MaterialLibrary::createFolder(const QString& path) } } +void MaterialLibraryLocal::renameFolder(const QString& oldPath, const QString& newPath) +{ + QString filePath = getLocalPath(oldPath); + QString newFilePath = getLocalPath(newPath); + + QDir fileDir(filePath); + if (fileDir.exists()) { + if (!fileDir.rename(filePath, newFilePath)) { + Base::Console().Error("Unable to rename directory path '%s'\n", + filePath.toStdString().c_str()); + } + } + + updatePaths(oldPath, newPath); +} + +void MaterialLibraryLocal::deleteRecursive(const QString& path) +{ + if (isRoot(path)) { + return; + } + + QString filePath = getLocalPath(path); + auto manager = MaterialManager::getManager(); + + QFileInfo info(filePath); + if (info.isDir()) { + deleteDir(manager, filePath); + } + else { + deleteFile(manager, filePath); + } +} + // This accepts the filesystem path as returned from getLocalPath -void MaterialLibrary::deleteDir(MaterialManager& manager, const QString& path) +void MaterialLibraryLocal::deleteDir(MaterialManager& manager, const QString& path) { // Remove the children first QDirIterator it(path, QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot); @@ -105,7 +242,7 @@ void MaterialLibrary::deleteDir(MaterialManager& manager, const QString& path) } // This accepts the filesystem path as returned from getLocalPath -void MaterialLibrary::deleteFile(MaterialManager& manager, const QString& path) +void MaterialLibraryLocal::deleteFile(MaterialManager& manager, const QString& path) { if (QFile::remove(path)) { // Remove from the map @@ -125,25 +262,7 @@ void MaterialLibrary::deleteFile(MaterialManager& manager, const QString& path) } } -void MaterialLibrary::deleteRecursive(const QString& path) -{ - if (isRoot(path)) { - return; - } - - QString filePath = getLocalPath(path); - MaterialManager manager; - - QFileInfo info(filePath); - if (info.isDir()) { - deleteDir(manager, filePath); - } - else { - deleteFile(manager, filePath); - } -} - -void MaterialLibrary::updatePaths(const QString& oldPath, const QString& newPath) +void MaterialLibraryLocal::updatePaths(const QString& oldPath, const QString& newPath) { // Update the path map QString op = getRelativePath(oldPath); @@ -162,27 +281,12 @@ void MaterialLibrary::updatePaths(const QString& oldPath, const QString& newPath _materialPathMap = std::move(pathMap); } -void MaterialLibrary::renameFolder(const QString& oldPath, const QString& newPath) -{ - QString filePath = getLocalPath(oldPath); - QString newFilePath = getLocalPath(newPath); - - QDir fileDir(filePath); - if (fileDir.exists()) { - if (!fileDir.rename(filePath, newFilePath)) { - Base::Console().Error("Unable to rename directory path '%s'\n", - filePath.toStdString().c_str()); - } - } - - updatePaths(oldPath, newPath); -} - -std::shared_ptr MaterialLibrary::saveMaterial(const std::shared_ptr& material, - const QString& path, - bool overwrite, - bool saveAsCopy, - bool saveInherited) +std::shared_ptr +MaterialLibraryLocal::saveMaterial(const std::shared_ptr& material, + const QString& path, + bool overwrite, + bool saveAsCopy, + bool saveInherited) { QString filePath = getLocalPath(path); QFile file(filePath); @@ -220,7 +324,7 @@ std::shared_ptr MaterialLibrary::saveMaterial(const std::shared_ptr MaterialLibrary::addMaterial(const std::shared_ptr& material, - const QString& path) +std::shared_ptr +MaterialLibraryLocal::addMaterial(const std::shared_ptr& material, const QString& path) { QString filePath = getRelativePath(path); + QFileInfo info(filePath); std::shared_ptr newMaterial = std::make_shared(*material); newMaterial->setLibrary(getptr()); - newMaterial->setDirectory(filePath); + newMaterial->setDirectory(getLibraryPath(filePath, info.fileName())); + newMaterial->setFilename(info.fileName()); (*_materialPathMap)[filePath] = newMaterial; return newMaterial; } -std::shared_ptr MaterialLibrary::getMaterialByPath(const QString& path) const +std::shared_ptr MaterialLibraryLocal::getMaterialByPath(const QString& path) const { QString filePath = getRelativePath(path); - try { - auto material = _materialPathMap->at(filePath); - return material; - } - catch (std::out_of_range&) { - throw MaterialNotFound(); + + auto search = _materialPathMap->find(filePath); + if (search != _materialPathMap->end()) { + return search->second; } + + throw MaterialNotFound(); } -QString MaterialLibrary::getUUIDFromPath(const QString& path) const +QString MaterialLibraryLocal::getUUIDFromPath(const QString& path) const { QString filePath = getRelativePath(path); - try { - auto material = _materialPathMap->at(filePath); - return material->getUUID(); - } - catch (std::out_of_range&) { - throw MaterialNotFound(); + + auto search = _materialPathMap->find(filePath); + if (search != _materialPathMap->end()) { + return search->second->getUUID(); } + + throw MaterialNotFound(); } - -bool MaterialLibrary::materialInTree(const std::shared_ptr& material, - const std::shared_ptr& filter, - const Materials::MaterialFilterOptions& options) const -{ - if (!filter) { - // If there's no filter we always include - return true; - } - - // filter out old format files - if (material->isOldFormat() && !options.includeLegacy()) { - return false; - } - - // filter based on models - return filter->modelIncluded(material); -} - -std::shared_ptr>> -MaterialLibrary::getMaterialTree(const std::shared_ptr& filter, - const Materials::MaterialFilterOptions& options) const -{ - std::shared_ptr>> materialTree = - std::make_shared>>(); - - for (auto& it : *_materialPathMap) { - auto filename = it.first; - auto material = it.second; - - if (materialInTree(material, filter, options)) { - QStringList list = filename.split(QStringLiteral("/")); - - // Start at the root - std::shared_ptr>> node = - materialTree; - for (auto& itp : list) { - if (itp.endsWith(QStringLiteral(".FCMat"))) { - std::shared_ptr child = std::make_shared(); - child->setData(material); - (*node)[itp] = child; - } - else { - // Add the folder only if it's not already there - if (node->count(itp) == 0) { - auto mapPtr = std::make_shared< - std::map>>(); - std::shared_ptr child = - std::make_shared(); - child->setFolder(mapPtr); - (*node)[itp] = child; - node = mapPtr; - } - else { - node = (*node)[itp]->getFolder(); - } - } - } - } - } - - // Empty folders aren't included in _materialPathMap, so we add them by looking at the file - // system - if (!filter || options.includeEmptyFolders()) { - auto folderList = MaterialLoader::getMaterialFolders(*this); - for (auto& folder : *folderList) { - QStringList list = folder.split(QStringLiteral("/")); - - // Start at the root - auto node = materialTree; - for (auto& itp : list) { - // Add the folder only if it's not already there - if (node->count(itp) == 0) { - std::shared_ptr>> mapPtr = - std::make_shared>>(); - std::shared_ptr child = std::make_shared(); - child->setFolder(mapPtr); - (*node)[itp] = child; - node = mapPtr; - } - else { - node = (*node)[itp]->getFolder(); - } - } - } - } - - return materialTree; -} - -TYPESYSTEM_SOURCE(Materials::MaterialExternalLibrary, Materials::MaterialLibrary) - -MaterialExternalLibrary::MaterialExternalLibrary(const QString& libraryName, - const QString& dir, - const QString& icon, - bool readOnly) - : MaterialLibrary(libraryName, dir, icon, readOnly) -{} diff --git a/src/Mod/Material/App/MaterialLibrary.h b/src/Mod/Material/App/MaterialLibrary.h index bb40951258..b1f4fc428c 100644 --- a/src/Mod/Material/App/MaterialLibrary.h +++ b/src/Mod/Material/App/MaterialLibrary.h @@ -31,6 +31,7 @@ #include #include +#include "Library.h" #include "Materials.h" #include "Model.h" #include "ModelLibrary.h" @@ -43,29 +44,50 @@ class MaterialManager; class MaterialFilter; class MaterialFilterOptions; -class MaterialsExport MaterialLibrary: public LibraryBase, - public std::enable_shared_from_this +class MaterialsExport MaterialLibrary + : public Library, + public std::enable_shared_from_this { TYPESYSTEM_HEADER_WITH_OVERRIDE(); public: MaterialLibrary() = default; - MaterialLibrary(const MaterialLibrary&) = delete; + MaterialLibrary(const QString& libraryName, const QString& icon, bool readOnly = true); MaterialLibrary(const QString& libraryName, const QString& dir, const QString& icon, bool readOnly = true); + MaterialLibrary(const MaterialLibrary&) = delete; ~MaterialLibrary() override = default; - bool operator==(const MaterialLibrary& library) const + bool isLocal() const; + void setLocal(bool local); + + virtual std::shared_ptr>> + getMaterialTree(const std::shared_ptr& filter, + const Materials::MaterialFilterOptions& options) const; + + // Use this to get a shared_ptr for *this + std::shared_ptr getptr() { - return LibraryBase::operator==(library); + return shared_from_this(); } - bool operator!=(const MaterialLibrary& library) const - { - return !operator==(library); - } - std::shared_ptr getMaterialByPath(const QString& path) const; + +protected: + bool _local; +}; + +class MaterialsExport MaterialLibraryLocal: public MaterialLibrary +{ + TYPESYSTEM_HEADER_WITH_OVERRIDE(); + +public: + MaterialLibraryLocal() = default; + MaterialLibraryLocal(const QString& libraryName, + const QString& dir, + const QString& icon, + bool readOnly = true); + ~MaterialLibraryLocal() override = default; void createFolder(const QString& path); void renameFolder(const QString& oldPath, const QString& newPath); @@ -79,50 +101,39 @@ public: bool fileExists(const QString& path) const; std::shared_ptr addMaterial(const std::shared_ptr& material, const QString& path); - std::shared_ptr>> - getMaterialTree(const std::shared_ptr& filter, - const Materials::MaterialFilterOptions& options) const; + std::shared_ptr getMaterialByPath(const QString& path) const; - bool isReadOnly() const + bool operator==(const MaterialLibrary& library) const { - return _readOnly; + return library.isLocal() ? Library::operator==(library) : false; + } + bool operator!=(const MaterialLibrary& library) const + { + return !operator==(library); } - // Use this to get a shared_ptr for *this - std::shared_ptr getptr() + bool operator==(const MaterialLibraryLocal& library) const { - return shared_from_this(); + return Library::operator==(library); + } + bool operator!=(const MaterialLibraryLocal& library) const + { + return !operator==(library); } protected: void deleteDir(MaterialManager& manager, const QString& path); void deleteFile(MaterialManager& manager, const QString& path); - void updatePaths(const QString& oldPath, const QString& newPath); + QString getUUIDFromPath(const QString& path) const; - bool materialInTree(const std::shared_ptr& material, - const std::shared_ptr& filter, - const Materials::MaterialFilterOptions& options) const; - bool _readOnly; std::unique_ptr>> _materialPathMap; }; -class MaterialsExport MaterialExternalLibrary: public MaterialLibrary -{ - TYPESYSTEM_HEADER_WITH_OVERRIDE(); - -public: - MaterialExternalLibrary() = default; - MaterialExternalLibrary(const QString& libraryName, - const QString& dir, - const QString& icon, - bool readOnly = true); - ~MaterialExternalLibrary() override = default; -}; - } // namespace Materials Q_DECLARE_METATYPE(std::shared_ptr) +Q_DECLARE_METATYPE(std::shared_ptr) #endif // MATERIAL_MATERIALLIBRARY_H diff --git a/src/Mod/Material/App/MaterialLibrary.pyi b/src/Mod/Material/App/MaterialLibrary.pyi new file mode 100644 index 0000000000..300689b02f --- /dev/null +++ b/src/Mod/Material/App/MaterialLibrary.pyi @@ -0,0 +1,32 @@ +from Base.Metadata import export, constmethod +from Base.BaseClass import BaseClass +from typing import Final + +@export( + Include="Mod/Material/App/MaterialLibrary.h", + Namespace="Materials", + Constructor=True, + Delete=True, +) +class MaterialLibrary(BaseClass): + """ + Material library. + + Author: DavidCarter (dcarter@davidcarter.ca) + Licence: LGPL + """ + + Name: str = ... + """Name of the library""" + + Icon: str = ... + """String value of the icon.""" + + Directory: str = ... + """Local directory where the library is located. For non-local libraries this will be empty""" + + ReadOnly: bool = ... + """True if the library is local.""" + + Local: bool = ... + """True if the library is local.""" diff --git a/src/Mod/Material/App/MaterialLibraryPyImp.cpp b/src/Mod/Material/App/MaterialLibraryPyImp.cpp new file mode 100644 index 0000000000..58c788a674 --- /dev/null +++ b/src/Mod/Material/App/MaterialLibraryPyImp.cpp @@ -0,0 +1,125 @@ +/*************************************************************************** + * Copyright (c) 2023-2024 David Carter * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + +#include "PreCompiled.h" + +#include + +#include +#include +#include +#include + +#include "MaterialLibrary.h" + +#include "MaterialLibraryPy.h" + +#include "MaterialLibraryPy.cpp" + +using namespace Materials; + +// Forward declaration +// static PyObject* _pyObjectFromVariant(const QVariant& value); +// static Py::List getList(const QVariant& value); + +// returns a string which represents the object e.g. when printed in python +std::string MaterialLibraryPy::representation() const +{ + std::stringstream str; + str << ""; + + return str.str(); +} + +PyObject* MaterialLibraryPy::PyMake(struct _typeobject*, PyObject*, PyObject*) // Python wrapper +{ + // never create such objects with the constructor + return new MaterialLibraryPy(new MaterialLibrary()); +} + +// constructor method +int MaterialLibraryPy::PyInit(PyObject* /*args*/, PyObject* /*kwd*/) +{ + return 0; +} + +Py::String MaterialLibraryPy::getName() const +{ + auto filterName = getMaterialLibraryPtr()->getName(); + return {filterName.toStdString()}; +} + +void MaterialLibraryPy::setName(const Py::String value) +{ + getMaterialLibraryPtr()->setName(QString::fromStdString(value)); +} + +Py::String MaterialLibraryPy::getIcon() const +{ + auto path = getMaterialLibraryPtr()->getIconPath(); + return {path.toStdString()}; +} + +void MaterialLibraryPy::setIcon(const Py::String value) +{ + getMaterialLibraryPtr()->setIconPath(QString::fromStdString(value)); +} + +Py::String MaterialLibraryPy::getDirectory() const +{ + auto path = getMaterialLibraryPtr()->getDirectory(); + return {path.toStdString()}; +} + +void MaterialLibraryPy::setDirectory(const Py::String value) +{ + getMaterialLibraryPtr()->setDirectory(QString::fromStdString(value)); +} + +Py::Boolean MaterialLibraryPy::getReadOnly() const +{ + return getMaterialLibraryPtr()->isReadOnly(); +} + +void MaterialLibraryPy::setReadOnly(Py::Boolean value) +{ + getMaterialLibraryPtr()->setReadOnly(value); +} + +Py::Boolean MaterialLibraryPy::getLocal() const +{ + return getMaterialLibraryPtr()->isLocal(); +} + +void MaterialLibraryPy::setLocal(Py::Boolean value) +{ + getMaterialLibraryPtr()->setLocal(value); +} + +PyObject* MaterialLibraryPy::getCustomAttributes(const char* /*attr*/) const +{ + return nullptr; +} + +int MaterialLibraryPy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/) +{ + return 0; +} diff --git a/src/Mod/Material/App/MaterialLoader.cpp b/src/Mod/Material/App/MaterialLoader.cpp index a4dd528002..7bdc4a881f 100644 --- a/src/Mod/Material/App/MaterialLoader.cpp +++ b/src/Mod/Material/App/MaterialLoader.cpp @@ -45,7 +45,7 @@ using namespace Materials; -MaterialEntry::MaterialEntry(const std::shared_ptr& library, +MaterialEntry::MaterialEntry(const std::shared_ptr& library, const QString& modelName, const QString& dir, const QString& modelUuid) @@ -55,7 +55,7 @@ MaterialEntry::MaterialEntry(const std::shared_ptr& library, , _uuid(modelUuid) {} -MaterialYamlEntry::MaterialYamlEntry(const std::shared_ptr& library, +MaterialYamlEntry::MaterialYamlEntry(const std::shared_ptr& library, const QString& modelName, const QString& dir, const QString& modelUuid, @@ -98,9 +98,9 @@ std::shared_ptr> MaterialYamlEntry::readImageList(const YAML::No return readList(node, true); } -std::shared_ptr MaterialYamlEntry::read2DArray(const YAML::Node& node, int columns) +std::shared_ptr MaterialYamlEntry::read2DArray(const YAML::Node& node, int columns) { - auto array2d = std::make_shared(); + auto array2d = std::make_shared(); array2d->setColumns(columns); if (node.size() == 1 || node.size() == 2) { @@ -126,9 +126,9 @@ std::shared_ptr MaterialYamlEntry::read2DArray(const YAML::Node return array2d; } -std::shared_ptr MaterialYamlEntry::read3DArray(const YAML::Node& node, int columns) +std::shared_ptr MaterialYamlEntry::read3DArray(const YAML::Node& node, int columns) { - auto array3d = std::make_shared(); + auto array3d = std::make_shared(); array3d->setColumns(columns - 1); // First column is third dimension if (node.size() == 1 || node.size() == 2) { @@ -347,16 +347,16 @@ MaterialLoader::MaterialLoader( : _materialMap(materialMap) , _libraryList(libraryList) { - loadLibraries(); + loadLibraries(libraryList); } -void MaterialLoader::addLibrary(const std::shared_ptr& model) +void MaterialLoader::addLibrary(const std::shared_ptr& model) { _libraryList->push_back(model); } std::shared_ptr -MaterialLoader::getMaterialFromYAML(const std::shared_ptr& library, +MaterialLoader::getMaterialFromYAML(const std::shared_ptr& library, YAML::Node& yamlroot, const QString& path) { @@ -387,18 +387,20 @@ MaterialLoader::getMaterialFromYAML(const std::shared_ptr& libr } std::shared_ptr -MaterialLoader::getMaterialFromPath(const std::shared_ptr& library, +MaterialLoader::getMaterialFromPath(const std::shared_ptr& library, const QString& path) const { std::shared_ptr model = nullptr; + auto materialLibrary = + reinterpret_cast&>(library); // Used for debugging std::string pathName = path.toStdString(); if (MaterialConfigLoader::isConfigStyle(path)) { - auto material = MaterialConfigLoader::getMaterialFromPath(library, path); + auto material = MaterialConfigLoader::getMaterialFromPath(materialLibrary, path); if (material) { - (*_materialMap)[material->getUUID()] = library->addMaterial(material, path); + (*_materialMap)[material->getUUID()] = materialLibrary->addMaterial(material, path); } // Return the nullptr as there are no intermediate steps to take, such @@ -417,7 +419,7 @@ MaterialLoader::getMaterialFromPath(const std::shared_ptr& libr try { yamlroot = YAML::Load(fin); - model = getMaterialFromYAML(library, yamlroot, path); + model = getMaterialFromYAML(materialLibrary, yamlroot, path); } catch (YAML::Exception const& e) { Base::Console().Error("YAML parsing error: '%s'\n", pathName.c_str()); @@ -511,7 +513,7 @@ void MaterialLoader::dereference(const std::shared_ptr& material) dereference(_materialMap, material); } -void MaterialLoader::loadLibrary(const std::shared_ptr& library) +void MaterialLoader::loadLibrary(const std::shared_ptr& library) { if (_materialEntryMap == nullptr) { _materialEntryMap = std::make_unique>>(); @@ -541,12 +543,16 @@ void MaterialLoader::loadLibrary(const std::shared_ptr& library } } -void MaterialLoader::loadLibraries() +void MaterialLoader::loadLibraries( + const std::shared_ptr>>& libraryList) { - auto _libraryList = getMaterialLibraries(); - if (_libraryList) { - for (auto& it : *_libraryList) { - loadLibrary(it); + if (libraryList) { + for (auto& it : *libraryList) { + if (it->isLocal()) { + auto materialLibrary = + reinterpret_cast&>(it); + loadLibrary(materialLibrary); + } } } @@ -555,92 +561,8 @@ void MaterialLoader::loadLibraries() } } -std::shared_ptr>> MaterialLoader::getMaterialLibraries() -{ - auto param = App::GetApplication().GetParameterGroupByPath( - "User parameter:BaseApp/Preferences/Mod/Material/Resources"); - bool useBuiltInMaterials = param->GetBool("UseBuiltInMaterials", true); - bool useMatFromModules = param->GetBool("UseMaterialsFromWorkbenches", true); - bool useMatFromConfigDir = param->GetBool("UseMaterialsFromConfigDir", true); - bool useMatFromCustomDir = param->GetBool("UseMaterialsFromCustomDir", true); - - if (useBuiltInMaterials) { - QString resourceDir = QString::fromStdString(App::Application::getResourceDir() - + "/Mod/Material/Resources/Materials"); - auto libData = - std::make_shared(QStringLiteral("System"), - resourceDir, - QStringLiteral(":/icons/freecad.svg"), - true); - _libraryList->push_back(libData); - } - - if (useMatFromModules) { - auto moduleParam = App::GetApplication().GetParameterGroupByPath( - "User parameter:BaseApp/Preferences/Mod/Material/Resources/Modules"); - for (auto& group : moduleParam->GetGroups()) { - // auto module = moduleParam->GetGroup(group->GetGroupName()); - auto moduleName = QString::fromStdString(group->GetGroupName()); - auto materialDir = QString::fromStdString(group->GetASCII("ModuleDir", "")); - auto materialIcon = QString::fromStdString(group->GetASCII("ModuleIcon", "")); - auto materialReadOnly = group->GetBool("ModuleReadOnly", true); - - if (materialDir.length() > 0) { - QDir dir(materialDir); - if (dir.exists()) { - auto libData = std::make_shared(moduleName, - materialDir, - materialIcon, - materialReadOnly); - _libraryList->push_back(libData); - } - } - } - } - - if (useMatFromConfigDir) { - QString resourceDir = - QString::fromStdString(App::Application::getUserAppDataDir() + "/Material"); - if (!resourceDir.isEmpty()) { - QDir materialDir(resourceDir); - if (!materialDir.exists()) { - // Try creating the user dir if it doesn't exist - if (!materialDir.mkpath(resourceDir)) { - Base::Console().Log("Unable to create user library '%s'\n", - resourceDir.toStdString().c_str()); - } - } - if (materialDir.exists()) { - auto libData = std::make_shared( - QStringLiteral("User"), - resourceDir, - QStringLiteral(":/icons/preferences-general.svg"), - false); - _libraryList->push_back(libData); - } - } - } - - if (useMatFromCustomDir) { - QString resourceDir = QString::fromStdString(param->GetASCII("CustomMaterialsDir", "")); - if (!resourceDir.isEmpty()) { - QDir materialDir(resourceDir); - if (materialDir.exists()) { - auto libData = - std::make_shared(QStringLiteral("Custom"), - resourceDir, - QStringLiteral(":/icons/user.svg"), - false); - _libraryList->push_back(libData); - } - } - } - - return _libraryList; -} - std::shared_ptr> -MaterialLoader::getMaterialFolders(const MaterialLibrary& library) +MaterialLoader::getMaterialFolders(const MaterialLibraryLocal& library) { std::shared_ptr> pathList = std::make_shared>(); QDirIterator it(library.getDirectory(), QDirIterator::Subdirectories); diff --git a/src/Mod/Material/App/MaterialLoader.h b/src/Mod/Material/App/MaterialLoader.h index 1d1eb9d7bb..cf48ff443b 100644 --- a/src/Mod/Material/App/MaterialLoader.h +++ b/src/Mod/Material/App/MaterialLoader.h @@ -33,12 +33,14 @@ namespace Materials { +class MaterialLibrary; +class MaterialLibraryLocal; class MaterialEntry { public: MaterialEntry() = default; - MaterialEntry(const std::shared_ptr& library, + MaterialEntry(const std::shared_ptr& library, const QString& modelName, const QString& dir, const QString& modelUuid); @@ -47,7 +49,7 @@ public: virtual void addToTree(std::shared_ptr>> materialMap) = 0; - std::shared_ptr getLibrary() const + std::shared_ptr getLibrary() const { return _library; } @@ -65,7 +67,7 @@ public: } protected: - std::shared_ptr _library; + std::shared_ptr _library; QString _name; QString _directory; QString _uuid; @@ -74,7 +76,7 @@ protected: class MaterialYamlEntry: public MaterialEntry { public: - MaterialYamlEntry(const std::shared_ptr& library, + MaterialYamlEntry(const std::shared_ptr& library, const QString& modelName, const QString& dir, const QString& modelUuid, @@ -101,8 +103,8 @@ private: static std::shared_ptr> readList(const YAML::Node& node, bool isImageList = false); static std::shared_ptr> readImageList(const YAML::Node& node); - static std::shared_ptr read2DArray(const YAML::Node& node, int columns); - static std::shared_ptr read3DArray(const YAML::Node& node, int columns); + static std::shared_ptr read2DArray(const YAML::Node& node, int columns); + static std::shared_ptr read3DArray(const YAML::Node& node, int columns); YAML::Node _model; }; @@ -114,14 +116,14 @@ public: const std::shared_ptr>>& libraryList); ~MaterialLoader() = default; - std::shared_ptr>> getMaterialLibraries(); - static std::shared_ptr> getMaterialFolders(const MaterialLibrary& library); + static std::shared_ptr> + getMaterialFolders(const MaterialLibraryLocal& library); static void showYaml(const YAML::Node& yaml); static void dereference(const std::shared_ptr>>& materialMap, const std::shared_ptr& material); static std::shared_ptr - getMaterialFromYAML(const std::shared_ptr& library, + getMaterialFromYAML(const std::shared_ptr& library, YAML::Node& yamlroot, const QString& path); @@ -131,10 +133,11 @@ private: void addToTree(std::shared_ptr model); void dereference(const std::shared_ptr& material); std::shared_ptr - getMaterialFromPath(const std::shared_ptr& library, const QString& path) const; - void addLibrary(const std::shared_ptr& model); - void loadLibrary(const std::shared_ptr& library); - void loadLibraries(); + getMaterialFromPath(const std::shared_ptr& library, const QString& path) const; + void addLibrary(const std::shared_ptr& model); + void loadLibrary(const std::shared_ptr& library); + void loadLibraries( + const std::shared_ptr>>& libraryList); static std::unique_ptr>> _materialEntryMap; std::shared_ptr>> _materialMap; diff --git a/src/Mod/Material/App/MaterialManager.cpp b/src/Mod/Material/App/MaterialManager.cpp index 5b23f288bb..eabe92a15b 100644 --- a/src/Mod/Material/App/MaterialManager.cpp +++ b/src/Mod/Material/App/MaterialManager.cpp @@ -24,8 +24,8 @@ #include #endif -#include #include +#include #include #include @@ -35,6 +35,7 @@ #include "MaterialConfigLoader.h" #include "MaterialLoader.h" #include "MaterialManager.h" +#include "MaterialManagerLocal.h" #include "ModelManager.h" #include "ModelUuids.h" @@ -45,100 +46,56 @@ using namespace Materials; /* TRANSLATOR Material::Materials */ -std::shared_ptr>> MaterialManager::_libraryList = - nullptr; -std::shared_ptr>> MaterialManager::_materialMap = - nullptr; -QMutex MaterialManager::_mutex; - TYPESYSTEM_SOURCE(Materials::MaterialManager, Base::BaseClass) +QMutex MaterialManager::_mutex; +MaterialManager* MaterialManager::_manager = nullptr; +std::unique_ptr MaterialManager::_localManager; + MaterialManager::MaterialManager() +{} + +MaterialManager::~MaterialManager() +{} + +MaterialManager& MaterialManager::getManager() { - // TODO: Add a mutex or similar - initLibraries(); + if (!_manager) { + initManagers(); + } + return *_manager; } -void MaterialManager::initLibraries() +void MaterialManager::initManagers() { QMutexLocker locker(&_mutex); - if (_materialMap == nullptr) { - // Load the models first - auto manager = std::make_unique(); - Q_UNUSED(manager) - - _materialMap = std::make_shared>>(); - - if (_libraryList == nullptr) { - _libraryList = std::make_shared>>(); - } - - // Load the libraries - MaterialLoader loader(_materialMap, _libraryList); + if (!_manager) { + // Can't use smart pointers for this since the constructor is private + _manager = new MaterialManager(); + } + if (!_localManager) { + _localManager = std::make_unique(); } } void MaterialManager::cleanup() { - QMutexLocker locker(&_mutex); - - if (_libraryList) { - _libraryList->clear(); - _libraryList = nullptr; - } - - if (_materialMap) { - for (auto& it : *_materialMap) { - // This is needed to resolve cyclic dependencies - it.second->setLibrary(nullptr); - } - _materialMap->clear(); - _materialMap = nullptr; + if (_localManager) { + _localManager->cleanup(); } } void MaterialManager::refresh() { - // This is very expensive and can be improved using observers? - cleanup(); - initLibraries(); + _localManager->refresh(); } -void MaterialManager::saveMaterial(const std::shared_ptr& library, - const std::shared_ptr& material, - const QString& path, - bool overwrite, - bool saveAsCopy, - bool saveInherited) const -{ - auto newMaterial = library->saveMaterial(material, path, overwrite, saveAsCopy, saveInherited); - (*_materialMap)[newMaterial->getUUID()] = newMaterial; -} - -bool MaterialManager::isMaterial(const fs::path& p) const -{ - if (!fs::is_regular_file(p)) { - return false; - } - // check file extension - if (p.extension() == ".FCMat") { - return true; - } - return false; -} - -bool MaterialManager::isMaterial(const QFileInfo& file) const -{ - if (!file.isFile()) { - return false; - } - // check file extension - if (file.suffix() == QStringLiteral("FCMat")) { - return true; - } - return false; -} +//===== +// +// Defaults +// +//===== std::shared_ptr MaterialManager::defaultAppearance() { @@ -149,7 +106,7 @@ std::shared_ptr MaterialManager::defaultAppearance() uint32_t packed = color.getPackedRGB(); packed = hGrp->GetUnsigned(parameter, packed); color.setPackedRGB(packed); - color.a = 1.0; // The default color sets fully transparent, not opaque + color.a = 1.0; // The default color sets fully transparent, not opaque }; auto intRandom = [](int min, int max) -> int { static std::mt19937 generator; @@ -187,9 +144,9 @@ std::shared_ptr MaterialManager::defaultMaterial() MaterialManager manager; auto mat = defaultAppearance(); - auto material = manager.getMaterial(defaultMaterialUUID()); + auto material = getManager().getMaterial(defaultMaterialUUID()); if (!material) { - material = manager.getMaterial(QLatin1String("7f9fd73b-50c9-41d8-b7b2-575a030c1eeb")); + material = getManager().getMaterial(QStringLiteral("7f9fd73b-50c9-41d8-b7b2-575a030c1eeb")); } if (material->hasAppearanceModel(ModelUUIDs::ModelUUID_Rendering_Basic)) { material->getAppearanceProperty(QStringLiteral("DiffuseColor")) @@ -218,14 +175,178 @@ QString MaterialManager::defaultMaterialUUID() return QString::fromStdString(uuid); } +//===== +// +// Library management +// +//===== + +std::shared_ptr>> MaterialManager::getLibraries() +{ + auto libraries = std::make_shared>>(); + auto localLibraries = _localManager->getLibraries(); + for (auto& local : *localLibraries) { + libraries->push_back(local); + } + + return libraries; +} + +std::shared_ptr>> MaterialManager::getLocalLibraries() +{ + return _localManager->getLibraries(); +} + +std::shared_ptr MaterialManager::getLibrary(const QString& name) const +{ + return _localManager->getLibrary(name); +} + +void MaterialManager::createLibrary(const QString& libraryName, const QString& icon, bool readOnly) +{ + throw CreationError("Local library requires a path"); +} + +void MaterialManager::createLocalLibrary(const QString& libraryName, + const QString& directory, + const QString& icon, + bool readOnly) +{ + _localManager->createLibrary(libraryName, directory, icon, readOnly); +} + +void MaterialManager::renameLibrary(const QString& libraryName, const QString& newName) +{ + _localManager->renameLibrary(libraryName, newName); +} + +void MaterialManager::changeIcon(const QString& libraryName, const QString& icon) +{ + _localManager->changeIcon(libraryName, icon); +} + +void MaterialManager::removeLibrary(const QString& libraryName) +{ + _localManager->removeLibrary(libraryName); +} + +std::shared_ptr>> +MaterialManager::libraryMaterials(const QString& libraryName) +{ + return _localManager->libraryMaterials(libraryName); +} + +std::shared_ptr>> +MaterialManager::libraryMaterials(const QString& libraryName, + const std::shared_ptr& filter, + const MaterialFilterOptions& options) +{ + return _localManager->libraryMaterials(libraryName, filter, options); +} + +bool MaterialManager::isLocalLibrary(const QString& libraryName) +{ + return true; +} + +//===== +// +// Folder management +// +//===== + +std::shared_ptr> +MaterialManager::getMaterialFolders(const std::shared_ptr& library) const +{ + if (library->isLocal()) { + auto materialLibrary = + reinterpret_cast&>(library); + + return _localManager->getMaterialFolders(materialLibrary); + } + + return std::make_shared>(); +} + +void MaterialManager::createFolder(const std::shared_ptr& library, + const QString& path) +{ + if (library->isLocal()) { + auto materialLibrary = + reinterpret_cast&>(library); + + _localManager->createFolder(materialLibrary, path); + } +} + +void MaterialManager::renameFolder(const std::shared_ptr& library, + const QString& oldPath, + const QString& newPath) +{ + if (library->isLocal()) { + auto materialLibrary = + reinterpret_cast&>(library); + + _localManager->renameFolder(materialLibrary, oldPath, newPath); + } +} + +void MaterialManager::deleteRecursive(const std::shared_ptr& library, + const QString& path) +{ + if (library->isLocal()) { + auto materialLibrary = + reinterpret_cast&>(library); + + _localManager->deleteRecursive(materialLibrary, path); + } +} + +//===== +// +// Tree management +// +//===== + +std::shared_ptr>> +MaterialManager::getMaterialTree(const std::shared_ptr& library, + const std::shared_ptr& filter) const +{ + MaterialFilterOptions options; + return library->getMaterialTree(filter, options); +} + +std::shared_ptr>> +MaterialManager::getMaterialTree(const std::shared_ptr& library, + const std::shared_ptr& filter, + const MaterialFilterOptions& options) const +{ + return library->getMaterialTree(filter, options); +} + +std::shared_ptr>> +MaterialManager::getMaterialTree(const std::shared_ptr& library) const +{ + std::shared_ptr filter; + MaterialFilterOptions options; + return library->getMaterialTree(filter, options); +} + +//===== +// +// Material management +// +//===== + +std::shared_ptr>> +MaterialManager::getLocalMaterials() const +{ + return _localManager->getLocalMaterials(); +} + std::shared_ptr MaterialManager::getMaterial(const QString& uuid) const { - try { - return _materialMap->at(uuid); - } - catch (std::out_of_range&) { - throw MaterialNotFound(); - } + return _localManager->getMaterial(uuid); } std::shared_ptr MaterialManager::getMaterial(const App::Material& material) @@ -237,65 +358,13 @@ std::shared_ptr MaterialManager::getMaterial(const App::Material& mate std::shared_ptr MaterialManager::getMaterialByPath(const QString& path) const { - QString cleanPath = QDir::cleanPath(path); - - for (auto& library : *_libraryList) { - if (cleanPath.startsWith(library->getDirectory())) { - try { - return library->getMaterialByPath(cleanPath); - } - catch (const MaterialNotFound&) { - } - - // See if it's a new file saved by the old editor - { - QMutexLocker locker(&_mutex); - - if (MaterialConfigLoader::isConfigStyle(path)) { - auto material = MaterialConfigLoader::getMaterialFromPath(library, path); - if (material) { - (*_materialMap)[material->getUUID()] = library->addMaterial(material, path); - } - - return material; - } - } - } - } - - // Older workbenches may try files outside the context of a library - { - QMutexLocker locker(&_mutex); - - if (MaterialConfigLoader::isConfigStyle(path)) { - auto material = MaterialConfigLoader::getMaterialFromPath(nullptr, path); - - return material; - } - } - - throw MaterialNotFound(); + return _localManager->getMaterialByPath(path); } std::shared_ptr MaterialManager::getMaterialByPath(const QString& path, const QString& lib) const { - auto library = getLibrary(lib); // May throw LibraryNotFound - return library->getMaterialByPath(path); // May throw MaterialNotFound -} - -bool MaterialManager::exists(const QString& uuid) const -{ - try { - auto material = getMaterial(uuid); - if (material) { - return true; - } - } - catch (const MaterialNotFound&) { - } - - return false; + return _localManager->getMaterialByPath(path, lib); } std::shared_ptr @@ -308,105 +377,69 @@ MaterialManager::getParent(const std::shared_ptr& material) const return getMaterial(material->getParentUUID()); } +bool MaterialManager::exists(const QString& uuid) const +{ + return _localManager->exists(uuid); +} + bool MaterialManager::exists(const std::shared_ptr& library, const QString& uuid) const { - try { - auto material = getMaterial(uuid); - if (material) { - return (*material->getLibrary() == *library); - } - } - catch (const MaterialNotFound&) { - } + if (library->isLocal()) { + auto materialLibrary = + reinterpret_cast&>(library); + return _localManager->exists(materialLibrary, uuid); + } return false; } -std::shared_ptr MaterialManager::getLibrary(const QString& name) const +void MaterialManager::remove(const QString& uuid) const { - for (auto& library : *_libraryList) { - if (library->getName() == name) { - return library; - } - } - - throw LibraryNotFound(); + _localManager->remove(uuid); } -std::shared_ptr>> -MaterialManager::getMaterialLibraries() const +void MaterialManager::saveMaterial(const std::shared_ptr& library, + const std::shared_ptr& material, + const QString& path, + bool overwrite, + bool saveAsCopy, + bool saveInherited) const { - if (_libraryList == nullptr) { - if (_materialMap == nullptr) { - _materialMap = std::make_shared>>(); - } - _libraryList = std::make_shared>>(); - - // Load the libraries - MaterialLoader loader(_materialMap, _libraryList); - } - return _libraryList; + auto materialLibrary = + reinterpret_cast&>(library); + _localManager + ->saveMaterial(materialLibrary, material, path, overwrite, saveAsCopy, saveInherited); } -std::shared_ptr> -MaterialManager::getMaterialFolders(const std::shared_ptr& library) const +bool MaterialManager::isMaterial(const fs::path& p) const { - return MaterialLoader::getMaterialFolders(*library); + return _localManager->isMaterial(p); +} + +bool MaterialManager::isMaterial(const QFileInfo& file) const +{ + return _localManager->isMaterial(file); } std::shared_ptr>> MaterialManager::materialsWithModel(const QString& uuid) const { - std::shared_ptr>> dict = - std::make_shared>>(); - - for (auto& it : *_materialMap) { - QString key = it.first; - auto material = it.second; - - if (material->hasModel(uuid)) { - (*dict)[key] = material; - } - } - - return dict; + return _localManager->materialsWithModel(uuid); } std::shared_ptr>> MaterialManager::materialsWithModelComplete(const QString& uuid) const { - std::shared_ptr>> dict = - std::make_shared>>(); - - for (auto& it : *_materialMap) { - QString key = it.first; - auto material = it.second; - - if (material->isModelComplete(uuid)) { - (*dict)[key] = material; - } - } - - return dict; + return _localManager->materialsWithModelComplete(uuid); } void MaterialManager::dereference() const { - // First clear the inheritences - for (auto& it : *_materialMap) { - auto material = it.second; - material->clearDereferenced(); - material->clearInherited(); - } - - // Run the dereference again - for (auto& it : *_materialMap) { - dereference(it.second); - } + _localManager->dereference(); } void MaterialManager::dereference(std::shared_ptr material) const { - MaterialLoader::dereference(_materialMap, material); + _localManager->dereference(material); } diff --git a/src/Mod/Material/App/MaterialManager.h b/src/Mod/Material/App/MaterialManager.h index 63f06dc772..c3ec6bb517 100644 --- a/src/Mod/Material/App/MaterialManager.h +++ b/src/Mod/Material/App/MaterialManager.h @@ -26,13 +26,14 @@ #include +#include #include #include "FolderTree.h" #include "Materials.h" -#include "MaterialLibrary.h" #include "MaterialFilter.h" +#include "MaterialLibrary.h" namespace fs = std::filesystem; @@ -45,78 +46,78 @@ class Material; namespace Materials { +class MaterialManagerExternal; +class MaterialManagerLocal; +class MaterialFilter; +class MaterialFilterOptions; class MaterialsExport MaterialManager: public Base::BaseClass { TYPESYSTEM_HEADER_WITH_OVERRIDE(); public: - MaterialManager(); - ~MaterialManager() override = default; + ~MaterialManager() override; + + static MaterialManager& getManager(); static void cleanup(); static void refresh(); + + // Defaults static std::shared_ptr defaultAppearance(); static std::shared_ptr defaultMaterial(); static QString defaultMaterialUUID(); - std::shared_ptr>> getMaterials() const - { - return _materialMap; - } + // Library management + std::shared_ptr>> getLibraries(); + std::shared_ptr>> getLocalLibraries(); + std::shared_ptr getLibrary(const QString& name) const; + void createLibrary(const QString& libraryName, const QString& icon, bool readOnly = true); + void createLocalLibrary(const QString& libraryName, + const QString& directory, + const QString& icon, + bool readOnly = true); + void renameLibrary(const QString& libraryName, const QString& newName); + void changeIcon(const QString& libraryName, const QString& icon); + void removeLibrary(const QString& libraryName); + std::shared_ptr>> + libraryMaterials(const QString& libraryName); + std::shared_ptr>> + libraryMaterials(const QString& libraryName, + const std::shared_ptr& filter, + const MaterialFilterOptions& options); + bool isLocalLibrary(const QString& libraryName); + + // Folder management + std::shared_ptr> + getMaterialFolders(const std::shared_ptr& library) const; + void createFolder(const std::shared_ptr& library, const QString& path); + void renameFolder(const std::shared_ptr& library, + const QString& oldPath, + const QString& newPath); + void deleteRecursive(const std::shared_ptr& library, const QString& path); + + // Tree management + std::shared_ptr>> + getMaterialTree(const std::shared_ptr& library, + const std::shared_ptr& filter) const; + std::shared_ptr>> + getMaterialTree(const std::shared_ptr& library, + const std::shared_ptr& filter, + const MaterialFilterOptions& options) const; + std::shared_ptr>> + getMaterialTree(const std::shared_ptr& library) const; + + // Material management + std::shared_ptr>> getLocalMaterials() const; std::shared_ptr getMaterial(const QString& uuid) const; static std::shared_ptr getMaterial(const App::Material& material); std::shared_ptr getMaterialByPath(const QString& path) const; std::shared_ptr getMaterialByPath(const QString& path, const QString& library) const; std::shared_ptr getParent(const std::shared_ptr& material) const; - std::shared_ptr getLibrary(const QString& name) const; bool exists(const QString& uuid) const; bool exists(const std::shared_ptr& library, const QString& uuid) const; - - // Library management - std::shared_ptr>> getMaterialLibraries() const; - std::shared_ptr>> - getMaterialTree(const std::shared_ptr& library, - const std::shared_ptr& filter) const - { - MaterialFilterOptions options; - return library->getMaterialTree(filter, options); - } - std::shared_ptr>> - getMaterialTree(const std::shared_ptr& library, - const std::shared_ptr& filter, - const MaterialFilterOptions& options) const - { - return library->getMaterialTree(filter, options); - } - std::shared_ptr>> - getMaterialTree(const std::shared_ptr& library) const - { - std::shared_ptr filter; - MaterialFilterOptions options; - return library->getMaterialTree(filter, options); - } - std::shared_ptr> - getMaterialFolders(const std::shared_ptr& library) const; - void createFolder(const std::shared_ptr& library, const QString& path) const - { - library->createFolder(path); - } - void renameFolder(const std::shared_ptr& library, - const QString& oldPath, - const QString& newPath) const - { - library->renameFolder(oldPath, newPath); - } - void deleteRecursive(const std::shared_ptr& library, const QString& path) const - { - library->deleteRecursive(path); - dereference(); - } - void remove(const QString& uuid) const - { - _materialMap->erase(uuid); - } + void remove(const QString& uuid) const; void saveMaterial(const std::shared_ptr& library, const std::shared_ptr& material, @@ -136,13 +137,15 @@ public: void dereference() const; private: - static std::shared_ptr>> _libraryList; - static std::shared_ptr>> _materialMap; - static QMutex _mutex; + MaterialManager(); + static void initManagers(); - static void initLibraries(); + static MaterialManager* _manager; + + static std::unique_ptr _localManager; + static QMutex _mutex; }; } // namespace Materials -#endif // MATERIAL_MATERIALMANAGER_H +#endif // MATERIAL_MATERIALMANAGER_H \ No newline at end of file diff --git a/src/Mod/Material/App/MaterialManager.pyi b/src/Mod/Material/App/MaterialManager.pyi new file mode 100644 index 0000000000..493725bc01 --- /dev/null +++ b/src/Mod/Material/App/MaterialManager.pyi @@ -0,0 +1,70 @@ +from Base.Metadata import export, constmethod +from Base.BaseClass import BaseClass +from typing import Final, List, Dict, overload + +@export( + Include="Mod/Material/App/MaterialManager.h", + Namespace="Materials", + Constructor=True +) +class MaterialManager(BaseClass): + """ + Material descriptions. + + Author: DavidCarter (dcarter@davidcarter.ca) + Licence: LGPL + """ + + MaterialLibraries: Final[List] = ... + """List of Material libraries.""" + + Materials: Final[Dict] = ... + """List of Materials.""" + + def getMaterial(self) -> None: + """ + Get a material object by specifying its UUID + """ + ... + + def getMaterialByPath(self) -> None: + """ + Get a material object by specifying its path and library name + """ + ... + + def inheritMaterial(self) -> None: + """ + Create a new material object by specifying the UUID of its parent + """ + ... + + def materialsWithModel(self) -> None: + """ + Get a list of materials implementing the specified model + """ + ... + + def materialsWithModelComplete(self) -> None: + """ + Get a list of materials implementing the specified model, with values for all properties + """ + ... + + def save(self, **kwargs) -> None: + """ + Save the material in the specified library + """ + ... + + def filterMaterials(self, **kwargs) -> None: + """ + Returns a filtered material list + """ + ... + + def refresh(self) -> None: + """ + Refreshes the material tree. Use sparingly as this is an expensive operation. + """ + ... \ No newline at end of file diff --git a/src/Mod/Material/App/MaterialManagerLocal.cpp b/src/Mod/Material/App/MaterialManagerLocal.cpp new file mode 100644 index 0000000000..42b3daabe1 --- /dev/null +++ b/src/Mod/Material/App/MaterialManagerLocal.cpp @@ -0,0 +1,593 @@ +/*************************************************************************** + * Copyright (c) 2023 David Carter * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + +#include "PreCompiled.h" +#ifndef _PreComp_ +#include +#endif + +#include +#include +#include + +#include +#include + +#include "Exceptions.h" +#include "MaterialConfigLoader.h" +#include "MaterialFilter.h" +#include "MaterialLibrary.h" +#include "MaterialLoader.h" +#include "MaterialManagerLocal.h" +#include "ModelManager.h" +#include "ModelUuids.h" + + +using namespace Materials; + +/* TRANSLATOR Material::Materials */ + +std::shared_ptr>> + MaterialManagerLocal::_libraryList = nullptr; +std::shared_ptr>> MaterialManagerLocal::_materialMap = + nullptr; +QMutex MaterialManagerLocal::_mutex; + +TYPESYSTEM_SOURCE(Materials::MaterialManagerLocal, Base::BaseClass) + +MaterialManagerLocal::MaterialManagerLocal() +{ + // TODO: Add a mutex or similar + initLibraries(); +} + +void MaterialManagerLocal::initLibraries() +{ + QMutexLocker locker(&_mutex); + + if (_materialMap == nullptr) { + // Load the models first + ModelManager::getManager(); + + _materialMap = std::make_shared>>(); + + if (_libraryList == nullptr) { + _libraryList = getConfiguredLibraries(); + } + + // Load the libraries + MaterialLoader loader(_materialMap, _libraryList); + } +} + +void MaterialManagerLocal::cleanup() +{ + QMutexLocker locker(&_mutex); + + if (_libraryList) { + _libraryList->clear(); + _libraryList = nullptr; + } + + if (_materialMap) { + for (auto& it : *_materialMap) { + // This is needed to resolve cyclic dependencies + it.second->setLibrary(nullptr); + } + _materialMap->clear(); + _materialMap = nullptr; + } +} + +void MaterialManagerLocal::refresh() +{ + // This is very expensive and can be improved using observers? + cleanup(); + initLibraries(); +} + +//===== +// +// Library management +// +//===== + +std::shared_ptr>> MaterialManagerLocal::getLibraries() +{ + if (_libraryList == nullptr) { + initLibraries(); + } + return _libraryList; +} + +std::shared_ptr>> +MaterialManagerLocal::getMaterialLibraries() +{ + if (_libraryList == nullptr) { + initLibraries(); + } + return _libraryList; +} + +std::shared_ptr MaterialManagerLocal::getLibrary(const QString& name) const +{ + for (auto& library : *_libraryList) { + if (library->isLocal() && library->sameName(name)) { + return library; + } + } + + throw LibraryNotFound(); +} + +void MaterialManagerLocal::createLibrary(const QString& libraryName, + const QString& directory, + const QString& icon, + bool readOnly) +{ + QDir dir; + if (!dir.exists(directory)) { + if (!dir.mkpath(directory)) { + throw CreationError("Unable to create library path"); + } + } + + auto materialLibrary = + std::make_shared(libraryName, directory, icon, readOnly); + _libraryList->push_back(materialLibrary); + + // This needs to be persisted somehow +} + +void MaterialManagerLocal::renameLibrary(const QString& libraryName, const QString& newName) +{ + for (auto& library : *_libraryList) { + if (library->isLocal() && library->sameName(libraryName)) { + auto materialLibrary = + reinterpret_cast&>(library); + materialLibrary->setName(newName); + return; + } + } + + throw LibraryNotFound(); +} + +void MaterialManagerLocal::changeIcon(const QString& libraryName, const QString& icon) +{ + for (auto& library : *_libraryList) { + if (library->isLocal() && library->sameName(libraryName)) { + auto materialLibrary = + reinterpret_cast&>(library); + materialLibrary->setIconPath(icon); + return; + } + } + + throw LibraryNotFound(); +} + +void MaterialManagerLocal::removeLibrary(const QString& libraryName) +{ + for (auto& library : *_libraryList) { + if (library->isLocal() && library->sameName(libraryName)) { + _libraryList->remove(library); + + // At this point we should rebuild the material map + return; + } + } + + throw LibraryNotFound(); +} + +std::shared_ptr>> +MaterialManagerLocal::libraryMaterials(const QString& libraryName) +{ + auto materials = std::make_shared>>(); + + for (auto& it : *_materialMap) { + // This is needed to resolve cyclic dependencies + auto library = it.second->getLibrary(); + if (library->sameName(libraryName)) { + materials->push_back(std::tuple(it.first, + it.second->getDirectory(), + it.second->getName())); + } + } + + return materials; +} + +bool MaterialManagerLocal::passFilter(const std::shared_ptr& material, + const std::shared_ptr& filter, + const Materials::MaterialFilterOptions& options) const +{ + if (!filter) { + // If there's no filter we always include + return true; + } + + // filter out old format files + if (material->isOldFormat() && !options.includeLegacy()) { + return false; + } + + // filter based on models + return filter->modelIncluded(material); +} + +std::shared_ptr>> +MaterialManagerLocal::libraryMaterials(const QString& libraryName, + const std::shared_ptr& filter, + const MaterialFilterOptions& options) +{ + auto materials = std::make_shared>>(); + + for (auto& it : *_materialMap) { + // This is needed to resolve cyclic dependencies + auto library = it.second->getLibrary(); + if (library->sameName(libraryName)) { + if (passFilter(it.second, filter, options)) { + materials->push_back(std::tuple(it.first, + it.second->getDirectory(), + it.second->getName())); + } + } + } + + return materials; +} + +//===== +// +// Folder management +// +//===== + +std::shared_ptr> +MaterialManagerLocal::getMaterialFolders(const std::shared_ptr& library) const +{ + // auto materialLibrary = + // reinterpret_cast&>(library); + return MaterialLoader::getMaterialFolders(*library); +} + +void MaterialManagerLocal::createFolder(const std::shared_ptr& library, + const QString& path) +{ + library->createFolder(path); +} + +void MaterialManagerLocal::renameFolder(const std::shared_ptr& library, + const QString& oldPath, + const QString& newPath) +{ + library->renameFolder(oldPath, newPath); +} + +void MaterialManagerLocal::deleteRecursive(const std::shared_ptr& library, + const QString& path) +{ + library->deleteRecursive(path); + dereference(); +} + +//===== +// +// Material management +// +//===== + +std::shared_ptr>> +MaterialManagerLocal::getLocalMaterials() const +{ + return _materialMap; +} + +std::shared_ptr MaterialManagerLocal::getMaterial(const QString& uuid) const +{ + try { + return _materialMap->at(uuid); + } + catch (std::out_of_range&) { + throw MaterialNotFound(); + } +} + +std::shared_ptr MaterialManagerLocal::getMaterialByPath(const QString& path) const +{ + QString cleanPath = QDir::cleanPath(path); + + for (auto& library : *_libraryList) { + if (library->isLocal()) { + auto materialLibrary = + reinterpret_cast&>(library); + if (cleanPath.startsWith(materialLibrary->getDirectory())) { + try { + return materialLibrary->getMaterialByPath(cleanPath); + } + catch (const MaterialNotFound&) { + } + + // See if it's a new file saved by the old editor + { + QMutexLocker locker(&_mutex); + + if (MaterialConfigLoader::isConfigStyle(path)) { + auto material = + MaterialConfigLoader::getMaterialFromPath(materialLibrary, path); + if (material) { + (*_materialMap)[material->getUUID()] = + materialLibrary->addMaterial(material, path); + } + + return material; + } + } + } + } + } + + // Older workbenches may try files outside the context of a library + { + QMutexLocker locker(&_mutex); + + if (MaterialConfigLoader::isConfigStyle(path)) { + auto material = MaterialConfigLoader::getMaterialFromPath(nullptr, path); + + return material; + } + } + + throw MaterialNotFound(); +} + +std::shared_ptr MaterialManagerLocal::getMaterialByPath(const QString& path, + const QString& lib) const +{ + auto library = getLibrary(lib); // May throw LibraryNotFound + if (library->isLocal()) { + auto materialLibrary = + reinterpret_cast&>(library); + return materialLibrary->getMaterialByPath(path); // May throw MaterialNotFound + } + + throw LibraryNotFound(); +} + +bool MaterialManagerLocal::exists(const QString& uuid) const +{ + try { + auto material = getMaterial(uuid); + if (material) { + return true; + } + } + catch (const MaterialNotFound&) { + } + + return false; +} + +bool MaterialManagerLocal::exists(const std::shared_ptr& library, + const QString& uuid) const +{ + try { + auto material = getMaterial(uuid); + if (material && material->getLibrary()->isLocal()) { + auto materialLibrary = + reinterpret_cast&>( + *(material->getLibrary())); + return (*materialLibrary == *library); + } + } + catch (const MaterialNotFound&) { + } + + return false; +} + +void MaterialManagerLocal::remove(const QString& uuid) +{ + _materialMap->erase(uuid); +} + +void MaterialManagerLocal::saveMaterial(const std::shared_ptr& library, + const std::shared_ptr& material, + const QString& path, + bool overwrite, + bool saveAsCopy, + bool saveInherited) const +{ + if (library->isLocal()) { + auto newMaterial = + library->saveMaterial(material, path, overwrite, saveAsCopy, saveInherited); + (*_materialMap)[newMaterial->getUUID()] = newMaterial; + } +} + +bool MaterialManagerLocal::isMaterial(const fs::path& p) const +{ + if (!fs::is_regular_file(p)) { + return false; + } + // check file extension + if (p.extension() == ".FCMat") { + return true; + } + return false; +} + +bool MaterialManagerLocal::isMaterial(const QFileInfo& file) const +{ + if (!file.isFile()) { + return false; + } + // check file extension + if (file.suffix() == QStringLiteral("FCMat")) { + return true; + } + return false; +} + +std::shared_ptr>> +MaterialManagerLocal::materialsWithModel(const QString& uuid) const +{ + std::shared_ptr>> dict = + std::make_shared>>(); + + for (auto& it : *_materialMap) { + QString key = it.first; + auto material = it.second; + + if (material->hasModel(uuid)) { + (*dict)[key] = material; + } + } + + return dict; +} + +std::shared_ptr>> +MaterialManagerLocal::materialsWithModelComplete(const QString& uuid) const +{ + std::shared_ptr>> dict = + std::make_shared>>(); + + for (auto& it : *_materialMap) { + QString key = it.first; + auto material = it.second; + + if (material->isModelComplete(uuid)) { + (*dict)[key] = material; + } + } + + return dict; +} + +void MaterialManagerLocal::dereference() const +{ + // First clear the inheritences + for (auto& it : *_materialMap) { + auto material = it.second; + material->clearDereferenced(); + material->clearInherited(); + } + + // Run the dereference again + for (auto& it : *_materialMap) { + dereference(it.second); + } +} + +void MaterialManagerLocal::dereference(std::shared_ptr material) const +{ + MaterialLoader::dereference(_materialMap, material); +} + +std::shared_ptr>> +MaterialManagerLocal::getConfiguredLibraries() +{ + auto libraryList = std::make_shared>>(); + + auto param = App::GetApplication().GetParameterGroupByPath( + "User parameter:BaseApp/Preferences/Mod/Material/Resources"); + bool useBuiltInMaterials = param->GetBool("UseBuiltInMaterials", true); + bool useMatFromModules = param->GetBool("UseMaterialsFromWorkbenches", true); + bool useMatFromConfigDir = param->GetBool("UseMaterialsFromConfigDir", true); + bool useMatFromCustomDir = param->GetBool("UseMaterialsFromCustomDir", true); + + if (useBuiltInMaterials) { + QString resourceDir = QString::fromStdString(App::Application::getResourceDir() + + "/Mod/Material/Resources/Materials"); + auto libData = + std::make_shared(QStringLiteral("System"), + resourceDir, + QStringLiteral(":/icons/freecad.svg"), + true); + libraryList->push_back(libData); + } + + if (useMatFromModules) { + auto moduleParam = App::GetApplication().GetParameterGroupByPath( + "User parameter:BaseApp/Preferences/Mod/Material/Resources/Modules"); + for (auto& group : moduleParam->GetGroups()) { + // auto module = moduleParam->GetGroup(group->GetGroupName()); + auto moduleName = QString::fromStdString(group->GetGroupName()); + auto materialDir = QString::fromStdString(group->GetASCII("ModuleDir", "")); + auto materialIcon = QString::fromStdString(group->GetASCII("ModuleIcon", "")); + auto materialReadOnly = group->GetBool("ModuleReadOnly", true); + + if (materialDir.length() > 0) { + QDir dir(materialDir); + if (dir.exists()) { + auto libData = std::make_shared(moduleName, + materialDir, + materialIcon, + materialReadOnly); + libraryList->push_back(libData); + } + } + } + } + + if (useMatFromConfigDir) { + QString resourceDir = + QString::fromStdString(App::Application::getUserAppDataDir() + "/Material"); + if (!resourceDir.isEmpty()) { + QDir materialDir(resourceDir); + if (!materialDir.exists()) { + // Try creating the user dir if it doesn't exist + if (!materialDir.mkpath(resourceDir)) { + Base::Console().Log("Unable to create user library '%s'\n", + resourceDir.toStdString().c_str()); + } + } + if (materialDir.exists()) { + auto libData = std::make_shared( + QStringLiteral("User"), + resourceDir, + QStringLiteral(":/icons/preferences-general.svg"), + false); + libraryList->push_back(libData); + } + } + } + + if (useMatFromCustomDir) { + QString resourceDir = QString::fromStdString(param->GetASCII("CustomMaterialsDir", "")); + if (!resourceDir.isEmpty()) { + QDir materialDir(resourceDir); + if (materialDir.exists()) { + auto libData = std::make_shared( + QStringLiteral("Custom"), + resourceDir, + QStringLiteral(":/icons/user.svg"), + false); + libraryList->push_back(libData); + } + } + } + + return libraryList; +} diff --git a/src/Mod/Material/App/MaterialManagerLocal.h b/src/Mod/Material/App/MaterialManagerLocal.h new file mode 100644 index 0000000000..ffb0cf2147 --- /dev/null +++ b/src/Mod/Material/App/MaterialManagerLocal.h @@ -0,0 +1,131 @@ +/*************************************************************************** + * Copyright (c) 2023 David Carter * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + +#ifndef MATERIAL_MATERIALMANAGERLOCAL_H +#define MATERIAL_MATERIALMANAGERLOCAL_H + +#include + +#include + +#include + +#include "FolderTree.h" +#include "Materials.h" + +namespace fs = std::filesystem; + +class QMutex; + +namespace App +{ +class Material; +} + +namespace Materials +{ + +class MaterialLibrary; +class MaterialLibraryLocal; +class MaterialFilter; +class MaterialFilterOptions; + +class MaterialsExport MaterialManagerLocal: public Base::BaseClass +{ + TYPESYSTEM_HEADER_WITH_OVERRIDE(); + +public: + MaterialManagerLocal(); + ~MaterialManagerLocal() override = default; + + static void cleanup(); + static void refresh(); + + // Library management + std::shared_ptr>> getLibraries(); + std::shared_ptr>> getMaterialLibraries(); + std::shared_ptr getLibrary(const QString& name) const; + void createLibrary(const QString& libraryName, + const QString& directory, + const QString& icon, + bool readOnly = true); + void renameLibrary(const QString& libraryName, const QString& newName); + void changeIcon(const QString& libraryName, const QString& icon); + void removeLibrary(const QString& libraryName); + std::shared_ptr>> + libraryMaterials(const QString& libraryName); + std::shared_ptr>> + libraryMaterials(const QString& libraryName, + const std::shared_ptr& filter, + const MaterialFilterOptions& options); + + // Folder management + std::shared_ptr> + getMaterialFolders(const std::shared_ptr& library) const; + void createFolder(const std::shared_ptr& library, const QString& path); + void renameFolder(const std::shared_ptr& library, + const QString& oldPath, + const QString& newPath); + void deleteRecursive(const std::shared_ptr& library, const QString& path); + + // Material management + std::shared_ptr>> getLocalMaterials() const; + std::shared_ptr getMaterial(const QString& uuid) const; + std::shared_ptr getMaterialByPath(const QString& path) const; + std::shared_ptr getMaterialByPath(const QString& path, const QString& library) const; + bool exists(const QString& uuid) const; + bool exists(const std::shared_ptr& library, const QString& uuid) const; + void remove(const QString& uuid); + + void saveMaterial(const std::shared_ptr& library, + const std::shared_ptr& material, + const QString& path, + bool overwrite, + bool saveAsCopy, + bool saveInherited) const; + + bool isMaterial(const fs::path& p) const; + bool isMaterial(const QFileInfo& file) const; + + std::shared_ptr>> + materialsWithModel(const QString& uuid) const; + std::shared_ptr>> + materialsWithModelComplete(const QString& uuid) const; + void dereference(std::shared_ptr material) const; + void dereference() const; + +protected: + static std::shared_ptr>> getConfiguredLibraries(); + bool passFilter(const std::shared_ptr& material, + const std::shared_ptr& filter, + const Materials::MaterialFilterOptions& options) const; + +private: + static std::shared_ptr>> _libraryList; + static std::shared_ptr>> _materialMap; + static QMutex _mutex; + + static void initLibraries(); +}; + +} // namespace Materials + +#endif // MATERIAL_MATERIALMANAGERLOCAL_H \ No newline at end of file diff --git a/src/Mod/Material/App/MaterialManagerPy.xml b/src/Mod/Material/App/MaterialManagerPy.xml deleted file mode 100644 index 354c403351..0000000000 --- a/src/Mod/Material/App/MaterialManagerPy.xml +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - Material descriptions. - - - - Get a material object by specifying its UUID - - - - - Get a material object by specifying its path and library name - - - - - Create a new material object by specifying the UUID of its parent - - - - - List of Material libraries. - - - - - - List of Materials. - - - - - - Get a list of materials implementing the specified model - - - - - Get a list of materials implementing the specified model, with values for all properties - - - - - Save the material in the specified library - - - - - Returns a filtered material list - - - - - Refreshes the material tree. Use sparingly as this is an expensive operation. - - - - diff --git a/src/Mod/Material/App/MaterialManagerPyImp.cpp b/src/Mod/Material/App/MaterialManagerPyImp.cpp index c84075d1cb..61afb574f1 100644 --- a/src/Mod/Material/App/MaterialManagerPyImp.cpp +++ b/src/Mod/Material/App/MaterialManagerPyImp.cpp @@ -47,7 +47,7 @@ std::string MaterialManagerPy::representation() const PyObject* MaterialManagerPy::PyMake(struct _typeobject*, PyObject*, PyObject*) // Python wrapper { // never create such objects with the constructor - return new MaterialManagerPy(new MaterialManager()); + return new MaterialManagerPy(&(MaterialManager::getManager())); } // constructor method @@ -136,15 +136,25 @@ PyObject* MaterialManagerPy::inheritMaterial(PyObject* args) Py::List MaterialManagerPy::getMaterialLibraries() const { - auto libraries = getMaterialManagerPtr()->getMaterialLibraries(); + auto libraries = getMaterialManagerPtr()->getLibraries(); Py::List list; for (auto it = libraries->begin(); it != libraries->end(); it++) { auto lib = *it; Py::Tuple libTuple(3); - libTuple.setItem(0, Py::String(lib->getName().toStdString())); - libTuple.setItem(1, Py::String(lib->getDirectoryPath().toStdString())); - libTuple.setItem(2, Py::String(lib->getIconPath().toStdString())); + if (lib->isLocal()) { + auto materialLibrary = + reinterpret_cast&>(lib); + libTuple.setItem(0, Py::String(materialLibrary->getName().toStdString())); + libTuple.setItem(1, Py::String(materialLibrary->getDirectoryPath().toStdString())); + libTuple.setItem(2, Py::String(materialLibrary->getIconPath().toStdString())); + } + else + { + libTuple.setItem(0, Py::String()); + libTuple.setItem(1, Py::String()); + libTuple.setItem(2, Py::String()); + } list.append(libTuple); } @@ -156,7 +166,7 @@ Py::Dict MaterialManagerPy::getMaterials() const { Py::Dict dict; - auto materials = getMaterialManagerPtr()->getMaterials(); + auto materials = getMaterialManagerPtr()->getLocalMaterials(); for (auto it = materials->begin(); it != materials->end(); it++) { QString key = it->first; @@ -284,17 +294,19 @@ PyObject* MaterialManagerPy::save(PyObject* args, PyObject* kwds) return Py_None; } -void addMaterials(Py::List& list, +void addMaterials(MaterialManager *manager, + Py::List& list, const std::shared_ptr>>& tree) { for (auto& node : *tree) { - if (node.second->getType() == MaterialTreeNode::DataNode) { - auto material = node.second->getData(); + if (node.second->getType() == MaterialTreeNode::NodeType::DataNode) { + auto uuid = node.second->getUUID(); + auto material = manager->getMaterial(uuid); PyObject* materialPy = new MaterialPy(new Material(*material)); list.append(Py::Object(materialPy, true)); } else { - addMaterials(list, node.second->getFolder()); + addMaterials(manager, list, node.second->getFolder()); } } } @@ -327,13 +339,13 @@ PyObject* MaterialManagerPy::filterMaterials(PyObject* args, PyObject* kwds) auto filter = std::make_shared(*(static_cast(filterPy)->getMaterialFilterPtr())); - auto libraries = getMaterialManagerPtr()->getMaterialLibraries(); + auto libraries = getMaterialManagerPtr()->getLibraries(); Py::List list; for (auto lib : *libraries) { auto tree = getMaterialManagerPtr()->getMaterialTree(lib, filter, options); if (tree->size() > 0) { - addMaterials(list, tree); + addMaterials(getMaterialManagerPtr(), list, tree); } } diff --git a/src/Mod/Material/App/MaterialProperty.pyi b/src/Mod/Material/App/MaterialProperty.pyi new file mode 100644 index 0000000000..c5c827d484 --- /dev/null +++ b/src/Mod/Material/App/MaterialProperty.pyi @@ -0,0 +1,25 @@ +from Base.Metadata import export +from ModelProperty import ModelProperty +from typing import Final + +@export( + Include="Mod/Material/App/Materials.h", + Namespace="Materials", + FatherInclude="Mod/Material/App/Model.h", + FatherNamespace="Materials", + Constructor=True, + Delete=False, +) +class MaterialProperty(ModelProperty): + """ + Material property descriptions. + + Author: DavidCarter (dcarter@davidcarter.ca) + Licence: LGPL + """ + + Value: Final[object] = None + """The value of the material property.""" + + Empty: Final[bool] = False + """The property value is undefined.""" diff --git a/src/Mod/Material/App/MaterialPropertyPyImp.cpp b/src/Mod/Material/App/MaterialPropertyPyImp.cpp new file mode 100644 index 0000000000..d6bced53c8 --- /dev/null +++ b/src/Mod/Material/App/MaterialPropertyPyImp.cpp @@ -0,0 +1,74 @@ +/*************************************************************************** + * Copyright (c) 2023 David Carter * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + +#include "PreCompiled.h" + +#include "Model.h" +#include "PyVariants.h" +#include "ModelPropertyPy.h" +#include "MaterialPropertyPy.h" + +#include "MaterialPropertyPy.cpp" + +using namespace Materials; + +// returns a string which represents the object e.g. when printed in python +std::string MaterialPropertyPy::representation() const +{ + std::stringstream str; + str << ""; + + return str.str(); +} + +PyObject* MaterialPropertyPy::PyMake(struct _typeobject*, PyObject*, PyObject*) // Python wrapper +{ + // never create such objects with the constructor + return new MaterialPropertyPy(new MaterialProperty()); +} + +// constructor method +int MaterialPropertyPy::PyInit(PyObject* /*args*/, PyObject* /*kwd*/) +{ + return 0; +} + +Py::Object MaterialPropertyPy::getValue() const +{ + auto value = getMaterialPropertyPtr()->getValue(); + + return Py::Object(_pyObjectFromVariant(value), true); +} + +Py::Boolean MaterialPropertyPy::getEmpty() const +{ + return getMaterialPropertyPtr()->isEmpty(); +} + +PyObject* MaterialPropertyPy::getCustomAttributes(const char* /*attr*/) const +{ + return nullptr; +} + +int MaterialPropertyPy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/) +{ + return 0; +} \ No newline at end of file diff --git a/src/Mod/Material/App/MaterialPy.xml b/src/Mod/Material/App/MaterialPy.xml deleted file mode 100644 index e6998690f1..0000000000 --- a/src/Mod/Material/App/MaterialPy.xml +++ /dev/null @@ -1,230 +0,0 @@ - - - - - - Material descriptions. - - - - Model library name. - - - - - - Model library path. - - - - - - Model icon path. - - - - - - Model name. - - - - - - Model directory relative to the library root. - - - - - - Unique model identifier. This is only valid after the material is saved. - - - - - - Description of the material. - - - - - - URL to a material reference. - - - - - - Reference for material data. - - - - - - Parent material UUID. - - - - - - deprecated -- Author and license information. - - - - - - Author information. - - - - - - License information. - - - - - - List of implemented models. - - - - - - List of implemented models. - - - - - - List of searchable tags. - - - - - - Add the physical model with the given UUID - - - - - Remove the physical model with the given UUID - - - - - Check if the material implements the physical model with the given UUID - - - - - Add the appearance model with the given UUID - - - - - Remove the appearance model with the given UUID - - - - - Check if the material implements the appearance model with the given UUID - - - - - Check if the material implements the physical model with the given UUID, and has values defined for each property - - - - - Check if the material implements the appearance model with the given UUID, and has values defined for each property - - - - - Check if the material implements the physical property with the given name - - - - - Check if the material implements the appearance property with the given name - - - - - Returns true of there are legacy properties - - - - - deprecated -- Dictionary of all material properties. - - - - - - deprecated -- Dictionary of material physical properties. - - - - - - deprecated -- Dictionary of material appearance properties. - - - - - - deprecated -- Dictionary of material legacy properties. - - - - - - Get the value associated with the property - - - - - Set the value associated with the property - - - - - Get the value associated with the property - - - - - Set the value associated with the property - - - - - Property keys - - - - - Property values - - - - - - diff --git a/src/Mod/Material/App/MaterialPyImp.cpp b/src/Mod/Material/App/MaterialPyImp.cpp index f650df64a5..5a267d29ed 100644 --- a/src/Mod/Material/App/MaterialPyImp.cpp +++ b/src/Mod/Material/App/MaterialPyImp.cpp @@ -36,15 +36,15 @@ #include "MaterialLibrary.h" #include "MaterialPy.h" #include "MaterialValue.h" +#include "PyVariants.h" + +#include "ModelPropertyPy.h" +#include "MaterialPropertyPy.h" #include "MaterialPy.cpp" using namespace Materials; -// Forward declaration -static PyObject* _pyObjectFromVariant(const QVariant& value); -static Py::List getList(const QVariant& value); - // returns a string which represents the object e.g. when printed in python std::string MaterialPy::representation() const { @@ -68,19 +68,34 @@ int MaterialPy::PyInit(PyObject* /*args*/, PyObject* /*kwd*/) Py::String MaterialPy::getLibraryName() const { auto library = getMaterialPtr()->getLibrary(); - return {library ? library->getName().toStdString() : ""}; + if (library->isLocal()) { + auto materialLibrary = + reinterpret_cast&>(library); + return {materialLibrary ? materialLibrary->getName().toStdString() : ""}; + } + return ""; } Py::String MaterialPy::getLibraryRoot() const { auto library = getMaterialPtr()->getLibrary(); - return {library ? library->getDirectoryPath().toStdString() : ""}; + if (library->isLocal()) { + auto materialLibrary = + reinterpret_cast&>(library); + return {materialLibrary ? materialLibrary->getDirectoryPath().toStdString() : ""}; + } + return ""; } Py::String MaterialPy::getLibraryIcon() const { auto library = getMaterialPtr()->getLibrary(); - return {library ? library->getIconPath().toStdString() : ""}; + if (library->isLocal()) { + auto materialLibrary = + reinterpret_cast&>(library); + return {materialLibrary ? materialLibrary->getIconPath().toStdString() : ""}; + } + return ""; } Py::String MaterialPy::getName() const @@ -98,6 +113,11 @@ Py::String MaterialPy::getDirectory() const return {getMaterialPtr()->getDirectory().toStdString()}; } +void MaterialPy::setDirectory(Py::String arg) +{ + getMaterialPtr()->setDirectory(QString::fromStdString(arg)); +} + Py::String MaterialPy::getUUID() const { return {getMaterialPtr()->getUUID().toStdString()}; @@ -138,6 +158,11 @@ Py::String MaterialPy::getParent() const return {getMaterialPtr()->getParentUUID().toStdString()}; } +void MaterialPy::setParent(Py::String arg) +{ + getMaterialPtr()->setParentUUID(QString::fromStdString(arg)); +} + Py::String MaterialPy::getAuthorAndLicense() const { return {getMaterialPtr()->getAuthorAndLicense().toStdString()}; @@ -438,52 +463,6 @@ Py::Dict MaterialPy::getLegacyProperties() const return dict; } -static Py::List getList(const QVariant& value) -{ - auto listValue = value.value>(); - Py::List list; - - for (auto& it : listValue) { - list.append(Py::Object(_pyObjectFromVariant(it))); - } - - return list; -} - -static PyObject* _pyObjectFromVariant(const QVariant& value) -{ - if (value.isNull()) { - Py_RETURN_NONE; - } - - if (value.userType() == qMetaTypeId()) { - return new Base::QuantityPy(new Base::Quantity(value.value())); - } - if (value.userType() == QMetaType::Double) { - return PyFloat_FromDouble(value.toDouble()); - } - if (value.userType() == QMetaType::Float) { - return PyFloat_FromDouble(value.toFloat()); - } - if (value.userType() == QMetaType::Int) { - return PyLong_FromLong(value.toInt()); - } - if (value.userType() == QMetaType::Long) { - return PyLong_FromLong(value.toInt()); - } - if (value.userType() == QMetaType::Bool) { - return Py::new_reference_to(Py::Boolean(value.toBool())); - } - if (value.userType() == QMetaType::QString) { - return PyUnicode_FromString(value.toString().toStdString().c_str()); - } - if (value.userType() == qMetaTypeId>()) { - return Py::new_reference_to(getList(value)); - } - - throw UnknownValueType(); -} - PyObject* MaterialPy::getPhysicalValue(PyObject* args) { char* name; @@ -502,13 +481,13 @@ PyObject* MaterialPy::getPhysicalValue(PyObject* args) if (property->getType() == MaterialValue::Array2D) { auto value = - std::static_pointer_cast(property->getMaterialValue()); - return new Array2DPy(new Material2DArray(*value)); + std::static_pointer_cast(property->getMaterialValue()); + return new Array2DPy(new Array2D(*value)); } if (property->getType() == MaterialValue::Array3D) { auto value = - std::static_pointer_cast(property->getMaterialValue()); - return new Array3DPy(new Material3DArray(*value)); + std::static_pointer_cast(property->getMaterialValue()); + return new Array3DPy(new Array3D(*value)); } QVariant value = property->getValue(); @@ -523,8 +502,7 @@ PyObject* MaterialPy::setPhysicalValue(PyObject* args) return nullptr; } - getMaterialPtr()->setPhysicalValue(QString::fromStdString(name), - QString::fromStdString(value)); + getMaterialPtr()->setPhysicalValue(QString::fromStdString(name), QString::fromStdString(value)); Py_INCREF(Py_None); return Py_None; } @@ -536,7 +514,27 @@ PyObject* MaterialPy::getAppearanceValue(PyObject* args) return nullptr; } - QVariant value = getMaterialPtr()->getAppearanceValue(QString::fromStdString(name)); + if (!getMaterialPtr()->hasAppearanceProperty(QString::fromStdString(name))) { + Py_RETURN_NONE; + } + + auto property = getMaterialPtr()->getAppearanceProperty(QString::fromStdString(name)); + if (!property) { + Py_RETURN_NONE; + } + + if (property->getType() == MaterialValue::Array2D) { + auto value = + std::static_pointer_cast(property->getMaterialValue()); + return new Array2DPy(new Array2D(*value)); + } + if (property->getType() == MaterialValue::Array3D) { + auto value = + std::static_pointer_cast(property->getMaterialValue()); + return new Array3DPy(new Array3D(*value)); + } + + QVariant value = property->getValue(); return _pyObjectFromVariant(value); } @@ -553,6 +551,93 @@ PyObject* MaterialPy::setAppearanceValue(PyObject* args) Py_INCREF(Py_None); return Py_None; } +PyObject* MaterialPy::setValue(PyObject* args) +{ + char* name; + char* value; + PyObject* listObj; + PyObject* arrayObj; + if (PyArg_ParseTuple(args, "ss", &name, &value)) { + getMaterialPtr()->setValue(QString::fromStdString(name), QString::fromStdString(value)); + Py_Return; + } + + PyErr_Clear(); + if (PyArg_ParseTuple(args, "sO!", &name, &PyList_Type, &listObj)) { + QList variantList; + Py::List list(listObj); + for (auto itemObj : list) { + Py::String item(itemObj); + QString value(QString::fromStdString(item.as_string())); + QVariant variant = QVariant::fromValue(value); + variantList.append(variant); + } + + getMaterialPtr()->setValue(QString::fromStdString(name), variantList); + Py_Return; + } + + PyErr_Clear(); + if (PyArg_ParseTuple(args, "sO!", &name, &(Array2DPy::Type), &arrayObj)) { + auto array = static_cast(arrayObj); + auto shared = std::make_shared(*array->getArray2DPtr()); + + getMaterialPtr()->setValue(QString::fromStdString(name), shared); + Py_Return; + } + + PyErr_Clear(); + if (PyArg_ParseTuple(args, "sO!", &name, &(Array3DPy::Type), &arrayObj)) { + auto array = static_cast(arrayObj); + auto shared = std::make_shared(*array->getArray3DPtr()); + + getMaterialPtr()->setValue(QString::fromStdString(name), shared); + Py_Return; + } + + PyErr_SetString(PyExc_TypeError, "Either a string, a list, or an array are expected"); + return nullptr; +} + +Py::Dict MaterialPy::getPropertyObjects() const +{ + Py::Dict dict; + + auto properties = getMaterialPtr()->getPhysicalProperties(); + for (auto& it : properties) { + QString key = it.first; + auto materialProperty = it.second; + + // if (materialProperty->getType() == MaterialValue::Array2D) { + // auto value = std::static_pointer_cast( + // materialProperty->getMaterialValue()); + // dict.setItem(Py::String(key.toStdString()), + // Py::Object(new Array2DPy(new Array2D(*value)), true)); + // } + // else if (materialProperty->getType() == MaterialValue::Array3D) { + // auto value = std::static_pointer_cast( + // materialProperty->getMaterialValue()); + // dict.setItem(Py::String(key.toStdString()), + // Py::Object(new Array3DPy(new Array3D(*value)), true)); + // } + // else { + dict.setItem( + Py::String(key.toStdString()), + Py::Object(new MaterialPropertyPy(new MaterialProperty(materialProperty)), true)); + // } + } + + properties = getMaterialPtr()->getAppearanceProperties(); + for (auto& it : properties) { + QString key = it.first; + auto materialProperty = it.second; + dict.setItem( + Py::String(key.toStdString()), + Py::Object(new MaterialPropertyPy(new MaterialProperty(materialProperty)), true)); + } + + return dict; +} PyObject* MaterialPy::keys() { @@ -584,4 +669,4 @@ PyObject* MaterialPy::mapping_subscript(PyObject* self, PyObject* key) { Py::Dict dict = static_cast(self)->getProperties(); return Py::new_reference_to(dict.getItem(Py::Object(key))); -} +} \ No newline at end of file diff --git a/src/Mod/Material/App/MaterialValue.cpp b/src/Mod/Material/App/MaterialValue.cpp index af84945adf..ba20eeb1c0 100644 --- a/src/Mod/Material/App/MaterialValue.cpp +++ b/src/Mod/Material/App/MaterialValue.cpp @@ -102,6 +102,44 @@ bool MaterialValue::operator==(const MaterialValue& other) const return (_valueType == other._valueType) && (_value == other._value); } +void MaterialValue::validate(const MaterialValue& other) const +{ + if (_valueType != other._valueType) { + throw InvalidProperty("Material property value types don't match"); + } + if (_valueType == Quantity) { + auto q1 = _value.value(); + auto q2 = other._value.value(); + if (q1.isValid()) { + if (!q2.isValid()) { + throw InvalidProperty("Invalid remote Material property quantity value"); + } + if (q1.getUserString() != q2.getUserString()) { + // Direct comparisons of the quantities may have precision issues + // throw InvalidProperty("Material property quantity values don't match"); + } + } + else { + if (q2.isValid()) { + throw InvalidProperty("Remote Material property quantity should not have a value"); + } + } + } + else if (_valueType == Array2D) { + auto a1 = static_cast(this); + auto a2 = static_cast(&other); + a1->validate(*a2); + } + else if (_valueType == Array3D) { + auto a1 = static_cast(this); + auto a2 = static_cast(&other); + a1->validate(*a2); + } + else if (!(_value.isNull() && other._value.isNull()) && (_value != other._value)) { + throw InvalidProperty("Material property values don't match"); + } +} + QString MaterialValue::escapeString(const QString& source) { QString res = source; @@ -205,6 +243,11 @@ void MaterialValue::setList(const QList& value) } bool MaterialValue::isNull() const +{ + return isEmpty(); +} + +bool MaterialValue::isEmpty() const { if (_value.isNull()) { return true; @@ -317,24 +360,24 @@ const Base::QuantityFormat MaterialValue::getQuantityFormat() //=== -TYPESYSTEM_SOURCE(Materials::Material2DArray, Materials::MaterialValue) +TYPESYSTEM_SOURCE(Materials::Array2D, Materials::MaterialValue) -Material2DArray::Material2DArray() - : MaterialValue(Array2D, Array2D) +Array2D::Array2D() + : MaterialValue(MaterialValue::Array2D, MaterialValue::Array2D) , _columns(0) { // Initialize separatelt to prevent recursion // setType(Array2D); } -Material2DArray::Material2DArray(const Material2DArray& other) +Array2D::Array2D(const Array2D& other) : MaterialValue(other) , _columns(other._columns) { deepCopy(other); } -Material2DArray& Material2DArray::operator=(const Material2DArray& other) +Array2D& Array2D::operator=(const Array2D& other) { if (this == &other) { return *this; @@ -348,7 +391,7 @@ Material2DArray& Material2DArray::operator=(const Material2DArray& other) return *this; } -void Material2DArray::deepCopy(const Material2DArray& other) +void Array2D::deepCopy(const Array2D& other) { // Deep copy for (auto& row : other._rows) { @@ -361,26 +404,55 @@ void Material2DArray::deepCopy(const Material2DArray& other) } } -bool Material2DArray::isNull() const +bool Array2D::isNull() const +{ + return isEmpty(); +} + +bool Array2D::isEmpty() const { return rows() <= 0; } -void Material2DArray::validateRow(int row) const +void Array2D::validateRow(int row) const { if (row < 0 || row >= rows()) { throw InvalidIndex(); } } -void Material2DArray::validateColumn(int column) const +void Array2D::validateColumn(int column) const { if (column < 0 || column >= columns()) { throw InvalidIndex(); } } -std::shared_ptr> Material2DArray::getRow(int row) const +void Array2D::validate(const Array2D& other) const +{ + if (rows() != other.rows()) { + Base::Console().Log("Local row count %d, remote %d\n", rows(), other.rows()); + throw InvalidProperty("Material property value row counts don't match"); + } + if (columns() != other.columns()) { + Base::Console().Log("Local column count %d, remote %d\n", columns(), other.columns()); + throw InvalidProperty("Material property value column counts don't match"); + } + try { + for (int i = 0; i < rows(); i++) { + for (int j = 0; j < columns(); j++) { + if (getValue(i, j) != other.getValue(i, j)) { + throw InvalidProperty("Material property values don't match"); + } + } + } + } + catch (const InvalidIndex&) { + throw InvalidProperty("Material property value invalid array index"); + } +} + +std::shared_ptr> Array2D::getRow(int row) const { validateRow(row); @@ -392,7 +464,7 @@ std::shared_ptr> Material2DArray::getRow(int row) const } } -std::shared_ptr> Material2DArray::getRow(int row) +std::shared_ptr> Array2D::getRow(int row) { validateRow(row); @@ -404,17 +476,17 @@ std::shared_ptr> Material2DArray::getRow(int row) } } -void Material2DArray::addRow(const std::shared_ptr>& row) +void Array2D::addRow(const std::shared_ptr>& row) { _rows.push_back(row); } -void Material2DArray::insertRow(int index, const std::shared_ptr>& row) +void Array2D::insertRow(int index, const std::shared_ptr>& row) { _rows.insert(_rows.begin() + index, row); } -void Material2DArray::deleteRow(int row) +void Array2D::deleteRow(int row) { if (row >= static_cast(_rows.size()) || row < 0) { throw InvalidIndex(); @@ -422,7 +494,18 @@ void Material2DArray::deleteRow(int row) _rows.erase(_rows.begin() + row); } -void Material2DArray::setValue(int row, int column, const QVariant& value) +void Array2D::setRows(int rowCount) +{ + while (rows() < rowCount) { + auto row = std::make_shared>(); + for (int i = 0; i < columns(); i++) { + row->append(QVariant()); + } + addRow(row); + } +} + +void Array2D::setValue(int row, int column, const QVariant& value) { validateRow(row); validateColumn(column); @@ -436,7 +519,7 @@ void Material2DArray::setValue(int row, int column, const QVariant& value) } } -QVariant Material2DArray::getValue(int row, int column) const +QVariant Array2D::getValue(int row, int column) const { validateColumn(column); @@ -449,7 +532,7 @@ QVariant Material2DArray::getValue(int row, int column) const } } -void Material2DArray::dumpRow(const std::shared_ptr>& row) +void Array2D::dumpRow(const std::shared_ptr>& row) { Base::Console().Log("row: "); for (auto& column : *row) { @@ -458,14 +541,14 @@ void Material2DArray::dumpRow(const std::shared_ptr>& row) Base::Console().Log("\n"); } -void Material2DArray::dump() const +void Array2D::dump() const { for (auto& row : _rows) { dumpRow(row); } } -QString Material2DArray::getYAMLString() const +QString Array2D::getYAMLString() const { if (isNull()) { return QString(); @@ -511,10 +594,10 @@ QString Material2DArray::getYAMLString() const //=== -TYPESYSTEM_SOURCE(Materials::Material3DArray, Materials::MaterialValue) +TYPESYSTEM_SOURCE(Materials::Array3D, Materials::MaterialValue) -Material3DArray::Material3DArray() - : MaterialValue(Array3D, Array3D) +Array3D::Array3D() + : MaterialValue(MaterialValue::Array3D, MaterialValue::Array3D) , _currentDepth(0) , _columns(0) { @@ -522,26 +605,71 @@ Material3DArray::Material3DArray() // setType(Array3D); } -bool Material3DArray::isNull() const +Array3D::Array3D(const Array3D& other) + : MaterialValue(other) + , _currentDepth(other._currentDepth) + , _columns(other._columns) +{ + deepCopy(other); +} + +Array3D& Array3D::operator=(const Array3D& other) +{ + if (this == &other) { + return *this; + } + + MaterialValue::operator=(other); + _columns = other._columns; + _currentDepth = other._currentDepth; + + deepCopy(other); + + return *this; +} + +void Array3D::deepCopy(const Array3D& other) +{ + // Deep copy + _rowMap.clear(); + for (auto& depthTable : other._rowMap) { + auto depth = addDepth(depthTable.first); + auto rows = depthTable.second; + for (auto row : *rows) { + auto newRow = std::make_shared>(); + for (auto column : *row) { + newRow->append(column); + } + addRow(depth, newRow); + } + } +} + +bool Array3D::isNull() const +{ + return isEmpty(); +} + +bool Array3D::isEmpty() const { return depth() <= 0; } -void Material3DArray::validateDepth(int level) const +void Array3D::validateDepth(int level) const { if (level < 0 || level >= depth()) { throw InvalidIndex(); } } -void Material3DArray::validateColumn(int column) const +void Array3D::validateColumn(int column) const { if (column < 0 || column >= columns()) { throw InvalidIndex(); } } -void Material3DArray::validateRow(int level, int row) const +void Array3D::validateRow(int level, int row) const { validateDepth(level); @@ -550,8 +678,18 @@ void Material3DArray::validateRow(int level, int row) const } } +void Array3D::validate(const Array3D& other) const +{ + if (depth() != other.depth()) { + throw InvalidProperty("Material property value row counts don't match"); + } + if (columns() != other.columns()) { + throw InvalidProperty("Material property value column counts don't match"); + } +} + const std::shared_ptr>>>& -Material3DArray::getTable(const Base::Quantity& depth) const +Array3D::getTable(const Base::Quantity& depth) const { for (auto& it : _rowMap) { if (std::get<0>(it) == depth) { @@ -563,7 +701,7 @@ Material3DArray::getTable(const Base::Quantity& depth) const } const std::shared_ptr>>>& -Material3DArray::getTable(int depthIndex) const +Array3D::getTable(int depthIndex) const { try { return std::get<1>(_rowMap.at(depthIndex)); @@ -573,7 +711,7 @@ Material3DArray::getTable(int depthIndex) const } } -std::shared_ptr> Material3DArray::getRow(int depth, int row) const +std::shared_ptr> Array3D::getRow(int depth, int row) const { validateRow(depth, row); @@ -585,13 +723,13 @@ std::shared_ptr> Material3DArray::getRow(int depth, int ro } } -std::shared_ptr> Material3DArray::getRow(int row) const +std::shared_ptr> Array3D::getRow(int row) const { // Check if we can convert otherwise throw error return getRow(_currentDepth, row); } -std::shared_ptr> Material3DArray::getRow(int depth, int row) +std::shared_ptr> Array3D::getRow(int depth, int row) { validateRow(depth, row); @@ -603,12 +741,12 @@ std::shared_ptr> Material3DArray::getRow(int depth, int ro } } -std::shared_ptr> Material3DArray::getRow(int row) +std::shared_ptr> Array3D::getRow(int row) { return getRow(_currentDepth, row); } -void Material3DArray::addRow(int depth, const std::shared_ptr>& row) +void Array3D::addRow(int depth, const std::shared_ptr>& row) { try { getTable(depth)->push_back(row); @@ -618,12 +756,12 @@ void Material3DArray::addRow(int depth, const std::shared_ptr>& row) +void Array3D::addRow(const std::shared_ptr>& row) { addRow(_currentDepth, row); } -int Material3DArray::addDepth(int depth, const Base::Quantity& value) +int Array3D::addDepth(int depth, const Base::Quantity& value) { if (depth == this->depth()) { // Append to the end @@ -639,7 +777,7 @@ int Material3DArray::addDepth(int depth, const Base::Quantity& value) return depth; } -int Material3DArray::addDepth(const Base::Quantity& value) +int Array3D::addDepth(const Base::Quantity& value) { auto rowVector = std::make_shared>>>(); auto entry = std::make_pair(value, rowVector); @@ -648,13 +786,22 @@ int Material3DArray::addDepth(const Base::Quantity& value) return depth() - 1; } -void Material3DArray::deleteDepth(int depth) +void Array3D::deleteDepth(int depth) { deleteRows(depth); // This may throw an InvalidIndex _rowMap.erase(_rowMap.begin() + depth); } -void Material3DArray::insertRow(int depth, +void Array3D::setDepth(int depthCount) +{ + Base::Quantity dummy; + dummy.setInvalid(); + while (depth() < depthCount) { + addDepth(dummy); + } +} + +void Array3D::insertRow(int depth, int row, const std::shared_ptr>& rowData) { @@ -667,12 +814,12 @@ void Material3DArray::insertRow(int depth, } } -void Material3DArray::insertRow(int row, const std::shared_ptr>& rowData) +void Array3D::insertRow(int row, const std::shared_ptr>& rowData) { insertRow(_currentDepth, row, rowData); } -void Material3DArray::deleteRow(int depth, int row) +void Array3D::deleteRow(int depth, int row) { auto table = getTable(depth); if (row >= static_cast(table->size()) || row < 0) { @@ -681,23 +828,23 @@ void Material3DArray::deleteRow(int depth, int row) table->erase(table->begin() + row); } -void Material3DArray::deleteRow(int row) +void Array3D::deleteRow(int row) { deleteRow(_currentDepth, row); } -void Material3DArray::deleteRows(int depth) +void Array3D::deleteRows(int depth) { auto table = getTable(depth); table->clear(); } -void Material3DArray::deleteRows() +void Array3D::deleteRows() { deleteRows(_currentDepth); } -int Material3DArray::rows(int depth) const +int Array3D::rows(int depth) const { if (depth < 0 || (depth == 0 && this->depth() == 0)) { return 0; @@ -707,7 +854,21 @@ int Material3DArray::rows(int depth) const return getTable(depth)->size(); } -void Material3DArray::setValue(int depth, int row, int column, const Base::Quantity& value) +void Array3D::setRows(int depth, int rowCount) +{ + Base::Quantity dummy; + dummy.setInvalid(); + + while (rows(depth) < rowCount) { + auto row = std::make_shared>(); + for (int i = 0; i < columns(); i++) { + row->append(dummy); + } + addRow(depth, row); + } +} + +void Array3D::setValue(int depth, int row, int column, const Base::Quantity& value) { validateRow(depth, row); validateColumn(column); @@ -721,12 +882,12 @@ void Material3DArray::setValue(int depth, int row, int column, const Base::Quant } } -void Material3DArray::setValue(int row, int column, const Base::Quantity& value) +void Array3D::setValue(int row, int column, const Base::Quantity& value) { setValue(_currentDepth, row, column, value); } -void Material3DArray::setDepthValue(int depth, const Base::Quantity& value) +void Array3D::setDepthValue(int depth, const Base::Quantity& value) { try { auto oldRows = getTable(depth); @@ -737,13 +898,13 @@ void Material3DArray::setDepthValue(int depth, const Base::Quantity& value) } } -void Material3DArray::setDepthValue(const Base::Quantity& value) +void Array3D::setDepthValue(const Base::Quantity& value) { setDepthValue(_currentDepth, value); } -Base::Quantity Material3DArray::getValue(int depth, int row, int column) const +Base::Quantity Array3D::getValue(int depth, int row, int column) const { // getRow validates depth and row. Do that first auto val = getRow(depth, row); @@ -757,12 +918,12 @@ Base::Quantity Material3DArray::getValue(int depth, int row, int column) const } } -Base::Quantity Material3DArray::getValue(int row, int column) const +Base::Quantity Array3D::getValue(int row, int column) const { return getValue(_currentDepth, row, column); } -Base::Quantity Material3DArray::getDepthValue(int depth) const +Base::Quantity Array3D::getDepthValue(int depth) const { validateDepth(depth); @@ -774,12 +935,12 @@ Base::Quantity Material3DArray::getDepthValue(int depth) const } } -int Material3DArray::currentDepth() const +int Array3D::currentDepth() const { return _currentDepth; } -void Material3DArray::setCurrentDepth(int depth) +void Array3D::setCurrentDepth(int depth) { validateDepth(depth); @@ -794,7 +955,7 @@ void Material3DArray::setCurrentDepth(int depth) } } -QString Material3DArray::getYAMLString() const +QString Array3D::getYAMLString() const { if (isNull()) { return QString(); diff --git a/src/Mod/Material/App/MaterialValue.h b/src/Mod/Material/App/MaterialValue.h index 7dbebfe734..ffe88e537e 100644 --- a/src/Mod/Material/App/MaterialValue.h +++ b/src/Mod/Material/App/MaterialValue.h @@ -91,6 +91,7 @@ public: return _value.value>(); } virtual bool isNull() const; + virtual bool isEmpty() const; virtual const QVariant getValueAt(const QVariant& value) const { @@ -111,6 +112,8 @@ public: // The precision is based on the value from the original materials editor static const int PRECISION = 6; + + void validate(const MaterialValue& other) const; protected: MaterialValue(ValueType type, ValueType inherited); @@ -133,18 +136,19 @@ private: static QMap _typeMap; }; -class MaterialsExport Material2DArray: public MaterialValue +class MaterialsExport Array2D: public MaterialValue { TYPESYSTEM_HEADER_WITH_OVERRIDE(); public: - Material2DArray(); - Material2DArray(const Material2DArray& other); - ~Material2DArray() override = default; + Array2D(); + Array2D(const Array2D& other); + ~Array2D() override = default; - Material2DArray& operator=(const Material2DArray& other); + Array2D& operator=(const Array2D& other); bool isNull() const override; + bool isEmpty() const override; const QList>>& getArray() const { @@ -153,6 +157,7 @@ public: void validateRow(int row) const; void validateColumn(int column) const; + void validate(const Array2D& other) const; std::shared_ptr> getRow(int row) const; std::shared_ptr> getRow(int row); @@ -171,6 +176,7 @@ public: void addRow(const std::shared_ptr>& row); void insertRow(int index, const std::shared_ptr>& row); void deleteRow(int row); + void setRows(int rowCount); void setValue(int row, int column, const QVariant& value); QVariant getValue(int row, int column) const; @@ -178,7 +184,7 @@ public: QString getYAMLString() const override; protected: - void deepCopy(const Material2DArray& other); + void deepCopy(const Array2D& other); QList>> _rows; int _columns; @@ -188,15 +194,19 @@ private: void dump() const; }; -class MaterialsExport Material3DArray: public MaterialValue +class MaterialsExport Array3D: public MaterialValue { TYPESYSTEM_HEADER_WITH_OVERRIDE(); public: - Material3DArray(); - ~Material3DArray() override = default; + Array3D(); + Array3D(const Array3D& other); + ~Array3D() override = default; + + Array3D& operator=(const Array3D& other); bool isNull() const override; + bool isEmpty() const override; const QList< std::pair>>>>>& @@ -208,6 +218,7 @@ public: void validateDepth(int level) const; void validateColumn(int column) const; void validateRow(int level, int row) const; + void validate(const Array3D& other) const; const std::shared_ptr>>>& getTable(const Base::Quantity& depth) const; @@ -245,6 +256,8 @@ public: { _columns = size; } + void setDepth(int depthCount); + void setRows(int depth, int rowCount); void setValue(int depth, int row, int column, const Base::Quantity& value); void setValue(int row, int column, const Base::Quantity& value); @@ -260,6 +273,8 @@ public: QString getYAMLString() const override; protected: + void deepCopy(const Array3D& other); + QList>>>>> _rowMap; int _currentDepth; @@ -269,7 +284,7 @@ protected: } // namespace Materials Q_DECLARE_METATYPE(Materials::MaterialValue) -Q_DECLARE_METATYPE(std::shared_ptr) -Q_DECLARE_METATYPE(std::shared_ptr) +Q_DECLARE_METATYPE(std::shared_ptr) +Q_DECLARE_METATYPE(std::shared_ptr) #endif // MATERIAL_MATERIALVALUE_H diff --git a/src/Mod/Material/App/Materials.cpp b/src/Mod/Material/App/Materials.cpp index 02ba167a30..90ad18b073 100644 --- a/src/Mod/Material/App/Materials.cpp +++ b/src/Mod/Material/App/Materials.cpp @@ -65,11 +65,11 @@ void MaterialProperty::copyValuePtr(const std::shared_ptr& value) { if (value->getType() == MaterialValue::Array2D) { _valuePtr = - std::make_shared(*(std::static_pointer_cast(value))); + std::make_shared(*(std::static_pointer_cast(value))); } else if (value->getType() == MaterialValue::Array3D) { _valuePtr = - std::make_shared(*(std::static_pointer_cast(value))); + std::make_shared(*(std::static_pointer_cast(value))); } else { _valuePtr = std::make_shared(*value); @@ -132,7 +132,7 @@ QString MaterialProperty::getString() const if (value.isNull()) { return {}; } - return QString(QLatin1String("%L1")).arg(value.toFloat(), 0, 'g', MaterialValue::PRECISION); + return QString(QStringLiteral("%L1")).arg(value.toFloat(), 0, 'g', MaterialValue::PRECISION); } return getValue().toString(); } @@ -177,7 +177,7 @@ QString MaterialProperty::getDictionaryString() const } if (getType() == MaterialValue::Quantity) { auto quantity = getValue().value(); - auto string = QString(QLatin1String("%1 %2")) + auto string = QString(QStringLiteral("%1 %2")) .arg(quantity.getValue(), 0, 'g', MaterialValue::PRECISION) .arg(QString::fromStdString(quantity.getUnit().getString())); return string; @@ -187,7 +187,7 @@ QString MaterialProperty::getDictionaryString() const if (value.isNull()) { return {}; } - return QString(QLatin1String("%1")).arg(value.toFloat(), 0, 'g', MaterialValue::PRECISION); + return QString(QStringLiteral("%1")).arg(value.toFloat(), 0, 'g', MaterialValue::PRECISION); } return getValue().toString(); } @@ -205,12 +205,12 @@ void MaterialProperty::setType(const QString& type) throw UnknownValueType(); } if (mappedType == MaterialValue::Array2D) { - auto arrayPtr = std::make_shared(); + auto arrayPtr = std::make_shared(); arrayPtr->setColumns(columns()); _valuePtr = arrayPtr; } else if (mappedType == MaterialValue::Array3D) { - auto arrayPtr = std::make_shared(); + auto arrayPtr = std::make_shared(); // First column is third dimension arrayPtr->setColumns(columns() - 1); _valuePtr = arrayPtr; @@ -448,6 +448,17 @@ bool MaterialProperty::operator==(const MaterialProperty& other) const return false; } +void MaterialProperty::validate(const MaterialProperty& other) const { + _valuePtr->validate(*other._valuePtr); + + if (_columns.size() != other._columns.size()) { + throw InvalidProperty("Model property column counts don't match"); + } + for (size_t i = 0; i < _columns.size(); i++) { + _columns[i].validate(other._columns[i]); + } +} + TYPESYSTEM_SOURCE(Materials::Material, Base::BaseClass) Material::Material() @@ -476,6 +487,7 @@ Material::Material(const std::shared_ptr& library, Material::Material(const Material& other) : _library(other._library) , _directory(other._directory) + , _filename(other._filename) , _uuid(other._uuid) , _name(other._name) , _author(other._author) @@ -513,6 +525,31 @@ Material::Material(const Material& other) } } +QString Material::getDirectory() const +{ + return _directory; +} + +void Material::setDirectory(const QString& directory) +{ + _directory = directory; +} + +QString Material::getFilename() const +{ + return _filename; +} + +void Material::setFilename(const QString& filename) +{ + _filename = filename; +} + +QString Material::getFilePath() const +{ + return QDir(_directory + QStringLiteral("/") + _filename).absolutePath(); +} + QString Material::getAuthorAndLicense() const { QString authorAndLicense; @@ -521,7 +558,7 @@ QString Material::getAuthorAndLicense() const if (!_author.isNull()) { authorAndLicense = _author; if (!_license.isNull()) { - authorAndLicense += QLatin1String(" ") + _license; + authorAndLicense += QStringLiteral(" ") + _license; } } else if (!_license.isNull()) { @@ -541,7 +578,7 @@ void Material::addModel(const QString& uuid) _allUuids << uuid; - ModelManager manager; + auto manager = ModelManager::getManager(); try { auto model = manager.getModel(uuid); @@ -641,7 +678,7 @@ void Material::addPhysical(const QString& uuid) return; } - ModelManager manager; + auto manager = ModelManager::getManager(); try { auto model = manager.getModel(uuid); @@ -688,7 +725,7 @@ void Material::removePhysical(const QString& uuid) return; } - ModelManager manager; + auto manager = ModelManager::getManager(); try { auto model = manager.getModel(uuid); @@ -718,7 +755,7 @@ void Material::addAppearance(const QString& uuid) return; } - ModelManager manager; + auto manager = ModelManager::getManager(); try { auto model = manager.getModel(uuid); @@ -759,7 +796,7 @@ void Material::removeAppearance(const QString& uuid) return; } - ModelManager manager; + auto manager = ModelManager::getManager(); try { auto model = manager.getModel(uuid); @@ -938,6 +975,22 @@ void Material::setValue(const QString& name, const QVariant& value) if (hasPhysicalProperty(name)) { setPhysicalValue(name, value); } + else if (hasAppearanceProperty(name)) { + setAppearanceValue(name, value); + } + else { + throw PropertyNotFound(); + } +} + +void Material::setValue(const QString& name, const std::shared_ptr& value) +{ + if (hasPhysicalProperty(name)) { + setPhysicalValue(name, value); + } + else if (hasAppearanceProperty(name)) { + setAppearanceValue(name, value); + } else { throw PropertyNotFound(); } @@ -1045,7 +1098,7 @@ Material::getValueString(const std::mapgetValue().toString(); @@ -1141,7 +1194,7 @@ bool Material::hasPhysicalModel(const QString& uuid) const return false; } - ModelManager manager; + auto manager = ModelManager::getManager(); try { auto model = manager.getModel(uuid); @@ -1161,7 +1214,7 @@ bool Material::hasAppearanceModel(const QString& uuid) const return false; } - ModelManager manager; + auto manager = ModelManager::getManager(); try { auto model = manager.getModel(uuid); @@ -1181,7 +1234,7 @@ bool Material::isPhysicalModelComplete(const QString& uuid) const return false; } - ModelManager manager; + auto manager = ModelManager::getManager(); try { auto model = manager.getModel(uuid); @@ -1207,7 +1260,7 @@ bool Material::isAppearanceModelComplete(const QString& uuid) const return false; } - ModelManager manager; + auto manager = ModelManager::getManager(); try { auto model = manager.getModel(uuid); @@ -1252,10 +1305,8 @@ void Material::saveGeneral(QTextStream& stream) const void Material::saveInherits(QTextStream& stream) const { if (!_parentUuid.isEmpty()) { - MaterialManager manager; - try { - auto material = manager.getMaterial(_parentUuid); + auto material = MaterialManager::getManager().getMaterial(_parentUuid); stream << "Inherits:\n"; stream << " " << material->getName() << ":\n"; @@ -1314,8 +1365,8 @@ void Material::saveModels(QTextStream& stream, bool saveInherited) const return; } - ModelManager modelManager; - MaterialManager materialManager; + auto modelManager = ModelManager::getManager(); + auto materialManager = MaterialManager::getManager(); bool inherited = saveInherited && (_parentUuid.size() > 0); std::shared_ptr parent; @@ -1368,8 +1419,8 @@ void Material::saveAppearanceModels(QTextStream& stream, bool saveInherited) con return; } - ModelManager modelManager; - MaterialManager materialManager; + auto modelManager = ModelManager::getManager(); + auto materialManager = MaterialManager::getManager(); bool inherited = saveInherited && (_parentUuid.size() > 0); std::shared_ptr parent; @@ -1421,7 +1472,7 @@ void Material::newUuid() QString Material::getModelByName(const QString& name) const { - ModelManager manager; + auto manager = ModelManager::getManager(); for (auto& it : _allUuids) { try { @@ -1442,8 +1493,7 @@ void Material::save(QTextStream& stream, bool overwrite, bool saveAsCopy, bool s if (saveInherited && !saveAsCopy) { // Check to see if we're an original or if we're already in the list of // models - MaterialManager materialManager; - if (materialManager.exists(_uuid) && !overwrite) { + if (MaterialManager::getManager().exists(_uuid) && !overwrite) { // Make a new version based on the current setParentUUID(_uuid); } @@ -1494,6 +1544,7 @@ Material& Material::operator=(const Material& other) _library = other._library; _directory = other._directory; + _filename = other._filename; _uuid = other._uuid; _name = other._name; _author = other._author; @@ -1548,20 +1599,20 @@ Material& Material::operator=(const App::Material& other) addAppearance(ModelUUIDs::ModelUUID_Rendering_Basic); } - getAppearanceProperty(QLatin1String("AmbientColor"))->setColor(other.ambientColor); - getAppearanceProperty(QLatin1String("DiffuseColor"))->setColor(other.diffuseColor); - getAppearanceProperty(QLatin1String("SpecularColor"))->setColor(other.specularColor); - getAppearanceProperty(QLatin1String("EmissiveColor"))->setColor(other.emissiveColor); - getAppearanceProperty(QLatin1String("Shininess"))->setFloat(other.shininess); - getAppearanceProperty(QLatin1String("Transparency"))->setFloat(other.transparency); + getAppearanceProperty(QStringLiteral("AmbientColor"))->setColor(other.ambientColor); + getAppearanceProperty(QStringLiteral("DiffuseColor"))->setColor(other.diffuseColor); + getAppearanceProperty(QStringLiteral("SpecularColor"))->setColor(other.specularColor); + getAppearanceProperty(QStringLiteral("EmissiveColor"))->setColor(other.emissiveColor); + getAppearanceProperty(QStringLiteral("Shininess"))->setFloat(other.shininess); + getAppearanceProperty(QStringLiteral("Transparency"))->setFloat(other.transparency); if (!other.image.empty() || !other.imagePath.empty()) { if (!hasAppearanceModel(ModelUUIDs::ModelUUID_Rendering_Texture)) { addAppearance(ModelUUIDs::ModelUUID_Rendering_Texture); } - getAppearanceProperty(QLatin1String("TextureImage"))->setString(other.image); - getAppearanceProperty(QLatin1String("TexturePath"))->setString(other.imagePath); + getAppearanceProperty(QStringLiteral("TextureImage"))->setString(other.image); + getAppearanceProperty(QStringLiteral("TexturePath"))->setString(other.imagePath); } return *this; @@ -1574,7 +1625,7 @@ QStringList Material::normalizeModels(const QStringList& models) { QStringList normalized; - ModelManager manager; + auto manager = ModelManager::getManager(); for (auto& uuid : models) { auto model = manager.getModel(uuid); @@ -1652,32 +1703,32 @@ App::Material Material::getMaterialAppearance() const App::Material material(App::Material::DEFAULT); bool custom = false; - if (hasAppearanceProperty(QLatin1String("AmbientColor"))) { - material.ambientColor = getAppearanceProperty(QLatin1String("AmbientColor"))->getColor(); + if (hasAppearanceProperty(QStringLiteral("AmbientColor"))) { + material.ambientColor = getAppearanceProperty(QStringLiteral("AmbientColor"))->getColor(); custom = true; } - if (hasAppearanceProperty(QLatin1String("DiffuseColor"))) { - material.diffuseColor = getAppearanceProperty(QLatin1String("DiffuseColor"))->getColor(); + if (hasAppearanceProperty(QStringLiteral("DiffuseColor"))) { + material.diffuseColor = getAppearanceProperty(QStringLiteral("DiffuseColor"))->getColor(); custom = true; } - if (hasAppearanceProperty(QLatin1String("SpecularColor"))) { - material.specularColor = getAppearanceProperty(QLatin1String("SpecularColor"))->getColor(); + if (hasAppearanceProperty(QStringLiteral("SpecularColor"))) { + material.specularColor = getAppearanceProperty(QStringLiteral("SpecularColor"))->getColor(); custom = true; } - if (hasAppearanceProperty(QLatin1String("EmissiveColor"))) { - material.emissiveColor = getAppearanceProperty(QLatin1String("EmissiveColor"))->getColor(); + if (hasAppearanceProperty(QStringLiteral("EmissiveColor"))) { + material.emissiveColor = getAppearanceProperty(QStringLiteral("EmissiveColor"))->getColor(); custom = true; } - if (hasAppearanceProperty(QLatin1String("Shininess"))) { - material.shininess = getAppearanceProperty(QLatin1String("Shininess"))->getFloat(); + if (hasAppearanceProperty(QStringLiteral("Shininess"))) { + material.shininess = getAppearanceProperty(QStringLiteral("Shininess"))->getFloat(); custom = true; } - if (hasAppearanceProperty(QLatin1String("Transparency"))) { - material.transparency = getAppearanceProperty(QLatin1String("Transparency"))->getFloat(); + if (hasAppearanceProperty(QStringLiteral("Transparency"))) { + material.transparency = getAppearanceProperty(QStringLiteral("Transparency"))->getFloat(); custom = true; } - if (hasAppearanceProperty(QLatin1String("TextureImage"))) { - auto property = getAppearanceProperty(QLatin1String("TextureImage")); + if (hasAppearanceProperty(QStringLiteral("TextureImage"))) { + auto property = getAppearanceProperty(QStringLiteral("TextureImage")); if (!property->isNull()) { Base::Console().Log("Has 'TextureImage'\n"); material.image = property->getString().toStdString(); @@ -1685,8 +1736,8 @@ App::Material Material::getMaterialAppearance() const custom = true; } - else if (hasAppearanceProperty(QLatin1String("TexturePath"))) { - auto property = getAppearanceProperty(QLatin1String("TexturePath")); + else if (hasAppearanceProperty(QStringLiteral("TexturePath"))) { + auto property = getAppearanceProperty(QStringLiteral("TexturePath")); if (!property->isNull()) { Base::Console().Log("Has 'TexturePath'\n"); material.imagePath = property->getString().toStdString(); @@ -1702,3 +1753,98 @@ App::Material Material::getMaterialAppearance() const return material; } + +void Material::validate(const std::shared_ptr& other) const +{ + + try { + _library->validate(*(other->_library)); + } + catch (const InvalidLibrary& e) { + throw InvalidMaterial(e.what()); + } + + if (_directory != other->_directory) { + throw InvalidMaterial("Model directories don't match"); + } + if (!other->_filename.isEmpty()) { + throw InvalidMaterial("Remote filename is not empty"); + } + if (_uuid != other->_uuid) { + throw InvalidMaterial("Model UUIDs don't match"); + } + if (_name != other->_name) { + throw InvalidMaterial("Model names don't match"); + } + if (_author != other->_author) { + throw InvalidMaterial("Model authors don't match"); + } + if (_license != other->_license) { + throw InvalidMaterial("Model licenses don't match"); + } + if (_parentUuid != other->_parentUuid) { + throw InvalidMaterial("Model parents don't match"); + } + if (_description != other->_description) { + throw InvalidMaterial("Model descriptions don't match"); + } + if (_url != other->_url) { + throw InvalidMaterial("Model URLs don't match"); + } + if (_reference != other->_reference) { + throw InvalidMaterial("Model references don't match"); + } + + if (_tags.size() != other->_tags.size()) { + Base::Console().Log("Local tags count %d\n", _tags.size()); + Base::Console().Log("Remote tags count %d\n", other->_tags.size()); + throw InvalidMaterial("Material tags counts don't match"); + } + if (!other->_tags.contains(_tags)) { + throw InvalidMaterial("Material tags don't match"); + } + + if (_physicalUuids.size() != other->_physicalUuids.size()) { + Base::Console().Log("Local physical model count %d\n", _physicalUuids.size()); + Base::Console().Log("Remote physical model count %d\n", other->_physicalUuids.size()); + throw InvalidMaterial("Material physical model counts don't match"); + } + if (!other->_physicalUuids.contains(_physicalUuids)) { + throw InvalidMaterial("Material physical models don't match"); + } + + if (_physicalUuids.size() != other->_physicalUuids.size()) { + Base::Console().Log("Local appearance model count %d\n", _physicalUuids.size()); + Base::Console().Log("Remote appearance model count %d\n", other->_physicalUuids.size()); + throw InvalidMaterial("Material appearance model counts don't match"); + } + if (!other->_physicalUuids.contains(_physicalUuids)) { + throw InvalidMaterial("Material appearance models don't match"); + } + + if (_allUuids.size() != other->_allUuids.size()) { + Base::Console().Log("Local model count %d\n", _allUuids.size()); + Base::Console().Log("Remote model count %d\n", other->_allUuids.size()); + throw InvalidMaterial("Material model counts don't match"); + } + if (!other->_allUuids.contains(_allUuids)) { + throw InvalidMaterial("Material models don't match"); + } + + // Need to compare properties + if (_physical.size() != other->_physical.size()) { + throw InvalidMaterial("Material physical property counts don't match"); + } + for (auto& property : _physical) { + auto& remote = other->_physical[property.first]; + property.second->validate(*remote); + } + + if (_appearance.size() != other->_appearance.size()) { + throw InvalidMaterial("Material appearance property counts don't match"); + } + for (auto& property : _appearance) { + auto& remote = other->_appearance[property.first]; + property.second->validate(*remote); + } +} diff --git a/src/Mod/Material/App/Materials.h b/src/Mod/Material/App/Materials.h index e94201dd22..4d57c70259 100644 --- a/src/Mod/Material/App/Materials.h +++ b/src/Mod/Material/App/Materials.h @@ -80,6 +80,10 @@ public: { return _valuePtr->isNull(); } + bool isEmpty() const + { + return _valuePtr->isEmpty(); + } std::shared_ptr getMaterialValue(); std::shared_ptr getMaterialValue() const; QString getString() const; @@ -109,6 +113,10 @@ public: MaterialValue::ValueType getColumnType(int column) const; QString getColumnUnits(int column) const; QVariant getColumnNull(int column) const; + const std::vector& getColumns() const + { + return _columns; + } void setModelUUID(const QString& uuid); void setPropertyType(const QString& type) override; @@ -140,6 +148,11 @@ public: return !operator==(other); } + void validate(const MaterialProperty& other) const; + + // Define precision for displaying floating point values + static int const PRECISION; + protected: void setType(const QString& type); // void setType(MaterialValue::ValueType type) { _valueType = type; } @@ -180,10 +193,9 @@ public: { return _library; } - QString getDirectory() const - { - return _directory; - } + QString getDirectory() const; + QString getFilename() const; + QString getFilePath() const; QString getUUID() const { return _uuid; @@ -240,10 +252,8 @@ public: { _library = library; } - void setDirectory(const QString& directory) - { - _directory = directory; - } + void setDirectory(const QString& directory); + void setFilename(const QString& filename); void setUUID(const QString& uuid) { _uuid = uuid; @@ -303,6 +313,7 @@ public: void setValue(const QString& name, const QString& value); void setValue(const QString& name, const QVariant& value); + void setValue(const QString& name, const std::shared_ptr& value); /* * Legacy values are thosed contained in old format files that don't fit in the new @@ -432,6 +443,8 @@ public: return getTypeId() == other.getTypeId() && _uuid == other._uuid; } + void validate(const std::shared_ptr& other) const; + protected: void addModel(const QString& uuid); static void removeUUID(QSet& uuidList, const QString& uuid); @@ -455,6 +468,7 @@ protected: private: std::shared_ptr _library; QString _directory; + QString _filename; QString _uuid; QString _name; QString _author; diff --git a/src/Mod/Material/App/Model.cpp b/src/Mod/Material/App/Model.cpp index fddc67b9d2..9c7d0a7bcf 100644 --- a/src/Mod/Material/App/Model.cpp +++ b/src/Mod/Material/App/Model.cpp @@ -101,11 +101,49 @@ bool ModelProperty::operator==(const ModelProperty& other) const return true; } - return (_name == other._name) && (_displayName == other._displayName) && (_propertyType == other._propertyType) - && (_units == other._units) && (_url == other._url) && (_description == other._description) + return (_name == other._name) && (_displayName == other._displayName) + && (_propertyType == other._propertyType) && (_units == other._units) + && (_url == other._url) && (_description == other._description) && (_inheritance == other._inheritance); } +void ModelProperty::validate(const ModelProperty& other) const +{ + if (_name != other._name) { + throw InvalidProperty("Model names don't match"); + } + if (getDisplayName() != other.getDisplayName()) { + Base::Console().Log("Local display name '%s'\n", getDisplayName().toStdString().c_str()); + Base::Console().Log("Remote display name '%s'\n", + other.getDisplayName().toStdString().c_str()); + throw InvalidProperty("Model display names don't match"); + } + if (_propertyType != other._propertyType) { + throw InvalidProperty("Model property types don't match"); + } + if (_units != other._units) { + throw InvalidProperty("Model units don't match"); + } + if (_url != other._url) { + throw InvalidProperty("Model URLs don't match"); + } + if (_description != other._description) { + throw InvalidProperty("Model descriptions don't match"); + } + if (_inheritance != other._inheritance) { + throw InvalidProperty("Model inheritance don't match"); + } + + if (_columns.size() != other._columns.size()) { + Base::Console().Log("Local property column count %d\n", _columns.size()); + Base::Console().Log("Remote property column count %d\n", other._columns.size()); + throw InvalidProperty("Model property column counts don't match"); + } + for (size_t i = 0; i < _columns.size(); i++) { + _columns[i].validate(other._columns[i]); + } +} + TYPESYSTEM_SOURCE(Materials::Model, Base::BaseClass) Model::Model() @@ -129,6 +167,31 @@ Model::Model(std::shared_ptr library, , _doi(doi) {} +QString Model::getDirectory() const +{ + return _directory; +} + +void Model::setDirectory(const QString& directory) +{ + _directory = directory; +} + +QString Model::getFilename() const +{ + return _filename; +} + +void Model::setFilename(const QString& filename) +{ + _filename = filename; +} + +QString Model::getFilePath() const +{ + return QDir(_directory + QStringLiteral("/") + _filename).absolutePath(); +} + ModelProperty& Model::operator[](const QString& key) { try { @@ -138,3 +201,54 @@ ModelProperty& Model::operator[](const QString& key) throw PropertyNotFound(); } } + +void Model::validate(const std::shared_ptr& other) const +{ + try { + _library->validate(*(other->_library)); + } + catch (const InvalidLibrary& e) + { + throw InvalidModel(e.what()); + } + + // std::map _properties; + if (_type != other->_type) { + throw InvalidModel("Model types don't match"); + } + if (_name != other->_name) { + throw InvalidModel("Model names don't match"); + } + if (_directory != other->_directory) { + throw InvalidModel("Model directories don't match"); + } + if (!other->_filename.isEmpty()) { + throw InvalidModel("Remote filename is not empty"); + } + if (_uuid != other->_uuid) { + throw InvalidModel("Model UUIDs don't match"); + } + if (_description != other->_description) { + throw InvalidModel("Model descriptions don't match"); + } + if (_url != other->_url) { + throw InvalidModel("Model URLs don't match"); + } + if (_doi != other->_doi) { + throw InvalidModel("Model DOIs don't match"); + } + if (_inheritedUuids != other->_inheritedUuids) { + throw InvalidModel("Model inherited UUIDs don't match"); + } + + // Need to compare properties + if (_properties.size() != other->_properties.size()) { + // Base::Console().Log("Local property count %d\n", _properties.size()); + // Base::Console().Log("Remote property count %d\n", other->_properties.size()); + throw InvalidModel("Model property counts don't match"); + } + for (auto& property : _properties) { + auto& remote = other->_properties[property.first]; + property.second.validate(remote); + } +} diff --git a/src/Mod/Material/App/Model.h b/src/Mod/Material/App/Model.h index 86b039ec33..517651e7d5 100644 --- a/src/Mod/Material/App/Model.h +++ b/src/Mod/Material/App/Model.h @@ -98,7 +98,7 @@ public: { _name = name; } - void setColumnHeader(const QString& header) + void setDisplayName(const QString& header) { _displayName = header; } @@ -143,6 +143,8 @@ public: return !operator==(other); } + void validate(const ModelProperty& other) const; + private: QString _name; QString _displayName; @@ -180,12 +182,12 @@ public: { return _library; } - const QString getBase() const + QString getBase() const { return (_type == ModelType_Physical) ? QStringLiteral("Model") : QStringLiteral("AppearanceModel"); } - const QString getName() const + QString getName() const { return _name; } @@ -193,27 +195,22 @@ public: { return _type; } - const QString getDirectory() const - { - return _directory; - } - const QString getDirectoryPath() const - { - return QDir(_directory).absolutePath(); - } - const QString getUUID() const + QString getDirectory() const; + QString getFilename() const; + QString getFilePath() const; + QString getUUID() const { return _uuid; } - const QString getDescription() const + QString getDescription() const { return _description; } - const QString getURL() const + QString getURL() const { return _url; } - const QString getDOI() const + QString getDOI() const { return _doi; } @@ -230,10 +227,8 @@ public: { _name = name; } - void setDirectory(const QString& directory) - { - _directory = directory; - } + void setDirectory(const QString& directory); + void setFilename(const QString& filename); void setUUID(const QString& uuid) { _uuid = uuid; @@ -306,11 +301,14 @@ public: return _properties.cend(); } + void validate(const std::shared_ptr& other) const; + private: std::shared_ptr _library; ModelType _type; QString _name; QString _directory; + QString _filename; QString _uuid; QString _description; QString _url; diff --git a/src/Mod/Material/App/Model.pyi b/src/Mod/Material/App/Model.pyi new file mode 100644 index 0000000000..ed88aa0e8f --- /dev/null +++ b/src/Mod/Material/App/Model.pyi @@ -0,0 +1,65 @@ +from Base.Metadata import export, constmethod +from Base.BaseClass import BaseClass +from typing import Final, List, Dict, overload + +@export( + Include="Mod/Material/App/Model.h", + Namespace="Materials", + Constructor=True, + Delete=True, +) +class Model(BaseClass): + """ + Material model descriptions. + + Author: DavidCarter (dcarter@davidcarter.ca) + Licence: LGPL + """ + + LibraryName: Final[str] = "" + """Model library name.""" + + LibraryRoot: Final[str] = "" + """Model library path.""" + + LibraryIcon: Final[str] = "" + """Model icon path.""" + + Name: str = "" + """Model name.""" + + Type: str = "" + """Model type.""" + + Directory: str = "" + """Model directory.""" + + UUID: Final[str] = "" + """Unique model identifier.""" + + Description: str = "" + """Description of the model.""" + + URL: str = "" + """URL to a detailed description of the model.""" + + DOI: str = "" + """Digital Object Identifier (see https://doi.org/)""" + + Inherited: Final[List[str]] = [] + """List of inherited models identified by UUID.""" + + Properties: Final[Dict[str, str]] = {} + """Dictionary of model properties.""" + + def addInheritance(self) -> None: + """ + Add an inherited model. + """ + ... + + def addProperty(self) -> None: + """ + Add a model property. + """ + ... diff --git a/src/Mod/Material/App/ModelLibrary.cpp b/src/Mod/Material/App/ModelLibrary.cpp index 2388fe740e..acc118a653 100644 --- a/src/Mod/Material/App/ModelLibrary.cpp +++ b/src/Mod/Material/App/ModelLibrary.cpp @@ -24,6 +24,8 @@ #include #endif +#include + #include #include "Exceptions.h" @@ -34,79 +36,13 @@ using namespace Materials; -TYPESYSTEM_SOURCE(Materials::LibraryBase, Base::BaseClass) +TYPESYSTEM_SOURCE(Materials::ModelLibrary, Materials::Library) -LibraryBase::LibraryBase(const QString& libraryName, const QString& dir, const QString& icon) - : _name(libraryName) - , _directory(QDir::cleanPath(dir)) - , _iconPath(icon) -{} - -bool LibraryBase::operator==(const LibraryBase& library) const -{ - return (_name == library._name) && (_directory == library._directory); -} - -QString LibraryBase::getLocalPath(const QString& path) const -{ - QString filePath = getDirectoryPath(); - if (!(filePath.endsWith(QLatin1String("/")) || filePath.endsWith(QLatin1String("\\")))) { - filePath += QLatin1String("/"); - } - - QString cleanPath = QDir::cleanPath(path); - QString prefix = QStringLiteral("/") + getName(); - if (cleanPath.startsWith(prefix)) { - // Remove the library name from the path - filePath += cleanPath.right(cleanPath.length() - prefix.length()); - } - else { - filePath += cleanPath; - } - - return filePath; -} - -bool LibraryBase::isRoot(const QString& path) const -{ - QString localPath = getLocalPath(path); - QString cleanPath = getLocalPath(QStringLiteral("")); - std::string pLocal = localPath.toStdString(); - std::string pclean = cleanPath.toStdString(); - return (cleanPath == localPath); -} - -QString LibraryBase::getRelativePath(const QString& path) const -{ - QString filePath; - QString cleanPath = QDir::cleanPath(path); - QString prefix = QStringLiteral("/") + getName(); - if (cleanPath.startsWith(prefix)) { - // Remove the library name from the path - filePath = cleanPath.right(cleanPath.length() - prefix.length()); - } - else { - filePath = cleanPath; - } - - prefix = getDirectoryPath(); - if (filePath.startsWith(prefix)) { - // Remove the library root from the path - filePath = filePath.right(filePath.length() - prefix.length()); - } - - // Remove any leading '/' - if (filePath.startsWith(QStringLiteral("/"))) { - filePath.remove(0, 1); - } - - return filePath; -} - -TYPESYSTEM_SOURCE(Materials::ModelLibrary, Materials::LibraryBase) - -ModelLibrary::ModelLibrary(const QString& libraryName, const QString& dir, const QString& icon) - : LibraryBase(libraryName, dir, icon) +ModelLibrary::ModelLibrary(const QString& libraryName, + const QString& dir, + const QString& icon, + bool readOnly) + : Library(libraryName, dir, icon, readOnly) { _modelPathMap = std::make_unique>>(); } @@ -131,9 +67,11 @@ std::shared_ptr ModelLibrary::getModelByPath(const QString& path) const std::shared_ptr ModelLibrary::addModel(const Model& model, const QString& path) { QString filePath = getRelativePath(path); + QFileInfo info(filePath); std::shared_ptr newModel = std::make_shared(model); newModel->setLibrary(getptr()); - newModel->setDirectory(filePath); + newModel->setDirectory(getLibraryPath(filePath, info.fileName())); + newModel->setFilename(info.fileName()); (*_modelPathMap)[filePath] = newModel; @@ -158,6 +96,7 @@ ModelLibrary::getModelTree(ModelFilter filter) const for (auto& itp : list) { if (ModelManager::isModel(itp)) { std::shared_ptr child = std::make_shared(); + child->setUUID(model->getUUID()); child->setData(model); (*node)[itp] = child; } diff --git a/src/Mod/Material/App/ModelLibrary.h b/src/Mod/Material/App/ModelLibrary.h index e77b2c9da1..ae31c982f0 100644 --- a/src/Mod/Material/App/ModelLibrary.h +++ b/src/Mod/Material/App/ModelLibrary.h @@ -32,68 +32,28 @@ #include +#include "Library.h" #include "MaterialValue.h" #include "Model.h" namespace Materials { -// class Model; - -class MaterialsExport LibraryBase: public Base::BaseClass -{ - TYPESYSTEM_HEADER_WITH_OVERRIDE(); - -public: - LibraryBase() = default; - LibraryBase(const QString& libraryName, const QString& dir, const QString& icon); - ~LibraryBase() override = default; - - const QString getName() const - { - return _name; - } - const QString getDirectory() const - { - return _directory; - } - const QString getDirectoryPath() const - { - return QDir(_directory).absolutePath(); - } - const QString getIconPath() const - { - return _iconPath; - } - bool operator==(const LibraryBase& library) const; - bool operator!=(const LibraryBase& library) const - { - return !operator==(library); - } - QString getLocalPath(const QString& path) const; - QString getRelativePath(const QString& path) const; - bool isRoot(const QString& path) const; - -private: - LibraryBase(const LibraryBase&); - - QString _name; - QString _directory; - QString _iconPath; -}; - -class MaterialsExport ModelLibrary: public LibraryBase, +class MaterialsExport ModelLibrary: public Library, public std::enable_shared_from_this { TYPESYSTEM_HEADER_WITH_OVERRIDE(); public: ModelLibrary(); - ModelLibrary(const QString& libraryName, const QString& dir, const QString& icon); + ModelLibrary(const QString& libraryName, + const QString& dir, + const QString& icon, + bool readOnly = true); ~ModelLibrary() override = default; bool operator==(const ModelLibrary& library) const { - return LibraryBase::operator==(library); + return Library::operator==(library); } bool operator!=(const ModelLibrary& library) const { @@ -119,4 +79,6 @@ private: } // namespace Materials +Q_DECLARE_METATYPE(std::shared_ptr) + #endif // MATERIAL_MODELLIBRARY_H diff --git a/src/Mod/Material/App/ModelManager.cpp b/src/Mod/Material/App/ModelManager.cpp index 8ca8a8a477..40b077836c 100644 --- a/src/Mod/Material/App/ModelManager.cpp +++ b/src/Mod/Material/App/ModelManager.cpp @@ -26,118 +26,141 @@ #include #include +#include #include #include "Model.h" #include "ModelLoader.h" #include "ModelManager.h" +#include "ModelManagerLocal.h" using namespace Materials; -std::shared_ptr>> ModelManager::_libraryList = nullptr; -std::shared_ptr>> ModelManager::_modelMap = nullptr; -QMutex ModelManager::_mutex; - TYPESYSTEM_SOURCE(Materials::ModelManager, Base::BaseClass) +QMutex ModelManager::_mutex; +ModelManager* ModelManager::_manager = nullptr; +std::unique_ptr ModelManager::_localManager; + ModelManager::ModelManager() +{} + +ModelManager::~ModelManager() +{} + +ModelManager& ModelManager::getManager() { - initLibraries(); + if (!_manager) { + initManagers(); + } + + return *_manager; } -void ModelManager::initLibraries() +void ModelManager::initManagers() { QMutexLocker locker(&_mutex); - if (_modelMap == nullptr) { - _modelMap = std::make_shared>>(); - if (_libraryList == nullptr) { - _libraryList = std::make_shared>>(); - } - - // Load the libraries - ModelLoader loader(_modelMap, _libraryList); + if (!_manager) { + // Can't use smart pointers for this since the constructor is private + _manager = new ModelManager(); + } + if (!_localManager) { + _localManager = std::make_unique(); } } bool ModelManager::isModel(const QString& file) { - // if (!fs::is_regular_file(p)) - // return false; - // check file extension - if (file.endsWith(QStringLiteral(".yml"))) { - return true; - } - return false; + return ModelManagerLocal::isModel(file); } void ModelManager::cleanup() { - if (_libraryList) { - _libraryList->clear(); - } - - if (_modelMap) { - for (auto& it : *_modelMap) { - // This is needed to resolve cyclic dependencies - it.second->setLibrary(nullptr); - } - _modelMap->clear(); - } + return ModelManagerLocal::cleanup(); } void ModelManager::refresh() { - _modelMap->clear(); - _libraryList->clear(); + _localManager->refresh(); +} - // Load the libraries - ModelLoader loader(_modelMap, _libraryList); +std::shared_ptr>> ModelManager::getLibraries() +{ + return _localManager->getLibraries(); +} + +std::shared_ptr>> ModelManager::getLocalLibraries() +{ + return _localManager->getLibraries(); +} + +void ModelManager::createLibrary(const QString& libraryName, const QString& icon, bool readOnly) +{} + +void ModelManager::createLocalLibrary(const QString& libraryName, + const QString& directory, + const QString& icon, + bool readOnly) +{ + _localManager->createLibrary(libraryName, directory, icon, readOnly); +} + +void ModelManager::renameLibrary(const QString& libraryName, const QString& newName) +{ + _localManager->renameLibrary(libraryName, newName); +} + +void ModelManager::changeIcon(const QString& libraryName, const QString& icon) +{ + _localManager->changeIcon(libraryName, icon); +} + +void ModelManager::removeLibrary(const QString& libraryName) +{ + _localManager->removeLibrary(libraryName); +} + +std::shared_ptr>> +ModelManager::libraryModels(const QString& libraryName) +{ + return _localManager->libraryModels(libraryName); +} + +bool ModelManager::isLocalLibrary(const QString& libraryName) +{ + return true; +} + +std::shared_ptr>> ModelManager::getModels() +{ + return _localManager->getModels(); +} + +std::shared_ptr>> ModelManager::getLocalModels() +{ + return _localManager->getModels(); } std::shared_ptr ModelManager::getModel(const QString& uuid) const { - try { - if (_modelMap == nullptr) { - throw Uninitialized(); - } - - return _modelMap->at(uuid); - } - catch (std::out_of_range const&) { - throw ModelNotFound(); - } + return _localManager->getModel(uuid); } std::shared_ptr ModelManager::getModelByPath(const QString& path) const { - QString cleanPath = QDir::cleanPath(path); - - for (auto& library : *_libraryList) { - if (cleanPath.startsWith(library->getDirectory())) { - return library->getModelByPath(cleanPath); - } - } - - throw MaterialNotFound(); + return _localManager->getModelByPath(path); } std::shared_ptr ModelManager::getModelByPath(const QString& path, const QString& lib) const { - auto library = getLibrary(lib); // May throw LibraryNotFound - return library->getModelByPath(path); // May throw ModelNotFound + return _localManager->getModelByPath(path, lib); } std::shared_ptr ModelManager::getLibrary(const QString& name) const { - for (auto& library : *_libraryList) { - if (library->getName() == name) { - return library; - } - } - - throw LibraryNotFound(); + return _localManager->getLibrary(name); } bool ModelManager::passFilter(ModelFilter filter, Model::ModelType modelType) diff --git a/src/Mod/Material/App/ModelManager.h b/src/Mod/Material/App/ModelManager.h index 15b433930d..958f0f5fb9 100644 --- a/src/Mod/Material/App/ModelManager.h +++ b/src/Mod/Material/App/ModelManager.h @@ -24,6 +24,7 @@ #include +#include #include #include @@ -35,26 +36,37 @@ namespace Materials { +class ModelManagerLocal; +class ModelManagerExternal; class MaterialsExport ModelManager: public Base::BaseClass { TYPESYSTEM_HEADER_WITH_OVERRIDE(); public: - ModelManager(); - ~ModelManager() override = default; + ~ModelManager() override; + + static ModelManager& getManager(); static void cleanup(); void refresh(); - std::shared_ptr>> getModelLibraries() - { - return _libraryList; - } - std::shared_ptr>> getModels() - { - return _modelMap; - } + std::shared_ptr>> getLibraries(); + std::shared_ptr>> getLocalLibraries(); + void createLibrary(const QString& libraryName, const QString& icon, bool readOnly = true); + void createLocalLibrary(const QString& libraryName, + const QString& directory, + const QString& icon, + bool readOnly = true); + void renameLibrary(const QString& libraryName, const QString& newName); + void changeIcon(const QString& libraryName, const QString& icon); + void removeLibrary(const QString& libraryName); + std::shared_ptr>> + libraryModels(const QString& libraryName); + bool isLocalLibrary(const QString& libraryName); + + std::shared_ptr>> getModels(); + std::shared_ptr>> getLocalModels(); std::shared_ptr>> getModelTree(std::shared_ptr library, ModelFilter filter = ModelFilter_None) const { @@ -69,13 +81,14 @@ public: static bool passFilter(ModelFilter filter, Model::ModelType modelType); private: - static void initLibraries(); + ModelManager(); + static void initManagers(); - static std::shared_ptr>> _libraryList; - static std::shared_ptr>> _modelMap; + static ModelManager* _manager; + static std::unique_ptr _localManager; static QMutex _mutex; }; } // namespace Materials -#endif // MATERIAL_MODELMANAGER_H +#endif // MATERIAL_MODELMANAGER_H \ No newline at end of file diff --git a/src/Mod/Material/App/ModelManager.pyi b/src/Mod/Material/App/ModelManager.pyi new file mode 100644 index 0000000000..e7e28f3435 --- /dev/null +++ b/src/Mod/Material/App/ModelManager.pyi @@ -0,0 +1,37 @@ +from Base.Metadata import export, constmethod +from Base.BaseClass import BaseClass +from typing import Final, List, Dict + +@export( + Include="Mod/Material/App/ModelManager.h", + Namespace="Materials", + Constructor=True +) +class ModelManager(BaseClass): + """ + Material model descriptions. + + Author: DavidCarter (dcarter@davidcarter.ca) + Licence: LGPL + """ + + ModelLibraries: Final[List] = ... + """List of model libraries.""" + + LocalModelLibraries: Final[List] = ... + """List of local model libraries.""" + + Models: Final[Dict] = ... + """List of model libraries.""" + + def getModel(self) -> ...: + """ + Get a model object by specifying its UUID + """ + ... + + def getModelByPath(self) -> ...: + """ + Get a model object by specifying its path + """ + ... diff --git a/src/Mod/Material/App/ModelManagerLocal.cpp b/src/Mod/Material/App/ModelManagerLocal.cpp new file mode 100644 index 0000000000..798736c467 --- /dev/null +++ b/src/Mod/Material/App/ModelManagerLocal.cpp @@ -0,0 +1,219 @@ +/*************************************************************************** + * Copyright (c) 2024 David Carter * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + +#include "PreCompiled.h" +#ifndef _PreComp_ +#endif + +#include +#include + +#include + +#include "Model.h" +#include "ModelLoader.h" +#include "ModelManagerLocal.h" + +using namespace Materials; + +std::shared_ptr>> ModelManagerLocal::_libraryList = nullptr; +std::shared_ptr>> ModelManagerLocal::_modelMap = nullptr; +QMutex ModelManagerLocal::_mutex; + + +TYPESYSTEM_SOURCE(Materials::ModelManagerLocal, Base::BaseClass) + +ModelManagerLocal::ModelManagerLocal() +{ + initLibraries(); +} + +void ModelManagerLocal::initLibraries() +{ + QMutexLocker locker(&_mutex); + + if (_modelMap == nullptr) { + _modelMap = std::make_shared>>(); + if (_libraryList == nullptr) { + _libraryList = std::make_shared>>(); + } + + // Load the libraries + ModelLoader loader(_modelMap, _libraryList); + } +} + +bool ModelManagerLocal::isModel(const QString& file) +{ + // if (!fs::is_regular_file(p)) + // return false; + // check file extension + if (file.endsWith(QStringLiteral(".yml"))) { + return true; + } + return false; +} + +void ModelManagerLocal::cleanup() +{ + if (_libraryList) { + _libraryList->clear(); + } + + if (_modelMap) { + for (auto& it : *_modelMap) { + // This is needed to resolve cyclic dependencies + it.second->setLibrary(nullptr); + } + _modelMap->clear(); + } +} + +void ModelManagerLocal::refresh() +{ + _modelMap->clear(); + _libraryList->clear(); + + // Load the libraries + ModelLoader loader(_modelMap, _libraryList); +} + +std::shared_ptr>> ModelManagerLocal::getLibraries() +{ + return _libraryList; +} + +void ModelManagerLocal::createLibrary(const QString& libraryName, + const QString& directory, + const QString& icon, + bool readOnly) +{ + QDir dir; + if (!dir.exists(directory)) { + if (!dir.mkpath(directory)) { + throw CreationError("Unable to create library path"); + } + } + + auto modelLibrary = std::make_shared(libraryName, directory, icon, readOnly); + _libraryList->push_back(modelLibrary); + + // This needs to be persisted somehow +} + +void ModelManagerLocal::renameLibrary(const QString& libraryName, const QString& newName) +{ + for (auto& library : *_libraryList) { + if (library->getName() == libraryName) { + library->setName(newName); + return; + } + } + + throw LibraryNotFound(); +} + +void ModelManagerLocal::changeIcon(const QString& libraryName, const QString& icon) +{ + for (auto& library : *_libraryList) { + if (library->getName() == libraryName) { + library->setIconPath(icon); + return; + } + } + + throw LibraryNotFound(); +} + +void ModelManagerLocal::removeLibrary(const QString& libraryName) +{ + for (auto& library : *_libraryList) { + if (library->getName() == libraryName) { + _libraryList->remove(library); + + // At this point we should rebuild the model map + return; + } + } + + throw LibraryNotFound(); +} + +std::shared_ptr>> +ModelManagerLocal::libraryModels(const QString& libraryName) +{ + auto models = std::make_shared>>(); + + for (auto& it : *_modelMap) { + // This is needed to resolve cyclic dependencies + if (it.second->getLibrary()->getName() == libraryName) { + models->push_back( + std::tuple(it.first, it.second->getDirectory(), it.second->getName())); + } + } + + return models; +} + +std::shared_ptr ModelManagerLocal::getModel(const QString& uuid) const +{ + try { + if (_modelMap == nullptr) { + throw Uninitialized(); + } + + return _modelMap->at(uuid); + } + catch (std::out_of_range const&) { + throw ModelNotFound(); + } +} + +std::shared_ptr ModelManagerLocal::getModelByPath(const QString& path) const +{ + QString cleanPath = QDir::cleanPath(path); + + for (auto& library : *_libraryList) { + if (cleanPath.startsWith(library->getDirectory())) { + return library->getModelByPath(cleanPath); + } + } + + throw MaterialNotFound(); +} + +std::shared_ptr ModelManagerLocal::getModelByPath(const QString& path, + const QString& lib) const +{ + auto library = getLibrary(lib); // May throw LibraryNotFound + return library->getModelByPath(path); // May throw ModelNotFound +} + +std::shared_ptr ModelManagerLocal::getLibrary(const QString& name) const +{ + for (auto& library : *_libraryList) { + if (library->getName() == name) { + return library; + } + } + + throw LibraryNotFound(); +} diff --git a/src/Mod/Material/App/ModelManagerLocal.h b/src/Mod/Material/App/ModelManagerLocal.h new file mode 100644 index 0000000000..535a3428c9 --- /dev/null +++ b/src/Mod/Material/App/ModelManagerLocal.h @@ -0,0 +1,87 @@ +/*************************************************************************** + * Copyright (c) 2024 David Carter * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + +#ifndef MATERIAL_MODELMANAGERLOCAL_H +#define MATERIAL_MODELMANAGERLOCAL_H + +#include + +#include + +#include + +#include "Exceptions.h" +#include "FolderTree.h" +#include "Model.h" +#include "ModelLibrary.h" + +namespace Materials +{ + +class MaterialsExport ModelManagerLocal: public Base::BaseClass +{ + TYPESYSTEM_HEADER_WITH_OVERRIDE(); + +public: + ModelManagerLocal(); + ~ModelManagerLocal() override = default; + + static void cleanup(); + void refresh(); + + std::shared_ptr>> getLibraries(); + void createLibrary(const QString& libraryName, + const QString& directory, + const QString& icon, + bool readOnly = true); + void renameLibrary(const QString& libraryName, const QString& newName); + void changeIcon(const QString& libraryName, const QString& icon); + void removeLibrary(const QString& libraryName); + std::shared_ptr>> + libraryModels(const QString& libraryName); + + std::shared_ptr>> getModels() + { + return _modelMap; + } + std::shared_ptr>> + getModelTree(std::shared_ptr library, ModelFilter filter = ModelFilter_None) const + { + return library->getModelTree(filter); + } + std::shared_ptr getModel(const QString& uuid) const; + std::shared_ptr getModelByPath(const QString& path) const; + std::shared_ptr getModelByPath(const QString& path, const QString& lib) const; + std::shared_ptr getLibrary(const QString& name) const; + + static bool isModel(const QString& file); + +private: + static void initLibraries(); + + static std::shared_ptr>> _libraryList; + static std::shared_ptr>> _modelMap; + static QMutex _mutex; +}; + +} // namespace Materials + +#endif // MATERIAL_MODELMANAGERLOCAL_H \ No newline at end of file diff --git a/src/Mod/Material/App/ModelManagerPy.xml b/src/Mod/Material/App/ModelManagerPy.xml deleted file mode 100644 index b6e48da967..0000000000 --- a/src/Mod/Material/App/ModelManagerPy.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - Material model descriptions. - - - - Get a model object by specifying its UUID - - - - - Get a model object by specifying its path - - - - - List of model libraries. - - - - - - List of model libraries. - - - - - diff --git a/src/Mod/Material/App/ModelManagerPyImp.cpp b/src/Mod/Material/App/ModelManagerPyImp.cpp index 6fdc872508..3dd8d6467d 100644 --- a/src/Mod/Material/App/ModelManagerPyImp.cpp +++ b/src/Mod/Material/App/ModelManagerPyImp.cpp @@ -43,7 +43,7 @@ std::string ModelManagerPy::representation() const PyObject* ModelManagerPy::PyMake(struct _typeobject*, PyObject*, PyObject*) // Python wrapper { // never create such objects with the constructor - return new ModelManagerPy(new ModelManager()); + return new ModelManagerPy(&(ModelManager::getManager())); } // constructor method @@ -116,7 +116,7 @@ PyObject* ModelManagerPy::getModelByPath(PyObject* args) Py::List ModelManagerPy::getModelLibraries() const { - auto libraries = getModelManagerPtr()->getModelLibraries(); + auto libraries = getModelManagerPtr()->getLibraries(); Py::List list; for (auto it = libraries->begin(); it != libraries->end(); it++) { @@ -125,6 +125,26 @@ Py::List ModelManagerPy::getModelLibraries() const libTuple.setItem(0, Py::String(lib->getName().toStdString())); libTuple.setItem(1, Py::String(lib->getDirectoryPath().toStdString())); libTuple.setItem(2, Py::String(lib->getIconPath().toStdString())); + libTuple.setItem(3, Py::Boolean(lib->isReadOnly())); + + list.append(libTuple); + } + + return list; +} + +Py::List ModelManagerPy::getLocalModelLibraries() const +{ + auto libraries = getModelManagerPtr()->getLocalLibraries(); + Py::List list; + + for (auto it = libraries->begin(); it != libraries->end(); it++) { + auto lib = *it; + Py::Tuple libTuple(3); + libTuple.setItem(0, Py::String(lib->getName().toStdString())); + libTuple.setItem(1, Py::String(lib->getDirectoryPath().toStdString())); + libTuple.setItem(2, Py::String(lib->getIconPath().toStdString())); + libTuple.setItem(3, Py::Boolean(lib->isReadOnly())); list.append(libTuple); } diff --git a/src/Mod/Material/App/ModelProperty.pyi b/src/Mod/Material/App/ModelProperty.pyi new file mode 100644 index 0000000000..370f6f3422 --- /dev/null +++ b/src/Mod/Material/App/ModelProperty.pyi @@ -0,0 +1,51 @@ +from Base.Metadata import export, constmethod +from Base.BaseClass import BaseClass +from typing import Final + + +@export( + Include="Mod/Material/App/Model.h", + Namespace="Materials", + Constructor=True, + Delete=True, +) +class ModelProperty(BaseClass): + """ + Material property descriptions. + + Author: DavidCarter (dcarter@davidcarter.ca) + Licence: LGPL + """ + + Name: str = ... + """Property name.""" + + DisplayName: str = ... + """Property display friendly name.""" + + Type: str = ... + """Property type.""" + + Units: str = ... + """Property units category.""" + + URL: str = ... + """URL to a detailed description of the property.""" + + Description: str = ... + """Property description.""" + + Columns: Final[list] = ... + """List of array columns.""" + + Inheritance: Final[str] = ... + """UUID of the model in which the property is defined.""" + + Inherited: Final[bool] = ... + """True if the property is inherited.""" + + def addColumn(self) -> None: + """ + Add a model property column. + """ + ... diff --git a/src/Mod/Material/App/ModelPropertyPy.xml b/src/Mod/Material/App/ModelPropertyPy.xml deleted file mode 100644 index ebe7a5d136..0000000000 --- a/src/Mod/Material/App/ModelPropertyPy.xml +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - Material property descriptions. - - - - Property name. - - - - - - Property type. - - - - - - Property units category. - - - - - - URL to a detailed description of the property. - - - - - - Property description. - - - - - diff --git a/src/Mod/Material/App/ModelPropertyPyImp.cpp b/src/Mod/Material/App/ModelPropertyPyImp.cpp index d701df9da6..90974e770b 100644 --- a/src/Mod/Material/App/ModelPropertyPyImp.cpp +++ b/src/Mod/Material/App/ModelPropertyPyImp.cpp @@ -31,19 +31,8 @@ using namespace Materials; // returns a string which represents the object e.g. when printed in python std::string ModelPropertyPy::representation() const { - ModelPropertyPy::PointerType ptr = getModelPropertyPtr(); std::stringstream str; - str << "Property [Name=("; - str << ptr->getName().toStdString(); - str << "), Type=("; - str << ptr->getPropertyType().toStdString(); - str << "), Units=("; - str << ptr->getUnits().toStdString(); - str << "), URL=("; - str << ptr->getURL().toStdString(); - str << "), Description=("; - str << ptr->getDescription().toStdString(); - str << ")]"; + str << ""; return str.str(); } @@ -65,26 +54,97 @@ Py::String ModelPropertyPy::getName() const return Py::String(getModelPropertyPtr()->getName().toStdString()); } +void ModelPropertyPy::setName(Py::String arg) +{ + getModelPropertyPtr()->setName(QString::fromStdString(arg)); +} + +Py::String ModelPropertyPy::getDisplayName() const +{ + return Py::String(getModelPropertyPtr()->getDisplayName().toStdString()); +} + +void ModelPropertyPy::setDisplayName(Py::String arg) +{ + getModelPropertyPtr()->setDisplayName(QString::fromStdString(arg)); +} + Py::String ModelPropertyPy::getType() const { return Py::String(getModelPropertyPtr()->getPropertyType().toStdString()); } +void ModelPropertyPy::setType(Py::String arg) +{ + getModelPropertyPtr()->setPropertyType(QString::fromStdString(arg)); +} + Py::String ModelPropertyPy::getUnits() const { return Py::String(getModelPropertyPtr()->getUnits().toStdString()); } +void ModelPropertyPy::setUnits(Py::String arg) +{ + getModelPropertyPtr()->setUnits(QString::fromStdString(arg)); +} + Py::String ModelPropertyPy::getURL() const { return Py::String(getModelPropertyPtr()->getURL().toStdString()); } +void ModelPropertyPy::setURL(Py::String arg) +{ + getModelPropertyPtr()->setURL(QString::fromStdString(arg)); +} + Py::String ModelPropertyPy::getDescription() const { return Py::String(getModelPropertyPtr()->getDescription().toStdString()); } +void ModelPropertyPy::setDescription(Py::String arg) +{ + getModelPropertyPtr()->setDescription(QString::fromStdString(arg)); +} + +Py::List ModelPropertyPy::getColumns() const +{ + Py::List list; + + auto columns = getModelPropertyPtr()->getColumns(); + for (auto& column : columns) { + PyObject* modelPropertyPy = new ModelPropertyPy(new ModelProperty(column)); + list.append(Py::Object(modelPropertyPy, true)); + } + + return list; +} + +Py::String ModelPropertyPy::getInheritance() const +{ + return Py::String(getModelPropertyPtr()->getInheritance().toStdString()); +} + +Py::Boolean ModelPropertyPy::getInherited() const +{ + return getModelPropertyPtr()->isInherited(); +} + +PyObject* ModelPropertyPy::addColumn(PyObject* args) +{ + PyObject* object; + if (!PyArg_ParseTuple(args, "O!", &ModelPropertyPy::Type, &object)) { + return nullptr; + } + ModelProperty* property = static_cast(object)->getModelPropertyPtr(); + + getModelPropertyPtr()->addColumn(*property); + Py_INCREF(Py_None); + return Py_None; +} + PyObject* ModelPropertyPy::getCustomAttributes(const char* /*attr*/) const { return nullptr; @@ -93,4 +153,4 @@ PyObject* ModelPropertyPy::getCustomAttributes(const char* /*attr*/) const int ModelPropertyPy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/) { return 0; -} +} \ No newline at end of file diff --git a/src/Mod/Material/App/ModelPy.xml b/src/Mod/Material/App/ModelPy.xml deleted file mode 100644 index 9202307244..0000000000 --- a/src/Mod/Material/App/ModelPy.xml +++ /dev/null @@ -1,85 +0,0 @@ - - - - - - Material model descriptions. - - - - Model library name. - - - - - - Model library path. - - - - - - Model icon path. - - - - - - Model name. - - - - - - Model directory. - - - - - - Unique model identifier. - - - - - - Description of the model. - - - - - - URL to a detailed description of the model. - - - - - - Digital Object Identifier (see https://doi.org/) - - - - - - List of inherited models identified by UUID. - - - - - - Dictionary of model properties. - - - - - diff --git a/src/Mod/Material/App/ModelPyImp.cpp b/src/Mod/Material/App/ModelPyImp.cpp index 1c8c769b1c..f90abf64bb 100644 --- a/src/Mod/Material/App/ModelPyImp.cpp +++ b/src/Mod/Material/App/ModelPyImp.cpp @@ -34,42 +34,8 @@ using namespace Materials; // returns a string which represents the object e.g. when printed in python std::string ModelPy::representation() const { - ModelPy::PointerType ptr = getModelPtr(); std::stringstream str; - str << "Property [Name=("; - str << ptr->getName().toStdString(); - str << "), UUID=("; - str << ptr->getUUID().toStdString(); - auto library = ptr->getLibrary(); - if (library) { - str << "), Library Name=("; - str << ptr->getLibrary()->getName().toStdString(); - str << "), Library Root=("; - str << ptr->getLibrary()->getDirectoryPath().toStdString(); - str << "), Library Icon=("; - str << ptr->getLibrary()->getIconPath().toStdString(); - } - str << "), Directory=("; - str << ptr->getDirectory().toStdString(); - str << "), URL=("; - str << ptr->getURL().toStdString(); - str << "), DOI=("; - str << ptr->getDOI().toStdString(); - str << "), Description=("; - str << ptr->getDescription().toStdString(); - str << "), Inherits=["; - auto& inherited = getModelPtr()->getInheritance(); - for (auto it = inherited.begin(); it != inherited.end(); it++) { - QString uuid = *it; - if (it != inherited.begin()) { - str << "), UUID=("; - } - else { - str << "UUID=("; - } - str << uuid.toStdString() << ")"; - } - str << "]]"; + str << ""; return str.str(); } @@ -109,9 +75,38 @@ Py::String ModelPy::getName() const return Py::String(getModelPtr()->getName().toStdString()); } +void ModelPy::setName(Py::String arg) +{ + getModelPtr()->setName(QString::fromStdString(arg)); +} + +Py::String ModelPy::getType() const +{ + auto type = (getModelPtr()->getType() == Model::ModelType_Physical) + ? "Physical" + : "Appearance"; + + return Py::String(type); +} + +void ModelPy::setType(Py::String arg) +{ + if (arg.as_std_string() == "Appearance") { + getModelPtr()->setType(Model::ModelType_Appearance); + } + else { + getModelPtr()->setType(Model::ModelType_Physical); + } +} + Py::String ModelPy::getDirectory() const { - return Py::String(getModelPtr()->getDirectoryPath().toStdString()); + return Py::String(getModelPtr()->getDirectory().toStdString()); +} + +void ModelPy::setDirectory(Py::String arg) +{ + getModelPtr()->setDirectory(QString::fromStdString(arg)); } Py::String ModelPy::getUUID() const @@ -124,16 +119,31 @@ Py::String ModelPy::getDescription() const return Py::String(getModelPtr()->getDescription().toStdString()); } +void ModelPy::setDescription(Py::String arg) +{ + getModelPtr()->setDescription(QString::fromStdString(arg)); +} + Py::String ModelPy::getURL() const { return Py::String(getModelPtr()->getURL().toStdString()); } +void ModelPy::setURL(Py::String arg) +{ + getModelPtr()->setURL(QString::fromStdString(arg)); +} + Py::String ModelPy::getDOI() const { return Py::String(getModelPtr()->getDOI().toStdString()); } +void ModelPy::setDOI(Py::String arg) +{ + getModelPtr()->setDOI(QString::fromStdString(arg)); +} + Py::List ModelPy::getInherited() const { auto& inherited = getModelPtr()->getInheritance(); @@ -148,7 +158,6 @@ Py::List ModelPy::getInherited() const Py::Dict ModelPy::getProperties() const { - // std::map *models = getModelPtr()->getModels(); Py::Dict dict; for (auto it = getModelPtr()->begin(); it != getModelPtr()->end(); it++) { @@ -162,6 +171,31 @@ Py::Dict ModelPy::getProperties() const return dict; } +PyObject* ModelPy::addInheritance(PyObject* args) +{ + char* uuid; + if (!PyArg_ParseTuple(args, "s", &uuid)) { + return nullptr; + } + + getModelPtr()->addInheritance(QString::fromStdString(uuid)); + Py_INCREF(Py_None); + return Py_None; +} + +PyObject* ModelPy::addProperty(PyObject* args) +{ + PyObject* object; + if (!PyArg_ParseTuple(args, "O!", &ModelPropertyPy::Type, &object)) { + return nullptr; + } + ModelProperty* property = static_cast(object)->getModelPropertyPtr(); + + getModelPtr()->addProperty(*property); + Py_INCREF(Py_None); + return Py_None; +} + PyObject* ModelPy::getCustomAttributes(const char* /*attr*/) const { return nullptr; diff --git a/src/Mod/Material/App/PreCompiled.h b/src/Mod/Material/App/PreCompiled.h index 085b2d5ca4..255a52e0de 100644 --- a/src/Mod/Material/App/PreCompiled.h +++ b/src/Mod/Material/App/PreCompiled.h @@ -42,7 +42,6 @@ #ifdef _PreComp_ // standard -#include #include // STL diff --git a/src/Mod/Material/App/PropertyMaterial.cpp b/src/Mod/Material/App/PropertyMaterial.cpp index 04d7579235..07f405ef03 100644 --- a/src/Mod/Material/App/PropertyMaterial.cpp +++ b/src/Mod/Material/App/PropertyMaterial.cpp @@ -88,14 +88,12 @@ void PropertyMaterial::Save(Base::Writer& writer) const void PropertyMaterial::Restore(Base::XMLReader& reader) { - MaterialManager manager; - // read my Element reader.readElement("PropertyMaterial"); // get the value of my Attribute auto uuid = reader.getAttribute("uuid"); - setValue(*manager.getMaterial(QString::fromLatin1(uuid))); + setValue(*MaterialManager::getManager().getMaterial(QString::fromLatin1(uuid))); } const char* PropertyMaterial::getEditorName() const diff --git a/src/Mod/Material/App/PyVariants.cpp b/src/Mod/Material/App/PyVariants.cpp new file mode 100644 index 0000000000..c1599e3661 --- /dev/null +++ b/src/Mod/Material/App/PyVariants.cpp @@ -0,0 +1,75 @@ +/*************************************************************************** + * Copyright (c) 2023 David Carter * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + +#include "PreCompiled.h" + +#include + +#include "PyVariants.h" +#include "Exceptions.h" + +using namespace Materials; + +PyObject* Materials::_pyObjectFromVariant(const QVariant& value) +{ + if (value.isNull()) { + Py_RETURN_NONE; + } + + if (value.userType() == qMetaTypeId()) { + return new Base::QuantityPy(new Base::Quantity(value.value())); + } + if (value.userType() == QMetaType::Double) { + return PyFloat_FromDouble(value.toDouble()); + } + if (value.userType() == QMetaType::Float) { + return PyFloat_FromDouble(value.toFloat()); + } + if (value.userType() == QMetaType::Int) { + return PyLong_FromLong(value.toInt()); + } + if (value.userType() == QMetaType::Long) { + return PyLong_FromLong(value.toInt()); + } + if (value.userType() == QMetaType::Bool) { + return Py::new_reference_to(Py::Boolean(value.toBool())); + } + if (value.userType() == QMetaType::QString) { + return PyUnicode_FromString(value.toString().toStdString().c_str()); + } + if (value.userType() == qMetaTypeId>()) { + return Py::new_reference_to(getList(value)); + } + + throw UnknownValueType(); +} + +Py::List Materials::getList(const QVariant& value) +{ + auto listValue = value.value>(); + Py::List list; + + for (auto& it : listValue) { + list.append(Py::Object(_pyObjectFromVariant(it))); + } + + return list; +} diff --git a/src/Mod/Material/App/PyVariants.h b/src/Mod/Material/App/PyVariants.h new file mode 100644 index 0000000000..f3ef124213 --- /dev/null +++ b/src/Mod/Material/App/PyVariants.h @@ -0,0 +1,42 @@ +/*************************************************************************** + * Copyright (c) 2023 David Carter * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + +#ifndef MATERIAL_PYVARIANTS_H +#define MATERIAL_PYVARIANTS_H + +#include + +#include +#include +#include +#include + +#include + +namespace Materials +{ + +extern MaterialsExport PyObject* _pyObjectFromVariant(const QVariant& value); +extern MaterialsExport Py::List getList(const QVariant& value); + +} // namespace Materials + +#endif // MATERIAL_PYVARIANTS_H diff --git a/src/Mod/Material/App/UUIDs.pyi b/src/Mod/Material/App/UUIDs.pyi new file mode 100644 index 0000000000..2fd83a6f39 --- /dev/null +++ b/src/Mod/Material/App/UUIDs.pyi @@ -0,0 +1,167 @@ +from Base.Metadata import export, constmethod +from Base.BaseClass import BaseClass +from typing import Final + +@export( + PythonName="Material.UUIDs", + Twin="ModelUUIDs", + TwinPointer="ModelUUIDs", + Include="Mod/Material/App/ModelUuids.h", + Namespace="Materials", + Constructor=True, + Delete=True, +) +class UUIDs(BaseClass): + """ + Material model UUID identifiers. + + Author: DavidCarter (dcarter@davidcarter.ca) + Licence: LGPL + """ + + Father: Final[str] = ... + """UUID for model System:Legacy/Father""" + + MaterialStandard: Final[str] = ... + """UUID for model System:Legacy/MaterialStandard""" + + ArrudaBoyce: Final[str] = ... + """UUID for model System:Mechanical/ArrudaBoyce""" + + Density: Final[str] = ... + """UUID for model System:Mechanical/Density""" + + Hardness: Final[str] = ... + """UUID for model System:Mechanical/Hardness""" + + IsotropicLinearElastic: Final[str] = ... + """UUID for model System:Mechanical/IsotropicLinearElastic""" + + LinearElastic: Final[str] = ... + """UUID for model System:Mechanical/LinearElastic""" + + Machinability: Final[str] = ... + """UUID for model System:Machining/Machinability""" + + MooneyRivlin: Final[str] = ... + """UUID for model System:Mechanical/MooneyRivlin""" + + NeoHooke: Final[str] = ... + """UUID for model System:Mechanical/NeoHooke""" + + OgdenN1: Final[str] = ... + """UUID for model System:Mechanical/OgdenN1""" + + OgdenN2: Final[str] = ... + """UUID for model System:Mechanical/OgdenN2""" + + OgdenN3: Final[str] = ... + """UUID for model System:Mechanical/OgdenN3""" + + OgdenYld2004p18: Final[str] = ... + """UUID for model System:Mechanical/OgdenYld2004p18""" + + OrthotropicLinearElastic: Final[str] = ... + """UUID for model System:Mechanical/OrthotropicLinearElastic""" + + PolynomialN1: Final[str] = ... + """UUID for model System:Mechanical/PolynomialN1""" + + PolynomialN2: Final[str] = ... + """UUID for model System:Mechanical/PolynomialN2""" + + PolynomialN3: Final[str] = ... + """UUID for model System:Mechanical/PolynomialN3""" + + ReducedPolynomialN1: Final[str] = ... + """UUID for model System:Mechanical/ReducedPolynomialN1""" + + ReducedPolynomialN2: Final[str] = ... + """UUID for model System:Mechanical/ReducedPolynomialN2""" + + ReducedPolynomialN3: Final[str] = ... + """UUID for model System:Mechanical/ReducedPolynomialN3""" + + Yeoh: Final[str] = ... + """UUID for model System:Mechanical/Yeoh""" + + Fluid: Final[str] = ... + """UUID for model System:Fluid/Fluid""" + + Thermal: Final[str] = ... + """UUID for model System:Thermal/Thermal""" + + Electromagnetic: Final[str] = ... + """UUID for model System:Electromagnetic/Electromagnetic""" + + Architectural: Final[str] = ... + """UUID for model System:Architectural/Architectural""" + + ArchitecturalRendering: Final[str] = ... + """UUID for model System:Architectural/ArchitecturalRendering""" + + Costs: Final[str] = ... + """UUID for model System:Costs/Costs""" + + BasicRendering: Final[str] = ... + """UUID for model System:Rendering/BasicRendering""" + + TextureRendering: Final[str] = ... + """UUID for model System:Rendering/TextureRendering""" + + AdvancedRendering: Final[str] = ... + """UUID for model System:Rendering/AdvancedRendering""" + + VectorRendering: Final[str] = ... + """UUID for model System:Rendering/VectorRendering""" + + RenderAppleseed: Final[str] = ... + """UUID for model System:Rendering/RenderAppleseed""" + + RenderCarpaint: Final[str] = ... + """UUID for model System:Rendering/RenderCarpaint""" + + RenderCycles: Final[str] = ... + """UUID for model System:Rendering/RenderCycles""" + + RenderDiffuse: Final[str] = ... + """UUID for model System:Rendering/RenderDiffuse""" + + RenderDisney: Final[str] = ... + """UUID for model System:Rendering/RenderDisney""" + + RenderEmission: Final[str] = ... + """UUID for model System:Rendering/RenderEmission""" + + RenderGlass: Final[str] = ... + """UUID for model System:Rendering/RenderGlass""" + + RenderLuxcore: Final[str] = ... + """UUID for model System:Rendering/RenderLuxcore""" + + RenderLuxrender: Final[str] = ... + """UUID for model System:Rendering/RenderLuxrender""" + + RenderMixed: Final[str] = ... + """UUID for model System:Rendering/RenderMixed""" + + RenderOspray: Final[str] = ... + """UUID for model System:Rendering/RenderOspray""" + + RenderPbrt: Final[str] = ... + """UUID for model System:Rendering/RenderPbrt""" + + RenderPovray: Final[str] = ... + """UUID for model System:Rendering/RenderPovray""" + + RenderSubstancePBR: Final[str] = ... + """UUID for model System:Rendering/RenderSubstancePBR""" + + RenderTexture: Final[str] = ... + """UUID for model System:Rendering/RenderTexture""" + + RenderWB: Final[str] = ... + """UUID for model System:Rendering/RenderWB""" + + TestModel: Final[str] = ... + """UUID for model System:Test/Test Model""" diff --git a/src/Mod/Material/App/UUIDsPy.xml b/src/Mod/Material/App/UUIDsPy.xml deleted file mode 100644 index 9daea8aeea..0000000000 --- a/src/Mod/Material/App/UUIDsPy.xml +++ /dev/null @@ -1,314 +0,0 @@ - - - - - - Material model UUID identifiers. - - - - UUID for model System:Legacy/Father - - - - - - UUID for model System:Legacy/MaterialStandard - - - - - - UUID for model System:Mechanical/ArrudaBoyce - - - - - - UUID for model System:Mechanical/Density - - - - - - UUID for model System:Mechanical/Hardness - - - - - - UUID for model System:Mechanical/IsotropicLinearElastic - - - - - - UUID for model System:Mechanical/LinearElastic - - - - - - UUID for model System:Machining/Machinability - - - - - - UUID for model System:Mechanical/MooneyRivlin - - - - - - UUID for model System:Mechanical/NeoHooke - - - - - - UUID for model System:Mechanical/OgdenN1 - - - - - - UUID for model System:Mechanical/OgdenN2 - - - - - - UUID for model System:Mechanical/OgdenN3 - - - - - - UUID for model System:Mechanical/OgdenYld2004p18 - - - - - - UUID for model System:Mechanical/OrthotropicLinearElastic - - - - - - UUID for model System:Mechanical/PolynomialN1 - - - - - - UUID for model System:Mechanical/PolynomialN2 - - - - - - UUID for model System:Mechanical/PolynomialN3 - - - - - - UUID for model System:Mechanical/ReducedPolynomialN1 - - - - - - UUID for model System:Mechanical/ReducedPolynomialN2 - - - - - - UUID for model System:Mechanical/ReducedPolynomialN3 - - - - - - UUID for model System:Mechanical/Yeoh - - - - - - UUID for model System:Fluid/Fluid - - - - - - UUID for model System:Thermal/Thermal - - - - - - UUID for model System:Electromagnetic/Electromagnetic - - - - - - UUID for model System:Architectural/Architectural - - - - - - UUID for model System:Architectural/ArchitecturalRendering - - - - - - UUID for model System:Costs/Costs - - - - - - UUID for model System:Rendering/BasicRendering - - - - - - UUID for model System:Rendering/TextureRendering - - - - - - UUID for model System:Rendering/AdvancedRendering - - - - - - UUID for model System:Rendering/VectorRendering - - - - - - UUID for model System:Rendering/RenderAppleseed - - - - - - UUID for model System:Rendering/RenderCarpaint - - - - - - UUID for model System:Rendering/RenderCycles - - - - - - UUID for model System:Rendering/RenderDiffuse - - - - - - UUID for model System:Rendering/RenderDisney - - - - - - UUID for model System:Rendering/RenderEmission - - - - - - UUID for model System:Rendering/RenderGlass - - - - - - UUID for model System:Rendering/RenderLuxcore - - - - - - UUID for model System:Rendering/RenderLuxrender - - - - - - UUID for model System:Rendering/RenderMixed - - - - - - UUID for model System:Rendering/RenderOspray - - - - - - UUID for model System:Rendering/RenderPbrt - - - - - - UUID for model System:Rendering/RenderPovray - - - - - - UUID for model System:Rendering/RenderSubstancePBR - - - - - - UUID for model System:Rendering/RenderTexture - - - - - - UUID for model System:Rendering/RenderWB - - - - - - UUID for model System:Test/Test Model - - - - - diff --git a/src/Mod/Material/App/UUIDsPyImp.cpp b/src/Mod/Material/App/UUIDsPyImp.cpp index 8e64b50ec8..4ff55ee7b8 100644 --- a/src/Mod/Material/App/UUIDsPyImp.cpp +++ b/src/Mod/Material/App/UUIDsPyImp.cpp @@ -199,7 +199,6 @@ Py::String UUIDsPy::getTextureRendering() const Py::String UUIDsPy::getAdvancedRendering() const { - Base::Console().Log(getModelUUIDsPtr()->ModelUUID_Rendering_Advanced.toStdString().c_str()); return Py::String(getModelUUIDsPtr()->ModelUUID_Rendering_Advanced.toStdString()); } diff --git a/src/Mod/Material/Gui/Array2D.cpp b/src/Mod/Material/Gui/Array2D.cpp index e2fdfb9860..2d2f6acefc 100644 --- a/src/Mod/Material/Gui/Array2D.cpp +++ b/src/Mod/Material/Gui/Array2D.cpp @@ -63,7 +63,7 @@ Array2D::Array2D(const QString& propertyName, } if (_property) { _value = - std::static_pointer_cast(_property->getMaterialValue()); + std::static_pointer_cast(_property->getMaterialValue()); setWindowTitle(_property->getDisplayName()); } else { diff --git a/src/Mod/Material/Gui/Array2D.h b/src/Mod/Material/Gui/Array2D.h index 3372d85c56..0bf71041b7 100644 --- a/src/Mod/Material/Gui/Array2D.h +++ b/src/Mod/Material/Gui/Array2D.h @@ -64,7 +64,7 @@ private: std::unique_ptr ui; std::shared_ptr _material; std::shared_ptr _property; - std::shared_ptr _value; + std::shared_ptr _value; QAction _deleteAction; diff --git a/src/Mod/Material/Gui/Array3D.cpp b/src/Mod/Material/Gui/Array3D.cpp index 450ca682cc..f91b3f5b83 100644 --- a/src/Mod/Material/Gui/Array3D.cpp +++ b/src/Mod/Material/Gui/Array3D.cpp @@ -59,7 +59,7 @@ Array3D::Array3D(const QString& propertyName, } if (_property) { _value = - std::static_pointer_cast(_property->getMaterialValue()); + std::static_pointer_cast(_property->getMaterialValue()); } else { _value = nullptr; diff --git a/src/Mod/Material/Gui/Array3D.h b/src/Mod/Material/Gui/Array3D.h index 00c9b6d68f..0bc629889f 100644 --- a/src/Mod/Material/Gui/Array3D.h +++ b/src/Mod/Material/Gui/Array3D.h @@ -67,7 +67,7 @@ private: std::unique_ptr ui; std::shared_ptr _material; std::shared_ptr _property; - std::shared_ptr _value; + std::shared_ptr _value; QAction _deleteDepthAction; QAction _delete2DAction; diff --git a/src/Mod/Material/Gui/ArrayDelegate.cpp b/src/Mod/Material/Gui/ArrayDelegate.cpp index 38f6f14246..91ff581927 100644 --- a/src/Mod/Material/Gui/ArrayDelegate.cpp +++ b/src/Mod/Material/Gui/ArrayDelegate.cpp @@ -125,7 +125,7 @@ QWidget* ArrayDelegate::createWidget(QWidget* parent, const QVariant& item) cons else if (_type == Materials::MaterialValue::Integer) { Gui::UIntSpinBox* spinner = new Gui::UIntSpinBox(parent); spinner->setMinimum(0); - spinner->setMaximum(UINT_MAX); + spinner->setMaximum(std::numeric_limits::max()); spinner->setValue(item.toUInt()); widget = spinner; } diff --git a/src/Mod/Material/Gui/ArrayModel.cpp b/src/Mod/Material/Gui/ArrayModel.cpp index ce4a00b867..c51fa0062e 100644 --- a/src/Mod/Material/Gui/ArrayModel.cpp +++ b/src/Mod/Material/Gui/ArrayModel.cpp @@ -47,7 +47,7 @@ AbstractArrayModel::AbstractArrayModel(QObject* parent) Array2DModel::Array2DModel(const std::shared_ptr& property, - const std::shared_ptr& value, + const std::shared_ptr& value, QObject* parent) : AbstractArrayModel(parent) , _property(property) @@ -199,7 +199,7 @@ bool Array2DModel::removeColumns(int column, int count, const QModelIndex& paren //=== Array3DDepthModel::Array3DDepthModel(const std::shared_ptr& property, - const std::shared_ptr& value, + const std::shared_ptr& value, QObject* parent) : AbstractArrayModel(parent) , _property(property) @@ -337,7 +337,7 @@ bool Array3DDepthModel::removeColumns(int column, int count, const QModelIndex& //=== Array3DModel::Array3DModel(const std::shared_ptr& property, - const std::shared_ptr& value, + const std::shared_ptr& value, QObject* parent) : AbstractArrayModel(parent) , _property(property) diff --git a/src/Mod/Material/Gui/ArrayModel.h b/src/Mod/Material/Gui/ArrayModel.h index da5bf69fac..f138e4e1bf 100644 --- a/src/Mod/Material/Gui/ArrayModel.h +++ b/src/Mod/Material/Gui/ArrayModel.h @@ -48,7 +48,7 @@ class Array2DModel: public AbstractArrayModel { public: explicit Array2DModel(const std::shared_ptr& property = nullptr, - const std::shared_ptr& value = nullptr, + const std::shared_ptr& value = nullptr, QObject* parent = nullptr); ~Array2DModel() override = default; @@ -71,7 +71,7 @@ public: private: std::shared_ptr _property; - std::shared_ptr _value; + std::shared_ptr _value; }; class Array3DDepthModel: public AbstractArrayModel @@ -79,7 +79,7 @@ class Array3DDepthModel: public AbstractArrayModel public: explicit Array3DDepthModel( const std::shared_ptr& property = nullptr, - const std::shared_ptr& value = nullptr, + const std::shared_ptr& value = nullptr, QObject* parent = nullptr); ~Array3DDepthModel() override = default; @@ -106,14 +106,14 @@ public: private: std::shared_ptr _property; - std::shared_ptr _value; + std::shared_ptr _value; }; class Array3DModel: public AbstractArrayModel { public: explicit Array3DModel(const std::shared_ptr& property = nullptr, - const std::shared_ptr& value = nullptr, + const std::shared_ptr& value = nullptr, QObject* parent = nullptr); ~Array3DModel() override = default; @@ -138,7 +138,7 @@ public: private: std::shared_ptr _property; - std::shared_ptr _value; + std::shared_ptr _value; }; } // namespace MatGui diff --git a/src/Mod/Material/Gui/BaseDelegate.cpp b/src/Mod/Material/Gui/BaseDelegate.cpp index 27123a2a0e..12291aaf93 100644 --- a/src/Mod/Material/Gui/BaseDelegate.cpp +++ b/src/Mod/Material/Gui/BaseDelegate.cpp @@ -414,7 +414,7 @@ BaseDelegate::createWidget(QWidget* parent, const QVariant& item, const QModelIn if (type == Materials::MaterialValue::Integer) { auto spinner = new Gui::UIntSpinBox(parent); spinner->setMinimum(0); - spinner->setMaximum(UINT_MAX); + spinner->setMaximum(std::numeric_limits::max()); spinner->setValue(item.toUInt()); widget = spinner; } diff --git a/src/Mod/Material/Gui/CMakeLists.txt b/src/Mod/Material/Gui/CMakeLists.txt index a466a6f988..b867b1a11b 100644 --- a/src/Mod/Material/Gui/CMakeLists.txt +++ b/src/Mod/Material/Gui/CMakeLists.txt @@ -30,10 +30,10 @@ qt_find_and_add_translation(QM_SRCS "Resources/translations/*_*.ts" qt_create_resource_file(${Material_TR_QRC} ${QM_SRCS}) qt_add_resources(MatGui_QRC_SRCS Resources/Material.qrc ${Material_TR_QRC}) -generate_from_xml(MaterialTreeWidgetPy) +generate_from_py(MaterialTreeWidget) SET(Python_SRCS - MaterialTreeWidgetPy.xml + MaterialTreeWidget.pyi MaterialTreeWidgetPyImp.cpp ) SOURCE_GROUP("Python" FILES ${Python_SRCS}) diff --git a/src/Mod/Material/Gui/Command.cpp b/src/Mod/Material/Gui/Command.cpp index fe36ed5102..37d1059cc7 100644 --- a/src/Mod/Material/Gui/Command.cpp +++ b/src/Mod/Material/Gui/Command.cpp @@ -61,8 +61,6 @@ void CmdMaterialEdit::activated(int iMsg) { Q_UNUSED(iMsg); - Base::Console().Log("Material_Edit\n"); - static QPointer dlg = nullptr; if (!dlg) { dlg = new MatGui::MaterialsEditor(Gui::getMainWindow()); diff --git a/src/Mod/Material/Gui/DlgInspectAppearance.cpp b/src/Mod/Material/Gui/DlgInspectAppearance.cpp index 25c36cfa41..2d67cfe676 100644 --- a/src/Mod/Material/Gui/DlgInspectAppearance.cpp +++ b/src/Mod/Material/Gui/DlgInspectAppearance.cpp @@ -151,7 +151,7 @@ void DlgInspectAppearance::update(std::vector& views) ui->editObjectLabel->setText(QString::fromUtf8(labelProp->getValue())); } else { - ui->editObjectLabel->setText(QLatin1String("")); + ui->editObjectLabel->setText(QStringLiteral("")); } ui->editObjectName->setText(QLatin1String(obj->getNameInDocument())); @@ -162,15 +162,15 @@ void DlgInspectAppearance::update(std::vector& views) ui->editSubShape->setText(QString::fromStdString(subObject.getSubNames()[0])); } else { - ui->editSubShape->setText(QLatin1String("")); + ui->editSubShape->setText(QStringLiteral("")); } } else { - ui->editSubShape->setText(QLatin1String("")); + ui->editSubShape->setText(QStringLiteral("")); } auto subShapeType = QString::fromUtf8(obj->getTypeId().getName()); - subShapeType.remove(subShapeType.indexOf(QLatin1String("::")), subShapeType.size()); + subShapeType.remove(subShapeType.indexOf(QStringLiteral("::")), subShapeType.size()); ui->editSubShapeType->setText(subShapeType); ui->editShapeType->setText(QString::fromUtf8(obj->getTypeId().getName())); diff --git a/src/Mod/Material/Gui/DlgInspectMaterial.cpp b/src/Mod/Material/Gui/DlgInspectMaterial.cpp index c599b7f75b..2c6fe1a0bc 100644 --- a/src/Mod/Material/Gui/DlgInspectMaterial.cpp +++ b/src/Mod/Material/Gui/DlgInspectMaterial.cpp @@ -120,7 +120,7 @@ void DlgInspectMaterial::appendClip(QString text) { // Need to add indent QString indent(clipboardIndent * 4, QLatin1Char(' ')); - clipboardText += indent + text + QLatin1String("\n"); + clipboardText += indent + text + QStringLiteral("\n"); } QStandardItem* DlgInspectMaterial::clipItem(QString text) @@ -144,7 +144,7 @@ void DlgInspectMaterial::unindent() void DlgInspectMaterial::update(std::vector& views) { - clipboardText = QLatin1String(""); + clipboardText = QStringLiteral(""); clipboardIndent = 0; App::Document* doc = App::GetApplication().getActiveDocument(); if (doc) { @@ -166,7 +166,7 @@ void DlgInspectMaterial::update(std::vector& views) appendClip(tr("Label: ") + QString::fromUtf8(labelProp->getValue())); } else { - ui->editObjectLabel->setText(QLatin1String("")); + ui->editObjectLabel->setText(QStringLiteral("")); } ui->editObjectName->setText(QLatin1String(obj->getNameInDocument())); appendClip(tr("Internal Name: ") + QString::fromUtf8(obj->getNameInDocument())); @@ -178,15 +178,15 @@ void DlgInspectMaterial::update(std::vector& views) ui->editSubShape->setText(QString::fromStdString(subObject.getSubNames()[0])); } else { - ui->editSubShape->setText(QLatin1String("")); + ui->editSubShape->setText(QStringLiteral("")); } } else { - ui->editSubShape->setText(QLatin1String("")); + ui->editSubShape->setText(QStringLiteral("")); } auto subShapeType = QString::fromUtf8(obj->getTypeId().getName()); - subShapeType.remove(subShapeType.indexOf(QLatin1String("::")), subShapeType.size()); + subShapeType.remove(subShapeType.indexOf(QStringLiteral("::")), subShapeType.size()); appendClip(tr("Type: ") + subShapeType); ui->editSubShapeType->setText(subShapeType); appendClip(tr("TypeID: ") + QString::fromUtf8(obj->getTypeId().getName())); @@ -248,7 +248,7 @@ void DlgInspectMaterial::addModels(QTreeView* tree, } else { for (const QString& uuid : *models) { - auto model = modelManager.getModel(uuid); + auto model = Materials::ModelManager::getManager().getModel(uuid); auto name = clipItem(tr("Name: ") + model->getName()); addExpanded(tree, parent, name); @@ -287,7 +287,7 @@ void DlgInspectMaterial::addModelDetails(QTreeView* tree, } else { for (const QString& inherited : inheritedUuids) { - auto inheritedModel = modelManager.getModel(inherited); + auto inheritedModel = Materials::ModelManager::getManager().getModel(inherited); auto name = clipItem(tr("Name: ") + inheritedModel->getName()); addExpanded(tree, inherits, name); @@ -340,10 +340,10 @@ void DlgInspectMaterial::addMaterialDetails(QTreeView* tree, { auto uuid = clipItem(tr("UUID: ") + material.getUUID()); addExpanded(tree, parent, uuid); - auto library = clipItem(tr("Library: ") + material.getLibrary()->getName()); + auto library = + clipItem(tr("Library: ") + material.getLibrary()->getName()); addExpanded(tree, parent, library); - auto libraryPath = - clipItem(tr("Library Directory: ") + material.getLibrary()->getDirectoryPath()); + auto libraryPath = clipItem(tr("Library Directory: ") + material.getLibrary()->getDirectoryPath()); addExpanded(tree, parent, libraryPath); auto directory = clipItem(tr("Sub Directory: ") + material.getDirectory()); addExpanded(tree, parent, directory); @@ -353,7 +353,7 @@ void DlgInspectMaterial::addMaterialDetails(QTreeView* tree, indent(); auto parentUUID = material.getParentUUID(); if (!parentUUID.isEmpty()) { - auto parentMaterial = materialManager.getMaterial(material.getParentUUID()); + auto parentMaterial = Materials::MaterialManager::getManager().getMaterial(material.getParentUUID()); addMaterial(tree, inherits, *parentMaterial); } else { diff --git a/src/Mod/Material/Gui/DlgInspectMaterial.h b/src/Mod/Material/Gui/DlgInspectMaterial.h index 710212a2a4..2ad4a4e026 100644 --- a/src/Mod/Material/Gui/DlgInspectMaterial.h +++ b/src/Mod/Material/Gui/DlgInspectMaterial.h @@ -60,8 +60,6 @@ public: private: std::unique_ptr ui; - Materials::MaterialManager materialManager; - Materials::ModelManager modelManager; QString clipboardText; int clipboardIndent; diff --git a/src/Mod/Material/Gui/MaterialDelegate.cpp b/src/Mod/Material/Gui/MaterialDelegate.cpp index 7e58258bc0..ad36b74462 100644 --- a/src/Mod/Material/Gui/MaterialDelegate.cpp +++ b/src/Mod/Material/Gui/MaterialDelegate.cpp @@ -424,7 +424,7 @@ QWidget* MaterialDelegate::createWidget(QWidget* parent, if (type == Materials::MaterialValue::Integer) { auto spinner = new Gui::IntSpinBox(parent); spinner->setMinimum(0); - spinner->setMaximum(INT_MAX); + spinner->setMaximum(std::numeric_limits::max()); spinner->setValue(item.toInt()); widget = spinner; } diff --git a/src/Mod/Material/Gui/MaterialSave.cpp b/src/Mod/Material/Gui/MaterialSave.cpp index fd0ebb729a..a0c2beca4a 100644 --- a/src/Mod/Material/Gui/MaterialSave.cpp +++ b/src/Mod/Material/Gui/MaterialSave.cpp @@ -137,22 +137,23 @@ void MaterialSave::onOk(bool checked) QFileInfo filepath(_selectedPath + QStringLiteral("/") + name + QStringLiteral(".FCMat")); - if (library->fileExists(filepath.filePath())) { + /*if (library->fileExists(filepath.filePath()))*/ { // confirm overwrite auto res = confirmOverwrite(_filename); if (res == QMessageBox::Cancel) { return; } - _manager.saveMaterial(library, _material, filepath.filePath(), true, false, _saveInherited); + Materials::MaterialManager::getManager() + .saveMaterial(library, _material, filepath.filePath(), true, false, _saveInherited); accept(); return; } bool saveAsCopy = false; - if (_manager.exists(_material->getUUID())) { + if (Materials::MaterialManager::getManager().exists(_material->getUUID())) { // Does it already exist in this library? - if (_manager.exists(library, _material->getUUID())) { + if (Materials::MaterialManager::getManager().exists(library, _material->getUUID())) { // Confirm saving a new material auto res = confirmNewMaterial(); if (res == QMessageBox::Cancel) { @@ -174,7 +175,7 @@ void MaterialSave::onOk(bool checked) } } - _manager + Materials::MaterialManager::getManager() .saveMaterial(library, _material, filepath.filePath(), false, saveAsCopy, _saveInherited); accept(); @@ -287,12 +288,16 @@ void MaterialSave::reject() void MaterialSave::setLibraries() { - auto libraries = _manager.getMaterialLibraries(); + auto libraries = Materials::MaterialManager::getManager().getLibraries(); for (auto& library : *libraries) { - if (!library->isReadOnly()) { - QVariant libraryVariant; - libraryVariant.setValue(library); - ui->comboLibrary->addItem(library->getName(), libraryVariant); + if (library->isLocal()) { + auto materialLibrary = + reinterpret_cast&>(library); + if (!materialLibrary->isReadOnly()) { + QVariant libraryVariant; + libraryVariant.setValue(materialLibrary); + ui->comboLibrary->addItem(materialLibrary->getName(), libraryVariant); + } } } } @@ -327,7 +332,7 @@ void MaterialSave::addMaterials( auto tree = ui->treeMaterials; for (auto& mat : *modelTree) { std::shared_ptr nodePtr = mat.second; - if (nodePtr->getType() == Materials::MaterialTreeNode::DataNode) { + if (nodePtr->getType() == Materials::MaterialTreeNode::NodeType::DataNode) { std::shared_ptr material = nodePtr->getData(); QString uuid = material->getUUID(); @@ -368,7 +373,7 @@ void MaterialSave::showSelectedTree() lib->setFlags(Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled); addExpanded(tree, model, lib); - auto modelTree = _manager.getMaterialTree(library); + auto modelTree = Materials::MaterialManager::getManager().getMaterialTree(library); addMaterials(*lib, modelTree, folderIcon, icon); } else { @@ -444,14 +449,14 @@ void MaterialSave::createFolder(const QString& path) { auto library = currentLibrary(); - _manager.createFolder(library, path); + Materials::MaterialManager::getManager().createFolder(library, path); } void MaterialSave::renameFolder(const QString& oldPath, const QString& newPath) { auto library = currentLibrary(); - _manager.renameFolder(library, oldPath, newPath); + Materials::MaterialManager::getManager().renameFolder(library, oldPath, newPath); } void MaterialSave::deleteRecursive(const QString& path) @@ -459,7 +464,7 @@ void MaterialSave::deleteRecursive(const QString& path) // This will delete files, folders, and any children auto library = currentLibrary(); - _manager.deleteRecursive(library, path); + Materials::MaterialManager::getManager().deleteRecursive(library, path); } void MaterialSave::onNewFolder(bool checked) @@ -552,9 +557,9 @@ int MaterialSave::confirmDelete(QWidget* parent) { auto library = currentLibrary(); - if (library->isRoot(_selectedFull)) { - return QMessageBox::Cancel; - } + // if (library->isRoot(_selectedFull)) { + // return QMessageBox::Cancel; + // } QMessageBox box(parent ? parent : this); box.setIcon(QMessageBox::Question); @@ -600,10 +605,10 @@ void MaterialSave::deleteSelected() { auto library = currentLibrary(); - if (!library->isRoot(_selectedFull)) { - _manager.deleteRecursive(library, _selectedFull); - removeSelectedFromTree(); - } + // if (!library->isRoot(_selectedFull)) { + // Materials::MaterialManager::getManager().deleteRecursive(library, _selectedFull); + // removeSelectedFromTree(); + // } } void MaterialSave::removeChildren(QStandardItem* item) diff --git a/src/Mod/Material/Gui/MaterialSave.h b/src/Mod/Material/Gui/MaterialSave.h index 9d4028a3b4..7e794f4784 100644 --- a/src/Mod/Material/Gui/MaterialSave.h +++ b/src/Mod/Material/Gui/MaterialSave.h @@ -35,6 +35,8 @@ namespace MatGui { +class MaterialLibrary; + class Ui_MaterialSave; class MaterialSave: public QDialog @@ -76,7 +78,6 @@ public: private: std::unique_ptr ui; - Materials::MaterialManager _manager; std::shared_ptr _material; bool _saveInherited; QString _selectedPath; diff --git a/src/Mod/Material/Gui/MaterialTreeWidget.cpp b/src/Mod/Material/Gui/MaterialTreeWidget.cpp index 872547ac2d..99df9e84a6 100644 --- a/src/Mod/Material/Gui/MaterialTreeWidget.cpp +++ b/src/Mod/Material/Gui/MaterialTreeWidget.cpp @@ -487,7 +487,7 @@ void MaterialTreeWidget::addRecent(const QString& uuid) } // Ensure it is a material. New, unsaved materials will not be try { - auto material = _materialManager.getMaterial(uuid); + auto material = Materials::MaterialManager::getManager().getMaterial(uuid); Q_UNUSED(material) } catch (const Materials::MaterialNotFound&) { @@ -555,12 +555,15 @@ void MaterialTreeWidget::fillMaterialTree() addRecents(lib); } - auto libraries = _materialManager.getMaterialLibraries(); + auto libraries = Materials::MaterialManager::getManager().getLibraries(); for (const auto& library : *libraries) { - auto modelTree = _materialManager.getMaterialTree(library, _filter, _filterOptions); + auto materialTree = + Materials::MaterialManager::getManager().getMaterialTree(library, + _filter, + _filterOptions); bool showLibraries = _filterOptions.includeEmptyLibraries(); - if (!_filterOptions.includeEmptyLibraries() && modelTree->size() > 0) { + if (!_filterOptions.includeEmptyLibraries() && materialTree->size() > 0) { showLibraries = true; } @@ -572,7 +575,7 @@ void MaterialTreeWidget::fillMaterialTree() QIcon icon(library->getIconPath()); QIcon folderIcon(QStringLiteral(":/icons/folder.svg")); - addMaterials(*lib, modelTree, folderIcon, icon, param); + addMaterials(*lib, materialTree, folderIcon, icon, param); } } } @@ -616,8 +619,7 @@ void MaterialTreeWidget::addRecents(QStandardItem* parent) for (auto& uuid : _recents) { try { auto material = getMaterialManager().getMaterial(uuid); - - QIcon icon = QIcon(material->getLibrary()->getIconPath()); + QIcon icon(material->getLibrary()->getIconPath()); auto card = new QStandardItem(icon, material->getName()); card->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); card->setData(QVariant(uuid), Qt::UserRole); @@ -634,8 +636,7 @@ void MaterialTreeWidget::addFavorites(QStandardItem* parent) for (auto& uuid : _favorites) { try { auto material = getMaterialManager().getMaterial(uuid); - - QIcon icon = QIcon(material->getLibrary()->getIconPath()); + QIcon icon(material->getLibrary()->getIconPath()); auto card = new QStandardItem(icon, material->getName()); card->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); card->setData(QVariant(uuid), Qt::UserRole); @@ -657,9 +658,8 @@ void MaterialTreeWidget::addMaterials( auto childParam = param->GetGroup(parent.text().toStdString().c_str()); for (auto& mat : *modelTree) { auto nodePtr = mat.second; - if (nodePtr->getType() == Materials::MaterialTreeNode::DataNode) { - auto material = nodePtr->getData(); - QString uuid = material->getUUID(); + if (nodePtr->getType() == Materials::MaterialTreeNode::NodeType::DataNode) { + QString uuid = nodePtr->getUUID(); auto card = new QStandardItem(icon, mat.first); card->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); diff --git a/src/Mod/Material/Gui/MaterialTreeWidget.h b/src/Mod/Material/Gui/MaterialTreeWidget.h index efdbf0e41e..4b58759c3c 100644 --- a/src/Mod/Material/Gui/MaterialTreeWidget.h +++ b/src/Mod/Material/Gui/MaterialTreeWidget.h @@ -48,7 +48,6 @@ namespace MatGui { -class CommandManager; class WidgetFactoryInst; class MaterialTreeWidgetPy; @@ -207,17 +206,13 @@ private: int _recentMax; MaterialTreeWidgetPy* pyTreeWidget {nullptr}; - Materials::MaterialManager _materialManager; - // friends friend class Gui::WidgetFactoryInst; protected: - // bool m_Restored = false; - Materials::MaterialManager& getMaterialManager() { - return _materialManager; + return Materials::MaterialManager::getManager(); } void getFavorites(); diff --git a/src/Mod/Material/Gui/MaterialTreeWidget.pyi b/src/Mod/Material/Gui/MaterialTreeWidget.pyi new file mode 100644 index 0000000000..e2acb43211 --- /dev/null +++ b/src/Mod/Material/Gui/MaterialTreeWidget.pyi @@ -0,0 +1,49 @@ +from Metadata import export, constmethod, forward_declarations, class_declarations, sequence_protocol +from Base.BaseClass import BaseClass +from typing import Final, overload + +@export( + Twin="MaterialTreeWidget", + TwinPointer="MaterialTreeWidget", + Include="Mod/Material/Gui/MaterialTreeWidget.h", + Namespace="MatGui", + Constructor=True, + Delete=False, +) +class MaterialTreeWidget(BaseClass): + """ + Material tree widget. + """ + + UUID: str = ... + """Material UUID.""" + + expanded: bool = ... + """Expand material tree.""" + + IncludeFavorites: bool = ... + """Include favorites in the material list.""" + + IncludeRecent: bool = ... + """Include recently used materials in the material list.""" + + IncludeEmptyFolders: bool = ... + """Include empty folders in the material list.""" + + IncludeEmptyLibraries: bool = ... + """Include empty libraries in the material list.""" + + IncludeLegacy: bool = ... + """Include legacy materials in the material list.""" + + def setFilter(self) -> None: + """ + Set the material filter or list of filters. + """ + ... + + def selectFilter(self) -> None: + """ + Set the current material filter. + """ + ... diff --git a/src/Mod/Material/Gui/MaterialTreeWidgetPy.xml b/src/Mod/Material/Gui/MaterialTreeWidgetPy.xml deleted file mode 100644 index 182aa1eb0e..0000000000 --- a/src/Mod/Material/Gui/MaterialTreeWidgetPy.xml +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - Material tree widget. - - - - Material UUID. - - - - - - Expand material tree. - - - - - - Include favorites in the material list. - - - - - - Include recently used materials in the material list. - - - - - - Include empty folders in the material list. - - - - - - Include empty libraries in the material list. - - - - - - Include legacy materials in the material list. - - - - - - Set the material filter or list of filters. - - - - - Set the current material filter. - - - - diff --git a/src/Mod/Material/Gui/MaterialsEditor.cpp b/src/Mod/Material/Gui/MaterialsEditor.cpp index d4d714c565..16530b487e 100644 --- a/src/Mod/Material/Gui/MaterialsEditor.cpp +++ b/src/Mod/Material/Gui/MaterialsEditor.cpp @@ -46,6 +46,7 @@ #include #include +#include #include #include @@ -89,7 +90,7 @@ void MaterialsEditor::setup() Gui::WaitCursor wc; ui->setupUi(this); - _warningIcon = QIcon(QLatin1String(":/icons/Warning.svg")); + _warningIcon = QIcon(QStringLiteral(":/icons/Warning.svg")); getFavorites(); getRecents(); @@ -108,7 +109,7 @@ void MaterialsEditor::setup() resize(width, height); - ui->buttonURL->setIcon(QIcon(QLatin1String(":/icons/internet-web-browser.svg"))); + ui->buttonURL->setIcon(QIcon(QStringLiteral(":/icons/internet-web-browser.svg"))); connect(ui->standardButtons->button(QDialogButtonBox::Ok), &QPushButton::clicked, @@ -214,7 +215,7 @@ void MaterialsEditor::addFavorite(const QString& uuid) { // Ensure it is a material. New, unsaved materials will not be try { - auto material = _materialManager.getMaterial(uuid); + auto material = Materials::MaterialManager::getManager().getMaterial(uuid); Q_UNUSED(material) } catch (const Materials::MaterialNotFound&) { @@ -299,7 +300,7 @@ void MaterialsEditor::addRecent(const QString& uuid) { // Ensure it is a material. New, unsaved materials will not be try { - auto material = _materialManager.getMaterial(uuid); + auto material = Materials::MaterialManager::getManager().getMaterial(uuid); Q_UNUSED(material) } catch (const Materials::MaterialNotFound&) { @@ -429,7 +430,7 @@ void MaterialsEditor::onAppearanceAdd(bool checked) if (dialog.exec() == QDialog::Accepted) { QString selected = dialog.selectedModel(); _material->addAppearance(selected); - auto model = getModelManager().getModel(selected); + auto model = Materials::ModelManager::getManager().getModel(selected); if (selected == Materials::ModelUUIDs::ModelUUID_Rendering_Basic || model->inherits(Materials::ModelUUIDs::ModelUUID_Rendering_Basic)) { // Add default appearance properties @@ -497,7 +498,7 @@ void MaterialsEditor::setMaterialDefaults() _material->setLicense(QLatin1String(name)); // Empty materials will have no parent - _materialManager.dereference(_material); + Materials::MaterialManager::getManager().dereference(_material); updateMaterial(); _material->resetEditState(); @@ -665,18 +666,22 @@ void MaterialsEditor::saveMaterialTree(const Base::Reference& para void MaterialsEditor::addMaterials( QStandardItem& parent, const std::shared_ptr>> - modelTree, + materialTree, const QIcon& folderIcon, const QIcon& icon, const Base::Reference& param) { auto childParam = param->GetGroup(parent.text().toStdString().c_str()); auto tree = ui->treeMaterials; - for (auto& mat : *modelTree) { + for (auto& mat : *materialTree) { std::shared_ptr nodePtr = mat.second; - if (nodePtr->getType() == Materials::MaterialTreeNode::DataNode) { + if (nodePtr->getType() == Materials::MaterialTreeNode::NodeType::DataNode) { + QString uuid = nodePtr->getUUID(); auto material = nodePtr->getData(); - QString uuid = material->getUUID(); + if (!material) { + material = Materials::MaterialManager::getManager().getMaterial(uuid); + nodePtr->setData(material); + } QIcon matIcon = icon; if (material->isOldFormat()) { @@ -697,7 +702,9 @@ void MaterialsEditor::addMaterials( addExpanded(tree, &parent, node, childParam); node->setFlags(Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled); auto treeMap = nodePtr->getFolder(); - addMaterials(*node, treeMap, folderIcon, icon, childParam); + // if (treeMap) { + addMaterials(*node, treeMap, folderIcon, icon, childParam); + // } } } } @@ -800,14 +807,15 @@ void MaterialsEditor::addRecents(QStandardItem* parent) for (auto& uuid : _recents) { try { auto material = getMaterialManager().getMaterial(uuid); + // if (material->getLibrary()->isLocal()) { + QIcon icon = QIcon(material->getLibrary()->getIconPath()); + auto card = new QStandardItem(icon, libraryPath(material)); + card->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled + | Qt::ItemIsDropEnabled); + card->setData(QVariant(uuid), Qt::UserRole); - QIcon icon = QIcon(material->getLibrary()->getIconPath()); - auto card = new QStandardItem(icon, libraryPath(material)); - card->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled - | Qt::ItemIsDropEnabled); - card->setData(QVariant(uuid), Qt::UserRole); - - addExpanded(tree, parent, card); + addExpanded(tree, parent, card); + // } } catch (const Materials::MaterialNotFound&) { } @@ -820,11 +828,10 @@ void MaterialsEditor::addFavorites(QStandardItem* parent) for (auto& uuid : _favorites) { try { auto material = getMaterialManager().getMaterial(uuid); - QIcon icon = QIcon(material->getLibrary()->getIconPath()); auto card = new QStandardItem(icon, libraryPath(material)); card->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled - | Qt::ItemIsDropEnabled); + | Qt::ItemIsDropEnabled); card->setData(QVariant(uuid), Qt::UserRole); addExpanded(tree, parent, card); @@ -856,12 +863,12 @@ void MaterialsEditor::fillMaterialTree() addRecents(lib); } - auto libraries = getMaterialManager().getMaterialLibraries(); + auto libraries = getMaterialManager().getLibraries(); for (const auto& library : *libraries) { - auto modelTree = getMaterialManager().getMaterialTree(library); + auto materialTree = getMaterialManager().getMaterialTree(library); bool showLibraries = _filterOptions.includeEmptyLibraries(); - if (!_filterOptions.includeEmptyLibraries() && modelTree->size() > 0) { + if (!_filterOptions.includeEmptyLibraries() && materialTree->size() > 0) { showLibraries = true; } @@ -873,7 +880,7 @@ void MaterialsEditor::fillMaterialTree() QIcon icon(library->getIconPath()); QIcon folderIcon(QStringLiteral(":/icons/folder.svg")); - addMaterials(*lib, modelTree, folderIcon, icon, param); + addMaterials(*lib, materialTree, folderIcon, icon, param); } } } @@ -901,11 +908,11 @@ bool MaterialsEditor::updateTexturePreview() const { bool hasImage = false; QImage image; - //double scaling = 99.0; + // double scaling = 99.0; if (_material->hasModel(Materials::ModelUUIDs::ModelUUID_Rendering_Texture)) { // First try loading an embedded image try { - auto property = _material->getAppearanceProperty(QLatin1String("TextureImage")); + auto property = _material->getAppearanceProperty(QStringLiteral("TextureImage")); if (!property->isNull()) { // Base::Console().Log("Has 'TextureImage'\n"); auto propertyValue = property->getString(); @@ -922,7 +929,7 @@ bool MaterialsEditor::updateTexturePreview() const // If no embedded image, load from a path if (!hasImage) { try { - auto property = _material->getAppearanceProperty(QLatin1String("TexturePath")); + auto property = _material->getAppearanceProperty(QStringLiteral("TexturePath")); if (!property->isNull()) { // Base::Console().Log("Has 'TexturePath'\n"); auto filePath = property->getString(); @@ -940,10 +947,10 @@ bool MaterialsEditor::updateTexturePreview() const // Apply any scaling try { - auto property = _material->getAppearanceProperty(QLatin1String("TextureScaling")); + auto property = _material->getAppearanceProperty(QStringLiteral("TextureScaling")); if (!property->isNull()) { - //scaling = property->getFloat(); - // Base::Console().Log("Has 'TextureScaling' = %g\n", scaling); + // scaling = property->getFloat(); + // Base::Console().Log("Has 'TextureScaling' = %g\n", scaling); } } catch (const Materials::PropertyNotFound&) { @@ -959,43 +966,43 @@ bool MaterialsEditor::updateTexturePreview() const bool MaterialsEditor::updateMaterialPreview() const { - if (_material->hasAppearanceProperty(QLatin1String("AmbientColor"))) { - QString color = _material->getAppearanceValueString(QLatin1String("AmbientColor")); + if (_material->hasAppearanceProperty(QStringLiteral("AmbientColor"))) { + QString color = _material->getAppearanceValueString(QStringLiteral("AmbientColor")); _rendered->setAmbientColor(getColorHash(color, 255)); } else { _rendered->resetAmbientColor(); } - if (_material->hasAppearanceProperty(QLatin1String("DiffuseColor"))) { - QString color = _material->getAppearanceValueString(QLatin1String("DiffuseColor")); + if (_material->hasAppearanceProperty(QStringLiteral("DiffuseColor"))) { + QString color = _material->getAppearanceValueString(QStringLiteral("DiffuseColor")); _rendered->setDiffuseColor(getColorHash(color, 255)); } else { _rendered->resetDiffuseColor(); } - if (_material->hasAppearanceProperty(QLatin1String("SpecularColor"))) { - QString color = _material->getAppearanceValueString(QLatin1String("SpecularColor")); + if (_material->hasAppearanceProperty(QStringLiteral("SpecularColor"))) { + QString color = _material->getAppearanceValueString(QStringLiteral("SpecularColor")); _rendered->setSpecularColor(getColorHash(color, 255)); } else { _rendered->resetSpecularColor(); } - if (_material->hasAppearanceProperty(QLatin1String("EmissiveColor"))) { - QString color = _material->getAppearanceValueString(QLatin1String("EmissiveColor")); + if (_material->hasAppearanceProperty(QStringLiteral("EmissiveColor"))) { + QString color = _material->getAppearanceValueString(QStringLiteral("EmissiveColor")); _rendered->setEmissiveColor(getColorHash(color, 255)); } else { _rendered->resetEmissiveColor(); } - if (_material->hasAppearanceProperty(QLatin1String("Shininess"))) { - double value = _material->getAppearanceValue(QLatin1String("Shininess")).toDouble(); + if (_material->hasAppearanceProperty(QStringLiteral("Shininess"))) { + double value = _material->getAppearanceValue(QStringLiteral("Shininess")).toDouble(); _rendered->setShininess(value); } else { _rendered->resetShininess(); } - if (_material->hasAppearanceProperty(QLatin1String("Transparency"))) { - double value = _material->getAppearanceValue(QLatin1String("Transparency")).toDouble(); + if (_material->hasAppearanceProperty(QStringLiteral("Transparency"))) { + double value = _material->getAppearanceValue(QStringLiteral("Transparency")).toDouble(); _rendered->setTransparency(value); } else { @@ -1065,7 +1072,7 @@ void MaterialsEditor::updateMaterialAppearance() for (auto it = models->begin(); it != models->end(); it++) { QString uuid = *it; try { - auto model = getModelManager().getModel(uuid); + auto model = Materials::ModelManager::getManager().getModel(uuid); QString name = model->getName(); auto modelRoot = new QStandardItem(name); @@ -1129,7 +1136,7 @@ void MaterialsEditor::updateMaterialProperties() for (auto it = models->begin(); it != models->end(); it++) { QString uuid = *it; try { - auto model = getModelManager().getModel(uuid); + auto model = Materials::ModelManager::getManager().getModel(uuid); QString name = model->getName(); auto modelRoot = new QStandardItem(name); @@ -1177,14 +1184,14 @@ QString MaterialsEditor::libraryPath(const std::shared_ptr& QString path; auto library = material->getLibrary(); if (library) { - path = QStringLiteral("/%1/%2") - .arg(material->getLibrary()->getName()) - .arg(material->getDirectory()); - } - else { - path = QStringLiteral("%1").arg(material->getDirectory()); + path = QStringLiteral("/%1/%2/%3") + .arg(library->getName()) + .arg(material->getDirectory()) + .arg(material->getName()); + return path; } + path = QStringLiteral("%1/%2").arg(material->getDirectory()).arg(material->getName()); return path; } @@ -1192,7 +1199,7 @@ void MaterialsEditor::updateMaterialGeneral() { QString parentString; try { - auto parent = _materialManager.getParent(_material); + auto parent = Materials::MaterialManager::getManager().getParent(_material); parentString = libraryPath(parent); } catch (const Materials::MaterialNotFound&) { diff --git a/src/Mod/Material/Gui/MaterialsEditor.h b/src/Mod/Material/Gui/MaterialsEditor.h index 9d27f86b9b..0ad9e9e5b7 100644 --- a/src/Mod/Material/Gui/MaterialsEditor.h +++ b/src/Mod/Material/Gui/MaterialsEditor.h @@ -82,11 +82,7 @@ public: Materials::MaterialManager& getMaterialManager() { - return _materialManager; - } - Materials::ModelManager& getModelManager() - { - return _modelManager; + return Materials::MaterialManager::getManager(); } static QString libraryPath(const std::shared_ptr& material); @@ -114,8 +110,6 @@ protected: private: std::unique_ptr ui; - Materials::MaterialManager _materialManager; - Materials::ModelManager _modelManager; std::shared_ptr _material; AppearancePreview* _rendered; bool _materialSelected; diff --git a/src/Mod/Material/Gui/ModelSelect.cpp b/src/Mod/Material/Gui/ModelSelect.cpp index 464c112f02..5151b22614 100644 --- a/src/Mod/Material/Gui/ModelSelect.cpp +++ b/src/Mod/Material/Gui/ModelSelect.cpp @@ -239,7 +239,7 @@ void ModelSelect::addModels( auto tree = ui->treeModels; for (auto& mod : *modelTree) { std::shared_ptr nodePtr = mod.second; - if (nodePtr->getType() == Materials::ModelTreeNode::DataNode) { + if (nodePtr->getType() == Materials::ModelTreeNode::NodeType::DataNode) { auto model = nodePtr->getData(); QString uuid = model->getUUID(); @@ -265,9 +265,9 @@ void ModelSelect::addRecents(QStandardItem* parent) auto tree = ui->treeModels; for (auto& uuid : _recents) { try { - auto model = getModelManager().getModel(uuid); + auto model = Materials::ModelManager::getManager().getModel(uuid); - if (getModelManager().passFilter(_filter, model->getType())) { + if (Materials::ModelManager::getManager().passFilter(_filter, model->getType())) { QIcon icon = QIcon(model->getLibrary()->getIconPath()); auto card = new QStandardItem(icon, model->getName()); card->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled @@ -287,9 +287,9 @@ void ModelSelect::addFavorites(QStandardItem* parent) auto tree = ui->treeModels; for (auto& uuid : _favorites) { try { - auto model = getModelManager().getModel(uuid); + auto model = Materials::ModelManager::getManager().getModel(uuid); - if (getModelManager().passFilter(_filter, model->getType())) { + if (Materials::ModelManager::getManager().passFilter(_filter, model->getType())) { QIcon icon = QIcon(model->getLibrary()->getIconPath()); auto card = new QStandardItem(icon, model->getName()); card->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled @@ -339,13 +339,13 @@ void ModelSelect::fillTree() addExpanded(tree, model, lib); addRecents(lib); - auto libraries = getModelManager().getModelLibraries(); + auto libraries = Materials::ModelManager::getManager().getLibraries(); for (auto& library : *libraries) { lib = new QStandardItem(library->getName()); lib->setFlags(Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled); addExpanded(tree, model, lib); - auto modelTree = getModelManager().getModelTree(library, _filter); + auto modelTree = Materials::ModelManager::getManager().getModelTree(library, _filter); addModels(*lib, modelTree, QIcon(library->getIconPath())); } } @@ -426,7 +426,7 @@ void ModelSelect::updateModelProperties(std::shared_ptr model) void ModelSelect::updateMaterialModel(const QString& uuid) { - auto model = getModelManager().getModel(uuid); + auto model = Materials::ModelManager::getManager().getModel(uuid); // Update the general information ui->editName->setText(model->getName()); diff --git a/src/Mod/Material/Gui/ModelSelect.h b/src/Mod/Material/Gui/ModelSelect.h index c316cf5414..c2fd8dba56 100644 --- a/src/Mod/Material/Gui/ModelSelect.h +++ b/src/Mod/Material/Gui/ModelSelect.h @@ -91,14 +91,9 @@ private: void setColumnWidths(QTableView* table); void updateModelProperties(std::shared_ptr model); void createModelProperties(); - Materials::ModelManager& getModelManager() - { - return _modelManager; - } Materials::ModelFilter _filter; std::unique_ptr ui; - Materials::ModelManager _modelManager; QString _selected; std::list _favorites; std::list _recents; diff --git a/src/Mod/Material/Gui/PreCompiled.h b/src/Mod/Material/Gui/PreCompiled.h index 85b68f8801..aef9192070 100644 --- a/src/Mod/Material/Gui/PreCompiled.h +++ b/src/Mod/Material/Gui/PreCompiled.h @@ -42,7 +42,6 @@ #ifdef _PreComp_ // standard -#include #include #include diff --git a/src/Mod/Material/materialtests/TestModels.py b/src/Mod/Material/materialtests/TestModels.py index 1091c9f80e..f2c252cf73 100644 --- a/src/Mod/Material/materialtests/TestModels.py +++ b/src/Mod/Material/materialtests/TestModels.py @@ -118,3 +118,220 @@ class ModelTestCases(unittest.TestCase): self.assertIn("URL", dir(prop)) self.assertIn("Units", dir(prop)) self.assertEqual(prop.Name, "Density") + + def testTestModelCompleteness(self): + """ Test that the Test model has been loaded correctly """ + model = self.ModelManager.getModel(self.uuids.TestModel) + self.assertIsNotNone(model) + self.assertEqual(model.Name, "Test Model") + self.assertEqual(model.UUID, "34d0583d-f999-49ba-99e6-aa40bd5c3a6b") + self.assertIn("TestString", model.Properties) + self.assertEqual(len(model.Properties), 17) + prop = model.Properties["TestString"] + self.assertIn("Description", dir(prop)) + self.assertIn("Name", dir(prop)) + self.assertIn("Type", dir(prop)) + self.assertIn("URL", dir(prop)) + self.assertIn("Units", dir(prop)) + self.assertEqual(prop.Name, "TestString") + self.assertEqual(prop.Type, "String") + self.assertEqual(prop.URL, "") + self.assertEqual(prop.Units, "") + self.assertEqual(prop.Description, "A String") + prop = model.Properties["TestURL"] + self.assertEqual(prop.Name, "TestURL") + self.assertEqual(prop.Type, "URL") + self.assertEqual(prop.URL, "") + self.assertEqual(prop.Units, "") + self.assertEqual(prop.Description, "A URL") + prop = model.Properties["TestList"] + self.assertEqual(prop.Name, "TestList") + self.assertEqual(prop.Type, "List") + self.assertEqual(prop.URL, "") + self.assertEqual(prop.Units, "") + self.assertEqual(prop.Description, "A List") + prop = model.Properties["TestFileList"] + self.assertEqual(prop.Name, "TestFileList") + self.assertEqual(prop.Type, "FileList") + self.assertEqual(prop.URL, "") + self.assertEqual(prop.Units, "") + self.assertEqual(prop.Description, "A List of file paths") + prop = model.Properties["TestImageList"] + self.assertEqual(prop.Name, "TestImageList") + self.assertEqual(prop.Type, "ImageList") + self.assertEqual(prop.URL, "") + self.assertEqual(prop.Units, "") + self.assertEqual(prop.Description, "A List of embedded images") + prop = model.Properties["TestInteger"] + self.assertEqual(prop.Name, "TestInteger") + self.assertEqual(prop.Type, "Integer") + self.assertEqual(prop.URL, "") + self.assertEqual(prop.Units, "") + self.assertEqual(prop.Description, "A Integer") + prop = model.Properties["TestFloat"] + self.assertEqual(prop.Name, "TestFloat") + self.assertEqual(prop.Type, "Float") + self.assertEqual(prop.URL, "") + self.assertEqual(prop.Units, "") + self.assertEqual(prop.Description, "A Float") + prop = model.Properties["TestBoolean"] + self.assertEqual(prop.Name, "TestBoolean") + self.assertEqual(prop.Type, "Boolean") + self.assertEqual(prop.URL, "") + self.assertEqual(prop.Units, "") + self.assertEqual(prop.Description, "A Boolean") + prop = model.Properties["TestColor"] + self.assertEqual(prop.Name, "TestColor") + self.assertEqual(prop.Type, "Color") + self.assertEqual(prop.URL, "") + self.assertEqual(prop.Units, "") + self.assertEqual(prop.Description, "A Color") + prop = model.Properties["TestFile"] + self.assertEqual(prop.Name, "TestFile") + self.assertEqual(prop.Type, "File") + self.assertEqual(prop.URL, "") + self.assertEqual(prop.Units, "") + self.assertEqual(prop.Description, "A File") + prop = model.Properties["TestSVG"] + self.assertEqual(prop.Name, "TestSVG") + self.assertEqual(prop.Type, "SVG") + self.assertEqual(prop.URL, "") + self.assertEqual(prop.Units, "") + self.assertEqual(prop.Description, "An SVG") + prop = model.Properties["TestImage"] + self.assertEqual(prop.Name, "TestImage") + self.assertEqual(prop.Type, "Image") + self.assertEqual(prop.URL, "") + self.assertEqual(prop.Units, "") + self.assertEqual(prop.Description, "An Image") + prop = model.Properties["TestQuantity"] + self.assertEqual(prop.Name, "TestQuantity") + self.assertEqual(prop.Type, "Quantity") + self.assertEqual(prop.URL, "") + self.assertEqual(prop.Units, "kg/m^3") + self.assertEqual(prop.Description, "A Quantity") + prop = model.Properties["TestMultiLineString"] + self.assertEqual(prop.Name, "TestMultiLineString") + self.assertEqual(prop.Type, "MultiLineString") + self.assertEqual(prop.URL, "") + self.assertEqual(prop.Units, "") + self.assertEqual(prop.Description, "A string that spans multiple lines") + + prop = model.Properties["TestArray2D"] + self.assertEqual(prop.Name, "TestArray2D") + self.assertEqual(prop.Type, "2DArray") + self.assertEqual(prop.URL, "") + self.assertEqual(prop.Units, "") + self.assertEqual(prop.Description, "2 Dimensional array showing density with temperature\n") + self.assertEqual(len(prop.Columns), 2) + col = prop.Columns[0] + self.assertIn("Description", dir(col)) + self.assertIn("Name", dir(col)) + self.assertIn("Type", dir(col)) + self.assertIn("URL", dir(col)) + self.assertIn("Units", dir(col)) + self.assertEqual(col.Name, "Temperature") + self.assertEqual(col.Type, "Quantity") + self.assertEqual(col.URL, "") + self.assertEqual(col.Units, "C") + self.assertEqual(col.Description, "Temperature") + col = prop.Columns[1] + self.assertEqual(col.Name, "Density") + self.assertEqual(col.Type, "Quantity") + self.assertEqual(col.URL, "https://en.wikipedia.org/wiki/Density") + self.assertEqual(col.Units, "kg/m^3") + self.assertEqual(col.Description, "Density in [FreeCAD Density unit]") + + prop = model.Properties["TestArray2D3Column"] + self.assertEqual(prop.Name, "TestArray2D3Column") + self.assertEqual(prop.Type, "2DArray") + self.assertEqual(prop.URL, "") + self.assertEqual(prop.Units, "") + self.assertEqual(prop.Description, "2 Dimensional array showing density and initial yield stress with temperature\n") + self.assertEqual(len(prop.Columns), 3) + col = prop.Columns[0] + self.assertEqual(col.Name, "Temperature") + self.assertEqual(col.Type, "Quantity") + self.assertEqual(col.URL, "") + self.assertEqual(col.Units, "C") + self.assertEqual(col.Description, "Temperature") + col = prop.Columns[1] + self.assertEqual(col.Name, "Density") + self.assertEqual(col.Type, "Quantity") + self.assertEqual(col.URL, "https://en.wikipedia.org/wiki/Density") + self.assertEqual(col.Units, "kg/m^3") + self.assertEqual(col.Description, "Density in [FreeCAD Density unit]") + col = prop.Columns[2] + self.assertEqual(col.Name, "InitialYieldStress") + self.assertEqual(col.Type, "Quantity") + self.assertEqual(col.URL, "") + self.assertEqual(col.Units, "kPa") + self.assertEqual(col.Description, "Saturation stress for Voce isotropic hardening [FreeCAD Pressure unit]\n") + + prop = model.Properties["TestArray3D"] + self.assertEqual(prop.Name, "TestArray3D") + self.assertEqual(prop.Type, "3DArray") + self.assertEqual(prop.URL, "") + self.assertEqual(prop.Units, "") + self.assertEqual(prop.Description, "3 Dimensional array showing stress and strain as a function of temperature\n") + self.assertEqual(len(prop.Columns), 3) + col = prop.Columns[0] + self.assertEqual(col.Name, "Temperature") + self.assertEqual(col.Type, "Quantity") + self.assertEqual(col.URL, "") + self.assertEqual(col.Units, "C") + self.assertEqual(col.Description, "Temperature") + col = prop.Columns[1] + self.assertEqual(col.Name, "Stress") + self.assertEqual(col.Type, "Quantity") + self.assertEqual(col.URL, "") + self.assertEqual(col.Units, "MPa") + self.assertEqual(col.Description, "Stress") + col = prop.Columns[2] + self.assertEqual(col.Name, "Strain") + self.assertEqual(col.Type, "Quantity") + self.assertEqual(col.URL, "") + self.assertEqual(col.Units, "MPa") + self.assertEqual(col.Description, "Strain") + + def testModelInheritance(self): + """ Test that the inherited models have been loaded correctly """ + model = self.ModelManager.getModel(self.uuids.LinearElastic) + self.assertIsNotNone(model) + self.assertEqual(model.Name, "Linear Elastic") + self.assertEqual(model.UUID, "7b561d1d-fb9b-44f6-9da9-56a4f74d7536") + self.assertIn("Density", model.Properties) + prop = model.Properties["Density"] + self.assertEqual(prop.Name, "Density") + self.assertEqual(prop.Type, "Quantity") + self.assertEqual(prop.URL, "https://en.wikipedia.org/wiki/Density") + self.assertEqual(prop.Units, "kg/m^3") + self.assertEqual(prop.Description, "Density in [FreeCAD Density unit]") + prop = model.Properties["BulkModulus"] + self.assertEqual(prop.Name, "BulkModulus") + self.assertEqual(prop.DisplayName, "Bulk Modulus") + self.assertEqual(prop.Type, "Quantity") + self.assertEqual(prop.URL, "https://en.wikipedia.org/wiki/Bulk_modulus") + self.assertEqual(prop.Units, "kPa") + self.assertEqual(prop.Description, "Bulk modulus in [FreeCAD Pressure unit]") + prop = model.Properties["PoissonRatio"] + self.assertEqual(prop.Name, "PoissonRatio") + self.assertEqual(prop.DisplayName, "Poisson Ratio") + self.assertEqual(prop.Type, "Float") + self.assertEqual(prop.URL, "https://en.wikipedia.org/wiki/Poisson%27s_ratio") + self.assertEqual(prop.Units, "") + self.assertEqual(prop.Description, "Poisson's ratio [unitless]") + prop = model.Properties["ShearModulus"] + self.assertEqual(prop.Name, "ShearModulus") + self.assertEqual(prop.DisplayName, "Shear Modulus") + self.assertEqual(prop.Type, "Quantity") + self.assertEqual(prop.URL, "https://en.wikipedia.org/wiki/Shear_modulus") + self.assertEqual(prop.Units, "kPa") + self.assertEqual(prop.Description, "Shear modulus in [FreeCAD Pressure unit]") + prop = model.Properties["YoungsModulus"] + self.assertEqual(prop.Name, "YoungsModulus") + self.assertEqual(prop.DisplayName, "Young's Modulus") + self.assertEqual(prop.Type, "Quantity") + self.assertEqual(prop.URL, "https://en.wikipedia.org/wiki/Young%27s_modulus") + self.assertEqual(prop.Units, "kPa") + self.assertEqual(prop.Description, "Young's modulus (or E-Module) in [FreeCAD Pressure unit]") \ No newline at end of file diff --git a/src/Mod/Measure/App/MeasureAngle.cpp b/src/Mod/Measure/App/MeasureAngle.cpp index 73021f0b4e..5e72fbe5b4 100644 --- a/src/Mod/Measure/App/MeasureAngle.cpp +++ b/src/Mod/Measure/App/MeasureAngle.cpp @@ -108,7 +108,7 @@ bool MeasureAngle::isPrioritizedSelection(const App::MeasureSelection& selection getVec(*ob2, sub2, vec2); - double angle = std::fmod(vec1.GetAngle(vec2), D_PI); + double angle = std::fmod(vec1.GetAngle(vec2), std::numbers::pi); return angle > Base::Precision::Angular(); } diff --git a/src/Mod/Measure/App/PreCompiled.h b/src/Mod/Measure/App/PreCompiled.h index c3f684ddc1..ee6e3a5092 100644 --- a/src/Mod/Measure/App/PreCompiled.h +++ b/src/Mod/Measure/App/PreCompiled.h @@ -30,7 +30,6 @@ #ifdef _PreComp_ // standard -#include #include // STL diff --git a/src/Mod/Measure/Gui/PreCompiled.h b/src/Mod/Measure/Gui/PreCompiled.h index a2a4065edf..c897421afa 100644 --- a/src/Mod/Measure/Gui/PreCompiled.h +++ b/src/Mod/Measure/Gui/PreCompiled.h @@ -44,7 +44,6 @@ #ifdef _PreComp_ // standard -#include #include // STL diff --git a/src/Mod/Measure/Gui/ViewProviderMeasureAngle.cpp b/src/Mod/Measure/Gui/ViewProviderMeasureAngle.cpp index 5c1efba5f3..33cfbd62b2 100644 --- a/src/Mod/Measure/Gui/ViewProviderMeasureAngle.cpp +++ b/src/Mod/Measure/Gui/ViewProviderMeasureAngle.cpp @@ -325,7 +325,7 @@ void ViewProviderMeasureAngle::redrawAnnotation() { auto obj = dynamic_cast(getMeasureObject()); double angleDeg = obj->Angle.getValue(); - constexpr double radiansPerDegree = M_PI / 180.0; + constexpr double radiansPerDegree = std::numbers::pi / 180.0; this->fieldAngle = angleDeg * radiansPerDegree; // Set matrix diff --git a/src/Mod/Mesh/App/Core/Algorithm.cpp b/src/Mod/Mesh/App/Core/Algorithm.cpp index 9b9ebc1b64..3f513925f8 100644 --- a/src/Mod/Mesh/App/Core/Algorithm.cpp +++ b/src/Mod/Mesh/App/Core/Algorithm.cpp @@ -295,7 +295,7 @@ float MeshAlgorithm::GetAverageEdgeLength() const float MeshAlgorithm::GetMinimumEdgeLength() const { - float fLen = FLOAT_MAX; + float fLen = std::numeric_limits::max(); MeshFacetIterator cF(_rclMesh); for (cF.Init(); cF.More(); cF.Next()) { for (int i = 0; i < 3; i++) { @@ -785,19 +785,20 @@ bool MeshAlgorithm::FillupHole(const std::vector& boundary, } } + constexpr auto max = std::numeric_limits::max(); // Get the new neighbour to our reference facet MeshFacet facet; unsigned short ref_side = rFace.Side(refPoint0, refPoint1); - unsigned short tri_side = USHRT_MAX; + unsigned short tri_side = max; if (cTria.NeedsReindexing()) { // the referenced indices of the polyline refPoint0 = 0; refPoint1 = 1; } - if (ref_side < USHRT_MAX) { + if (ref_side < max) { for (const auto& face : faces) { tri_side = face.Side(refPoint0, refPoint1); - if (tri_side < USHRT_MAX) { + if (tri_side < max) { facet = face; break; } @@ -805,7 +806,7 @@ bool MeshAlgorithm::FillupHole(const std::vector& boundary, } // in case the reference facet has not an open edge print a log message - if (ref_side == USHRT_MAX || tri_side == USHRT_MAX) { + if (ref_side == max || tri_side == max) { Base::Console().Log( "MeshAlgorithm::FillupHole: Expected open edge for facet <%d, %d, %d>\n", rFace._aulPoints[0], @@ -1461,7 +1462,7 @@ bool MeshAlgorithm::NearestPointFromPoint(const Base::Vector3f& rclPt, } // calc each facet - float fMinDist = FLOAT_MAX; + float fMinDist = std::numeric_limits::max(); FacetIndex ulInd = FACET_INDEX_MAX; MeshFacetIterator pF(_rclMesh); for (pF.Init(); pF.More(); pF.Next()) { diff --git a/src/Mod/Mesh/App/Core/Approximation.cpp b/src/Mod/Mesh/App/Core/Approximation.cpp index 8b728ece41..cf7123a9ed 100644 --- a/src/Mod/Mesh/App/Core/Approximation.cpp +++ b/src/Mod/Mesh/App/Core/Approximation.cpp @@ -143,7 +143,7 @@ float PlaneFit::Fit() { _bIsFitted = true; if (CountPoints() < 3) { - return FLOAT_MAX; + return std::numeric_limits::max(); } double sxx {0.0}; @@ -207,7 +207,7 @@ float PlaneFit::Fit() akMat.EigenDecomposition(rkRot, rkDiag); } catch (const std::exception&) { - return FLOAT_MAX; + return std::numeric_limits::max(); } // We know the Eigenvalues are ordered @@ -215,7 +215,7 @@ float PlaneFit::Fit() // // points describe a line or even are identical if (rkDiag(1, 1) <= 0) { - return FLOAT_MAX; + return std::numeric_limits::max(); } Wm4::Vector3 U = rkRot.GetColumn(1); @@ -225,7 +225,7 @@ float PlaneFit::Fit() // It may happen that the result have nan values for (int i = 0; i < 3; i++) { if (boost::math::isnan(W[i])) { - return FLOAT_MAX; + return std::numeric_limits::max(); } } @@ -253,7 +253,7 @@ float PlaneFit::Fit() // In case sigma is nan if (boost::math::isnan(sigma)) { - return FLOAT_MAX; + return std::numeric_limits::max(); } // This must be caused by some round-off errors. Theoretically it's impossible @@ -318,7 +318,7 @@ Base::Vector3f PlaneFit::GetNormal() const float PlaneFit::GetDistanceToPlane(const Base::Vector3f& rcPoint) const { - float fResult = FLOAT_MAX; + float fResult = std::numeric_limits::max(); if (_bIsFitted) { fResult = (rcPoint - _vBase) * _vDirW; } @@ -332,7 +332,7 @@ float PlaneFit::GetStdDeviation() const // Standard deviation: SD=SQRT(VAR) // Standard error of the mean: SE=SD/SQRT(N) if (!_bIsFitted) { - return FLOAT_MAX; + return std::numeric_limits::max(); } float fSumXi = 0.0F, fSumXi2 = 0.0F, fMean = 0.0F, fDist = 0.0F; @@ -356,11 +356,11 @@ float PlaneFit::GetSignedStdDeviation() const // of normal direction the value will be // positive otherwise negative if (!_bIsFitted) { - return FLOAT_MAX; + return std::numeric_limits::max(); } float fSumXi = 0.0F, fSumXi2 = 0.0F, fMean = 0.0F, fDist = 0.0F; - float fMinDist = FLOAT_MAX; + float fMinDist = std::numeric_limits::max(); float fFactor = 0.0F; float ulPtCt = float(CountPoints()); @@ -426,7 +426,7 @@ void PlaneFit::Dimension(float& length, float& width) const std::vector PlaneFit::GetLocalPoints() const { std::vector localPoints; - if (_bIsFitted && _fLastResult < FLOAT_MAX) { + if (_bIsFitted && _fLastResult < std::numeric_limits::max()) { Base::Vector3d bs = Base::convertTo(this->_vBase); Base::Vector3d ex = Base::convertTo(this->_vDirU); Base::Vector3d ey = Base::convertTo(this->_vDirV); @@ -507,12 +507,12 @@ double QuadraticFit::GetCoeff(std::size_t ulIndex) const return _fCoeff[ulIndex]; } - return double(FLOAT_MAX); + return double(std::numeric_limits::max()); } float QuadraticFit::Fit() { - float fResult = FLOAT_MAX; + float fResult = std::numeric_limits::max(); if (CountPoints() > 0) { std::vector> cPts; @@ -595,14 +595,14 @@ void QuadraticFit::CalcZValues(double x, double y, double& dZ1, double& dZ2) con - 4 * _fCoeff[6] * _fCoeff[4] * x * x - 4 * _fCoeff[6] * _fCoeff[5] * y * y; if (fabs(_fCoeff[6]) < 0.000005) { - dZ1 = double(FLOAT_MAX); - dZ2 = double(FLOAT_MAX); + dZ1 = double(std::numeric_limits::max()); + dZ2 = double(std::numeric_limits::max()); return; } if (dDisk < 0.0) { - dZ1 = double(FLOAT_MAX); - dZ2 = double(FLOAT_MAX); + dZ1 = double(std::numeric_limits::max()); + dZ2 = double(std::numeric_limits::max()); return; } @@ -620,7 +620,7 @@ SurfaceFit::SurfaceFit() float SurfaceFit::Fit() { - float fResult = FLOAT_MAX; + float fResult = std::numeric_limits::max(); if (CountPoints() > 0) { fResult = float(PolynomFit()); @@ -671,8 +671,8 @@ bool SurfaceFit::GetCurvatureInfo(double x, double y, double z, double& rfCurv0, double SurfaceFit::PolynomFit() { - if (PlaneFit::Fit() >= FLOAT_MAX) { - return double(FLOAT_MAX); + if (PlaneFit::Fit() >= std::numeric_limits::max()) { + return double(std::numeric_limits::max()); } Base::Vector3d bs = Base::convertTo(this->_vBase); @@ -1165,7 +1165,7 @@ void CylinderFit::SetInitialValues(const Base::Vector3f& b, const Base::Vector3f float CylinderFit::Fit() { if (CountPoints() < 7) { - return FLOAT_MAX; + return std::numeric_limits::max(); } _bIsFitted = true; @@ -1180,7 +1180,7 @@ float CylinderFit::Fit() } float result = cylFit.Fit(); - if (result < FLOAT_MAX) { + if (result < std::numeric_limits::max()) { Base::Vector3d base = cylFit.GetBase(); Base::Vector3d dir = cylFit.GetAxis(); @@ -1284,7 +1284,7 @@ Base::Vector3f CylinderFit::GetAxis() const float CylinderFit::GetDistanceToCylinder(const Base::Vector3f& rcPoint) const { - float fResult = FLOAT_MAX; + float fResult = std::numeric_limits::max(); if (_bIsFitted) { fResult = rcPoint.DistanceToLine(_vBase, _vAxis) - _fRadius; } @@ -1298,7 +1298,7 @@ float CylinderFit::GetStdDeviation() const // Standard deviation: SD=SQRT(VAR) // Standard error of the mean: SE=SD/SQRT(N) if (!_bIsFitted) { - return FLOAT_MAX; + return std::numeric_limits::max(); } float fSumXi = 0.0F, fSumXi2 = 0.0F, fMean = 0.0F, fDist = 0.0F; @@ -1318,8 +1318,8 @@ float CylinderFit::GetStdDeviation() const void CylinderFit::GetBounding(Base::Vector3f& bottom, Base::Vector3f& top) const { - float distMin = FLT_MAX; - float distMax = FLT_MIN; + float distMin = std::numeric_limits::max(); + float distMax = std::numeric_limits::min(); std::list::const_iterator cIt; for (cIt = _vPoints.begin(); cIt != _vPoints.end(); ++cIt) { @@ -1384,7 +1384,7 @@ float SphereFit::GetRadius() const return _fRadius; } - return FLOAT_MAX; + return std::numeric_limits::max(); } Base::Vector3f SphereFit::GetCenter() const @@ -1400,7 +1400,7 @@ float SphereFit::Fit() { _bIsFitted = true; if (CountPoints() < 4) { - return FLOAT_MAX; + return std::numeric_limits::max(); } std::vector input; @@ -1433,7 +1433,7 @@ float SphereFit::Fit() sphereFit.AddPoints(_vPoints); sphereFit.ComputeApproximations(); float result = sphereFit.Fit(); - if (result < FLOAT_MAX) { + if (result < std::numeric_limits::max()) { Base::Vector3d center = sphereFit.GetCenter(); #if defined(_DEBUG) Base::Console().Message("MeshCoreFit::Sphere Fit: Center: (%0.4f, %0.4f, %0.4f), Radius: " @@ -1455,7 +1455,7 @@ float SphereFit::Fit() float SphereFit::GetDistanceToSphere(const Base::Vector3f& rcPoint) const { - float fResult = FLOAT_MAX; + float fResult = std::numeric_limits::max(); if (_bIsFitted) { fResult = Base::Vector3f(rcPoint - _vCenter).Length() - _fRadius; } @@ -1469,7 +1469,7 @@ float SphereFit::GetStdDeviation() const // Standard deviation: SD=SQRT(VAR) // Standard error of the mean: SE=SD/SQRT(N) if (!_bIsFitted) { - return FLOAT_MAX; + return std::numeric_limits::max(); } float fSumXi = 0.0F, fSumXi2 = 0.0F, fMean = 0.0F, fDist = 0.0F; @@ -1533,7 +1533,7 @@ float PolynomialFit::Fit() } } catch (const std::exception&) { - return FLOAT_MAX; + return std::numeric_limits::max(); } return 0.0F; diff --git a/src/Mod/Mesh/App/Core/Approximation.h b/src/Mod/Mesh/App/Core/Approximation.h index 1bbe71ebd5..32e7211390 100644 --- a/src/Mod/Mesh/App/Core/Approximation.h +++ b/src/Mod/Mesh/App/Core/Approximation.h @@ -201,7 +201,8 @@ protected: // NOLINTBEGIN std::list _vPoints; /**< Holds the points for the fit algorithm. */ bool _bIsFitted {false}; /**< Flag, whether the fit has been called. */ - float _fLastResult {FLOAT_MAX}; /**< Stores the last result of the fit */ + float _fLastResult { + std::numeric_limits::max()}; /**< Stores the last result of the fit */ // NOLINTEND }; diff --git a/src/Mod/Mesh/App/Core/Curvature.cpp b/src/Mod/Mesh/App/Core/Curvature.cpp index 07334f7785..15b2c8733b 100644 --- a/src/Mod/Mesh/App/Core/Curvature.cpp +++ b/src/Mod/Mesh/App/Core/Curvature.cpp @@ -423,14 +423,14 @@ CurvatureInfo FacetCurvature::Compute(FacetIndex index) const fMax = (float)dMax; } else { - fMin = FLT_MAX; - fMax = FLT_MAX; + fMin = std::numeric_limits::max(); + fMax = std::numeric_limits::max(); } } else { // too few points => cannot calc any properties - fMin = FLT_MAX; - fMax = FLT_MAX; + fMin = std::numeric_limits::max(); + fMax = std::numeric_limits::max(); } CurvatureInfo info; diff --git a/src/Mod/Mesh/App/Core/CylinderFit.cpp b/src/Mod/Mesh/App/Core/CylinderFit.cpp index face997e6b..cc44aa2d74 100644 --- a/src/Mod/Mesh/App/Core/CylinderFit.cpp +++ b/src/Mod/Mesh/App/Core/CylinderFit.cpp @@ -81,7 +81,7 @@ void CylinderFit::SetApproximations(double radius, const Base::Vector3d& axis) { _bIsFitted = false; - _fLastResult = FLOAT_MAX; + _fLastResult = std::numeric_limits::max(); _numIter = 0; _dRadius = radius; _vBase = base; @@ -94,7 +94,7 @@ void CylinderFit::SetApproximations(double radius, void CylinderFit::SetApproximations(const Base::Vector3d& base, const Base::Vector3d& axis) { _bIsFitted = false; - _fLastResult = FLOAT_MAX; + _fLastResult = std::numeric_limits::max(); _numIter = 0; _vBase = base; _vAxis = axis; @@ -168,7 +168,7 @@ int CylinderFit::GetNumIterations() const float CylinderFit::GetDistanceToCylinder(const Base::Vector3f& rcPoint) const { - float fResult = FLOAT_MAX; + float fResult = std::numeric_limits::max(); if (_bIsFitted) { Base::Vector3d pt(rcPoint.x, rcPoint.y, rcPoint.z); fResult = static_cast(pt.DistanceToLine(_vBase, _vAxis) - _dRadius); @@ -182,7 +182,7 @@ float CylinderFit::GetStdDeviation() const // Variance: VAR=(N/N-1)*[(1/N)*SUM(Xi^2)-M^2] // Standard deviation: SD=SQRT(VAR) if (!_bIsFitted) { - return FLOAT_MAX; + return std::numeric_limits::max(); } double sumXi = 0.0; @@ -239,7 +239,7 @@ void CylinderFit::ProjectToCylinder() void CylinderFit::ComputeApproximationsLine() { _bIsFitted = false; - _fLastResult = FLOAT_MAX; + _fLastResult = std::numeric_limits::max(); _numIter = 0; _vBase.Set(0.0, 0.0, 0.0); _vAxis.Set(0.0, 0.0, 0.0); @@ -266,13 +266,13 @@ void CylinderFit::ComputeApproximationsLine() float CylinderFit::Fit() { _bIsFitted = false; - _fLastResult = FLOAT_MAX; + _fLastResult = std::numeric_limits::max(); _numIter = 0; // A minimum of 5 surface points is needed to define a cylinder const int minPts = 5; if (CountPoints() < minPts) { - return FLOAT_MAX; + return std::numeric_limits::max(); } // If approximations have not been set/computed then compute some now using the line fit method @@ -308,7 +308,7 @@ float CylinderFit::Fit() // Solve the equations for the unknown corrections Eigen::LLT llt(atpa); if (llt.info() != Eigen::Success) { - return FLOAT_MAX; + return std::numeric_limits::max(); } Eigen::VectorXd x = llt.solve(atpl); @@ -327,7 +327,7 @@ float CylinderFit::Fit() // convergence bool vConverged {}; if (!computeResiduals(solDir, x, residuals, sigma0, _vConvLimit, vConverged)) { - return FLOAT_MAX; + return std::numeric_limits::max(); } if (!vConverged) { cont = true; @@ -335,13 +335,13 @@ float CylinderFit::Fit() // Update the parameters if (!updateParameters(solDir, x)) { - return FLOAT_MAX; + return std::numeric_limits::max(); } } // Check for convergence if (cont) { - return FLOAT_MAX; + return std::numeric_limits::max(); } _bIsFitted = true; diff --git a/src/Mod/Mesh/App/Core/Decimation.cpp b/src/Mod/Mesh/App/Core/Decimation.cpp index 096b438add..da40713799 100644 --- a/src/Mod/Mesh/App/Core/Decimation.cpp +++ b/src/Mod/Mesh/App/Core/Decimation.cpp @@ -123,7 +123,7 @@ void MeshSimplify::simplify(int targetSize) } // Simplification starts - alg.simplify_mesh(targetSize, FLT_MAX); + alg.simplify_mesh(targetSize, std::numeric_limits::max()); // Simplification done MeshPointArray new_points; diff --git a/src/Mod/Mesh/App/Core/Definitions.h b/src/Mod/Mesh/App/Core/Definitions.h index e8b2569d20..7802c3c512 100644 --- a/src/Mod/Mesh/App/Core/Definitions.h +++ b/src/Mod/Mesh/App/Core/Definitions.h @@ -27,7 +27,7 @@ #include #endif -#include +#include // default values #define MESH_MIN_PT_DIST 1.0e-6F @@ -36,33 +36,16 @@ #define MESH_REMOVE_MIN_LEN true #define MESH_REMOVE_G3_EDGES true -/* - * general constant definitions - */ -#define FLOAT_EPS 1.0e-4F - -#ifndef FLOAT_MAX -#define FLOAT_MAX 1e30F -#endif - -#ifndef DOUBLE_MAX -#define DOUBLE_MAX 1.7976931348623157E+308 /* max decimal value of a "double"*/ -#endif - -#ifndef DOUBLE_MIN -#define DOUBLE_MIN 2.2250738585072014E-308 /* min decimal value of a "double"*/ -#endif - namespace MeshCore { // type definitions using ElementIndex = unsigned long; -const ElementIndex ELEMENT_INDEX_MAX = ULONG_MAX; +const ElementIndex ELEMENT_INDEX_MAX = std::numeric_limits::max(); using FacetIndex = ElementIndex; -const FacetIndex FACET_INDEX_MAX = ULONG_MAX; +const FacetIndex FACET_INDEX_MAX = std::numeric_limits::max(); using PointIndex = ElementIndex; -const PointIndex POINT_INDEX_MAX = ULONG_MAX; +const PointIndex POINT_INDEX_MAX = std::numeric_limits::max(); template class Math diff --git a/src/Mod/Mesh/App/Core/Elements.cpp b/src/Mod/Mesh/App/Core/Elements.cpp index 5d365251db..428358f9ca 100644 --- a/src/Mod/Mesh/App/Core/Elements.cpp +++ b/src/Mod/Mesh/App/Core/Elements.cpp @@ -1309,9 +1309,9 @@ unsigned short MeshGeomFacet::NearestEdgeToPoint(const Base::Vector3f& rclPt) co const Base::Vector3f& rcP2 = _aclPoints[1]; const Base::Vector3f& rcP3 = _aclPoints[2]; - float fD1 = FLOAT_MAX; - float fD2 = FLOAT_MAX; - float fD3 = FLOAT_MAX; + float fD1 = std::numeric_limits::max(); + float fD2 = std::numeric_limits::max(); + float fD3 = std::numeric_limits::max(); // 1st edge Base::Vector3f clDir = rcP2 - rcP1; @@ -1383,9 +1383,9 @@ void MeshGeomFacet::NearestEdgeToPoint(const Base::Vector3f& rclPt, const Base::Vector3f& rcP2 = _aclPoints[1]; const Base::Vector3f& rcP3 = _aclPoints[2]; - float fD1 = FLOAT_MAX; - float fD2 = FLOAT_MAX; - float fD3 = FLOAT_MAX; + float fD1 = std::numeric_limits::max(); + float fD2 = std::numeric_limits::max(); + float fD3 = std::numeric_limits::max(); // 1st edge Base::Vector3f clDir = rcP2 - rcP1; diff --git a/src/Mod/Mesh/App/Core/Elements.h b/src/Mod/Mesh/App/Core/Elements.h index 2181301bca..1c2521576f 100644 --- a/src/Mod/Mesh/App/Core/Elements.h +++ b/src/Mod/Mesh/App/Core/Elements.h @@ -23,7 +23,6 @@ #ifndef MESH_ELEMENTS_H #define MESH_ELEMENTS_H -#include #include #include #include @@ -1147,7 +1146,7 @@ inline unsigned short MeshFacet::Side(FacetIndex ulNIndex) const return 2; } - return USHRT_MAX; + return std::numeric_limits::max(); } inline unsigned short MeshFacet::Side(PointIndex ulP0, PointIndex ulP1) const @@ -1177,7 +1176,7 @@ inline unsigned short MeshFacet::Side(PointIndex ulP0, PointIndex ulP1) const } } - return USHRT_MAX; + return std::numeric_limits::max(); } inline unsigned short MeshFacet::Side(const MeshFacet& rFace) const @@ -1185,12 +1184,12 @@ inline unsigned short MeshFacet::Side(const MeshFacet& rFace) const unsigned short side {}; for (int i = 0; i < 3; i++) { side = Side(rFace._aulPoints[i], rFace._aulPoints[(i + 1) % 3]); - if (side != USHRT_MAX) { + if (side != std::numeric_limits::max()) { return side; } } - return USHRT_MAX; + return std::numeric_limits::max(); } inline bool MeshFacet::IsEqual(const MeshFacet& rcFace) const diff --git a/src/Mod/Mesh/App/Core/Grid.cpp b/src/Mod/Mesh/App/Core/Grid.cpp index 603650ed7b..083d834f10 100644 --- a/src/Mod/Mesh/App/Core/Grid.cpp +++ b/src/Mod/Mesh/App/Core/Grid.cpp @@ -639,7 +639,7 @@ unsigned long MeshGrid::GetIndexToPosition(unsigned long ulX, unsigned long ulY, unsigned long ulZ) const { if (!CheckPos(ulX, ulY, ulZ)) { - return ULONG_MAX; + return std::numeric_limits::max(); } return (ulZ * _ulCtGridsY + ulY) * _ulCtGridsX + ulX; } @@ -654,9 +654,9 @@ bool MeshGrid::GetPositionToIndex(unsigned long id, ulZ = id / (_ulCtGridsX * _ulCtGridsY); if (!CheckPos(ulX, ulY, ulZ)) { - ulX = ULONG_MAX; - ulY = ULONG_MAX; - ulZ = ULONG_MAX; + ulX = std::numeric_limits::max(); + ulY = std::numeric_limits::max(); + ulZ = std::numeric_limits::max(); return false; } @@ -760,7 +760,7 @@ void MeshFacetGrid::RebuildGrid() unsigned long MeshFacetGrid::SearchNearestFromPoint(const Base::Vector3f& rclPt) const { ElementIndex ulFacetInd = ELEMENT_INDEX_MAX; - float fMinDist = FLOAT_MAX; + float fMinDist = std::numeric_limits::max(); Base::BoundBox3f clBB = GetBoundBox(); if (clBB.IsInBox(rclPt)) { // Point lies within @@ -1154,7 +1154,7 @@ bool MeshGridIterator::InitOnRay(const Base::Vector3f& rclPt, // needed in NextOnRay() to avoid an infinite loop _cSearchPositions.clear(); - _fMaxSearchArea = FLOAT_MAX; + _fMaxSearchArea = std::numeric_limits::max(); raulElements.clear(); diff --git a/src/Mod/Mesh/App/Core/Grid.h b/src/Mod/Mesh/App/Core/Grid.h index 3cd9990ce9..e5f9c46360 100644 --- a/src/Mod/Mesh/App/Core/Grid.h +++ b/src/Mod/Mesh/App/Core/Grid.h @@ -447,7 +447,7 @@ private: Base::Vector3f _clPt; /**< Base point of search ray. */ Base::Vector3f _clDir; /**< Direction of search ray. */ bool _bValidRay {false}; /**< Search ray ok? */ - float _fMaxSearchArea {FLOAT_MAX}; + float _fMaxSearchArea {std::numeric_limits::max()}; /** Checks if a grid position is already visited by NextOnRay(). */ struct GridElement { diff --git a/src/Mod/Mesh/App/Core/Iterator.h b/src/Mod/Mesh/App/Core/Iterator.h index 4b8c5dddd3..3b7d741d02 100644 --- a/src/Mod/Mesh/App/Core/Iterator.h +++ b/src/Mod/Mesh/App/Core/Iterator.h @@ -23,7 +23,6 @@ #ifndef MESH_ITERATOR_H #define MESH_ITERATOR_H -#include #include diff --git a/src/Mod/Mesh/App/Core/MeshKernel.cpp b/src/Mod/Mesh/App/Core/MeshKernel.cpp index be46d392c4..b2def70220 100644 --- a/src/Mod/Mesh/App/Core/MeshKernel.cpp +++ b/src/Mod/Mesh/App/Core/MeshKernel.cpp @@ -310,7 +310,7 @@ unsigned long MeshKernel::AddFacets(const std::vector& rclFAry, bool if (ulF0 != FACET_INDEX_MAX) { unsigned short usSide = _aclFacetArray[ulF0].Side(ulP0, ulP1); - assert(usSide != USHRT_MAX); + assert(usSide != std::numeric_limits::max()); _aclFacetArray[ulF0]._aulNeighbours[usSide] = FACET_INDEX_MAX; } } @@ -342,13 +342,13 @@ unsigned long MeshKernel::AddFacets(const std::vector& rclFAry, bool if (ulF0 != FACET_INDEX_MAX) { unsigned short usSide = _aclFacetArray[ulF0].Side(ulP0, ulP1); - assert(usSide != USHRT_MAX); + assert(usSide != std::numeric_limits::max()); _aclFacetArray[ulF0]._aulNeighbours[usSide] = ulF1; } if (ulF1 != FACET_INDEX_MAX) { unsigned short usSide = _aclFacetArray[ulF1].Side(ulP0, ulP1); - assert(usSide != USHRT_MAX); + assert(usSide != std::numeric_limits::max()); _aclFacetArray[ulF1]._aulNeighbours[usSide] = ulF0; } } diff --git a/src/Mod/Mesh/App/Core/Projection.cpp b/src/Mod/Mesh/App/Core/Projection.cpp index b3bd4d584e..06c676b234 100644 --- a/src/Mod/Mesh/App/Core/Projection.cpp +++ b/src/Mod/Mesh/App/Core/Projection.cpp @@ -78,7 +78,7 @@ bool MeshProjection::connectLines(std::list& polyline) const { - const float fMaxDist = float(std::sqrt(FLOAT_MAX)); // max. length of a gap + const float fMaxDist = std::sqrt(std::numeric_limits::max()); // max. length of a gap const float fMinEps = 1.0e-4F; polyline.clear(); diff --git a/src/Mod/Mesh/App/Core/Segmentation.cpp b/src/Mod/Mesh/App/Core/Segmentation.cpp index f21245dbe8..5fd0dd410f 100644 --- a/src/Mod/Mesh/App/Core/Segmentation.cpp +++ b/src/Mod/Mesh/App/Core/Segmentation.cpp @@ -201,7 +201,7 @@ std::vector PlaneSurfaceFit::Parameters() const // -------------------------------------------------------- CylinderSurfaceFit::CylinderSurfaceFit() - : radius(FLOAT_MAX) + : radius(std::numeric_limits::max()) , fitter(new CylinderFit) { axis.Set(0, 0, 0); @@ -266,7 +266,7 @@ float CylinderSurfaceFit::Fit() } float fit = fitter->Fit(); - if (fit < FLOAT_MAX) { + if (fit < std::numeric_limits::max()) { basepoint = fitter->GetBase(); axis = fitter->GetAxis(); radius = fitter->GetRadius(); @@ -309,7 +309,7 @@ std::vector CylinderSurfaceFit::Parameters() const // -------------------------------------------------------- SphereSurfaceFit::SphereSurfaceFit() - : radius(FLOAT_MAX) + : radius(std::numeric_limits::max()) , fitter(new SphereFit) { center.Set(0, 0, 0); @@ -367,7 +367,7 @@ float SphereSurfaceFit::Fit() } float fit = fitter->Fit(); - if (fit < FLOAT_MAX) { + if (fit < std::numeric_limits::max()) { center = fitter->GetCenter(); radius = fitter->GetRadius(); } diff --git a/src/Mod/Mesh/App/Core/Simplify.h b/src/Mod/Mesh/App/Core/Simplify.h index e2c0cd6672..d45a3ae7c2 100644 --- a/src/Mod/Mesh/App/Core/Simplify.h +++ b/src/Mod/Mesh/App/Core/Simplify.h @@ -13,11 +13,6 @@ #include - -#ifndef _USE_MATH_DEFINES -#define _USE_MATH_DEFINES -#endif - using vec3f = Base::Vector3f; class SymmetricMatrix { diff --git a/src/Mod/Mesh/App/Core/Smoothing.h b/src/Mod/Mesh/App/Core/Smoothing.h index 6f15ffad2a..4579f01959 100644 --- a/src/Mod/Mesh/App/Core/Smoothing.h +++ b/src/Mod/Mesh/App/Core/Smoothing.h @@ -23,7 +23,6 @@ #ifndef MESH_SMOOTHING_H #define MESH_SMOOTHING_H -#include #include #include "Definitions.h" @@ -88,7 +87,7 @@ public: void SmoothPoints(unsigned int, const std::vector&) override; private: - float maximum {FLT_MAX}; + float maximum {std::numeric_limits::max()}; }; class MeshExport LaplaceSmoothing: public AbstractSmoothing diff --git a/src/Mod/Mesh/App/Core/SphereFit.cpp b/src/Mod/Mesh/App/Core/SphereFit.cpp index 7c7c1ebaad..c53812a1b1 100644 --- a/src/Mod/Mesh/App/Core/SphereFit.cpp +++ b/src/Mod/Mesh/App/Core/SphereFit.cpp @@ -41,7 +41,7 @@ SphereFit::SphereFit() void SphereFit::SetApproximations(double radius, const Base::Vector3d& center) { _bIsFitted = false; - _fLastResult = FLOAT_MAX; + _fLastResult = std::numeric_limits::max(); _numIter = 0; _dRadius = radius; _vCenter = center; @@ -92,7 +92,7 @@ int SphereFit::GetNumIterations() const float SphereFit::GetDistanceToSphere(const Base::Vector3f& rcPoint) const { - float fResult = FLOAT_MAX; + float fResult = std::numeric_limits::max(); if (_bIsFitted) { fResult = Base::Vector3d((double)rcPoint.x - _vCenter.x, (double)rcPoint.y - _vCenter.y, @@ -109,7 +109,7 @@ float SphereFit::GetStdDeviation() const // Variance: VAR=(N/N-1)*[(1/N)*SUM(Xi^2)-M^2] // Standard deviation: SD=SQRT(VAR) if (!_bIsFitted) { - return FLOAT_MAX; + return std::numeric_limits::max(); } double sumXi = 0.0, sumXi2 = 0.0, dist = 0.0; @@ -156,7 +156,7 @@ void SphereFit::ProjectToSphere() void SphereFit::ComputeApproximations() { _bIsFitted = false; - _fLastResult = FLOAT_MAX; + _fLastResult = std::numeric_limits::max(); _numIter = 0; _vCenter.Set(0.0, 0.0, 0.0); _dRadius = 0.0; @@ -182,12 +182,12 @@ void SphereFit::ComputeApproximations() float SphereFit::Fit() { _bIsFitted = false; - _fLastResult = FLOAT_MAX; + _fLastResult = std::numeric_limits::max(); _numIter = 0; // A minimum of 4 surface points is needed to define a sphere if (CountPoints() < 4) { - return FLOAT_MAX; + return std::numeric_limits::max(); } // If approximations have not been set/computed then compute some now @@ -212,7 +212,7 @@ float SphereFit::Fit() // Solve the equations for the unknown corrections Eigen::LLT llt(atpa); if (llt.info() != Eigen::Success) { - return FLOAT_MAX; + return std::numeric_limits::max(); } Eigen::VectorXd x = llt.solve(atpl); @@ -227,7 +227,7 @@ float SphereFit::Fit() // convergence bool vConverged {}; if (!computeResiduals(x, residuals, sigma0, _vConvLimit, vConverged)) { - return FLOAT_MAX; + return std::numeric_limits::max(); } if (!vConverged) { cont = true; @@ -242,7 +242,7 @@ float SphereFit::Fit() // Check for convergence if (cont) { - return FLOAT_MAX; + return std::numeric_limits::max(); } _bIsFitted = true; diff --git a/src/Mod/Mesh/App/Core/Tools.h b/src/Mod/Mesh/App/Core/Tools.h index ab1a49bb96..4c61987538 100644 --- a/src/Mod/Mesh/App/Core/Tools.h +++ b/src/Mod/Mesh/App/Core/Tools.h @@ -209,7 +209,7 @@ public: // NOLINTBEGIN Index nearest_index; - float nearest_dist {FLOAT_MAX}; + float nearest_dist {std::numeric_limits::max()}; // NOLINTEND private: diff --git a/src/Mod/Mesh/App/Core/TopoAlgorithm.cpp b/src/Mod/Mesh/App/Core/TopoAlgorithm.cpp index dfd38113f2..48d92741de 100644 --- a/src/Mod/Mesh/App/Core/TopoAlgorithm.cpp +++ b/src/Mod/Mesh/App/Core/TopoAlgorithm.cpp @@ -121,7 +121,7 @@ bool MeshTopoAlgorithm::SnapVertex(FacetIndex ulFacetPos, const Base::Vector3f& float fTV = (rP - rPt1) * (rPt2 - rPt1); // Point is on the edge - if (cNo3.Length() < FLOAT_EPS) { + if (cNo3.Length() < std::numeric_limits::epsilon()) { return SplitOpenEdge(ulFacetPos, i, rP); } if ((rP - rPt1) * cNo2 > 0.0F && fD2 >= fTV && fTV >= 0.0F) { @@ -290,7 +290,7 @@ float MeshTopoAlgorithm::SwapEdgeBenefit(FacetIndex f, int e) const PointIndex v2 = faces[f]._aulPoints[(e + 1) % 3]; PointIndex v3 = faces[f]._aulPoints[(e + 2) % 3]; unsigned short s = faces[n].Side(faces[f]); - if (s == USHRT_MAX) { + if (s == std::numeric_limits::max()) { std::cerr << "MeshTopoAlgorithm::SwapEdgeBenefit: error in neighbourhood " << "of faces " << f << " and " << n << std::endl; return 0.0F; // topological error @@ -600,7 +600,8 @@ bool MeshTopoAlgorithm::IsSwapEdgeLegal(FacetIndex ulFacetPos, FacetIndex ulNeig unsigned short uFSide = rclF.Side(rclN); unsigned short uNSide = rclN.Side(rclF); - if (uFSide == USHRT_MAX || uNSide == USHRT_MAX) { + constexpr auto max = std::numeric_limits::max(); + if (uFSide == max || uNSide == max) { return false; // not neighbours } @@ -686,7 +687,8 @@ void MeshTopoAlgorithm::SwapEdge(FacetIndex ulFacetPos, FacetIndex ulNeighbour) unsigned short uFSide = rclF.Side(rclN); unsigned short uNSide = rclN.Side(rclF); - if (uFSide == USHRT_MAX || uNSide == USHRT_MAX) { + constexpr auto max = std::numeric_limits::max(); + if (uFSide == max || uNSide == max) { return; // not neighbours } @@ -720,7 +722,8 @@ bool MeshTopoAlgorithm::SplitEdge(FacetIndex ulFacetPos, unsigned short uFSide = rclF.Side(rclN); unsigned short uNSide = rclN.Side(rclF); - if (uFSide == USHRT_MAX || uNSide == USHRT_MAX) { + constexpr auto max = std::numeric_limits::max(); + if (uFSide == max || uNSide == max) { return false; // not neighbours } @@ -816,13 +819,13 @@ bool MeshTopoAlgorithm::SplitOpenEdge(FacetIndex ulFacetPos, bool MeshTopoAlgorithm::Vertex_Less::operator()(const Base::Vector3f& u, const Base::Vector3f& v) const { - if (std::fabs(u.x - v.x) > FLOAT_EPS) { + if (std::fabs(u.x - v.x) > std::numeric_limits::epsilon()) { return u.x < v.x; } - if (std::fabs(u.y - v.y) > FLOAT_EPS) { + if (std::fabs(u.y - v.y) > std::numeric_limits::epsilon()) { return u.y < v.y; } - if (std::fabs(u.z - v.z) > FLOAT_EPS) { + if (std::fabs(u.z - v.z) > std::numeric_limits::epsilon()) { return u.z < v.z; } return false; @@ -980,7 +983,8 @@ bool MeshTopoAlgorithm::CollapseEdge(FacetIndex ulFacetPos, FacetIndex ulNeighbo unsigned short uFSide = rclF.Side(rclN); unsigned short uNSide = rclN.Side(rclF); - if (uFSide == USHRT_MAX || uNSide == USHRT_MAX) { + constexpr auto max = std::numeric_limits::max(); + if (uFSide == max || uNSide == max) { return false; // not neighbours } @@ -1214,7 +1218,7 @@ void MeshTopoAlgorithm::SplitFacet(FacetIndex ulFacetPos, MeshPoint& rVertex2 = _rclMesh._aclPointArray[rFace._aulPoints[2]]; auto pointIndex = [=](const Base::Vector3f& rP) { - unsigned short equalP = USHRT_MAX; + unsigned short equalP = std::numeric_limits::max(); if (Base::Distance(rVertex0, rP) < fEps) { equalP = 0; } @@ -1230,16 +1234,17 @@ void MeshTopoAlgorithm::SplitFacet(FacetIndex ulFacetPos, unsigned short equalP1 = pointIndex(rP1); unsigned short equalP2 = pointIndex(rP2); + constexpr auto max = std::numeric_limits::max(); // both points are coincident with the corner points - if (equalP1 != USHRT_MAX && equalP2 != USHRT_MAX) { + if (equalP1 != max && equalP2 != max) { return; // must not split the facet } - if (equalP1 != USHRT_MAX) { + if (equalP1 != max) { // get the edge to the second given point and perform a split edge operation SplitFacetOnOneEdge(ulFacetPos, rP2); } - else if (equalP2 != USHRT_MAX) { + else if (equalP2 != max) { // get the edge to the first given point and perform a split edge operation SplitFacetOnOneEdge(ulFacetPos, rP1); } @@ -1250,8 +1255,8 @@ void MeshTopoAlgorithm::SplitFacet(FacetIndex ulFacetPos, void MeshTopoAlgorithm::SplitFacetOnOneEdge(FacetIndex ulFacetPos, const Base::Vector3f& rP) { - float fMinDist = FLOAT_MAX; - unsigned short iEdgeNo = USHRT_MAX; + float fMinDist = std::numeric_limits::max(); + unsigned short iEdgeNo = std::numeric_limits::max(); MeshFacet& rFace = _rclMesh._aclFacetArray[ulFacetPos]; for (unsigned short i = 0; i < 3; i++) { @@ -1281,8 +1286,10 @@ void MeshTopoAlgorithm::SplitFacetOnTwoEdges(FacetIndex ulFacetPos, const Base::Vector3f& rP2) { // search for the matching edges - unsigned short iEdgeNo1 = USHRT_MAX, iEdgeNo2 = USHRT_MAX; - float fMinDist1 = FLOAT_MAX, fMinDist2 = FLOAT_MAX; + unsigned short iEdgeNo1 = std::numeric_limits::max(); + unsigned short iEdgeNo2 = std::numeric_limits::max(); + float fMinDist1 = std::numeric_limits::max(); + float fMinDist2 = std::numeric_limits::max(); MeshFacet& rFace = _rclMesh._aclFacetArray[ulFacetPos]; for (unsigned short i = 0; i < 3; i++) { @@ -1392,7 +1399,7 @@ void MeshTopoAlgorithm::SplitFacet(FacetIndex ulFacetPos, { MeshFacet& rFace = _rclMesh._aclFacetArray[ulFacetPos]; unsigned short side = rFace.Side(P1, P2); - if (side != USHRT_MAX) { + if (side != std::numeric_limits::max()) { PointIndex V1 = rFace._aulPoints[(side + 1) % 3]; PointIndex V2 = rFace._aulPoints[(side + 2) % 3]; FacetIndex size = _rclMesh._aclFacetArray.size(); @@ -1455,12 +1462,12 @@ void MeshTopoAlgorithm::HarmonizeNeighbours(FacetIndex facet1, FacetIndex facet2 MeshFacet& rFace2 = _rclMesh._aclFacetArray[facet2]; unsigned short side = rFace1.Side(rFace2); - if (side != USHRT_MAX) { + if (side != std::numeric_limits::max()) { rFace1._aulNeighbours[side] = facet2; } side = rFace2.Side(rFace1); - if (side != USHRT_MAX) { + if (side != std::numeric_limits::max()) { rFace2._aulNeighbours[side] = facet1; } } diff --git a/src/Mod/Mesh/App/Core/Triangulation.cpp b/src/Mod/Mesh/App/Core/Triangulation.cpp index f78a00ec42..7d6855b573 100644 --- a/src/Mod/Mesh/App/Core/Triangulation.cpp +++ b/src/Mod/Mesh/App/Core/Triangulation.cpp @@ -144,7 +144,7 @@ Base::Matrix4D AbstractPolygonTriangulator::GetTransformToFitPlane() const planeFit.AddPoint(point); } - if (planeFit.Fit() >= FLOAT_MAX) { + if (planeFit.Fit() >= std::numeric_limits::max()) { throw Base::RuntimeError("Plane fit failed"); } @@ -215,7 +215,7 @@ void AbstractPolygonTriangulator::PostProcessing(const std::vector= uMinPts && polyFit.Fit() < FLOAT_MAX) { + if (polyFit.CountPoints() >= uMinPts && polyFit.Fit() < std::numeric_limits::max()) { for (auto& newpoint : _newpoints) { newpoint.z = static_cast(polyFit.Value(newpoint.x, newpoint.y)); } @@ -377,7 +377,9 @@ bool EarClippingTriangulator::Triangulate::InsideTriangle(float Ax, cCROSSap = cx * apy - cy * apx; bCROSScp = bx * cpy - by * cpx; - return ((aCROSSbp >= FLOAT_EPS) && (bCROSScp >= FLOAT_EPS) && (cCROSSap >= FLOAT_EPS)); + return ((aCROSSbp >= std::numeric_limits::epsilon()) + && (bCROSScp >= std::numeric_limits::epsilon()) + && (cCROSSap >= std::numeric_limits::epsilon())); } bool EarClippingTriangulator::Triangulate::Snip(const std::vector& contour, @@ -399,7 +401,8 @@ bool EarClippingTriangulator::Triangulate::Snip(const std::vector (((Bx - Ax) * (Cy - Ay)) - ((By - Ay) * (Cx - Ax)))) { + constexpr float eps = std::numeric_limits::epsilon(); + if (eps > (((Bx - Ax) * (Cy - Ay)) - ((By - Ay) * (Cx - Ax)))) { return false; } diff --git a/src/Mod/Mesh/App/FeatureMeshSegmentByMesh.cpp b/src/Mod/Mesh/App/FeatureMeshSegmentByMesh.cpp index f402424c98..7cdb9060fe 100644 --- a/src/Mod/Mesh/App/FeatureMeshSegmentByMesh.cpp +++ b/src/Mod/Mesh/App/FeatureMeshSegmentByMesh.cpp @@ -119,7 +119,7 @@ App::DocumentObjectExecReturn* SegmentByMesh::execute() // now we have too many facets since we have (invisible) facets near to the back clipping // plane, so we need the nearest facet to the front clipping plane // - float fDist = FLOAT_MAX; + float fDist = std::numeric_limits::max(); MeshCore::FacetIndex uIdx = MeshCore::FACET_INDEX_MAX; MeshFacetIterator cFIt(rMeshKernel); diff --git a/src/Mod/Mesh/App/FeatureMeshSolid.cpp b/src/Mod/Mesh/App/FeatureMeshSolid.cpp index 490428e5d2..e2d1a53e76 100644 --- a/src/Mod/Mesh/App/FeatureMeshSolid.cpp +++ b/src/Mod/Mesh/App/FeatureMeshSolid.cpp @@ -29,8 +29,10 @@ namespace Mesh { -const App::PropertyIntegerConstraint::Constraints intSampling = {0, INT_MAX, 1}; -const App::PropertyLength::Constraints floatRange = {0.0, FLT_MAX, 1.0}; +const App::PropertyIntegerConstraint::Constraints intSampling = {0, + std::numeric_limits::max(), + 1}; +const App::PropertyLength::Constraints floatRange = {0.0, std::numeric_limits::max(), 1.0}; } // namespace Mesh using namespace Mesh; diff --git a/src/Mod/Mesh/App/MeshFeaturePyImp.cpp b/src/Mod/Mesh/App/MeshFeaturePyImp.cpp index aaaf58aff0..f3770dac7b 100644 --- a/src/Mod/Mesh/App/MeshFeaturePyImp.cpp +++ b/src/Mod/Mesh/App/MeshFeaturePyImp.cpp @@ -73,7 +73,7 @@ PyObject* MeshFeaturePy::harmonizeNormals(PyObject* args) PyObject* MeshFeaturePy::smooth(PyObject* args) { int iter = 1; - float d_max = FLOAT_MAX; + float d_max = std::numeric_limits::max(); if (!PyArg_ParseTuple(args, "|if", &iter, &d_max)) { return nullptr; } diff --git a/src/Mod/Mesh/App/MeshPoint.h b/src/Mod/Mesh/App/MeshPoint.h index 1b5043b7f2..a29270108e 100644 --- a/src/Mod/Mesh/App/MeshPoint.h +++ b/src/Mod/Mesh/App/MeshPoint.h @@ -23,7 +23,6 @@ #ifndef MESH_MESHPOINT_H #define MESH_MESHPOINT_H -#include #include #include @@ -51,7 +50,7 @@ public: /// simple constructor explicit MeshPoint(const Vector3d& vec = Vector3d(), const MeshObject* obj = nullptr, - unsigned int index = UINT_MAX) + unsigned int index = std::numeric_limits::max()) : Vector3d(vec) , Index(index) , Mesh(obj) @@ -59,7 +58,7 @@ public: bool isBound() const { - return Index != UINT_MAX; + return Index != std::numeric_limits::max(); } unsigned int Index; diff --git a/src/Mod/Mesh/App/MeshPointPyImp.cpp b/src/Mod/Mesh/App/MeshPointPyImp.cpp index 00f0509655..a4b62e1569 100644 --- a/src/Mod/Mesh/App/MeshPointPyImp.cpp +++ b/src/Mod/Mesh/App/MeshPointPyImp.cpp @@ -86,7 +86,7 @@ PyObject* MeshPointPy::unbound(PyObject* args) if (!PyArg_ParseTuple(args, "")) { return nullptr; } - getMeshPointPtr()->Index = UINT_MAX; + getMeshPointPtr()->Index = std::numeric_limits::max(); getMeshPointPtr()->Mesh = nullptr; Py_Return; } @@ -98,7 +98,7 @@ Py::Long MeshPointPy::getIndex() const Py::Boolean MeshPointPy::getBound() const { - return {getMeshPointPtr()->Index != UINT_MAX}; + return {getMeshPointPtr()->Index != std::numeric_limits::max()}; } Py::Object MeshPointPy::getNormal() const diff --git a/src/Mod/Mesh/App/PreCompiled.h b/src/Mod/Mesh/App/PreCompiled.h index 4d4317c9d0..7484b92f22 100644 --- a/src/Mod/Mesh/App/PreCompiled.h +++ b/src/Mod/Mesh/App/PreCompiled.h @@ -39,7 +39,6 @@ // standard #include -#include #include #include #include diff --git a/src/Mod/Mesh/App/WildMagic4/Wm4Math.cpp b/src/Mod/Mesh/App/WildMagic4/Wm4Math.cpp index efa1a2f762..f497f0851e 100644 --- a/src/Mod/Mesh/App/WildMagic4/Wm4Math.cpp +++ b/src/Mod/Mesh/App/WildMagic4/Wm4Math.cpp @@ -19,9 +19,9 @@ namespace Wm4 { -template<> WM4_FOUNDATION_ITEM const float Math::EPSILON = FLT_EPSILON; +template<> WM4_FOUNDATION_ITEM const float Math::EPSILON = std::numeric_limits::epsilon(); template<> WM4_FOUNDATION_ITEM const float Math::ZERO_TOLERANCE = 1e-06f; -template<> WM4_FOUNDATION_ITEM const float Math::MAX_REAL = FLT_MAX; +template<> WM4_FOUNDATION_ITEM const float Math::MAX_REAL = std::numeric_limits::max(); template<> WM4_FOUNDATION_ITEM const float Math::PI = (float)(4.0*atan(1.0)); template<> WM4_FOUNDATION_ITEM const float Math::TWO_PI = 2.0f*Math::PI; template<> WM4_FOUNDATION_ITEM const float Math::HALF_PI = 0.5f*Math::PI; @@ -34,9 +34,9 @@ template<> WM4_FOUNDATION_ITEM const float Math::LN_10 = Math::Log template<> WM4_FOUNDATION_ITEM const float Math::INV_LN_2 = 1.0f/Math::LN_2; template<> WM4_FOUNDATION_ITEM const float Math::INV_LN_10 = 1.0f/Math::LN_10; -template<> WM4_FOUNDATION_ITEM const double Math::EPSILON = DBL_EPSILON; +template<> WM4_FOUNDATION_ITEM const double Math::EPSILON = std::numeric_limits::epsilon(); template<> WM4_FOUNDATION_ITEM const double Math::ZERO_TOLERANCE = 1e-08; -template<> WM4_FOUNDATION_ITEM const double Math::MAX_REAL = DBL_MAX; +template<> WM4_FOUNDATION_ITEM const double Math::MAX_REAL = std::numeric_limits::max(); template<> WM4_FOUNDATION_ITEM const double Math::PI = 4.0*atan(1.0); template<> WM4_FOUNDATION_ITEM const double Math::TWO_PI = 2.0*Math::PI; template<> WM4_FOUNDATION_ITEM const double Math::HALF_PI = 0.5*Math::PI; diff --git a/src/Mod/Mesh/App/WildMagic4/Wm4System.h b/src/Mod/Mesh/App/WildMagic4/Wm4System.h index 13d1c815a0..3bd3051a1d 100644 --- a/src/Mod/Mesh/App/WildMagic4/Wm4System.h +++ b/src/Mod/Mesh/App/WildMagic4/Wm4System.h @@ -23,7 +23,6 @@ // common standard library headers #include #include -#include #include #include #include diff --git a/src/Mod/Mesh/Gui/Command.cpp b/src/Mod/Mesh/Gui/Command.cpp index 6411e6ac8d..6bfe41ea90 100644 --- a/src/Mod/Mesh/Gui/Command.cpp +++ b/src/Mod/Mesh/Gui/Command.cpp @@ -1798,7 +1798,7 @@ void CmdMeshScale::activated(int) QObject::tr("Enter scaling factor:"), 1, 0, - DBL_MAX, + std::numeric_limits::max(), 5, &ok, Qt::MSWindowsFixedSizeDialogHint); diff --git a/src/Mod/Mesh/Gui/DlgRegularSolidImp.cpp b/src/Mod/Mesh/Gui/DlgRegularSolidImp.cpp index 10e996b5fa..1c541831fa 100644 --- a/src/Mod/Mesh/Gui/DlgRegularSolidImp.cpp +++ b/src/Mod/Mesh/Gui/DlgRegularSolidImp.cpp @@ -23,7 +23,6 @@ #include "PreCompiled.h" #ifndef _PreComp_ -#include #include #endif @@ -55,45 +54,46 @@ DlgRegularSolidImp::DlgRegularSolidImp(QWidget* parent, Qt::WindowFlags fl) Gui::Command::doCommand(Gui::Command::Doc, "import Mesh,BuildRegularGeoms"); // set limits + constexpr double doubleMax = std::numeric_limits::max(); // Box - ui->boxLength->setMaximum(DBL_MAX); + ui->boxLength->setMaximum(doubleMax); ui->boxLength->setMinimum(0); - ui->boxWidth->setMaximum(DBL_MAX); + ui->boxWidth->setMaximum(doubleMax); ui->boxWidth->setMinimum(0); - ui->boxHeight->setMaximum(DBL_MAX); + ui->boxHeight->setMaximum(doubleMax); ui->boxHeight->setMinimum(0); // Cylinder - ui->cylinderRadius->setMaximum(DBL_MAX); + ui->cylinderRadius->setMaximum(doubleMax); ui->cylinderRadius->setMinimum(0); - ui->cylinderLength->setMaximum(DBL_MAX); + ui->cylinderLength->setMaximum(doubleMax); ui->cylinderLength->setMinimum(0); - ui->cylinderEdgeLength->setMaximum(DBL_MAX); + ui->cylinderEdgeLength->setMaximum(doubleMax); ui->cylinderEdgeLength->setMinimum(0); ui->cylinderCount->setMaximum(1000); // Cone - ui->coneRadius1->setMaximum(DBL_MAX); + ui->coneRadius1->setMaximum(doubleMax); ui->coneRadius1->setMinimum(0); - ui->coneRadius2->setMaximum(DBL_MAX); + ui->coneRadius2->setMaximum(doubleMax); ui->coneRadius2->setMinimum(0); - ui->coneLength->setMaximum(DBL_MAX); + ui->coneLength->setMaximum(doubleMax); ui->coneLength->setMinimum(0); - ui->coneEdgeLength->setMaximum(DBL_MAX); + ui->coneEdgeLength->setMaximum(doubleMax); ui->coneEdgeLength->setMinimum(0); ui->coneCount->setMaximum(1000); // Sphere - ui->sphereRadius->setMaximum(DBL_MAX); + ui->sphereRadius->setMaximum(doubleMax); ui->sphereRadius->setMinimum(0); ui->sphereCount->setMaximum(1000); // Ellipsoid - ui->ellipsoidRadius1->setMaximum(DBL_MAX); + ui->ellipsoidRadius1->setMaximum(doubleMax); ui->ellipsoidRadius1->setMinimum(0); - ui->ellipsoidRadius2->setMaximum(DBL_MAX); + ui->ellipsoidRadius2->setMaximum(doubleMax); ui->ellipsoidRadius2->setMinimum(0); ui->ellipsoidCount->setMaximum(1000); // Torus - ui->toroidRadius1->setMaximum(DBL_MAX); + ui->toroidRadius1->setMaximum(doubleMax); ui->toroidRadius1->setMinimum(0); - ui->toroidRadius2->setMaximum(DBL_MAX); + ui->toroidRadius2->setMaximum(doubleMax); ui->toroidRadius2->setMinimum(0); ui->toroidCount->setMaximum(1000); } diff --git a/src/Mod/Mesh/Gui/MeshEditor.cpp b/src/Mod/Mesh/Gui/MeshEditor.cpp index 9c15efd34f..9ffa3ce5a3 100644 --- a/src/Mod/Mesh/Gui/MeshEditor.cpp +++ b/src/Mod/Mesh/Gui/MeshEditor.cpp @@ -318,7 +318,7 @@ void MeshFaceAddition::showMarker(SoPickedPoint* pp) } int point_index = -1; - float distance = FLT_MAX; + float distance = std::numeric_limits::max(); Base::Vector3f pnt; SbVec3f face_pnt; @@ -654,7 +654,7 @@ float MeshFillHole::findClosestPoint(const SbLine& ray, SbVec3f& closestPoint) const { // now check which vertex of the polygon is closest to the ray - float minDist = FLT_MAX; + float minDist = std::numeric_limits::max(); vertex_index = MeshCore::POINT_INDEX_MAX; const MeshCore::MeshKernel& rMesh = myMesh->Mesh.getValue().getKernel(); diff --git a/src/Mod/Mesh/Gui/MeshSelection.cpp b/src/Mod/Mesh/Gui/MeshSelection.cpp index 56876c9b52..728a3a978c 100644 --- a/src/Mod/Mesh/Gui/MeshSelection.cpp +++ b/src/Mod/Mesh/Gui/MeshSelection.cpp @@ -23,7 +23,6 @@ #include "PreCompiled.h" #ifndef _PreComp_ #include -#include #include #include diff --git a/src/Mod/Mesh/Gui/PreCompiled.h b/src/Mod/Mesh/Gui/PreCompiled.h index 5db5693792..8fe2574154 100644 --- a/src/Mod/Mesh/Gui/PreCompiled.h +++ b/src/Mod/Mesh/Gui/PreCompiled.h @@ -39,7 +39,6 @@ // standard #include -#include // STL #include diff --git a/src/Mod/Mesh/Gui/RemoveComponents.cpp b/src/Mod/Mesh/Gui/RemoveComponents.cpp index 0f8c9644d5..5e54c225b7 100644 --- a/src/Mod/Mesh/Gui/RemoveComponents.cpp +++ b/src/Mod/Mesh/Gui/RemoveComponents.cpp @@ -41,9 +41,9 @@ RemoveComponents::RemoveComponents(QWidget* parent, Qt::WindowFlags fl) { ui->setupUi(this); setupConnections(); - ui->spSelectComp->setRange(1, INT_MAX); + ui->spSelectComp->setRange(1, std::numeric_limits::max()); ui->spSelectComp->setValue(10); - ui->spDeselectComp->setRange(1, INT_MAX); + ui->spDeselectComp->setRange(1, std::numeric_limits::max()); ui->spDeselectComp->setValue(10); Gui::Selection().clearSelection(); diff --git a/src/Mod/Mesh/Gui/Segmentation.cpp b/src/Mod/Mesh/Gui/Segmentation.cpp index 235ecd43d4..050e9f5327 100644 --- a/src/Mod/Mesh/Gui/Segmentation.cpp +++ b/src/Mod/Mesh/Gui/Segmentation.cpp @@ -44,18 +44,19 @@ Segmentation::Segmentation(Mesh::Feature* mesh, QWidget* parent, Qt::WindowFlags , ui(new Ui_Segmentation) , myMesh(mesh) { + constexpr int max = std::numeric_limits::max(); ui->setupUi(this); - ui->numPln->setRange(1, INT_MAX); + ui->numPln->setRange(1, max); ui->numPln->setValue(100); - ui->crvCyl->setRange(0, INT_MAX); - ui->numCyl->setRange(1, INT_MAX); + ui->crvCyl->setRange(0, max); + ui->numCyl->setRange(1, max); ui->numCyl->setValue(100); - ui->crvSph->setRange(0, INT_MAX); - ui->numSph->setRange(1, INT_MAX); + ui->crvSph->setRange(0, max); + ui->numSph->setRange(1, max); ui->numSph->setValue(100); - ui->crv1Free->setRange(-INT_MAX, INT_MAX); - ui->crv2Free->setRange(-INT_MAX, INT_MAX); - ui->numFree->setRange(1, INT_MAX); + ui->crv1Free->setRange(-max, max); + ui->crv2Free->setRange(-max, max); + ui->numFree->setRange(1, max); ui->numFree->setValue(100); ui->checkBoxSmooth->setChecked(false); diff --git a/src/Mod/Mesh/Gui/SegmentationBestFit.cpp b/src/Mod/Mesh/Gui/SegmentationBestFit.cpp index 464ffda0aa..2a147ac04b 100644 --- a/src/Mod/Mesh/Gui/SegmentationBestFit.cpp +++ b/src/Mod/Mesh/Gui/SegmentationBestFit.cpp @@ -57,7 +57,7 @@ public: std::vector values; MeshCore::PlaneFit fit; fit.AddPoints(pts.points); - if (fit.Fit() < FLOAT_MAX) { + if (fit.Fit() < std::numeric_limits::max()) { Base::Vector3f base = fit.GetBase(); Base::Vector3f axis = fit.GetNormal(); values.push_back(base.x); @@ -90,7 +90,7 @@ public: #endif } - if (fit.Fit() < FLOAT_MAX) { + if (fit.Fit() < std::numeric_limits::max()) { Base::Vector3f base, top; fit.GetBounding(base, top); Base::Vector3f axis = fit.GetAxis(); @@ -145,7 +145,7 @@ public: std::vector values; MeshCore::SphereFit fit; fit.AddPoints(pts.points); - if (fit.Fit() < FLOAT_MAX) { + if (fit.Fit() < std::numeric_limits::max()) { Base::Vector3f base = fit.GetCenter(); float radius = fit.GetRadius(); values.push_back(base.x); @@ -228,7 +228,7 @@ ParametersDialog::ParametersDialog(std::vector& val, QDoubleSpinBox* doubleSpinBox = new QDoubleSpinBox(groupBox); doubleSpinBox->setObjectName(it.first); - doubleSpinBox->setRange(-INT_MAX, INT_MAX); + doubleSpinBox->setRange(-std::numeric_limits::max(), std::numeric_limits::max()); doubleSpinBox->setValue(it.second); layout->addWidget(doubleSpinBox, index, 1, 1, 1); spinBoxes.push_back(doubleSpinBox); @@ -335,11 +335,11 @@ SegmentationBestFit::SegmentationBestFit(Mesh::Feature* mesh, QWidget* parent, Q ui->setupUi(this); setupConnections(); - ui->numPln->setRange(1, INT_MAX); + ui->numPln->setRange(1, std::numeric_limits::max()); ui->numPln->setValue(100); - ui->numCyl->setRange(1, INT_MAX); + ui->numCyl->setRange(1, std::numeric_limits::max()); ui->numCyl->setValue(100); - ui->numSph->setRange(1, INT_MAX); + ui->numSph->setRange(1, std::numeric_limits::max()); ui->numSph->setValue(100); Gui::SelectionObject obj(myMesh); diff --git a/src/Mod/Mesh/Gui/SoFCIndexedFaceSet.cpp b/src/Mod/Mesh/Gui/SoFCIndexedFaceSet.cpp index 080e0584b2..be7c40a5ea 100644 --- a/src/Mod/Mesh/Gui/SoFCIndexedFaceSet.cpp +++ b/src/Mod/Mesh/Gui/SoFCIndexedFaceSet.cpp @@ -30,7 +30,6 @@ #ifndef _PreComp_ #include -#include #ifdef FC_OS_MACOSX #include #include @@ -459,7 +458,7 @@ void SoFCIndexedFaceSet::initClass() } SoFCIndexedFaceSet::SoFCIndexedFaceSet() - : renderTriangleLimit(UINT_MAX) + : renderTriangleLimit(std::numeric_limits::max()) { SO_NODE_CONSTRUCTOR(SoFCIndexedFaceSet); SO_NODE_ADD_FIELD(updateGLArray, (false)); diff --git a/src/Mod/Mesh/Gui/SoFCMeshObject.cpp b/src/Mod/Mesh/Gui/SoFCMeshObject.cpp index 7ff55890e4..904f6fdf48 100644 --- a/src/Mod/Mesh/Gui/SoFCMeshObject.cpp +++ b/src/Mod/Mesh/Gui/SoFCMeshObject.cpp @@ -23,7 +23,6 @@ #include "PreCompiled.h" #ifndef _PreComp_ #include -#include #ifdef FC_OS_WIN32 #include #endif @@ -607,7 +606,7 @@ void SoFCMeshObjectShape::initClass() } SoFCMeshObjectShape::SoFCMeshObjectShape() - : renderTriangleLimit(UINT_MAX) + : renderTriangleLimit(std::numeric_limits::max()) { SO_NODE_CONSTRUCTOR(SoFCMeshObjectShape); setName(SoFCMeshObjectShape::getClassTypeId().getName()); @@ -1235,7 +1234,7 @@ void SoFCMeshSegmentShape::initClass() } SoFCMeshSegmentShape::SoFCMeshSegmentShape() - : renderTriangleLimit(UINT_MAX) + : renderTriangleLimit(std::numeric_limits::max()) { SO_NODE_CONSTRUCTOR(SoFCMeshSegmentShape); SO_NODE_ADD_FIELD(index, (0)); diff --git a/src/Mod/Mesh/Gui/SoPolygon.cpp b/src/Mod/Mesh/Gui/SoPolygon.cpp index acc5701058..a5b242c82b 100644 --- a/src/Mod/Mesh/Gui/SoPolygon.cpp +++ b/src/Mod/Mesh/Gui/SoPolygon.cpp @@ -31,7 +31,6 @@ #include #endif #include -#include #include #include @@ -145,8 +144,9 @@ void SoPolygon::computeBBox(SoAction* action, SbBox3f& box, SbVec3f& center) if (!points) { return; } - float maxX = -FLT_MAX, minX = FLT_MAX, maxY = -FLT_MAX, minY = FLT_MAX, maxZ = -FLT_MAX, - minZ = FLT_MAX; + constexpr float floatMax = std::numeric_limits::max(); + float maxX = -floatMax, minX = floatMax, maxY = -floatMax, minY = floatMax, maxZ = -floatMax, + minZ = floatMax; int32_t len = coords->getNum(); int32_t beg = startIndex.getValue(); int32_t cnt = numVertices.getValue(); diff --git a/src/Mod/Mesh/Gui/ViewProviderCurvature.cpp b/src/Mod/Mesh/Gui/ViewProviderCurvature.cpp index 2fe95398b6..aaf5580564 100644 --- a/src/Mod/Mesh/Gui/ViewProviderCurvature.cpp +++ b/src/Mod/Mesh/Gui/ViewProviderCurvature.cpp @@ -126,10 +126,23 @@ ViewProviderMeshCurvature::ViewProviderMeshCurvature() ViewProviderMeshCurvature::~ViewProviderMeshCurvature() { - pcColorRoot->unref(); - pcColorMat->unref(); - deleteColorBar(); - pcLinkRoot->unref(); + try { + pcColorRoot->unref(); + pcColorMat->unref(); + pcLinkRoot->unref(); + deleteColorBar(); + } + catch (Base::Exception& e) { + Base::Console().DestructorError( + "ViewProviderMeshCurvature", + "ViewProviderMeshCurvature::deleteColorBar() threw an exception: %s\n", + e.what()); + } + catch (...) { + Base::Console().DestructorError( + "ViewProviderInspection", + "ViewProviderInspection destructor threw an unknown exception"); + } } void ViewProviderMeshCurvature::onChanged(const App::Property* prop) diff --git a/src/Mod/MeshPart/App/CurveProjector.cpp b/src/Mod/MeshPart/App/CurveProjector.cpp index ceab83bff4..db9926b50d 100644 --- a/src/Mod/MeshPart/App/CurveProjector.cpp +++ b/src/Mod/MeshPart/App/CurveProjector.cpp @@ -180,7 +180,7 @@ void CurveProjectorShape::projectCurve(const TopoDS_Edge& aEdge, / ((cP1 - cP0) * (cP1 - cP0)); // is the Point on the Edge of the facet? if (l < 0.0 || l > 1.0) { - PointOnEdge[i] = Base::Vector3f(FLOAT_MAX, 0, 0); + PointOnEdge[i] = Base::Vector3f(std::numeric_limits::max(), 0, 0); } else { cSplitPoint = (1 - l) * cP0 + l * cP1; @@ -191,11 +191,11 @@ void CurveProjectorShape::projectCurve(const TopoDS_Edge& aEdge, // no intersection } else if (Alg.NbPoints() == 0) { - PointOnEdge[i] = Base::Vector3f(FLOAT_MAX, 0, 0); + PointOnEdge[i] = Base::Vector3f(std::numeric_limits::max(), 0, 0); // more the one intersection (@ToDo) } else if (Alg.NbPoints() > 1) { - PointOnEdge[i] = Base::Vector3f(FLOAT_MAX, 0, 0); + PointOnEdge[i] = Base::Vector3f(std::numeric_limits::max(), 0, 0); Base::Console().Log("MeshAlgos::projectCurve(): More then one intersection in " "Facet %lu, Edge %d\n", uCurFacetIdx, @@ -234,7 +234,7 @@ bool CurveProjectorShape::findStartPoint(const MeshKernel& MeshK, MeshCore::FacetIndex& FaceIndex) { Base::Vector3f TempResultPoint; - float MinLength = FLOAT_MAX; + float MinLength = std::numeric_limits::max(); bool bHit = false; // go through the whole Mesh @@ -363,7 +363,7 @@ bool CurveProjectorSimple::findStartPoint(const MeshKernel& MeshK, MeshCore::FacetIndex& FaceIndex) { Base::Vector3f TempResultPoint; - float MinLength = FLOAT_MAX; + float MinLength = std::numeric_limits::max(); bool bHit = false; // go through the whole Mesh @@ -465,11 +465,11 @@ void CurveProjectorWithToolMesh::makeToolMesh(const TopoDS_Edge& aEdge, // build up the new mesh - Base::Vector3f lp(FLOAT_MAX, 0, 0), ln, p1, p2, p3, p4, p5, p6; + Base::Vector3f lp(std::numeric_limits::max(), 0, 0), ln, p1, p2, p3, p4, p5, p6; float ToolSize = 0.2f; for (const auto& It2 : LineSegs) { - if (lp.x != FLOAT_MAX) { + if (lp.x != std::numeric_limits::max()) { p1 = lp + (ln * (-ToolSize)); p2 = lp + (ln * ToolSize); p3 = lp; diff --git a/src/Mod/MeshPart/App/CurveProjector.h b/src/Mod/MeshPart/App/CurveProjector.h index f2c84e4c2b..d8fd6386d1 100644 --- a/src/Mod/MeshPart/App/CurveProjector.h +++ b/src/Mod/MeshPart/App/CurveProjector.h @@ -66,7 +66,8 @@ public: std::hash hasher; return hasher(x) < hasher(y); #else - return x.HashCode(INT_MAX - 1) < y.HashCode(INT_MAX - 1); + constexpr int max = std::numeric_limits::max(); + return x.HashCode(max - 1) < y.HashCode(max - 1); #endif } }; diff --git a/src/Mod/MeshPart/App/MeshFlatteningLscmRelax.cpp b/src/Mod/MeshPart/App/MeshFlatteningLscmRelax.cpp index 1071e0c9c8..4c73f3f651 100644 --- a/src/Mod/MeshPart/App/MeshFlatteningLscmRelax.cpp +++ b/src/Mod/MeshPart/App/MeshFlatteningLscmRelax.cpp @@ -26,14 +26,11 @@ #include #include #include +#include #include #include #endif -#ifndef M_PI -#define M_PI 3.14159265358979323846f -#endif - #include #include "MeshFlatteningLscmRelax.h" @@ -649,7 +646,7 @@ void LscmRelax::rotate_by_min_bound_area() // rotate vector by 90 degree and find min area for (int i = 0; i < n + 1; i++ ) { - phi = i * M_PI / n; + phi = i * std::numbers::pi / n; Eigen::VectorXd x_proj = this->flat_vertices.transpose() * Vector2(std::cos(phi), std::sin(phi)); Eigen::VectorXd y_proj = this->flat_vertices.transpose() * Vector2(-std::sin(phi), std::cos(phi)); double x_distance = x_proj.maxCoeff() - x_proj.minCoeff(); @@ -663,7 +660,7 @@ void LscmRelax::rotate_by_min_bound_area() } } Eigen::Matrix rot; - min_phi += x_dominant * M_PI / 2; + min_phi += x_dominant * std::numbers::pi / 2; rot << std::cos(min_phi), std::sin(min_phi), -std::sin(min_phi), std::cos(min_phi); this->flat_vertices = rot * this->flat_vertices; } diff --git a/src/Mod/MeshPart/App/PreCompiled.h b/src/Mod/MeshPart/App/PreCompiled.h index 7c1101f012..6b867c5e80 100644 --- a/src/Mod/MeshPart/App/PreCompiled.h +++ b/src/Mod/MeshPart/App/PreCompiled.h @@ -43,6 +43,7 @@ #include #include #include +#include #include #include #include diff --git a/src/Mod/MeshPart/Gui/CrossSections.cpp b/src/Mod/MeshPart/Gui/CrossSections.cpp index 01ba18888e..2ba73eeeac 100644 --- a/src/Mod/MeshPart/Gui/CrossSections.cpp +++ b/src/Mod/MeshPart/Gui/CrossSections.cpp @@ -22,7 +22,6 @@ #include "PreCompiled.h" #ifndef _PreComp_ -#include #include #include @@ -179,9 +178,10 @@ CrossSections::CrossSections(const Base::BoundBox3d& bb, QWidget* parent, Qt::Wi ui->setupUi(this); setupConnections(); - ui->position->setRange(-DBL_MAX, DBL_MAX); + constexpr double max = std::numeric_limits::max(); + ui->position->setRange(-max, max); ui->position->setUnit(Base::Unit::Length); - ui->distance->setRange(0, DBL_MAX); + ui->distance->setRange(0, max); ui->distance->setUnit(Base::Unit::Length); ui->spinEpsilon->setMinimum(0.0001); vp = new ViewProviderCrossSections(); diff --git a/src/Mod/MeshPart/Gui/PreCompiled.h b/src/Mod/MeshPart/Gui/PreCompiled.h index 7b8b8dda80..408fcd88ed 100644 --- a/src/Mod/MeshPart/Gui/PreCompiled.h +++ b/src/Mod/MeshPart/Gui/PreCompiled.h @@ -34,7 +34,6 @@ #ifdef _PreComp_ // STL -#include #include // Qt Toolkit diff --git a/src/Mod/MeshPart/Gui/Tessellation.cpp b/src/Mod/MeshPart/Gui/Tessellation.cpp index f063bd91be..c726659bbe 100644 --- a/src/Mod/MeshPart/Gui/Tessellation.cpp +++ b/src/Mod/MeshPart/Gui/Tessellation.cpp @@ -71,11 +71,11 @@ Tessellation::Tessellation(QWidget* parent) relative = handle->GetBool("RelativeLinearDeflection", relative); ui->relativeDeviation->setChecked(relative); - ui->spinSurfaceDeviation->setMaximum(INT_MAX); + ui->spinSurfaceDeviation->setMaximum(std::numeric_limits::max()); ui->spinSurfaceDeviation->setValue(value); ui->spinAngularDeviation->setValue(angle); - ui->spinMaximumEdgeLength->setRange(0, INT_MAX); + ui->spinMaximumEdgeLength->setRange(0, std::numeric_limits::max()); ui->comboFineness->setCurrentIndex(2); onComboFinenessCurrentIndexChanged(2); diff --git a/src/Mod/Part/App/AppPartPy.cpp b/src/Mod/Part/App/AppPartPy.cpp index f094479e61..b6734ca17b 100644 --- a/src/Mod/Part/App/AppPartPy.cpp +++ b/src/Mod/Part/App/AppPartPy.cpp @@ -1622,7 +1622,8 @@ private: } Py::Object makeRevolution(const Py::Tuple& args) { - double vmin = DBL_MAX, vmax=-DBL_MAX; + constexpr double doubleMax = std::numeric_limits::max(); + double vmin = doubleMax, vmax=-doubleMax; double angle=360; PyObject *pPnt=nullptr, *pDir=nullptr, *pCrv; Handle(Geom_Curve) curve; @@ -1641,10 +1642,10 @@ private: if (curve.IsNull()) { throw Py::Exception(PyExc_TypeError, "geometry is not a curve"); } - if (vmin == DBL_MAX) + if (vmin == doubleMax) vmin = curve->FirstParameter(); - if (vmax == -DBL_MAX) + if (vmax == -doubleMax) vmax = curve->LastParameter(); break; } @@ -1675,9 +1676,9 @@ private: throw Py::Exception(PartExceptionOCCError, "invalid curve in edge"); } - if (vmin == DBL_MAX) + if (vmin == doubleMax) vmin = adapt.FirstParameter(); - if (vmax == -DBL_MAX) + if (vmax == -doubleMax) vmax = adapt.LastParameter(); break; } @@ -1981,7 +1982,7 @@ private: double height; double track = 0; - Py_UNICODE *unichars = nullptr; + Py_UCS4 *unichars = nullptr; Py_ssize_t pysize; PyObject *CharList; @@ -2014,28 +2015,11 @@ private: } pysize = PyUnicode_GetLength(p); - -#ifdef FC_OS_WIN32 - //PyUNICODE is only 16 bits on Windows (wchar_t), so passing 32 bit UCS4 - //will result in unknown glyph in even positions, and wrong characters in - //odd positions. - unichars = (Py_UNICODE*)PyUnicode_AsWideCharString(p, &pysize); -#else - unichars = (Py_UNICODE *)PyUnicode_AsUCS4Copy(p); -#endif + unichars = PyUnicode_AsUCS4Copy(p); } else if (PyUnicode_Check(intext)) { pysize = PyUnicode_GetLength(intext); - - -#ifdef FC_OS_WIN32 - //PyUNICODE is only 16 bits on Windows (wchar_t), so passing 32 bit UCS4 - //will result in unknown glyph in even positions, and wrong characters in - //odd positions. - unichars = (Py_UNICODE*)PyUnicode_AsWideCharString(intext, &pysize); -#else - unichars = (Py_UNICODE *)PyUnicode_AsUCS4Copy(intext); -#endif + unichars = PyUnicode_AsUCS4Copy(intext); } else { throw Py::TypeError("** makeWireString bad text parameter"); diff --git a/src/Mod/Part/App/Arc.pyi b/src/Mod/Part/App/Arc.pyi new file mode 100644 index 0000000000..cf31913405 --- /dev/null +++ b/src/Mod/Part/App/Arc.pyi @@ -0,0 +1,28 @@ +from Base.Metadata import export +from TrimmedCurve import TrimmedCurve +from Geometry import Geom_Circle, Geom_Ellipse +from typing import overload + + +@export( + Father="TrimmedCurvePy", + PythonName="Part.Arc", + Twin="GeomTrimmedCurve", + TwinPointer="GeomTrimmedCurve", + Include="Mod/Part/App/Geometry.h", + FatherInclude="Mod/Part/App/TrimmedCurvePy.h", + Constructor=True, +) +class Arc(TrimmedCurve): + """ + Describes a portion of a curve + + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + @overload + def __init__(self, circ: Geom_Circle, T: type = ...) -> None: ... + + @overload + def __init__(self, circ: Geom_Ellipse, T: type = ...) -> None: ... diff --git a/src/Mod/Part/App/ArcOfCircle.pyi b/src/Mod/Part/App/ArcOfCircle.pyi new file mode 100644 index 0000000000..1599037ffa --- /dev/null +++ b/src/Mod/Part/App/ArcOfCircle.pyi @@ -0,0 +1,25 @@ +from Base.Metadata import export +from ArcOfConic import ArcOfConic +from typing import Final + + +@export( + PythonName="Part.ArcOfCircle", + Twin="GeomArcOfCircle", + TwinPointer="GeomArcOfCircle", + Include="Mod/Part/App/Geometry.h", + FatherInclude="Mod/Part/App/ArcOfConicPy.h", + Constructor=True, +) +class ArcOfCircle(ArcOfConic): + """ + Author: Werner Mayer (wmayer[at]users.sourceforge.net) + Licence: LGPL + Describes a portion of a circle + """ + + Radius: float = ... + """The radius of the circle.""" + + Circle: Final[object] = ... + """The internal circle representation""" diff --git a/src/Mod/Part/App/ArcOfCirclePy.xml b/src/Mod/Part/App/ArcOfCirclePy.xml deleted file mode 100644 index 1416c68f34..0000000000 --- a/src/Mod/Part/App/ArcOfCirclePy.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - Describes a portion of a circle - - - - The radius of the circle. - - - - - - The internal circle representation - - - - - diff --git a/src/Mod/Part/App/ArcOfConic.pyi b/src/Mod/Part/App/ArcOfConic.pyi new file mode 100644 index 0000000000..903a2b9e1a --- /dev/null +++ b/src/Mod/Part/App/ArcOfConic.pyi @@ -0,0 +1,43 @@ +from Base.Metadata import export +from Base.Vector import Vector +from TrimmedCurve import TrimmedCurve +from typing import overload + + +@export( + Father="TrimmedCurvePy", + PythonName="Part.ArcOfConic", + Twin="GeomArcOfConic", + TwinPointer="GeomArcOfConic", + Include="Mod/Part/App/Geometry.h", + FatherInclude="Mod/Part/App/TrimmedCurvePy.h", + Constructor=True, +) +class ArcOfConic(TrimmedCurve): + """ + Describes a portion of a conic + + Author: Abdullah Tahiri (abdullah.tahiri.yo@gmail.com) + Licence: LGPL + """ + + @overload + def __init__(self) -> None: ... + + Location: Vector = ... + """Center of the conic.""" + + Center: Vector = ... + """Deprecated -- use Location.""" + + AngleXU: float = ... + """The angle between the X axis and the major axis of the conic.""" + + Axis: Vector = ... + """The axis direction of the conic""" + + XAxis: Vector = ... + """The X axis direction of the circle""" + + YAxis: Vector = ... + """The Y axis direction of the circle""" diff --git a/src/Mod/Part/App/ArcOfConicPy.xml b/src/Mod/Part/App/ArcOfConicPy.xml deleted file mode 100644 index 43745ba0fe..0000000000 --- a/src/Mod/Part/App/ArcOfConicPy.xml +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - Describes a portion of a conic - - - - Center of the conic. - - - - - - Deprecated -- use Location. - - - - - - The angle between the X axis and the major axis of the conic. - - - - - - The axis direction of the conic - - - - - - The X axis direction of the circle - - - - - - The Y axis direction of the circle - - - - - diff --git a/src/Mod/Part/App/ArcOfEllipse.pyi b/src/Mod/Part/App/ArcOfEllipse.pyi new file mode 100644 index 0000000000..664a0c7d66 --- /dev/null +++ b/src/Mod/Part/App/ArcOfEllipse.pyi @@ -0,0 +1,30 @@ +from Base.Metadata import export +from ArcOfConic import ArcOfConic +from typing import Final + + +@export( + PythonName="Part.ArcOfEllipse", + Twin="GeomArcOfEllipse", + TwinPointer="GeomArcOfEllipse", + Include="Mod/Part/App/Geometry.h", + FatherInclude="Mod/Part/App/ArcOfConicPy.h", + FatherNamespace="Part", + Constructor=True, +) +class ArcOfEllipse(ArcOfConic): + """ + Describes a portion of an ellipse + + Author: Abdullah Tahiri (abdullah.tahiri.yo[at]gmail.com) + Licence: LGPL + """ + + MajorRadius: float = ... + """The major radius of the ellipse.""" + + MinorRadius: float = ... + """The minor radius of the ellipse.""" + + Ellipse: Final[object] = ... + """The internal ellipse representation""" diff --git a/src/Mod/Part/App/ArcOfEllipsePy.xml b/src/Mod/Part/App/ArcOfEllipsePy.xml deleted file mode 100644 index 58b79fa0bb..0000000000 --- a/src/Mod/Part/App/ArcOfEllipsePy.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - Describes a portion of an ellipse - - - - The major radius of the ellipse. - - - - - - The minor radius of the ellipse. - - - - - - The internal ellipse representation - - - - - diff --git a/src/Mod/Part/App/ArcOfHyperbola.pyi b/src/Mod/Part/App/ArcOfHyperbola.pyi new file mode 100644 index 0000000000..bc489e0d34 --- /dev/null +++ b/src/Mod/Part/App/ArcOfHyperbola.pyi @@ -0,0 +1,30 @@ +from Base.Metadata import export +from Part.ArcOfConic import ArcOfConic +from typing import Final + + +@export( + Father="ArcOfConicPy", + Name="ArcOfHyperbolaPy", + PythonName="Part.ArcOfHyperbola", + Twin="GeomArcOfHyperbola", + TwinPointer="GeomArcOfHyperbola", + Include="Mod/Part/App/Geometry.h", + Namespace="Part", + FatherInclude="Mod/Part/App/ArcOfConicPy.h", + FatherNamespace="Part", + Constructor=True, +) +class ArcOfHyperbola(ArcOfConic): + """ + Describes a portion of an hyperbola + """ + + MajorRadius: float = 0.0 + """The major radius of the hyperbola.""" + + MinorRadius: float = 0.0 + """The minor radius of the hyperbola.""" + + Hyperbola: Final[object] = None + """The internal hyperbola representation""" diff --git a/src/Mod/Part/App/ArcOfHyperbolaPy.xml b/src/Mod/Part/App/ArcOfHyperbolaPy.xml deleted file mode 100644 index 4b7cc14c97..0000000000 --- a/src/Mod/Part/App/ArcOfHyperbolaPy.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - Describes a portion of an hyperbola - - - - The major radius of the hyperbola. - - - - - - The minor radius of the hyperbola. - - - - - - The internal hyperbola representation - - - - - diff --git a/src/Mod/Part/App/ArcOfParabola.pyi b/src/Mod/Part/App/ArcOfParabola.pyi new file mode 100644 index 0000000000..75d12a300a --- /dev/null +++ b/src/Mod/Part/App/ArcOfParabola.pyi @@ -0,0 +1,30 @@ +from Base.Metadata import export +from ArcOfConic import ArcOfConic +from typing import Final + + +@export( + Father="ArcOfConicPy", + Name="ArcOfParabolaPy", + PythonName="Part.ArcOfParabola", + Twin="GeomArcOfParabola", + TwinPointer="GeomArcOfParabola", + Include="Mod/Part/App/Geometry.h", + Namespace="Part", + FatherInclude="Mod/Part/App/ArcOfConicPy.h", + FatherNamespace="Part", + Constructor=True, +) +class ArcOfParabola(ArcOfConic): + """ + Describes a portion of a parabola + + Author: Abdullah Tahiri (abdullah.tahiri.yo@gmail.com) + Licence: LGPL + """ + + Focal: float = ... + """The focal length of the parabola.""" + + Parabola: Final[object] = ... + """The internal parabola representation""" diff --git a/src/Mod/Part/App/ArcOfParabolaPy.xml b/src/Mod/Part/App/ArcOfParabolaPy.xml deleted file mode 100644 index 23c980f010..0000000000 --- a/src/Mod/Part/App/ArcOfParabolaPy.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - Describes a portion of a parabola - - - - The focal length of the parabola. - - - - - - The internal parabola representation - - - - - diff --git a/src/Mod/Part/App/ArcPy.xml b/src/Mod/Part/App/ArcPy.xml deleted file mode 100644 index 32948233c9..0000000000 --- a/src/Mod/Part/App/ArcPy.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - Describes a portion of a curve - - - - - diff --git a/src/Mod/Part/App/AttachEngine.pyi b/src/Mod/Part/App/AttachEngine.pyi new file mode 100644 index 0000000000..65d1f01278 --- /dev/null +++ b/src/Mod/Part/App/AttachEngine.pyi @@ -0,0 +1,150 @@ +from Base.Metadata import export, constmethod +from Base.BaseClass import BaseClass +from Base.Placement import Placement +from App.DocumentObject import DocumentObject +from typing import Final, Optional + + +@export( + Include="Mod/Part/App/Attacher.h", + Namespace="Attacher", + Constructor=True, + Delete=True, +) +class AttachEngine(BaseClass): + """ + AttachEngine abstract class - the functionality of AttachableObject, but outside of DocumentObject + + Author: DeepSOIC (vv.titov@gmail.com) + Licence: LGPL + DeveloperDocu: AttachEngine abstract class + """ + + AttacherType: Final[str] = "" + """Type of engine: 3d, plane, line, or point.""" + + Mode: str = "" + """Current attachment mode.""" + + References: object = None + """Current attachment mode.""" + + AttachmentOffset: object = None + """Current attachment mode.""" + + Reverse: bool = False + """If True, Z axis of attached placement is flipped. X axis is flipped in addition (CS has to remain right-handed).""" + + Parameter: float = 0.0 + """Value of parameter for some curve attachment modes. Range of 0..1 spans the length of the edge (parameter value can be outside of the range for curves that allow extrapolation.""" + + CompleteModeList: Final[list] = [] + """List of all attachment modes of all AttachEngines. This is the list of modes in MapMode enum properties of AttachableObjects.""" + + ImplementedModes: Final[list] = [] + """List of all attachment modes of all AttachEngines. This is the list of modes in MapMode enum properties of AttachableObjects.""" + + CompleteRefTypeList: Final[list] = [] + """List of all reference shape types recognized by AttachEngine.""" + + def getModeInfo(self, mode: str) -> dict: + """ + getModeInfo(mode): returns supported reference combinations, user-friendly name, and so on. + """ + ... + + def getRefTypeOfShape(self, shape: str) -> str: + """ + getRefTypeOfShape(shape): returns shape type as interpreted by AttachEngine. Returns a string. + """ + ... + + def isFittingRefType(self, type_shape: str, type_needed: str) -> bool: + """ + isFittingRefType(type_shape, type_needed): tests if shape type, specified by type_shape (string), fits a type required by attachment mode type_needed (string). e.g. 'Circle' fits a requirement of 'Edge', and 'Curve' doesn't fit if a 'Circle' is required. + """ + ... + + def downgradeRefType(self, type: str) -> str: + """ + downgradeRefType(type): returns next more general type. E.g. downgradeType('Circle') yields 'Curve'. + """ + ... + + def getRefTypeInfo(self, type: str) -> dict: + """ + getRefTypeInfo(type): returns information (dict) on shape type. Keys:'UserFriendlyName', 'TypeIndex', 'Rank'. Rank is the number of times reftype can be downgraded, before it becomes 'Any'. + """ + ... + + @constmethod + def copy(self) -> 'AttachEngine': + """ + copy(): returns a new instance of AttachEngine. + """ + ... + + @constmethod + def calculateAttachedPlacement(self, orig_placement: Placement) -> Optional[Placement]: + """ + calculateAttachedPlacement(orig_placement): returns result of attachment, based + on current Mode, References, etc. AttachmentOffset is included. + + original_placement is the previous placement of the object being attached. It + is used to preserve orientation for Translate attachment mode. For other modes, + it is ignored. + + Returns the new placement. If not attached, returns None. If attachment fails, + an exception is raised. + """ + ... + + def suggestModes(self) -> dict: + """ + suggestModes(): runs mode suggestion routine and returns a dictionary with + results and supplementary information. + + Keys: + 'allApplicableModes': list of modes that can accept current references. Note + that it is only a check by types, and does not guarantee the modes will + actually work. + + 'bestFitMode': mode that fits current references best. Note that the mode may + not be valid for the set of references; check for if 'message' is 'OK'. + + 'error': error message for when 'message' is 'UnexpectedError' or + 'LinkBroken'. + + 'message': general result of suggestion. 'IncompatibleGeometry', 'NoModesFit': + no modes accept current set of references; 'OK': some modes do accept current + set of references (though it's not guarantted the modes will work - surrestor + only checks for correct types); 'UnexpectedError': should never happen. + + 'nextRefTypeHint': what more can be added to references to reach other modes + ('reachableModes' provide more extended information on this) + + 'reachableModes': a dict, where key is mode, and value is a list of sequences + of references that can be added to fit that mode. + + 'references_Types': a list of types of geometry linked by references (that's + the input information for suggestor, actually). + """ + ... + + def readParametersFromFeature(self, document_object: DocumentObject) -> None: + """ + readParametersFromFeature(document_object): sets AttachEngine parameters (References, Mode, etc.) by reading out properties of AttachableObject-derived feature. + """ + ... + + def writeParametersToFeature(self, document_object: DocumentObject) -> None: + """ + writeParametersToFeature(document_object): updates properties of + AttachableObject-derived feature with current AttachEngine parameters + (References, Mode, etc.). + + Warning: if a feature linked by AttachEngine.References was deleted, this method + will crash FreeCAD. + """ + ... + diff --git a/src/Mod/Part/App/AttachEnginePy.xml b/src/Mod/Part/App/AttachEnginePy.xml deleted file mode 100644 index e0ddc60795..0000000000 --- a/src/Mod/Part/App/AttachEnginePy.xml +++ /dev/null @@ -1,168 +0,0 @@ - - - - - - AttachEngine abstract class - AttachEngine abstract class - the functionality of AttachableObject, but outside of DocumentObject - - - - Type of engine: 3d, plane, line, or point. - - - - - - Current attachment mode. - - - - - - Current attachment mode. - - - - - - Current attachment mode. - - - - - - If True, Z axis of attached placement is flipped. X axis is flipped in addition (CS has to remain right-handed). - - - - - - Value of parameter for some curve attachment modes. Range of 0..1 spans the length of the edge (parameter value can be outside of the range for curves that allow extrapolation. - - - - - - - List of all attachment modes of all AttachEngines. This is the list of modes in MapMode enum properties of AttachableObjects. - - - - - - List of all attachment modes of all AttachEngines. This is the list of modes in MapMode enum properties of AttachableObjects. - - - - - - List of all reference shape types recognized by AttachEngine. - - - - - - - getModeInfo(mode): returns supported reference combinations, user-friendly name, and so on. - - - - - getRefTypeOfShape(shape): returns shape type as interpreted by AttachEngine. Returns a string. - - - - - isFittingRefType(type_shape, type_needed): tests if shape type, specified by type_shape (string), fits a type required by attachment mode type_needed (string). e.g. 'Circle' fits a requirement of 'Edge', and 'Curve' doesn't fit if a 'Circle' is required. - - - - - downgradeRefType(type): returns next more general type. E.g. downgradeType('Circle') yields 'Curve'. - - - - - getRefTypeInfo(type): returns information (dict) on shape type. Keys:'UserFriendlyName', 'TypeIndex', 'Rank'. Rank is the number of times reftype can be downgraded, before it becomes 'Any'. - - - - - copy(): returns a new instance of AttachEngine. - - - - - calculateAttachedPlacement(orig_placement): returns result of attachment, based -on current Mode, References, etc. AttachmentOffset is included. - -original_placement is the previous placement of the object being attached. It -is used to preserve orientation for Translate attachment mode. For other modes, -it is ignored. - -Returns the new placement. If not attached, returns None. If attachment fails, -an exception is raised. - - - - - -suggestModes(): runs mode suggestion routine and returns a dictionary with -results and supplementary information. - -Keys: -'allApplicableModes': list of modes that can accept current references. Note -that it is only a check by types, and does not guarantee the modes will -actually work. - -'bestFitMode': mode that fits current references best. Note that the mode may -not be valid for the set of references; check for if 'message' is 'OK'. - -'error': error message for when 'message' is 'UnexpectedError' or -'LinkBroken'. - -'message': general result of suggestion. 'IncompatibleGeometry', 'NoModesFit': -no modes accept current set of references; 'OK': some modes do accept current -set of references (though it's not guarantted the modes will work - surrestor -only checks for correct types); 'UnexpectedError': should never happen. - -'nextRefTypeHint': what more can be added to references to reach other modes -('reachableModes' provide more extended information on this) - -'reachableModes': a dict, where key is mode, and value is a list of sequences -of references that can be added to fit that mode. - -'references_Types': a list of types of geometry linked by references (that's -the input information for suggestor, actually). - - - - - readParametersFromFeature(document_object): sets AttachEngine parameters (References, Mode, etc.) by reading out properties of AttachableObject-derived feature. - - - - - -writeParametersToFeature(document_object): updates properties of -AttachableObject-derived feature with current AttachEngine parameters -(References, Mode, etc.). - -Warning: if a feature linked by AttachEngine.References was deleted, this method -will crash FreeCAD. - - - - - diff --git a/src/Mod/Part/App/AttachExtension.pyi b/src/Mod/Part/App/AttachExtension.pyi new file mode 100644 index 0000000000..f31bce5873 --- /dev/null +++ b/src/Mod/Part/App/AttachExtension.pyi @@ -0,0 +1,44 @@ +from Base.Metadata import export +from App.DocumentObjectExtension import DocumentObjectExtension +from typing import Any, Final + + +@export( + Twin="AttachExtension", + TwinPointer="AttachExtension", + Include="Mod/Part/App/AttachExtension.h", + FatherInclude="App/DocumentObjectExtensionPy.h", +) +class AttachExtension(DocumentObjectExtension): + """ + This object represents an attachable object with OCC shape. + + Author: DeepSOIC (vv.titov@gmail.com) + Licence: LGPL + """ + + Attacher: Final[Any] = ... + """AttachEngine object driving this AttachableObject. Returns a copy.""" + + def positionBySupport(self) -> bool: + """ + positionBySupport() -> bool + + Reposition object based on AttachmentSupport, MapMode and MapPathParameter properties. + Returns True if attachment calculation was successful, false if object is not attached and Placement wasn't updated, + and raises an exception if attachment calculation fails. + """ + ... + + def changeAttacherType(self, typename: str) -> None: + """ + changeAttacherType(typename) -> None + + Changes Attacher class of this object. + typename: string. The following are accepted so far: + 'Attacher::AttachEngine3D' + 'Attacher::AttachEnginePlane' + 'Attacher::AttachEngineLine' + 'Attacher::AttachEnginePoint' + """ + ... diff --git a/src/Mod/Part/App/AttachExtensionPy.xml b/src/Mod/Part/App/AttachExtensionPy.xml deleted file mode 100644 index d47cc4fb5b..0000000000 --- a/src/Mod/Part/App/AttachExtensionPy.xml +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - This object represents an attachable object with OCC shape. - - - - positionBySupport() -> bool - -Reposition object based on AttachmentSupport, MapMode and MapPathParameter properties. -Returns True if attachment calculation was successful, false if object is not attached and Placement wasn't updated, -and raises an exception if attachment calculation fails. - - - - - changeAttacherType(typename) -> None - -Changes Attacher class of this object. -typename: string. The following are accepted so far: -'Attacher::AttachEngine3D' -'Attacher::AttachEnginePlane' -'Attacher::AttachEngineLine' -'Attacher::AttachEnginePoint' - - - - - - AttachEngine object driving this AttachableObject. Returns a copy. - - - - - diff --git a/src/Mod/Part/App/Attacher.cpp b/src/Mod/Part/App/Attacher.cpp index 5009ce260b..255d54b702 100644 --- a/src/Mod/Part/App/Attacher.cpp +++ b/src/Mod/Part/App/Attacher.cpp @@ -1912,7 +1912,7 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vectormapReverse) { - rot = rot * Base::Rotation(Base::Vector3d(0, 1, 0), D_PI); + rot = rot * Base::Rotation(Base::Vector3d(0, 1, 0), std::numbers::pi); } Base::Placement plm = Base::Placement( diff --git a/src/Mod/Part/App/BRepFeat/MakePrism.pyi b/src/Mod/Part/App/BRepFeat/MakePrism.pyi new file mode 100644 index 0000000000..1484996a59 --- /dev/null +++ b/src/Mod/Part/App/BRepFeat/MakePrism.pyi @@ -0,0 +1,99 @@ +from Base.Metadata import export, constmethod +from Base.PyObjectBase import PyObjectBase +from typing import List + +@export( + PythonName="Part.BRepFeat.MakePrism", + Twin="BRepFeat_MakePrism", + TwinPointer="BRepFeat_MakePrism", + Include="BRepFeat_MakePrism.hxx", + Constructor=True, + Delete=True, +) +class MakePrism(PyObjectBase): + """ + Describes functions to build prism features. + + Author: Werner Mayer (wmayer[at]users.sourceforge.net) + Licence: LGPL + """ + + def init(self, **kwargs) -> None: + """ + Initializes this algorithm for building prisms along surfaces. + A face Pbase is selected in the shape Sbase + to serve as the basis for the prism. The orientation + of the prism will be defined by the vector Direction. + + Fuse offers a choice between: + - removing matter with a Boolean cut using the setting 0 + - adding matter with Boolean fusion using the setting 1. + The sketch face Skface serves to determine + the type of operation. If it is inside the basis + shape, a local operation such as glueing can be performed. + """ + ... + + def add(self, **kwargs) -> None: + """ + Indicates that the edge will slide on the face. + Raises ConstructionError if the face does not belong to the + basis shape, or the edge to the prismed shape. + """ + ... + + def perform(self, **kwargs) -> None: + """ + Assigns one of the following semantics. + 1. to a height Length + 2. to a face Until + 3. from a face From to a height Until. Reconstructs the feature topologically according to the semantic option chosen. + """ + ... + + def performUntilEnd(self) -> None: + """ + Realizes a semi-infinite prism, limited by the + position of the prism base. All other faces extend infinitely. + """ + ... + + def performFromEnd(self) -> None: + """ + Realizes a semi-infinite prism, limited by the face Funtil. + """ + ... + + def performThruAll(self) -> None: + """ + Builds an infinite prism. The infinite descendants will not be kept in the result. + """ + ... + + def performUntilHeight(self) -> None: + """ + Assigns both a limiting shape, Until from TopoDS_Shape + and a height, Length at which to stop generation of the prism feature. + """ + ... + + @constmethod + def curves(self) -> List: + """ + Returns the list of curves S parallel to the axis of the prism. + """ + ... + + @constmethod + def barycCurve(self) -> object: + """ + Generates a curve along the center of mass of the primitive. + """ + ... + + @constmethod + def shape(self) -> object: + """ + Returns a shape built by the shape construction algorithm. + """ + ... diff --git a/src/Mod/Part/App/BRepFeat/MakePrismPy.xml b/src/Mod/Part/App/BRepFeat/MakePrismPy.xml deleted file mode 100644 index 54a6e59fe1..0000000000 --- a/src/Mod/Part/App/BRepFeat/MakePrismPy.xml +++ /dev/null @@ -1,105 +0,0 @@ - - - - - - Describes functions to build prism features. - - - - -Initializes this algorithm for building prisms along surfaces. -A face Pbase is selected in the shape Sbase -to serve as the basis for the prism. The orientation -of the prism will be defined by the vector Direction. - -Fuse offers a choice between: -- removing matter with a Boolean cut using the setting 0 -- adding matter with Boolean fusion using the setting 1. -The sketch face Skface serves to determine -the type of operation. If it is inside the basis -shape, a local operation such as glueing can be performed. - - - - - - -Indicates that the edge will slide on the face. -Raises ConstructionError if the face does not belong to the -basis shape, or the edge to the prismed shape. - - - - - - - Assigns one of the following semantics. - 1. to a height Length - 2. to a face Until - 3. from a face From to a height Until. Reconstructs the feature topologically according to the semantic option chosen. - - - - - - -Realizes a semi-infinite prism, limited by the -position of the prism base. All other faces extend infinitely. - - - - - - -Realizes a semi-infinite prism, limited by the face Funtil. - - - - - - -Builds an infinite prism. The infinite descendants will not be kept in the result. - - - - - - -Assigns both a limiting shape, Until from TopoDS_Shape -and a height, Length at which to stop generation of the prism feature. - - - - - - -Returns the list of curves S parallel to the axis of the prism. - - - - - - -Generates a curve along the center of mass of the primitive. - - - - - - Returns a shape built by the shape construction algorithm. - - - - diff --git a/src/Mod/Part/App/BRepOffsetAPI_MakeFilling.pyi b/src/Mod/Part/App/BRepOffsetAPI_MakeFilling.pyi new file mode 100644 index 0000000000..22a8d9d142 --- /dev/null +++ b/src/Mod/Part/App/BRepOffsetAPI_MakeFilling.pyi @@ -0,0 +1,145 @@ +from Base.Metadata import export, constmethod +from Base.PyObjectBase import PyObjectBase +from Part.App.Point import Point +from Part.App.TopoShape import TopoShape +from Part.App.TopoShapeEdge import TopoShapeEdge +from Part.App.TopoShapeFace import TopoShapeFace +from typing import overload, Final + + +@export( + PythonName="Part.BRepOffsetAPI_MakeFilling", + Include="BRepOffsetAPI_MakeFilling.hxx", + Constructor=True, + Delete=True, +) +class BRepOffsetAPI_MakeFilling(PyObjectBase): + """ + N-Side Filling + + Author: Werner Mayer (wmayer[at]users.sourceforge.net) + Licence: LGPL + """ + + def setConstrParam(self, *, Tol2d: float = 0.00001, Tol3d: float = 0.0001, TolAng: float = 0.01, TolCurv: float = 0.1) -> None: + """ + setConstrParam(Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1) + Sets the values of Tolerances used to control the constraint. + """ + ... + + def setResolParam(self, *, Degree: int = 3, NbPtsOnCur: int = 15, NbIter: int = 2, Anisotropy: bool = False) -> None: + """ + setResolParam(Degree=3, NbPtsOnCur=15, NbIter=2, Anisotropy=False) + Sets the parameters used for resolution. + """ + ... + + def setApproxParam(self, *, MaxDeg: int = 8, MaxSegments: int = 9) -> None: + """ + setApproxParam(MaxDeg=8, MaxSegments=9) + Sets the parameters used to approximate the filling the surface + """ + ... + + def loadInitSurface(self, face: TopoShapeFace) -> None: + """ + loadInitSurface(face) + Loads the initial surface. + """ + ... + + @overload + def add(self, Edge: TopoShapeEdge, Order: int, *, IsBound: bool = True) -> None: + ... + + @overload + def add(self, Edge: TopoShapeEdge, Support: TopoShapeFace, Order: int, *, IsBound: bool = True) -> None: + ... + + @overload + def add(self, Support: TopoShapeFace, Order: int) -> None: + ... + + @overload + def add(self, Point: Point) -> None: + ... + + @overload + def add(self, U: float, V: float, Support: TopoShapeFace, Order: int) -> None: + ... + + def add(self, **kwargs) -> None: + """ + add(Edge, Order, IsBound=True) + add(Edge, Support, Order, IsBound=True) + add(Support, Order) + add(Point) + add(U, V, Support, Order) + Adds a new constraint. + """ + ... + + def build(self) -> None: + """ + Builds the resulting faces. + """ + ... + + def isDone(self) -> bool: + """ + Tests whether computation of the filling plate has been completed. + """ + ... + + @overload + def G0Error(self) -> float: + ... + + @overload + def G0Error(self, arg: int) -> float: + ... + + def G0Error(self, arg: int = 0) -> float: + """ + G0Error([int]) + Returns the maximum distance between the result and the constraints. + """ + ... + + @overload + def G1Error(self) -> float: + ... + + @overload + def G1Error(self, arg: int) -> float: + ... + + def G1Error(self, arg: int = 0) -> float: + """ + G1Error([int]) + Returns the maximum angle between the result and the constraints. + """ + ... + + @overload + def G2Error(self) -> float: + ... + + @overload + def G2Error(self, arg: int) -> float: + ... + + def G2Error(self, arg: int = 0) -> float: + """ + G2Error([int]) + Returns the greatest difference in curvature between the result and the constraints. + """ + ... + + def shape(self) -> TopoShape: + """ + shape() + Returns the resulting shape. + """ + ... diff --git a/src/Mod/Part/App/BRepOffsetAPI_MakeFillingPy.xml b/src/Mod/Part/App/BRepOffsetAPI_MakeFillingPy.xml deleted file mode 100644 index 1377b98522..0000000000 --- a/src/Mod/Part/App/BRepOffsetAPI_MakeFillingPy.xml +++ /dev/null @@ -1,106 +0,0 @@ - - - - - - N-Side Filling - - - - -setConstrParam(Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1) -Sets the values of Tolerances used to control the constraint. - - - - - - -setResolParam(Degree=3, NbPtsOnCur=15, NbIter=2, Anisotropy=False) -Sets the parameters used for resolution. - - - - - - -setApproxParam(MaxDeg=8, MaxSegments=9) -Sets the parameters used to approximate the filling the surface - - - - - - -loadInitSurface(face) -Loads the initial surface. - - - - - - -add(Edge, Order, IsBound=True) -add(Edge, Support, Order, IsBound=True) -add(Support, Order) -add(Point) -add(U, V, Support, Order) -Adds a new constraint. - - - - - - Builds the resulting faces. - - - - - Tests whether computation of the filling plate has been completed. - - - - - -G0Error([int]) -Returns the maximum distance between the result and the constraints. - - - - - - -G1Error([int]) -Returns the maximum angle between the result and the constraints. - - - - - - -G2Error([int]) -Returns the greatest difference in curvature between the result and the constraints. - - - - - - -shape() -Returns the resulting shape. - - - - - diff --git a/src/Mod/Part/App/BRepOffsetAPI_MakePipeShell.pyi b/src/Mod/Part/App/BRepOffsetAPI_MakePipeShell.pyi new file mode 100644 index 0000000000..ad875527a8 --- /dev/null +++ b/src/Mod/Part/App/BRepOffsetAPI_MakePipeShell.pyi @@ -0,0 +1,196 @@ +from Base.Metadata import export, constmethod +from Base.PyObjectBase import PyObjectBase +from Base.Vector import Vector +from TopoShape import TopoShape +from typing import overload + +@export( + PythonName="Part.BRepOffsetAPI_MakePipeShell", + Include="BRepOffsetAPI_MakePipeShell.hxx", + Constructor=True, + Delete=True, +) +class BRepOffsetAPI_MakePipeShell(PyObjectBase): + """ + Low level API to create a PipeShell using OCC API + + Ref: https://dev.opencascade.org/doc/refman/html/class_b_rep_offset_a_p_i___make_pipe_shell.html + + Author: Werner Mayer (wmayer[at]users.sourceforge.net) + Licence: LGPL + """ + + def setFrenetMode(self, mode: bool) -> None: + """ + setFrenetMode(True|False) + Sets a Frenet or a CorrectedFrenet trihedron to perform the sweeping. + True = Frenet + False = CorrectedFrenet + """ + ... + + def setTrihedronMode(self, point: Vector, direction: Vector) -> None: + """ + setTrihedronMode(point,direction) + Sets a fixed trihedron to perform the sweeping. + All sections will be parallel. + """ + ... + + def setBiNormalMode(self, direction: Vector) -> None: + """ + setBiNormalMode(direction) + Sets a fixed BiNormal direction to perform the sweeping. + Angular relations between the section(s) and the BiNormal direction will be constant. + """ + ... + + def setSpineSupport(self, shape: TopoShape) -> None: + """ + setSpineSupport(shape) + Sets support to the spine to define the BiNormal of the trihedron, like the normal to the surfaces. + Warning: To be effective, Each edge of the spine must have an representation on one face of SpineSupport. + """ + ... + + def setAuxiliarySpine(self, wire: TopoShape, CurvilinearEquivalence: bool, TypeOfContact: int) -> None: + """ + setAuxiliarySpine(wire, CurvilinearEquivalence, TypeOfContact) + Sets an auxiliary spine to define the Normal. + + CurvilinearEquivalence = bool + For each Point of the Spine P, an Point Q is evalued on AuxiliarySpine. + If CurvilinearEquivalence=True Q split AuxiliarySpine with the same length ratio than P split Spine. + + * OCC >= 6.7 + TypeOfContact = long + 0: No contact + 1: Contact + 2: Contact On Border (The auxiliary spine becomes a boundary of the swept surface) + """ + ... + + @overload + def add(self, Profile: TopoShape, *, WithContact: bool = False, WithCorrection: bool = False) -> None: ... + + @overload + def add(self, Profile: TopoShape, Location: TopoShape, *, WithContact: bool = False, WithCorrection: bool = False) -> None: ... + + def add(self, **kwargs) -> None: + """ + add(shape Profile, bool WithContact=False, bool WithCorrection=False) + add(shape Profile, vertex Location, bool WithContact=False, bool WithCorrection=False) + Adds the section Profile to this framework. + First and last sections may be punctual, so the shape Profile may be both wire and vertex. + If WithContact is true, the section is translated to be in contact with the spine. + If WithCorrection is true, the section is rotated to be orthogonal to the spine tangent in the correspondent point. + """ + ... + + def remove(self, Profile: TopoShape) -> None: + """ + remove(shape Profile) + Removes the section Profile from this framework. + """ + ... + + def isReady(self) -> bool: + """ + isReady() + Returns true if this tool object is ready to build the shape. + """ + ... + + def getStatus(self) -> int: + """ + getStatus() + Get a status, when Simulate or Build failed. + """ + ... + + def makeSolid(self) -> bool: + """ + makeSolid() + Transforms the sweeping Shell in Solid. If a propfile is not closed returns False. + """ + ... + + def setTolerance(self, tol3d: float, boundTol: float, tolAngular: float) -> None: + """ + setTolerance( tol3d, boundTol, tolAngular) + Tol3d = 3D tolerance + BoundTol = boundary tolerance + TolAngular = angular tolerance + """ + ... + + def setTransitionMode(self, mode: int) -> None: + """ + 0: BRepBuilderAPI_Transformed + 1: BRepBuilderAPI_RightCorner + 2: BRepBuilderAPI_RoundCorner + """ + ... + + def firstShape(self) -> TopoShape: + """ + firstShape() + Returns the Shape of the bottom of the sweep. + """ + ... + + def lastShape(self) -> TopoShape: + """ + lastShape() + Returns the Shape of the top of the sweep. + """ + ... + + def build(self) -> None: + """ + build() + Builds the resulting shape. + """ + ... + + def shape(self) -> TopoShape: + """ + shape() + Returns the resulting shape. + """ + ... + + def generated(self, S: TopoShape) -> list[TopoShape]: + """ + generated(shape S) + Returns a list of new shapes generated from the shape S by the shell-generating algorithm. + """ + ... + + def setMaxDegree(self, degree: int) -> None: + """ + setMaxDegree(int degree) + Define the maximum V degree of resulting surface. + """ + ... + + def setMaxSegments(self, num: int) -> None: + """ + setMaxSegments(int num) + Define the maximum number of spans in V-direction on resulting surface. + """ + ... + + def setForceApproxC1(self, flag: bool) -> None: + """ + setForceApproxC1(bool) + Set the flag that indicates attempt to approximate a C1-continuous surface if a swept surface proved to be C0. + """ + ... + + def simulate(self, nbsec: int) -> None: + """ + simulate(int nbsec) + Simulates the resulting shape by calculating the given number of cross-sections. + """ + ... diff --git a/src/Mod/Part/App/BRepOffsetAPI_MakePipeShellPy.xml b/src/Mod/Part/App/BRepOffsetAPI_MakePipeShellPy.xml deleted file mode 100644 index cbf0276268..0000000000 --- a/src/Mod/Part/App/BRepOffsetAPI_MakePipeShellPy.xml +++ /dev/null @@ -1,171 +0,0 @@ - - - - - - Low level API to create a PipeShell using OCC API - -Ref: https://dev.opencascade.org/doc/refman/html/class_b_rep_offset_a_p_i___make_pipe_shell.html - - - - - setFrenetMode(True|False) -Sets a Frenet or a CorrectedFrenet trihedron to perform the sweeping. -True = Frenet -False = CorrectedFrenet - - - - - setTrihedronMode(point,direction) -Sets a fixed trihedron to perform the sweeping. -All sections will be parallel. - - - - - setBiNormalMode(direction) -Sets a fixed BiNormal direction to perform the sweeping. -Angular relations between the section(s) and the BiNormal direction will be constant. - - - - - setSpineSupport(shape) -Sets support to the spine to define the BiNormal of the trihedron, like the normal to the surfaces. -Warning: To be effective, Each edge of the spine must have an representation on one face of SpineSupport. - - - - - setAuxiliarySpine(wire, CurvilinearEquivalence, TypeOfContact) -Sets an auxiliary spine to define the Normal. - -CurvilinearEquivalence = bool -For each Point of the Spine P, an Point Q is evalued on AuxiliarySpine. -If CurvilinearEquivalence=True Q split AuxiliarySpine with the same length ratio than P split Spine. - -* OCC >= 6.7 -TypeOfContact = long -0: No contact -1: Contact -2: Contact On Border (The auxiliary spine becomes a boundary of the swept surface) - - - - - add(shape Profile, bool WithContact=False, bool WithCorrection=False) -add(shape Profile, vertex Location, bool WithContact=False, bool WithCorrection=False) -Adds the section Profile to this framework. -First and last sections may be punctual, so the shape Profile may be both wire and vertex. -If WithContact is true, the section is translated to be in contact with the spine. -If WithCorrection is true, the section is rotated to be orthogonal to the spine tangent in the correspondent point. - - - - - remove(shape Profile) -Removes the section Profile from this framework. - - - - - isReady() -Returns true if this tool object is ready to build the shape. - - - - - getStatus() -Get a status, when Simulate or Build failed. - - - - - makeSolid() -Transforms the sweeping Shell in Solid. If a propfile is not closed returns False. - - - - - setTolerance( tol3d, boundTol, tolAngular) -Tol3d = 3D tolerance -BoundTol = boundary tolerance -TolAngular = angular tolerance - - - - - 0: BRepBuilderAPI_Transformed -1: BRepBuilderAPI_RightCorner -2: BRepBuilderAPI_RoundCorner - - - - - firstShape() -Returns the Shape of the bottom of the sweep. - - - - - lastShape() -Returns the Shape of the top of the sweep. - - - - - build() -Builds the resulting shape. - - - - - shape() -Returns the resulting shape. - - - - - generated(shape S) -Returns a list of new shapes generated from the shape S by the shell-generating algorithm. - - - - - setMaxDegree(int degree) -Define the maximum V degree of resulting surface. - - - - - setMaxSegments(int num) -Define the maximum number of spans in V-direction on resulting surface. - - - - - setForceApproxC1(bool) -Set the flag that indicates attempt to approximate a C1-continuous surface if a swept surface proved to be C0. - - - - - simulate(int nbsec) -Simulates the resulting shape by calculating the given number of cross-sections. - - - - diff --git a/src/Mod/Part/App/BSplineCurve.pyi b/src/Mod/Part/App/BSplineCurve.pyi new file mode 100644 index 0000000000..0ec0d21992 --- /dev/null +++ b/src/Mod/Part/App/BSplineCurve.pyi @@ -0,0 +1,550 @@ +from Base.Metadata import export, constmethod +from Base.Vector import Vector +from BoundedCurve import BoundedCurve +from typing import Final, List, overload + + +@export( + PythonName="Part.BSplineCurve", + Twin="GeomBSplineCurve", + TwinPointer="GeomBSplineCurve", + Include="Mod/Part/App/Geometry.h", + FatherInclude="Mod/Part/App/BoundedCurvePy.h", + Constructor=True, +) +class BSplineCurve(BoundedCurve): + """ + Describes a B-Spline curve in 3D space + + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + Degree: Final[int] = 0 + """Returns the polynomial degree of this B-Spline curve.""" + + MaxDegree: Final[int] = 25 + """ + Returns the value of the maximum polynomial degree of any + B-Spline curve curve. This value is 25. + """ + + NbPoles: Final[int] = 0 + """Returns the number of poles of this B-Spline curve.""" + + NbKnots: Final[int] = 0 + """Returns the number of knots of this B-Spline curve.""" + + StartPoint: Final[Vector] = Vector() + """Returns the start point of this B-Spline curve.""" + + EndPoint: Final[Vector] = Vector() + """Returns the end point of this B-Spline curve.""" + + FirstUKnotIndex: Final[int] = 0 + """ + Returns the index in the knot array of the knot + corresponding to the first or last parameter + of this B-Spline curve. + """ + + LastUKnotIndex: Final[int] = 0 + """ + Returns the index in the knot array of the knot + corresponding to the first or last parameter + of this B-Spline curve. + """ + + KnotSequence: Final[List[float]] = [] + """Returns the knots sequence of this B-Spline curve.""" + + @constmethod + def __reduce__(self) -> Any: + """ + __reduce__() + Serialization of Part.BSplineCurve objects + """ + ... + + @constmethod + def isRational(self) -> bool: + """ + Returns true if this B-Spline curve is rational. + A B-Spline curve is rational if, at the time of construction, + the weight table has been initialized. + """ + ... + + @constmethod + def isPeriodic(self) -> bool: + """ + Returns true if this BSpline curve is periodic. + """ + ... + + @constmethod + def isClosed(self) -> bool: + """ + Returns true if the distance between the start point and end point of + this B-Spline curve is less than or equal to gp::Resolution(). + """ + ... + + def increaseDegree(self, Degree: int = ...) -> None: + """ + increase(Int=Degree) + Increases the degree of this B-Spline curve to Degree. + As a result, the poles, weights and multiplicities tables + are modified; the knots table is not changed. Nothing is + done if Degree is less than or equal to the current degree. + """ + ... + + @overload + def increaseMultiplicity(self, index: int, mult: int) -> None: ... + + @overload + def increaseMultiplicity(self, start: int, end: int, mult: int) -> None: ... + + def increaseMultiplicity(self, *args, **kwargs) -> None: + """ + increaseMultiplicity(int index, int mult) + increaseMultiplicity(int start, int end, int mult) + Increases multiplicity of knots up to mult. + + index: the index of a knot to modify (1-based) + start, end: index range of knots to modify. + If mult is lower or equal to the current multiplicity nothing is done. + If mult is higher than the degree the degree is used. + """ + ... + + def incrementMultiplicity(self, start: int, end: int, mult: int) -> None: + """ + incrementMultiplicity(int start, int end, int mult) + + Raises multiplicity of knots by mult. + + start, end: index range of knots to modify. + """ + ... + + def insertKnot(self, u: float, mult: int = 1, tol: float = 0.0) -> None: + """ + insertKnot(u, mult = 1, tol = 0.0) + + Inserts a knot value in the sequence of knots. If u is an existing knot the + multiplicity is increased by mult. + """ + ... + + def insertKnots( + self, + list_of_floats: List[float], + list_of_ints: List[int], + tol: float = 0.0, + bool_add: bool = True, + ) -> None: + """ + insertKnots(list_of_floats, list_of_ints, tol = 0.0, bool_add = True) + + Inserts a set of knots values in the sequence of knots. + + For each u = list_of_floats[i], mult = list_of_ints[i] + + If u is an existing knot the multiplicity is increased by mult if bool_add is + True, otherwise increased to mult. + + If u is not on the parameter range nothing is done. + + If the multiplicity is negative or null nothing is done. The new multiplicity + is limited to the degree. + + The tolerance criterion for knots equality is the max of Epsilon(U) and ParametricTolerance. + """ + ... + + def removeKnot(self, Index: int, M: int, tol: float) -> bool: + """ + removeKnot(Index, M, tol) + + Reduces the multiplicity of the knot of index Index to M. + If M is equal to 0, the knot is removed. + With a modification of this type, the array of poles is also modified. + Two different algorithms are systematically used to compute the new + poles of the curve. If, for each pole, the distance between the pole + calculated using the first algorithm and the same pole calculated using + the second algorithm, is less than Tolerance, this ensures that the curve + is not modified by more than Tolerance. Under these conditions, true is + returned; otherwise, false is returned. + + A low tolerance is used to prevent modification of the curve. + A high tolerance is used to 'smooth' the curve. + """ + ... + + def segment(self, u1: float, u2: float) -> None: + """ + segment(u1,u2) + + Modifies this B-Spline curve by segmenting it. + """ + ... + + def setKnot(self, knot: float, index: int) -> None: + """ + Set a knot of the B-Spline curve. + """ + ... + + @constmethod + def getKnot(self, index: int) -> float: + """ + Get a knot of the B-Spline curve. + """ + ... + + def setKnots(self, knots: List[float]) -> None: + """ + Set knots of the B-Spline curve. + """ + ... + + @constmethod + def getKnots(self) -> List[float]: + """ + Get all knots of the B-Spline curve. + """ + ... + + def setPole(self, P: Vector, Index: int) -> None: + """ + Modifies this B-Spline curve by assigning P + to the pole of index Index in the poles table. + """ + ... + + @constmethod + def getPole(self, Index: int) -> Vector: + """ + Get a pole of the B-Spline curve. + """ + ... + + @constmethod + def getPoles(self) -> List[Vector]: + """ + Get all poles of the B-Spline curve. + """ + ... + + def setWeight(self, weight: float, index: int) -> None: + """ + Set a weight of the B-Spline curve. + """ + ... + + @constmethod + def getWeight(self, index: int) -> float: + """ + Get a weight of the B-Spline curve. + """ + ... + + @constmethod + def getWeights(self) -> List[float]: + """ + Get all weights of the B-Spline curve. + """ + ... + + @constmethod + def getPolesAndWeights(self) -> List[float]: + """ + Returns the table of poles and weights in homogeneous coordinates. + """ + ... + + @constmethod + def getResolution(self, Tolerance3D: float) -> float: + """ + Computes for this B-Spline curve the parametric tolerance (UTolerance) + for a given 3D tolerance (Tolerance3D). + If f(t) is the equation of this B-Spline curve, the parametric tolerance + ensures that: + |t1-t0| < UTolerance =\"\"==> |f(t1)-f(t0)| < Tolerance3D + """ + ... + + def movePoint( + self, U: float, P: Vector, Index1: int, Index2: int + ) -> tuple[int, int]: + """ + movePoint(U, P, Index1, Index2) + + Moves the point of parameter U of this B-Spline curve to P. + Index1 and Index2 are the indexes in the table of poles of this B-Spline curve + of the first and last poles designated to be moved. + + Returns: (FirstModifiedPole, LastModifiedPole). They are the indexes of the + first and last poles which are effectively modified. + """ + ... + + def setNotPeriodic(self) -> None: + """ + Changes this B-Spline curve into a non-periodic curve. + If this curve is already non-periodic, it is not modified. + """ + ... + + def setPeriodic(self) -> None: + """ + Changes this B-Spline curve into a periodic curve. + """ + ... + + def setOrigin(self, Index: int) -> None: + """ + Assigns the knot of index Index in the knots table + as the origin of this periodic B-Spline curve. As a consequence, + the knots and poles tables are modified. + """ + ... + + @constmethod + def getMultiplicity(self, index: int) -> int: + """ + Returns the multiplicity of the knot of index + from the knots table of this B-Spline curve. + """ + ... + + @constmethod + def getMultiplicities(self) -> List[int]: + """ + Returns the multiplicities table M of the knots of this B-Spline curve. + """ + ... + + @overload + def approximate( + self, + Points: List[Vector], + DegMin: int = 3, + DegMax: int = 8, + Tolerance: float = 1e-3, + Continuity: str = "C2", + LengthWeight: float = 0.0, + CurvatureWeight: float = 0.0, + TorsionWeight: float = 0.0, + Parameters: List[float] = None, + ParamType: str = "Uniform", + ) -> None: ... + + def approximate(self, **kwargs) -> None: + """ + Replaces this B-Spline curve by approximating a set of points. + + The function accepts keywords as arguments. + + approximate(Points = list_of_points) + + Optional arguments : + + DegMin = integer (3) : Minimum degree of the curve. + DegMax = integer (8) : Maximum degree of the curve. + Tolerance = float (1e-3) : approximating tolerance. + Continuity = string ('C2') : Desired continuity of the curve. + Possible values : 'C0','G1','C1','G2','C2','C3','CN' + + LengthWeight = float, CurvatureWeight = float, TorsionWeight = float + If one of these arguments is not null, the functions approximates the + points using variational smoothing algorithm, which tries to minimize + additional criterium: + LengthWeight*CurveLength + CurvatureWeight*Curvature + TorsionWeight*Torsion + Continuity must be C0, C1(with DegMax >= 3) or C2(with DegMax >= 5). + + Parameters = list of floats : knot sequence of the approximated points. + This argument is only used if the weights above are all null. + + ParamType = string ('Uniform','Centripetal' or 'ChordLength') + Parameterization type. Only used if weights and Parameters above aren't specified. + + Note : Continuity of the spline defaults to C2. However, it may not be applied if + it conflicts with other parameters ( especially DegMax ). + """ + ... + + @overload + @constmethod + def getCardinalSplineTangents(self, **kwargs) -> List[Vector]: ... + + @constmethod + def getCardinalSplineTangents(self, **kwargs) -> List[Vector]: + """ + Compute the tangents for a Cardinal spline + """ + ... + + @overload + def interpolate( + self, + Points: List[Vector], + PeriodicFlag: bool = False, + Tolerance: float = 1e-6, + Parameters: List[float] = None, + InitialTangent: Vector = None, + FinalTangent: Vector = None, + Tangents: List[Vector] = None, + TangentFlags: List[bool] = None, + ) -> None: ... + + def interpolate(self, **kwargs) -> None: + """ + Replaces this B-Spline curve by interpolating a set of points. + + The function accepts keywords as arguments. + + interpolate(Points = list_of_points) + + Optional arguments : + + PeriodicFlag = bool (False) : Sets the curve closed or opened. + Tolerance = float (1e-6) : interpolating tolerance + + Parameters : knot sequence of the interpolated points. + If not supplied, the function defaults to chord-length parameterization. + If PeriodicFlag == True, one extra parameter must be appended. + + EndPoint Tangent constraints : + + InitialTangent = vector, FinalTangent = vector + specify tangent vectors for starting and ending points + of the BSpline. Either none, or both must be specified. + + Full Tangent constraints : + + Tangents = list_of_vectors, TangentFlags = list_of_bools + Both lists must have the same length as Points list. + Tangents specifies the tangent vector of each point in Points list. + TangentFlags (bool) activates or deactivates the corresponding tangent. + These arguments will be ignored if EndPoint Tangents (above) are also defined. + + Note : Continuity of the spline defaults to C2. However, if periodic, or tangents + are supplied, the continuity will drop to C1. + """ + ... + + def buildFromPoles( + self, + poles: List[Vector], + periodic: bool = False, + degree: int = 3, + interpolate: bool = False, + ) -> None: + """ + Builds a B-Spline by a list of poles. + arguments: poles (sequence of Base.Vector), [periodic (default is False), degree (default is 3), interpolate (default is False)] + + Examples: + from FreeCAD import Base + import Part + V = Base.Vector + poles = [V(-2, 2, 0),V(0, 2, 1),V(2, 2, 0),V(2, -2, 0),V(0, -2, 1),V(-2, -2, 0)] + + # non-periodic spline + n=Part.BSplineCurve() + n.buildFromPoles(poles) + Part.show(n.toShape()) + + # periodic spline + n=Part.BSplineCurve() + n.buildFromPoles(poles, True) + Part.show(n.toShape()) + """ + ... + + @overload + def buildFromPolesMultsKnots( + self, + poles: List[Vector], + mults: List[int], + knots: List[float], + periodic: bool, + degree: int, + weights: List[float] = None, + CheckRational: bool = False, + ) -> None: ... + + def buildFromPolesMultsKnots(self, **kwargs) -> None: + """ + Builds a B-Spline by a lists of Poles, Mults, Knots. + arguments: poles (sequence of Base.Vector), [mults , knots, periodic, degree, weights (sequence of float), CheckRational] + + Examples: + from FreeCAD import Base + import Part + V=Base.Vector + poles=[V(-10,-10),V(10,-10),V(10,10),V(-10,10)] + + # non-periodic spline + n=Part.BSplineCurve() + n.buildFromPolesMultsKnots(poles,(3,1,3),(0,0.5,1),False,2) + Part.show(n.toShape()) + + # periodic spline + p=Part.BSplineCurve() + p.buildFromPolesMultsKnots(poles,(1,1,1,1,1),(0,0.25,0.5,0.75,1),True,2) + Part.show(p.toShape()) + + # periodic and rational spline + r=Part.BSplineCurve() + r.buildFromPolesMultsKnots(poles,(1,1,1,1,1),(0,0.25,0.5,0.75,1),True,2,(1,0.8,0.7,0.2)) + Part.show(r.toShape()) + """ + ... + + @constmethod + def toBezier(self) -> List["BezierCurve"]: + """ + Build a list of Bezier splines. + """ + ... + + @constmethod + def toBiArcs(self, tolerance: float) -> List["Arc"]: + """ + Build a list of arcs and lines to approximate the B-spline. + toBiArcs(tolerance) -> list. + """ + ... + + def join(self, other: "BSplineCurve") -> None: + """ + Build a new spline by joining this and a second spline. + """ + ... + + def makeC1Continuous( + self, tol: float = 1e-6, ang_tol: float = 1e-7 + ) -> "BSplineCurve": + """ + makeC1Continuous(tol = 1e-6, ang_tol = 1e-7) + Reduces as far as possible the multiplicities of the knots of this BSpline + (keeping the geometry). It returns a new BSpline, which could still be C0. + tol is a geometrical tolerance. + The tol_ang is angular tolerance, in radians. It sets tolerable angle mismatch + of the tangents on the left and on the right to decide if the curve is G1 or + not at a given point. + """ + ... + + def scaleKnotsToBounds(self, u0: float = 0.0, u1: float = 1.0) -> None: + """ + Scales the knots list to fit the specified bounds. + The shape of the curve is not modified. + bspline_curve.scaleKnotsToBounds(u0, u1) + Default arguments are (0.0, 1.0) + """ + ... diff --git a/src/Mod/Part/App/BSplineCurvePy.xml b/src/Mod/Part/App/BSplineCurvePy.xml deleted file mode 100644 index 393213c205..0000000000 --- a/src/Mod/Part/App/BSplineCurvePy.xml +++ /dev/null @@ -1,469 +0,0 @@ - - - - - - Describes a B-Spline curve in 3D space - - - - __reduce__() -Serialization of Part.BSplineCurve objects - - - - - - Returns the polynomial degree of this B-Spline curve. - - - - - - Returns the value of the maximum polynomial degree of any -B-Spline curve curve. This value is 25. - - - - - - Returns the number of poles of this B-Spline curve. - - - - - - - - Returns the number of knots of this B-Spline curve. - - - - - - - Returns the start point of this B-Spline curve. - - - - - - Returns the end point of this B-Spline curve. - - - - - - Returns the index in the knot array of the knot -corresponding to the first or last parameter -of this B-Spline curve. - - - - - - Returns the index in the knot array of the knot -corresponding to the first or last parameter -of this B-Spline curve. - - - - - - Returns the knots sequence of this B-Spline curve. - - - - - - Returns true if this B-Spline curve is rational. -A B-Spline curve is rational if, at the time of construction, -the weight table has been initialized. - - - - - - Returns true if this BSpline curve is periodic. - - - - - Returns true if the distance between the start point and end point of -this B-Spline curve is less than or equal to gp::Resolution(). - - - - - - increase(Int=Degree) -Increases the degree of this B-Spline curve to Degree. -As a result, the poles, weights and multiplicities tables -are modified; the knots table is not changed. Nothing is -done if Degree is less than or equal to the current degree. - - - - - increaseMultiplicity(int index, int mult) -increaseMultiplicity(int start, int end, int mult) -Increases multiplicity of knots up to mult. - -index: the index of a knot to modify (1-based) -start, end: index range of knots to modify. -If mult is lower or equal to the current multiplicity nothing is done. -If mult is higher than the degree the degree is used. - - - - - - incrementMultiplicity(int start, int end, int mult) - -Raises multiplicity of knots by mult. - -start, end: index range of knots to modify. - - - - - - insertKnot(u, mult = 1, tol = 0.0) - -Inserts a knot value in the sequence of knots. If u is an existing knot the -multiplicity is increased by mult. - - - - - insertKnots(list_of_floats, list_of_ints, tol = 0.0, bool_add = True) - -Inserts a set of knots values in the sequence of knots. - -For each u = list_of_floats[i], mult = list_of_ints[i] - -If u is an existing knot the multiplicity is increased by mult if bool_add is -True, otherwise increased to mult. - -If u is not on the parameter range nothing is done. - -If the multiplicity is negative or null nothing is done. The new multiplicity -is limited to the degree. - -The tolerance criterion for knots equality is the max of Epsilon(U) and ParametricTolerance. - - - - - - removeKnot(Index, M, tol) - -Reduces the multiplicity of the knot of index Index to M. -If M is equal to 0, the knot is removed. -With a modification of this type, the array of poles is also modified. -Two different algorithms are systematically used to compute the new -poles of the curve. If, for each pole, the distance between the pole -calculated using the first algorithm and the same pole calculated using -the second algorithm, is less than Tolerance, this ensures that the curve -is not modified by more than Tolerance. Under these conditions, true is -returned; otherwise, false is returned. - -A low tolerance is used to prevent modification of the curve. -A high tolerance is used to 'smooth' the curve. - - - - - - segment(u1,u2) - -Modifies this B-Spline curve by segmenting it. - - - - - Set a knot of the B-Spline curve. - - - - - Get a knot of the B-Spline curve. - - - - - Set knots of the B-Spline curve. - - - - - Get all knots of the B-Spline curve. - - - - - Modifies this B-Spline curve by assigning P -to the pole of index Index in the poles table. - - - - - Get a pole of the B-Spline curve. - - - - - Get all poles of the B-Spline curve. - - - - - Set a weight of the B-Spline curve. - - - - - Get a weight of the B-Spline curve. - - - - - Get all weights of the B-Spline curve. - - - - - Returns the table of poles and weights in homogeneous coordinates. - - - - - Computes for this B-Spline curve the parametric tolerance (UTolerance) -for a given 3D tolerance (Tolerance3D). -If f(t) is the equation of this B-Spline curve, the parametric tolerance -ensures that: -|t1-t0| < UTolerance =""==> |f(t1)-f(t0)| < Tolerance3D - - - - - movePoint(U, P, Index1, Index2) - -Moves the point of parameter U of this B-Spline curve to P. -Index1 and Index2 are the indexes in the table of poles of this B-Spline curve -of the first and last poles designated to be moved. - -Returns: (FirstModifiedPole, LastModifiedPole). They are the indexes of the -first and last poles which are effectively modified. - - - - - Changes this B-Spline curve into a non-periodic curve. -If this curve is already non-periodic, it is not modified. - - - - - Changes this B-Spline curve into a periodic curve. - - - - - Assigns the knot of index Index in the knots table -as the origin of this periodic B-Spline curve. As a consequence, -the knots and poles tables are modified. - - - - - Returns the multiplicity of the knot of index -from the knots table of this B-Spline curve. - - - - - - Returns the multiplicities table M of the knots of this B-Spline curve. - - - - - - Replaces this B-Spline curve by approximating a set of points. - -The function accepts keywords as arguments. - -approximate(Points = list_of_points) - -Optional arguments : - -DegMin = integer (3) : Minimum degree of the curve. -DegMax = integer (8) : Maximum degree of the curve. -Tolerance = float (1e-3) : approximating tolerance. -Continuity = string ('C2') : Desired continuity of the curve. -Possible values : 'C0','G1','C1','G2','C2','C3','CN' - -LengthWeight = float, CurvatureWeight = float, TorsionWeight = float -If one of these arguments is not null, the functions approximates the -points using variational smoothing algorithm, which tries to minimize -additional criterium: -LengthWeight*CurveLength + CurvatureWeight*Curvature + TorsionWeight*Torsion - Continuity must be C0, C1(with DegMax >= 3) or C2(with DegMax >= 5). - -Parameters = list of floats : knot sequence of the approximated points. -This argument is only used if the weights above are all null. - -ParamType = string ('Uniform','Centripetal' or 'ChordLength') -Parameterization type. Only used if weights and Parameters above aren't specified. - -Note : Continuity of the spline defaults to C2. However, it may not be applied if -it conflicts with other parameters ( especially DegMax ). - - - - - - Compute the tangents for a Cardinal spline - - - - - Replaces this B-Spline curve by interpolating a set of points. - -The function accepts keywords as arguments. - -interpolate(Points = list_of_points) - -Optional arguments : - -PeriodicFlag = bool (False) : Sets the curve closed or opened. -Tolerance = float (1e-6) : interpolating tolerance - -Parameters : knot sequence of the interpolated points. -If not supplied, the function defaults to chord-length parameterization. -If PeriodicFlag == True, one extra parameter must be appended. - -EndPoint Tangent constraints : - -InitialTangent = vector, FinalTangent = vector -specify tangent vectors for starting and ending points -of the BSpline. Either none, or both must be specified. - -Full Tangent constraints : - -Tangents = list_of_vectors, TangentFlags = list_of_bools -Both lists must have the same length as Points list. -Tangents specifies the tangent vector of each point in Points list. -TangentFlags (bool) activates or deactivates the corresponding tangent. -These arguments will be ignored if EndPoint Tangents (above) are also defined. - -Note : Continuity of the spline defaults to C2. However, if periodic, or tangents -are supplied, the continuity will drop to C1. - - - - - - Builds a B-Spline by a list of poles. -arguments: poles (sequence of Base.Vector), [periodic (default is False), degree (default is 3), interpolate (default is False)] - -Examples: -from FreeCAD import Base -import Part -V = Base.Vector -poles = [V(-2, 2, 0),V(0, 2, 1),V(2, 2, 0),V(2, -2, 0),V(0, -2, 1),V(-2, -2, 0)] - -# non-periodic spline -n=Part.BSplineCurve() -n.buildFromPoles(poles) -Part.show(n.toShape()) - -# periodic spline -n=Part.BSplineCurve() -n.buildFromPoles(poles, True) -Part.show(n.toShape()) - - - - - - Builds a B-Spline by a lists of Poles, Mults, Knots. -arguments: poles (sequence of Base.Vector), [mults , knots, periodic, degree, weights (sequence of float), CheckRational] - -Examples: -from FreeCAD import Base -import Part -V=Base.Vector -poles=[V(-10,-10),V(10,-10),V(10,10),V(-10,10)] - -# non-periodic spline -n=Part.BSplineCurve() -n.buildFromPolesMultsKnots(poles,(3,1,3),(0,0.5,1),False,2) -Part.show(n.toShape()) - -# periodic spline -p=Part.BSplineCurve() -p.buildFromPolesMultsKnots(poles,(1,1,1,1,1),(0,0.25,0.5,0.75,1),True,2) -Part.show(p.toShape()) - -# periodic and rational spline -r=Part.BSplineCurve() -r.buildFromPolesMultsKnots(poles,(1,1,1,1,1),(0,0.25,0.5,0.75,1),True,2,(1,0.8,0.7,0.2)) -Part.show(r.toShape()) - - - - - - Build a list of Bezier splines. - - - - - - Build a list of arcs and lines to approximate the B-spline. -toBiArcs(tolerance) -> list. - - - - - - Build a new spline by joining this and a second spline. - - - - - - makeC1Continuous(tol = 1e-6, ang_tol = 1e-7) -Reduces as far as possible the multiplicities of the knots of this BSpline -(keeping the geometry). It returns a new BSpline, which could still be C0. -tol is a geometrical tolerance. -The tol_ang is angular tolerance, in radians. It sets tolerable angle mismatch -of the tangents on the left and on the right to decide if the curve is G1 or -not at a given point. - - - - - - -Scales the knots list to fit the specified bounds. -The shape of the curve is not modified. -bspline_curve.scaleKnotsToBounds(u0, u1) -Default arguments are (0.0, 1.0) - - - - - diff --git a/src/Mod/Part/App/BSplineSurface.pyi b/src/Mod/Part/App/BSplineSurface.pyi new file mode 100644 index 0000000000..1772556793 --- /dev/null +++ b/src/Mod/Part/App/BSplineSurface.pyi @@ -0,0 +1,747 @@ +from Base.Metadata import export, constmethod +from GeometrySurface import GeometrySurface +from typing import Final, List, Any + + +@export( + PythonName="Part.BSplineSurface", + Twin="GeomBSplineSurface", + TwinPointer="GeomBSplineSurface", + Include="Mod/Part/App/Geometry.h", + FatherInclude="Mod/Part/App/GeometrySurfacePy.h", + Constructor=True, +) +class BSplineSurface(GeometrySurface): + """ + Describes a B-Spline surface in 3D space + + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + DeveloperDocu: Describes a B-Spline surface in 3D space + """ + + UDegree: Final[int] = 0 + """Returns the degree of this B-Spline surface in the u parametric direction.""" + + VDegree: Final[int] = 0 + """Returns the degree of this B-Spline surface in the v parametric direction.""" + + MaxDegree: Final[int] = 25 + """ + Returns the value of the maximum polynomial degree of any + B-Spline surface surface in either parametric directions. + This value is 25. + """ + + NbUPoles: Final[int] = 0 + """Returns the number of poles of this B-Spline surface in the u parametric direction.""" + + NbVPoles: Final[int] = 0 + """Returns the number of poles of this B-Spline surface in the v parametric direction.""" + + NbUKnots: Final[int] = 0 + """Returns the number of knots of this B-Spline surface in the u parametric direction.""" + + NbVKnots: Final[int] = 0 + """Returns the number of knots of this B-Spline surface in the v parametric direction.""" + + FirstUKnotIndex: Final[Any] = None + """ + Returns the index in the knot array associated with the u parametric direction, + which corresponds to the first parameter of this B-Spline surface in the specified + parametric direction. + + The isoparametric curves corresponding to these values are the boundary curves of + this surface. + + Note: The index does not correspond to the first knot of the surface in the specified + parametric direction unless the multiplicity of the first knot is equal to Degree + 1, + where Degree is the degree of this surface in the corresponding parametric direction. + """ + + LastUKnotIndex: Final[Any] = None + """ + Returns the index in the knot array associated with the u parametric direction, + which corresponds to the last parameter of this B-Spline surface in the specified + parametric direction. + + The isoparametric curves corresponding to these values are the boundary curves of + this surface. + + Note: The index does not correspond to the first knot of the surface in the specified + parametric direction unless the multiplicity of the last knot is equal to Degree + 1, + where Degree is the degree of this surface in the corresponding parametric direction. + """ + + FirstVKnotIndex: Final[Any] = None + """ + Returns the index in the knot array associated with the v parametric direction, + which corresponds to the first parameter of this B-Spline surface in the specified + parametric direction. + + The isoparametric curves corresponding to these values are the boundary curves of + this surface. + + Note: The index does not correspond to the first knot of the surface in the specified + parametric direction unless the multiplicity of the first knot is equal to Degree + 1, + where Degree is the degree of this surface in the corresponding parametric direction. + """ + + LastVKnotIndex: Final[Any] = None + """ + Returns the index in the knot array associated with the v parametric direction, + which corresponds to the last parameter of this B-Spline surface in the specified + parametric direction. + + The isoparametric curves corresponding to these values are the boundary curves of + this surface. + + Note: The index does not correspond to the first knot of the surface in the specified + parametric direction unless the multiplicity of the last knot is equal to Degree + 1, + where Degree is the degree of this surface in the corresponding parametric direction. + """ + + UKnotSequence: Final[List[Any]] = [] + """ + Returns the knots sequence of this B-Spline surface in + the u direction. + """ + + VKnotSequence: Final[List[Any]] = [] + """ + Returns the knots sequence of this B-Spline surface in + the v direction. + """ + + @constmethod + def bounds(self) -> Any: + """ + Returns the parametric bounds (U1, U2, V1, V2) of this B-Spline surface. + """ + ... + + @constmethod + def isURational(self) -> Any: + """ + Returns false if the equation of this B-Spline surface is polynomial + (e.g. non-rational) in the u or v parametric direction. + In other words, returns false if for each row of poles, the associated + weights are identical + """ + ... + + @constmethod + def isVRational(self) -> Any: + """ + Returns false if the equation of this B-Spline surface is polynomial + (e.g. non-rational) in the u or v parametric direction. + In other words, returns false if for each column of poles, the associated + weights are identical + """ + ... + + @constmethod + def isUPeriodic(self) -> Any: + """ + Returns true if this surface is periodic in the u parametric direction. + """ + ... + + @constmethod + def isVPeriodic(self) -> Any: + """ + Returns true if this surface is periodic in the v parametric direction. + """ + ... + + @constmethod + def isUClosed(self) -> Any: + """ + Checks if this surface is closed in the u parametric direction. + Returns true if, in the table of poles the first row and the last + row are identical. + """ + ... + + @constmethod + def isVClosed(self) -> Any: + """ + Checks if this surface is closed in the v parametric direction. + Returns true if, in the table of poles the first column and the + last column are identical. + """ + ... + + def increaseDegree( + self, + DegMin: int, + DegMax: int, + Continuity: int, + Tolerance: float, + X0: float = ..., + dX: float = ..., + Y0: float = ..., + dY: float = ..., + ) -> None: + """ + increase(Int=UDegree, int=VDegree) + Increases the degrees of this B-Spline surface to UDegree and VDegree + in the u and v parametric directions respectively. + As a result, the tables of poles, weights and multiplicities are modified. + The tables of knots is not changed. + + Note: Nothing is done if the given degree is less than or equal to the + current degree in the corresponding parametric direction. + """ + ... + + def increaseUMultiplicity(self) -> None: + """ + Increases the multiplicity in the u direction. + """ + ... + + def increaseVMultiplicity(self) -> None: + """ + Increases the multiplicity in the v direction. + """ + ... + + def incrementUMultiplicity(self) -> None: + """ + Increment the multiplicity in the u direction + """ + ... + + def incrementVMultiplicity(self) -> None: + """ + Increment the multiplicity in the v direction + """ + ... + + def insertUKnot(self, U: float, Index: int, Tolerance: float) -> None: + """ + insertUKnote(float U, int Index, float Tolerance) - Insert or override a knot + """ + ... + + def insertUKnots(self, U: List[float], Mult: List[float], Tolerance: float) -> None: + """ + insertUKnote(List of float U, List of float Mult, float Tolerance) - Inserts knots. + """ + ... + + def insertVKnot(self, V: float, Index: int, Tolerance: float) -> None: + """ + insertUKnote(float V, int Index, float Tolerance) - Insert or override a knot. + """ + ... + + def insertVKnots(self, V: List[float], Mult: List[float], Tolerance: float) -> None: + """ + insertUKnote(List of float V, List of float Mult, float Tolerance) - Inserts knots. + """ + ... + + def removeUKnot(self, M: int, Index: int, Tolerance: float) -> bool: + """ + Reduces to M the multiplicity of the knot of index Index in the given + parametric direction. If M is 0, the knot is removed. + With a modification of this type, the table of poles is also modified. + Two different algorithms are used systematically to compute the new + poles of the surface. For each pole, the distance between the pole + calculated using the first algorithm and the same pole calculated using + the second algorithm, is checked. If this distance is less than Tolerance + it ensures that the surface is not modified by more than Tolerance. + Under these conditions, the function returns true; otherwise, it returns + false. + + A low tolerance prevents modification of the surface. A high tolerance + 'smoothes' the surface. + """ + ... + + def removeVKnot(self, M: int, Index: int, Tolerance: float) -> bool: + """ + Reduces to M the multiplicity of the knot of index Index in the given + parametric direction. If M is 0, the knot is removed. + With a modification of this type, the table of poles is also modified. + Two different algorithms are used systematically to compute the new + poles of the surface. For each pole, the distance between the pole + calculated using the first algorithm and the same pole calculated using + the second algorithm, is checked. If this distance is less than Tolerance + it ensures that the surface is not modified by more than Tolerance. + Under these conditions, the function returns true; otherwise, it returns + false. + + A low tolerance prevents modification of the surface. A high tolerance + 'smoothes' the surface. + """ + ... + + def segment(self, U1: float, U2: float, V1: float, V2: float) -> None: + """ + Modifies this B-Spline surface by segmenting it between U1 and U2 in the + u parametric direction and between V1 and V2 in the v parametric direction. + Any of these values can be outside the bounds of this surface, but U2 must + be greater than U1 and V2 must be greater than V1. + + All the data structure tables of this B-Spline surface are modified but the + knots located between U1 and U2 in the u parametric direction, and between + V1 and V2 in the v parametric direction are retained. + The degree of the surface in each parametric direction is not modified. + """ + ... + + def setUKnot(self, K: float, UIndex: int, M: int = ...) -> None: + """ + Modifies this B-Spline surface by assigning the value K to the knot of index + UIndex of the knots table corresponding to the u parametric direction. + This modification remains relatively local, since K must lie between the values + of the knots which frame the modified knot. + + You can also increase the multiplicity of the modified knot to M. Note however + that it is not possible to decrease the multiplicity of a knot with this function. + """ + ... + + def setVKnot(self, K: float, VIndex: int, M: int = ...) -> None: + """ + Modifies this B-Spline surface by assigning the value K to the knot of index + VIndex of the knots table corresponding to the v parametric direction. + This modification remains relatively local, since K must lie between the values + of the knots which frame the modified knot. + + You can also increase the multiplicity of the modified knot to M. Note however + that it is not possible to decrease the multiplicity of a knot with this function. + """ + ... + + @constmethod + def getUKnot(self, UIndex: int) -> Any: + """ + Returns, for this B-Spline surface, in the u parametric direction + the knot of index UIndex of the knots table. + """ + ... + + @constmethod + def getVKnot(self, VIndex: int) -> Any: + """ + Returns, for this B-Spline surface, in the v parametric direction + the knot of index VIndex of the knots table. + """ + ... + + def setUKnots(self, knots: List[Any]) -> None: + """ + Changes all knots of this B-Spline surface in the u parametric + direction. The multiplicity of the knots is not modified. + """ + ... + + def setVKnots(self, knots: List[Any]) -> None: + """ + Changes all knots of this B-Spline surface in the v parametric + direction. The multiplicity of the knots is not modified. + """ + ... + + @constmethod + def getUKnots(self) -> List[Any]: + """ + Returns, for this B-Spline surface, the knots table + in the u parametric direction + """ + ... + + @constmethod + def getVKnots(self) -> List[Any]: + """ + Returns, for this B-Spline surface, the knots table + in the v parametric direction + """ + ... + + def setPole(self, P: Any, UIndex: int, VIndex: int, Weight: float = ...) -> None: + """ + Modifies this B-Spline surface by assigning P to the pole of + index (UIndex, VIndex) in the poles table. + The second syntax allows you also to change the weight of the + modified pole. The weight is set to Weight. This syntax must + only be used for rational surfaces. + Modifies this B-Spline curve by assigning P to the pole of + index Index in the poles table. + """ + ... + + def setPoleCol( + self, VIndex: int, values: List[Any], CPoleWeights: List[float] + ) -> None: + """ + Modifies this B-Spline surface by assigning values to all or part + of the column of poles of index VIndex, of this B-Spline surface. + You can also change the weights of the modified poles. The weights + are set to the corresponding values of CPoleWeights. + These syntaxes must only be used for rational surfaces. + """ + ... + + def setPoleRow( + self, UIndex: int, values: List[Any], CPoleWeights: List[float] + ) -> None: + """ + Modifies this B-Spline surface by assigning values to all or part + of the row of poles of index UIndex, of this B-Spline surface. + You can also change the weights of the modified poles. The weights + are set to the corresponding values of CPoleWeights. + These syntaxes must only be used for rational surfaces. + """ + ... + + @constmethod + def getPole(self, UIndex: int, VIndex: int) -> Any: + """ + Returns the pole of index (UIndex,VIndex) of this B-Spline surface. + """ + ... + + @constmethod + def getPoles(self) -> List[Any]: + """ + Returns the table of poles of this B-Spline surface. + """ + ... + + def setWeight(self, Weight: float, UIndex: int, VIndex: int) -> None: + """ + Modifies this B-Spline surface by assigning the value Weight to the weight + of the pole of index (UIndex, VIndex) in the poles tables of this B-Spline + surface. + + This function must only be used for rational surfaces. + """ + ... + + def setWeightCol(self, VIndex: int, CPoleWeights: List[float]) -> None: + """ + Modifies this B-Spline surface by assigning values to all or part of the + weights of the column of poles of index VIndex of this B-Spline surface. + + The modified part of the column of weights is defined by the bounds + of the array CPoleWeights. + + This function must only be used for rational surfaces. + """ + ... + + def setWeightRow(self, UIndex: int, CPoleWeights: List[float]) -> None: + """ + Modifies this B-Spline surface by assigning values to all or part of the + weights of the row of poles of index UIndex of this B-Spline surface. + + The modified part of the row of weights is defined by the bounds of the + array CPoleWeights. + + This function must only be used for rational surfaces. + """ + ... + + @constmethod + def getWeight(self, UIndex: int, VIndex: int) -> float: + """ + Return the weight of the pole of index (UIndex,VIndex) + in the poles table for this B-Spline surface. + """ + ... + + @constmethod + def getWeights(self) -> List[float]: + """ + Returns the table of weights of the poles for this B-Spline surface. + """ + ... + + @constmethod + def getPolesAndWeights(self) -> List[Any]: + """ + Returns the table of poles and weights in homogeneous coordinates. + """ + ... + + @constmethod + def getResolution(self, Tolerance3D: float) -> Any: + """ + Computes two tolerance values for this B-Spline surface, based on the + given tolerance in 3D space Tolerance3D. The tolerances computed are: + -- UTolerance in the u parametric direction and + -- VTolerance in the v parametric direction. + + If f(u,v) is the equation of this B-Spline surface, UTolerance and + VTolerance guarantee that: + |u1 - u0| < UTolerance + |v1 - v0| < VTolerance + ====> ||f(u1, v1) - f(u2, v2)|| < Tolerance3D + """ + ... + + def movePoint( + self, + U: float, + V: float, + P: Any, + UIndex1: int = ..., + UIndex2: int = ..., + VIndex1: int = ..., + VIndex2: int = ..., + ) -> Any: + """ + Moves the point of parameters (U, V) of this B-Spline surface to P. + UIndex1, UIndex2, VIndex1 and VIndex2 are the indexes in the poles + table of this B-Spline surface, of the first and last poles which + can be moved in each parametric direction. + The returned indexes UFirstIndex, ULastIndex, VFirstIndex and + VLastIndex are the indexes of the first and last poles effectively + modified in each parametric direction. + In the event of incompatibility between UIndex1, UIndex2, VIndex1, + VIndex2 and the values U and V: + -- no change is made to this B-Spline surface, and + -- UFirstIndex, ULastIndex, VFirstIndex and VLastIndex are set to + null. + """ + ... + + def setUNotPeriodic(self) -> None: + """ + Changes this B-Spline surface into a non-periodic one in the u parametric direction. + If this B-Spline surface is already non-periodic in the given parametric direction, + it is not modified. + If this B-Spline surface is periodic in the given parametric direction, the boundaries + of the surface are not given by the first and last rows (or columns) of poles (because + the multiplicity of the first knot and of the last knot in the given parametric direction + are not modified, nor are they equal to Degree+1, where Degree is the degree of this + B-Spline surface in the given parametric direction). Only the function Segment ensures + this property. + + Note: the poles and knots tables are modified. + """ + ... + + def setVNotPeriodic(self) -> None: + """ + Changes this B-Spline surface into a non-periodic one in the v parametric direction. + If this B-Spline surface is already non-periodic in the given parametric direction, + it is not modified. + If this B-Spline surface is periodic in the given parametric direction, the boundaries + of the surface are not given by the first and last rows (or columns) of poles (because + the multiplicity of the first knot and of the last knot in the given parametric direction + are not modified, nor are they equal to Degree+1, where Degree is the degree of this + B-Spline surface in the given parametric direction). Only the function Segment ensures + this property. + + Note: the poles and knots tables are modified. + """ + ... + + def setUPeriodic(self, I1: int, I2: int) -> None: + """ + Modifies this surface to be periodic in the u parametric direction. + To become periodic in a given parametric direction a surface must + be closed in that parametric direction, and the knot sequence relative + to that direction must be periodic. + To generate this periodic sequence of knots, the functions FirstUKnotIndex + and LastUKnotIndex are used to compute I1 and I2. These are the indexes, + in the knot array associated with the given parametric direction, of the + knots that correspond to the first and last parameters of this B-Spline + surface in the given parametric direction. Hence the period is: + + Knots(I1) - Knots(I2) + + As a result, the knots and poles tables are modified. + """ + ... + + def setVPeriodic(self, I1: int, I2: int) -> None: + """ + Modifies this surface to be periodic in the v parametric direction. + To become periodic in a given parametric direction a surface must + be closed in that parametric direction, and the knot sequence relative + to that direction must be periodic. + To generate this periodic sequence of knots, the functions FirstUKnotIndex + and LastUKnotIndex are used to compute I1 and I2. These are the indexes, + in the knot array associated with the given parametric direction, of the + knots that correspond to the first and last parameters of this B-Spline + surface in the given parametric direction. Hence the period is: + + Knots(I1) - Knots(I2) + + As a result, the knots and poles tables are modified. + """ + ... + + def setUOrigin(self, Index: int) -> None: + """ + Assigns the knot of index Index in the knots table + in the u parametric direction to be the origin of + this periodic B-Spline surface. As a consequence, + the knots and poles tables are modified. + """ + ... + + def setVOrigin(self, Index: int) -> None: + """ + Assigns the knot of index Index in the knots table + in the v parametric direction to be the origin of + this periodic B-Spline surface. As a consequence, + the knots and poles tables are modified. + """ + ... + + @constmethod + def getUMultiplicity(self, UIndex: int) -> Any: + """ + Returns, for this B-Spline surface, the multiplicity of + the knot of index UIndex in the u parametric direction. + """ + ... + + @constmethod + def getVMultiplicity(self, VIndex: int) -> Any: + """ + Returns, for this B-Spline surface, the multiplicity of + the knot of index VIndex in the v parametric direction. + """ + ... + + @constmethod + def getUMultiplicities(self) -> List[Any]: + """ + Returns, for this B-Spline surface, the table of + multiplicities in the u parametric direction + """ + ... + + @constmethod + def getVMultiplicities(self) -> List[Any]: + """ + Returns, for this B-Spline surface, the table of + multiplicities in the v parametric direction + """ + ... + + def exchangeUV(self) -> None: + """ + Exchanges the u and v parametric directions on this B-Spline surface. + As a consequence: + -- the poles and weights tables are transposed, + -- the knots and multiplicities tables are exchanged, + -- degrees of continuity and rational, periodic and uniform + characteristics are exchanged and + -- the orientation of the surface is reversed. + """ + ... + + @constmethod + def reparametrize(self) -> Any: + """ + Returns a reparametrized copy of this surface + """ + ... + + def approximate( + self, + *, + Points: Any = ..., + DegMin: int = ..., + DegMax: int = ..., + Continuity: int = ..., + Tolerance: float = ..., + X0: float = ..., + dX: float = ..., + Y0: float = ..., + dY: float = ..., + ParamType: str = ..., + LengthWeight: float = ..., + CurvatureWeight: float = ..., + TorsionWeight: float = ... + ) -> None: + """ + Replaces this B-Spline surface by approximating a set of points. + This method uses keywords : + - Points = 2Darray of points (or floats, in combination with X0, dX, Y0, dY) + - DegMin (int), DegMax (int) + - Continuity = 0,1 or 2 (for C0, C1, C2) + - Tolerance (float) + - X0, dX, Y0, dY (floats) with Points = 2Darray of floats + - ParamType = 'Uniform','Centripetal' or 'ChordLength' + - LengthWeight, CurvatureWeight, TorsionWeight (floats) + (with this smoothing algorithm, continuity C1 requires DegMax >= 3 and C2, DegMax >=5) + + Possible combinations : + - approximate(Points, DegMin, DegMax, Continuity, Tolerance) + - approximate(Points, DegMin, DegMax, Continuity, Tolerance, X0, dX, Y0, dY) + With explicit keywords : + - approximate(Points, DegMin, DegMax, Continuity, Tolerance, ParamType) + - approximate(Points, DegMax, Continuity, Tolerance, LengthWeight, CurvatureWeight, TorsionWeight) + """ + ... + + def interpolate( + self, + points: Any = ..., + zpoints: Any = ..., + X0: float = ..., + dX: float = ..., + Y0: float = ..., + dY: float = ..., + ) -> None: + """ + interpolate(points) + interpolate(zpoints, X0, dX, Y0, dY) + + Replaces this B-Spline surface by interpolating a set of points. + The resulting surface is of degree 3 and continuity C2. + Arguments: + a 2 dimensional array of vectors, that the surface passes through + or + a 2 dimensional array of floats with the z values, + the x starting point X0 (float), + the x increment dX (float), + the y starting point Y0 and increment dY + """ + ... + + def buildFromPolesMultsKnots( + self, + *, + poles: List[List[Any]], + umults: List[Any], + vmults: List[Any], + uknots: List[Any] = ..., + vknots: List[Any] = ..., + uperiodic: bool = ..., + vperiodic: bool = ..., + udegree: int = ..., + vdegree: int = ..., + weights: List[List[float]] = ... + ) -> None: + """ + Builds a B-Spline by a lists of Poles, Mults and Knots + arguments: poles (sequence of sequence of Base.Vector), umults, vmults, [uknots, vknots, uperiodic, vperiodic, udegree, vdegree, weights (sequence of sequence of float)] + """ + ... + + def buildFromNSections(self, control_curves: Any) -> None: + """ + Builds a B-Spline from a list of control curves + """ + ... + + def scaleKnotsToBounds(self, u0: float, u1: float, v0: float, v1: float) -> None: + """ + Scales the U and V knots lists to fit the specified bounds. + The shape of the surface is not modified. + bspline_surf.scaleKnotsToBounds(u0, u1, v0, v1) + Default arguments are 0.0, 1.0, 0.0, 1.0 + """ + ... diff --git a/src/Mod/Part/App/BSplineSurfacePy.xml b/src/Mod/Part/App/BSplineSurfacePy.xml deleted file mode 100644 index ad2b031310..0000000000 --- a/src/Mod/Part/App/BSplineSurfacePy.xml +++ /dev/null @@ -1,743 +0,0 @@ - - - - - - Describes a B-Spline surface in 3D space - - - - - Returns the degree of this B-Spline surface in the u parametric direction. - - - - - - - - Returns the degree of this B-Spline surface in the v parametric direction. - - - - - - - -Returns the value of the maximum polynomial degree of any -B-Spline surface surface in either parametric directions. -This value is 25. - - - - - - - -Returns the number of poles of this B-Spline surface in the u parametric direction. - - - - - - - -Returns the number of poles of this B-Spline surface in the v parametric direction. - - - - - - - -Returns the number of knots of this B-Spline surface in the u parametric direction. - - - - - - - -Returns the number of knots of this B-Spline surface in the v parametric direction. - - - - - - - -Returns the index in the knot array associated with the u parametric direction, -which corresponds to the first parameter of this B-Spline surface in the specified -parametric direction. - -The isoparametric curves corresponding to these values are the boundary curves of -this surface. - -Note: The index does not correspond to the first knot of the surface in the specified -parametric direction unless the multiplicity of the first knot is equal to Degree + 1, -where Degree is the degree of this surface in the corresponding parametric direction. - - - - - - - -Returns the index in the knot array associated with the u parametric direction, -which corresponds to the last parameter of this B-Spline surface in the specified -parametric direction. - -The isoparametric curves corresponding to these values are the boundary curves of -this surface. - -Note: The index does not correspond to the first knot of the surface in the specified -parametric direction unless the multiplicity of the last knot is equal to Degree + 1, -where Degree is the degree of this surface in the corresponding parametric direction. - - - - - - - -Returns the index in the knot array associated with the v parametric direction, -which corresponds to the first parameter of this B-Spline surface in the specified -parametric direction. - -The isoparametric curves corresponding to these values are the boundary curves of -this surface. - -Note: The index does not correspond to the first knot of the surface in the specified -parametric direction unless the multiplicity of the first knot is equal to Degree + 1, -where Degree is the degree of this surface in the corresponding parametric direction. - - - - - - - -Returns the index in the knot array associated with the v parametric direction, -which corresponds to the last parameter of this B-Spline surface in the specified -parametric direction. - -The isoparametric curves corresponding to these values are the boundary curves of -this surface. - -Note: The index does not correspond to the first knot of the surface in the specified -parametric direction unless the multiplicity of the last knot is equal to Degree + 1, -where Degree is the degree of this surface in the corresponding parametric direction. - - - - - - - -Returns the knots sequence of this B-Spline surface in -the u direction. - - - - - - - -Returns the knots sequence of this B-Spline surface in -the v direction. - - - - - - - -Returns the parametric bounds (U1, U2, V1, V2) of this B-Spline surface. - - - - - - -Returns false if the equation of this B-Spline surface is polynomial -(e.g. non-rational) in the u or v parametric direction. -In other words, returns false if for each row of poles, the associated -weights are identical - - - - - - -Returns false if the equation of this B-Spline surface is polynomial -(e.g. non-rational) in the u or v parametric direction. -In other words, returns false if for each column of poles, the associated -weights are identical - - - - - - Returns true if this surface is periodic in the u parametric direction. - - - - - Returns true if this surface is periodic in the v parametric direction. - - - - - -Checks if this surface is closed in the u parametric direction. -Returns true if, in the table of poles the first row and the last -row are identical. - - - - - - -Checks if this surface is closed in the v parametric direction. -Returns true if, in the table of poles the first column and the -last column are identical. - - - - - - -increase(Int=UDegree, int=VDegree) -Increases the degrees of this B-Spline surface to UDegree and VDegree -in the u and v parametric directions respectively. -As a result, the tables of poles, weights and multiplicities are modified. -The tables of knots is not changed. - -Note: Nothing is done if the given degree is less than or equal to the -current degree in the corresponding parametric direction. - - - - - - Increases the multiplicity in the u direction. - - - - - Increases the multiplicity in the v direction. - - - - - Increment the multiplicity in the u direction - - - - - Increment the multiplicity in the v direction - - - - - insertUKnote(float U, int Index, float Tolerance) - Insert or override a knot - - - - - insertUKnote(List of float U, List of float Mult, float Tolerance) - Inserts knots. - - - - - insertUKnote(float V, int Index, float Tolerance) - Insert or override a knot. - - - - - insertUKnote(List of float V, List of float Mult, float Tolerance) - Inserts knots. - - - - - -Reduces to M the multiplicity of the knot of index Index in the given -parametric direction. If M is 0, the knot is removed. -With a modification of this type, the table of poles is also modified. -Two different algorithms are used systematically to compute the new -poles of the surface. For each pole, the distance between the pole -calculated using the first algorithm and the same pole calculated using -the second algorithm, is checked. If this distance is less than Tolerance -it ensures that the surface is not modified by more than Tolerance. -Under these conditions, the function returns true; otherwise, it returns -false. - -A low tolerance prevents modification of the surface. A high tolerance -'smoothes' the surface. - - - - - - -Reduces to M the multiplicity of the knot of index Index in the given -parametric direction. If M is 0, the knot is removed. -With a modification of this type, the table of poles is also modified. -Two different algorithms are used systematically to compute the new -poles of the surface. For each pole, the distance between the pole -calculated using the first algorithm and the same pole calculated using -the second algorithm, is checked. If this distance is less than Tolerance -it ensures that the surface is not modified by more than Tolerance. -Under these conditions, the function returns true; otherwise, it returns -false. - -A low tolerance prevents modification of the surface. A high tolerance -'smoothes' the surface. - - - - - - -Modifies this B-Spline surface by segmenting it between U1 and U2 in the -u parametric direction and between V1 and V2 in the v parametric direction. -Any of these values can be outside the bounds of this surface, but U2 must -be greater than U1 and V2 must be greater than V1. - -All the data structure tables of this B-Spline surface are modified but the -knots located between U1 and U2 in the u parametric direction, and between -V1 and V2 in the v parametric direction are retained. -The degree of the surface in each parametric direction is not modified. - - - - - - -Modifies this B-Spline surface by assigning the value K to the knot of index -UIndex of the knots table corresponding to the u parametric direction. -This modification remains relatively local, since K must lie between the values -of the knots which frame the modified knot. - -You can also increase the multiplicity of the modified knot to M. Note however -that it is not possible to decrease the multiplicity of a knot with this function. - - - - - - -Modifies this B-Spline surface by assigning the value K to the knot of index -VIndex of the knots table corresponding to the v parametric direction. -This modification remains relatively local, since K must lie between the values -of the knots which frame the modified knot. - -You can also increase the multiplicity of the modified knot to M. Note however -that it is not possible to decrease the multiplicity of a knot with this function. - - - - - - -Returns, for this B-Spline surface, in the u parametric direction -the knot of index UIndex of the knots table. - - - - - - -Returns, for this B-Spline surface, in the v parametric direction -the knot of index VIndex of the knots table. - - - - - - -Changes all knots of this B-Spline surface in the u parametric -direction. The multiplicity of the knots is not modified. - - - - - - -Changes all knots of this B-Spline surface in the v parametric -direction. The multiplicity of the knots is not modified. - - - - - - -Returns, for this B-Spline surface, the knots table -in the u parametric direction - - - - - - -Returns, for this B-Spline surface, the knots table -in the v parametric direction - - - - - - -Modifies this B-Spline surface by assigning P to the pole of -index (UIndex, VIndex) in the poles table. -The second syntax allows you also to change the weight of the -modified pole. The weight is set to Weight. This syntax must -only be used for rational surfaces. -Modifies this B-Spline curve by assigning P to the pole of -index Index in the poles table. - - - - - - -Modifies this B-Spline surface by assigning values to all or part -of the column of poles of index VIndex, of this B-Spline surface. -You can also change the weights of the modified poles. The weights -are set to the corresponding values of CPoleWeights. -These syntaxes must only be used for rational surfaces. - - - - - - -Modifies this B-Spline surface by assigning values to all or part -of the row of poles of index UIndex, of this B-Spline surface. -You can also change the weights of the modified poles. The weights -are set to the corresponding values of CPoleWeights. -These syntaxes must only be used for rational surfaces. - - - - - - - Returns the pole of index (UIndex,VIndex) of this B-Spline surface. - - - - - - Returns the table of poles of this B-Spline surface. - - - - - -Modifies this B-Spline surface by assigning the value Weight to the weight -of the pole of index (UIndex, VIndex) in the poles tables of this B-Spline -surface. - -This function must only be used for rational surfaces. - - - - - - -Modifies this B-Spline surface by assigning values to all or part of the -weights of the column of poles of index VIndex of this B-Spline surface. - -The modified part of the column of weights is defined by the bounds -of the array CPoleWeights. - -This function must only be used for rational surfaces. - - - - - - -Modifies this B-Spline surface by assigning values to all or part of the -weights of the row of poles of index UIndex of this B-Spline surface. - -The modified part of the row of weights is defined by the bounds of the -array CPoleWeights. - -This function must only be used for rational surfaces. - - - - - - -Return the weight of the pole of index (UIndex,VIndex) -in the poles table for this B-Spline surface. - - - - - - Returns the table of weights of the poles for this B-Spline surface. - - - - - Returns the table of poles and weights in homogeneous coordinates. - - - - - -Computes two tolerance values for this B-Spline surface, based on the -given tolerance in 3D space Tolerance3D. The tolerances computed are: --- UTolerance in the u parametric direction and --- VTolerance in the v parametric direction. - -If f(u,v) is the equation of this B-Spline surface, UTolerance and -VTolerance guarantee that: -|u1 - u0| < UTolerance -|v1 - v0| < VTolerance -====> ||f(u1, v1) - f(u2, v2)|| < Tolerance3D - - - - - - -Moves the point of parameters (U, V) of this B-Spline surface to P. -UIndex1, UIndex2, VIndex1 and VIndex2 are the indexes in the poles -table of this B-Spline surface, of the first and last poles which -can be moved in each parametric direction. -The returned indexes UFirstIndex, ULastIndex, VFirstIndex and -VLastIndex are the indexes of the first and last poles effectively -modified in each parametric direction. -In the event of incompatibility between UIndex1, UIndex2, VIndex1, -VIndex2 and the values U and V: --- no change is made to this B-Spline surface, and --- UFirstIndex, ULastIndex, VFirstIndex and VLastIndex are set to - null. - - - - - - -Changes this B-Spline surface into a non-periodic one in the u parametric direction. -If this B-Spline surface is already non-periodic in the given parametric direction, -it is not modified. -If this B-Spline surface is periodic in the given parametric direction, the boundaries -of the surface are not given by the first and last rows (or columns) of poles (because -the multiplicity of the first knot and of the last knot in the given parametric direction -are not modified, nor are they equal to Degree+1, where Degree is the degree of this -B-Spline surface in the given parametric direction). Only the function Segment ensures -this property. - -Note: the poles and knots tables are modified. - - - - - - -Changes this B-Spline surface into a non-periodic one in the v parametric direction. -If this B-Spline surface is already non-periodic in the given parametric direction, -it is not modified. -If this B-Spline surface is periodic in the given parametric direction, the boundaries -of the surface are not given by the first and last rows (or columns) of poles (because -the multiplicity of the first knot and of the last knot in the given parametric direction -are not modified, nor are they equal to Degree+1, where Degree is the degree of this -B-Spline surface in the given parametric direction). Only the function Segment ensures -this property. - -Note: the poles and knots tables are modified. - - - - - - -Modifies this surface to be periodic in the u parametric direction. -To become periodic in a given parametric direction a surface must -be closed in that parametric direction, and the knot sequence relative -to that direction must be periodic. -To generate this periodic sequence of knots, the functions FirstUKnotIndex -and LastUKnotIndex are used to compute I1 and I2. These are the indexes, -in the knot array associated with the given parametric direction, of the -knots that correspond to the first and last parameters of this B-Spline -surface in the given parametric direction. Hence the period is: - -Knots(I1) - Knots(I2) - -As a result, the knots and poles tables are modified. - - - - - - -Modifies this surface to be periodic in the v parametric direction. -To become periodic in a given parametric direction a surface must -be closed in that parametric direction, and the knot sequence relative -to that direction must be periodic. -To generate this periodic sequence of knots, the functions FirstUKnotIndex -and LastUKnotIndex are used to compute I1 and I2. These are the indexes, -in the knot array associated with the given parametric direction, of the -knots that correspond to the first and last parameters of this B-Spline -surface in the given parametric direction. Hence the period is: - -Knots(I1) - Knots(I2) - -As a result, the knots and poles tables are modified. - - - - - - -Assigns the knot of index Index in the knots table -in the u parametric direction to be the origin of -this periodic B-Spline surface. As a consequence, -the knots and poles tables are modified. - - - - - - -Assigns the knot of index Index in the knots table -in the v parametric direction to be the origin of -this periodic B-Spline surface. As a consequence, -the knots and poles tables are modified. - - - - - - -Returns, for this B-Spline surface, the multiplicity of -the knot of index UIndex in the u parametric direction. - - - - - - -Returns, for this B-Spline surface, the multiplicity of -the knot of index VIndex in the v parametric direction. - - - - - - -Returns, for this B-Spline surface, the table of -multiplicities in the u parametric direction - - - - - - -Returns, for this B-Spline surface, the table of -multiplicities in the v parametric direction - - - - - - -Exchanges the u and v parametric directions on this B-Spline surface. -As a consequence: --- the poles and weights tables are transposed, --- the knots and multiplicities tables are exchanged, --- degrees of continuity and rational, periodic and uniform - characteristics are exchanged and --- the orientation of the surface is reversed. - - - - - - Returns a reparametrized copy of this surface - - - - - -Replaces this B-Spline surface by approximating a set of points. -This method uses keywords : -- Points = 2Darray of points (or floats, in combination with X0, dX, Y0, dY) -- DegMin (int), DegMax (int) -- Continuity = 0,1 or 2 (for C0, C1, C2) -- Tolerance (float) -- X0, dX, Y0, dY (floats) with Points = 2Darray of floats -- ParamType = 'Uniform','Centripetal' or 'ChordLength' -- LengthWeight, CurvatureWeight, TorsionWeight (floats) -(with this smoothing algorithm, continuity C1 requires DegMax >= 3 and C2, DegMax >=5) - -Possible combinations : -- approximate(Points, DegMin, DegMax, Continuity, Tolerance) -- approximate(Points, DegMin, DegMax, Continuity, Tolerance, X0, dX, Y0, dY) -With explicit keywords : -- approximate(Points, DegMin, DegMax, Continuity, Tolerance, ParamType) -- approximate(Points, DegMax, Continuity, Tolerance, LengthWeight, CurvatureWeight, TorsionWeight) - - - - - - -interpolate(points) -interpolate(zpoints, X0, dX, Y0, dY) - -Replaces this B-Spline surface by interpolating a set of points. -The resulting surface is of degree 3 and continuity C2. -Arguments: -a 2 dimensional array of vectors, that the surface passes through -or -a 2 dimensional array of floats with the z values, -the x starting point X0 (float), -the x increment dX (float), -the y starting point Y0 and increment dY - - - - - - -Builds a B-Spline by a lists of Poles, Mults and Knots -arguments: poles (sequence of sequence of Base.Vector), umults, vmults, [uknots, vknots, uperiodic, vperiodic, udegree, vdegree, weights (sequence of sequence of float)] - - - - - - -Builds a B-Spline from a list of control curves - - - - - - -Scales the U and V knots lists to fit the specified bounds. -The shape of the surface is not modified. -bspline_surf.scaleKnotsToBounds(u0, u1, v0, v1) -Default arguments are 0.0, 1.0, 0.0, 1.0 - - - - - diff --git a/src/Mod/Part/App/BezierCurve.pyi b/src/Mod/Part/App/BezierCurve.pyi new file mode 100644 index 0000000000..b47c8f224d --- /dev/null +++ b/src/Mod/Part/App/BezierCurve.pyi @@ -0,0 +1,179 @@ +from Base.Metadata import export, constmethod +from Base.Vector import Vector +from BoundedCurve import BoundedCurve +from typing import Final, List + + +@export( + Twin="GeomBezierCurve", + TwinPointer="GeomBezierCurve", + PythonName="Part.BezierCurve", + FatherInclude="Mod/Part/App/BoundedCurvePy.h", + Include="Mod/Part/App/Geometry.h", + Constructor=True, +) +class BezierCurve(BoundedCurve): + """ + Describes a rational or non-rational Bezier curve: + -- a non-rational Bezier curve is defined by a table of poles (also called control points) + -- a rational Bezier curve is defined by a table of poles with varying weights + + Constructor takes no arguments. + + Example usage: + p1 = Base.Vector(-1, 0, 0) + p2 = Base.Vector(0, 1, 0.2) + p3 = Base.Vector(1, 0, 0.4) + p4 = Base.Vector(0, -1, 1) + + bc = BezierCurve() + bc.setPoles([p1, p2, p3, p4]) + curveShape = bc.toShape() + """ + + Degree: Final[int] = 0 + """ + Returns the polynomial degree of this Bezier curve, + which is equal to the number of poles minus 1. + """ + + MaxDegree: Final[int] = 25 + """ + Returns the value of the maximum polynomial degree of any + Bezier curve curve. This value is 25. + """ + + NbPoles: Final[int] = 0 + """Returns the number of poles of this Bezier curve.""" + + StartPoint: Final[Vector] = Vector() + """Returns the start point of this Bezier curve.""" + + EndPoint: Final[Vector] = Vector() + """Returns the end point of this Bezier curve.""" + + @constmethod + def isRational(self) -> bool: + """ + Returns false if the weights of all the poles of this Bezier curve are equal. + """ + ... + + @constmethod + def isPeriodic(self) -> bool: + """ + Returns false. + """ + ... + + @constmethod + def isClosed(self) -> bool: + """ + Returns true if the distance between the start point and end point of + this Bezier curve is less than or equal to gp::Resolution(). + """ + ... + + def increase(self, Int: int = ...) -> None: + """ + Increases the degree of this Bezier curve to Degree. + As a result, the poles and weights tables are modified. + """ + ... + + def insertPoleAfter(self, index: int) -> None: + """ + Inserts after the pole of index. + """ + ... + + def insertPoleBefore(self, index: int) -> None: + """ + Inserts before the pole of index. + """ + ... + + def removePole(self, Index: int) -> None: + """ + Removes the pole of index Index from the table of poles of this Bezier curve. + If this Bezier curve is rational, it can become non-rational. + """ + ... + + def segment(self) -> None: + """ + Modifies this Bezier curve by segmenting it. + """ + ... + + def setPole(self, pole: Vector) -> None: + """ + Set a pole of the Bezier curve. + """ + ... + + @constmethod + def getPole(self, index: int) -> Vector: + """ + Get a pole of the Bezier curve. + """ + ... + + @constmethod + def getPoles(self) -> List[Vector]: + """ + Get all poles of the Bezier curve. + """ + ... + + def setPoles(self, poles: List[Vector]) -> None: + """ + Set the poles of the Bezier curve. + + Takes a list of 3D Base.Vector objects. + """ + ... + + def setWeight(self, id: int, weight: float) -> None: + """ + (id, weight) Set a weight of the Bezier curve. + """ + ... + + @constmethod + def getWeight(self, id: int) -> float: + """ + Get a weight of the Bezier curve. + """ + ... + + @constmethod + def getWeights(self) -> List[float]: + """ + Get all weights of the Bezier curve. + """ + ... + + @constmethod + def getResolution(self, Tolerance3D: float) -> float: + """ + Computes for this Bezier curve the parametric tolerance (UTolerance) + for a given 3D tolerance (Tolerance3D). + If f(t) is the equation of this Bezier curve, the parametric tolerance + ensures that: + |t1-t0| < UTolerance =\"\"==> |f(t1)-f(t0)| < Tolerance3D + """ + ... + + def interpolate( + self, constraints: List[List], parameters: List[float] = ... + ) -> None: + """ + Interpolates a list of constraints. + Each constraint is a list of a point and some optional derivatives + An optional list of parameters can be passed. It must be of same size as constraint list. + Otherwise, a simple uniform parametrization is used. + Example : + bezier.interpolate([[pt1, deriv11, deriv12], [pt2,], [pt3, deriv31]], [0, 0.4, 1.0]) + """ + ... diff --git a/src/Mod/Part/App/BezierCurvePy.xml b/src/Mod/Part/App/BezierCurvePy.xml deleted file mode 100644 index d496c8d946..0000000000 --- a/src/Mod/Part/App/BezierCurvePy.xml +++ /dev/null @@ -1,165 +0,0 @@ - - - - - - Describes a rational or non-rational Bezier curve: --- a non-rational Bezier curve is defined by a table of poles (also called control points) --- a rational Bezier curve is defined by a table of poles with varying weights - -Constructor takes no arguments. - -Example usage: - p1 = Base.Vector(-1, 0, 0) - p2 = Base.Vector(0, 1, 0.2) - p3 = Base.Vector(1, 0, 0.4) - p4 = Base.Vector(0, -1, 1) - - bc = BezierCurve() - bc.setPoles([p1, p2, p3, p4]) - curveShape = bc.toShape() - - - - - Returns the polynomial degree of this Bezier curve, -which is equal to the number of poles minus 1. - - - - - - Returns the value of the maximum polynomial degree of any -Bezier curve curve. This value is 25. - - - - - - Returns the number of poles of this Bezier curve. - - - - - - Returns the start point of this Bezier curve. - - - - - - Returns the end point of this Bezier curve. - - - - - - Returns false if the weights of all the poles of this Bezier curve are equal. - - - - - Returns false. - - - - - Returns true if the distance between the start point and end point of -this Bezier curve is less than or equal to gp::Resolution(). - - - - - Increases the degree of this Bezier curve to Degree. -As a result, the poles and weights tables are modified. - - - - - Inserts after the pole of index. - - - - - Inserts before the pole of index. - - - - - Removes the pole of index Index from the table of poles of this Bezier curve. -If this Bezier curve is rational, it can become non-rational. - - - - - Modifies this Bezier curve by segmenting it. - - - - - Set a pole of the Bezier curve. - - - - - Get a pole of the Bezier curve. - - - - - Get all poles of the Bezier curve. - - - - - Set the poles of the Bezier curve. - -Takes a list of 3D Base.Vector objects. - - - - - (id, weight) Set a weight of the Bezier curve. - - - - - Get a weight of the Bezier curve. - - - - - Get all weights of the Bezier curve. - - - - - Computes for this Bezier curve the parametric tolerance (UTolerance) -for a given 3D tolerance (Tolerance3D). -If f(t) is the equation of this Bezier curve, the parametric tolerance -ensures that: -|t1-t0| < UTolerance =""==> |f(t1)-f(t0)| < Tolerance3D - - - - - Interpolates a list of constraints. -Each constraint is a list of a point and some optional derivatives -An optional list of parameters can be passed. It must be of same size as constraint list. -Otherwise, a simple uniform parametrization is used. -Example : -bezier.interpolate([[pt1, deriv11, deriv12], [pt2,], [pt3, deriv31]], [0, 0.4, 1.0]) - - - - diff --git a/src/Mod/Part/App/BezierSurface.pyi b/src/Mod/Part/App/BezierSurface.pyi new file mode 100644 index 0000000000..fd899c6379 --- /dev/null +++ b/src/Mod/Part/App/BezierSurface.pyi @@ -0,0 +1,297 @@ +from Base.Metadata import ( + export, + constmethod, + forward_declarations, + class_declarations, + sequence_protocol, +) +from GeometrySurface import GeometrySurface +from typing import Final, Tuple + + +@export( + Twin="GeomBezierSurface", + TwinPointer="GeomBezierSurface", + PythonName="Part.BezierSurface", + FatherInclude="Mod/Part/App/GeometrySurfacePy.h", + Include="Mod/Part/App/Geometry.h", + Constructor=True, +) +class BezierSurface(GeometrySurface): + """ + Describes a rational or non-rational Bezier surface + -- A non-rational Bezier surface is defined by a table of poles (also known as control points). + -- A rational Bezier surface is defined by a table of poles with varying associated weights. + """ + + UDegree: Final[int] + """ + Returns the polynomial degree in u direction of this Bezier surface, + which is equal to the number of poles minus 1. + """ + + VDegree: Final[int] + """ + Returns the polynomial degree in v direction of this Bezier surface, + which is equal to the number of poles minus 1. + """ + + MaxDegree: Final[int] + """ + Returns the value of the maximum polynomial degree of any + Bezier surface. This value is 25. + """ + + NbUPoles: Final[int] + """Returns the number of poles in u direction of this Bezier surface.""" + + NbVPoles: Final[int] + """Returns the number of poles in v direction of this Bezier surface.""" + + @constmethod + def bounds(self) -> Tuple[float, float, float, float]: + """ + Returns the parametric bounds (U1, U2, V1, V2) of this Bezier surface. + """ + ... + + @constmethod + def isURational(self) -> bool: + """ + Returns false if the equation of this Bezier surface is polynomial + (e.g. non-rational) in the u or v parametric direction. + In other words, returns false if for each row of poles, the associated + weights are identical. + """ + ... + + @constmethod + def isVRational(self) -> bool: + """ + Returns false if the equation of this Bezier surface is polynomial + (e.g. non-rational) in the u or v parametric direction. + In other words, returns false if for each column of poles, the associated + weights are identical. + """ + ... + + @constmethod + def isUPeriodic(self) -> bool: + """ + Returns false. + """ + ... + + @constmethod + def isVPeriodic(self) -> bool: + """ + Returns false. + """ + ... + + @constmethod + def isUClosed(self) -> bool: + """ + Checks if this surface is closed in the u parametric direction. + Returns true if, in the table of poles the first row and the last + row are identical. + """ + ... + + @constmethod + def isVClosed(self) -> bool: + """ + Checks if this surface is closed in the v parametric direction. + Returns true if, in the table of poles the first column and the + last column are identical. + """ + ... + + def increase(self, DegreeU: int, DegreeV: int) -> None: + """ + increase(DegreeU: int, DegreeV: int) + Increases the degree of this Bezier surface in the two + parametric directions. + """ + ... + + def insertPoleColAfter(self, index: int) -> None: + """ + Inserts into the table of poles of this surface, after the column + of poles of index. + If this Bezier surface is non-rational, it can become rational if + the weights associated with the new poles are different from each + other, or collectively different from the existing weights in the + table. + """ + ... + + def insertPoleRowAfter(self, index: int) -> None: + """ + Inserts into the table of poles of this surface, after the row + of poles of index. + If this Bezier surface is non-rational, it can become rational if + the weights associated with the new poles are different from each + other, or collectively different from the existing weights in the + table. + """ + ... + + def insertPoleColBefore(self, index: int) -> None: + """ + Inserts into the table of poles of this surface, before the column + of poles of index. + If this Bezier surface is non-rational, it can become rational if + the weights associated with the new poles are different from each + other, or collectively different from the existing weights in the + table. + """ + ... + + def insertPoleRowBefore(self, index: int) -> None: + """ + Inserts into the table of poles of this surface, before the row + of poles of index. + If this Bezier surface is non-rational, it can become rational if + the weights associated with the new poles are different from each + other, or collectively different from the existing weights in the + table. + """ + ... + + def removePoleCol(self, VIndex: int) -> None: + """ + removePoleRow(VIndex: int) + Removes the column of poles of index VIndex from the table of + poles of this Bezier surface. + If this Bezier curve is rational, it can become non-rational. + """ + ... + + def removePoleRow(self, UIndex: int) -> None: + """ + removePoleRow(UIndex: int) + Removes the row of poles of index UIndex from the table of + poles of this Bezier surface. + If this Bezier curve is rational, it can become non-rational. + """ + ... + + def segment(self, U1: float, U2: float, V1: float, V2: float) -> None: + """ + segment(U1: double, U2: double, V1: double, V2: double) + Modifies this Bezier surface by segmenting it between U1 and U2 + in the u parametric direction, and between V1 and V2 in the v + parametric direction. + U1, U2, V1, and V2 can be outside the bounds of this surface. + + -- U1 and U2 isoparametric Bezier curves, segmented between + V1 and V2, become the two bounds of the surface in the v + parametric direction (0. and 1. u isoparametric curves). + -- V1 and V2 isoparametric Bezier curves, segmented between + U1 and U2, become the two bounds of the surface in the u + parametric direction (0. and 1. v isoparametric curves). + + The poles and weights tables are modified, but the degree of + this surface in the u and v parametric directions does not + change.U1 can be greater than U2, and V1 can be greater than V2. + In these cases, the corresponding parametric direction is inverted. + The orientation of the surface is inverted if one (and only one) + parametric direction is inverted. + """ + ... + + def setPole(self, pole: Any) -> None: + """ + Set a pole of the Bezier surface. + """ + ... + + def setPoleCol(self, poles: Any) -> None: + """ + Set the column of poles of the Bezier surface. + """ + ... + + def setPoleRow(self, poles: Any) -> None: + """ + Set the row of poles of the Bezier surface. + """ + ... + + @constmethod + def getPole(self, UIndex: int, VIndex: int) -> Any: + """ + Get a pole of index (UIndex, VIndex) of the Bezier surface. + """ + ... + + @constmethod + def getPoles(self) -> Any: + """ + Get all poles of the Bezier surface. + """ + ... + + def setWeight(self, UIndex: int, VIndex: int, weight: float) -> None: + """ + Set the weight of pole of the index (UIndex, VIndex) + for the Bezier surface. + """ + ... + + def setWeightCol(self, VIndex: int, weights: Any) -> None: + """ + Set the weights of the poles in the column of poles + of index VIndex of the Bezier surface. + """ + ... + + def setWeightRow(self, UIndex: int, weights: Any) -> None: + """ + Set the weights of the poles in the row of poles + of index UIndex of the Bezier surface. + """ + ... + + @constmethod + def getWeight(self, UIndex: int, VIndex: int) -> float: + """ + Get a weight of the pole of index (UIndex, VIndex) + of the Bezier surface. + """ + ... + + @constmethod + def getWeights(self) -> Any: + """ + Get all weights of the Bezier surface. + """ + ... + + @constmethod + def getResolution(self, Tolerance3D: float) -> Tuple[float, float]: + """ + Computes two tolerance values for this Bezier surface, based on the + given tolerance in 3D space Tolerance3D. The tolerances computed are: + -- UTolerance in the u parametric direction and + -- VTolerance in the v parametric direction. + + If f(u,v) is the equation of this Bezier surface, UTolerance and VTolerance + guarantee that: + |u1 - u0| < UTolerance + |v1 - v0| < VTolerance + ====> ||f(u1, v1) - f(u2, v2)|| < Tolerance3D + """ + ... + + def exchangeUV(self) -> None: + """ + Exchanges the u and v parametric directions on this Bezier surface. + As a consequence: + -- the poles and weights tables are transposed, + -- degrees, rational characteristics and so on are exchanged between + the two parametric directions, and + -- the orientation of the surface is reversed. + """ + ... diff --git a/src/Mod/Part/App/BezierSurfacePy.xml b/src/Mod/Part/App/BezierSurfacePy.xml deleted file mode 100644 index 798e9a3135..0000000000 --- a/src/Mod/Part/App/BezierSurfacePy.xml +++ /dev/null @@ -1,263 +0,0 @@ - - - - - - Describes a rational or non-rational Bezier surface --- A non-rational Bezier surface is defined by a table of poles (also known as control points). --- A rational Bezier surface is defined by a table of poles with varying associated weights. - - - - Returns the polynomial degree in u direction of this Bezier surface, -which is equal to the number of poles minus 1. - - - - - - Returns the polynomial degree in v direction of this Bezier surface, -which is equal to the number of poles minus 1. - - - - - - Returns the value of the maximum polynomial degree of any -Bezier surface. This value is 25. - - - - - - Returns the number of poles in u direction of this Bezier surface. - - - - - - Returns the number of poles in v direction of this Bezier surface. - - - - - - Returns the parametric bounds (U1, U2, V1, V2) of this Bezier surface. - - - - - Returns false if the equation of this Bezier surface is polynomial -(e.g. non-rational) in the u or v parametric direction. -In other words, returns false if for each row of poles, the associated -weights are identical. - - - - - Returns false if the equation of this Bezier surface is polynomial -(e.g. non-rational) in the u or v parametric direction. -In other words, returns false if for each column of poles, the associated -weights are identical. - - - - - Returns false. - - - - - Returns false. - - - - - Checks if this surface is closed in the u parametric direction. -Returns true if, in the table of poles the first row and the last -row are identical. - - - - - Checks if this surface is closed in the v parametric direction. -Returns true if, in the table of poles the first column and the -last column are identical. - - - - - increase(DegreeU: int, DegreeV: int) -Increases the degree of this Bezier surface in the two -parametric directions. - - - - - Inserts into the table of poles of this surface, after the column -of poles of index. -If this Bezier surface is non-rational, it can become rational if -the weights associated with the new poles are different from each -other, or collectively different from the existing weights in the -table. - - - - - Inserts into the table of poles of this surface, after the row -of poles of index. -If this Bezier surface is non-rational, it can become rational if -the weights associated with the new poles are different from each -other, or collectively different from the existing weights in the -table. - - - - - Inserts into the table of poles of this surface, before the column -of poles of index. -If this Bezier surface is non-rational, it can become rational if -the weights associated with the new poles are different from each -other, or collectively different from the existing weights in the -table. - - - - - Inserts into the table of poles of this surface, before the row -of poles of index. -If this Bezier surface is non-rational, it can become rational if -the weights associated with the new poles are different from each -other, or collectively different from the existing weights in the -table. - - - - - removePoleRow(VIndex: int) -Removes the column of poles of index VIndex from the table of -poles of this Bezier surface. -If this Bezier curve is rational, it can become non-rational. - - - - - removePoleRow(UIndex: int) -Removes the row of poles of index UIndex from the table of -poles of this Bezier surface. -If this Bezier curve is rational, it can become non-rational. - - - - - segment(U1: double, U2: double, V1: double, V2: double) -Modifies this Bezier surface by segmenting it between U1 and U2 -in the u parametric direction, and between V1 and V2 in the v -parametric direction. -U1, U2, V1, and V2 can be outside the bounds of this surface. - --- U1 and U2 isoparametric Bezier curves, segmented between - V1 and V2, become the two bounds of the surface in the v - parametric direction (0. and 1. u isoparametric curves). --- V1 and V2 isoparametric Bezier curves, segmented between - U1 and U2, become the two bounds of the surface in the u - parametric direction (0. and 1. v isoparametric curves). - -The poles and weights tables are modified, but the degree of -this surface in the u and v parametric directions does not -change.U1 can be greater than U2, and V1 can be greater than V2. -In these cases, the corresponding parametric direction is inverted. -The orientation of the surface is inverted if one (and only one) -parametric direction is inverted. - - - - - Set a pole of the Bezier surface. - - - - - Set the column of poles of the Bezier surface. - - - - - Set the row of poles of the Bezier surface. - - - - - Get a pole of index (UIndex, VIndex) of the Bezier surface. - - - - - Get all poles of the Bezier surface. - - - - - Set the weight of pole of the index (UIndex, VIndex) -for the Bezier surface. - - - - - Set the weights of the poles in the column of poles -of index VIndex of the Bezier surface. - - - - - Set the weights of the poles in the row of poles -of index UIndex of the Bezier surface. - - - - - Get a weight of the pole of index (UIndex, VIndex) -of the Bezier surface. - - - - - Get all weights of the Bezier surface. - - - - - Computes two tolerance values for this Bezier surface, based on the -given tolerance in 3D space Tolerance3D. The tolerances computed are: --- UTolerance in the u parametric direction and --- VTolerance in the v parametric direction. - -If f(u,v) is the equation of this Bezier surface, UTolerance and VTolerance -guarantee that: -|u1 - u0| < UTolerance -|v1 - v0| < VTolerance -====> ||f(u1, v1) - f(u2, v2)|| < Tolerance3D - - - - - Exchanges the u and v parametric directions on this Bezier surface. -As a consequence: --- the poles and weights tables are transposed, --- degrees, rational characteristics and so on are exchanged between - the two parametric directions, and --- the orientation of the surface is reversed. - - - - diff --git a/src/Mod/Part/App/BodyBase.pyi b/src/Mod/Part/App/BodyBase.pyi new file mode 100644 index 0000000000..668fafddf1 --- /dev/null +++ b/src/Mod/Part/App/BodyBase.pyi @@ -0,0 +1,21 @@ +from Base.Metadata import export, constmethod +from PartFeature import PartFeature + + +@export( + Twin="BodyBase", + TwinPointer="BodyBase", + Include="Mod/Part/App/BodyBase.h", + Namespace="Part", + FatherInclude="Mod/Part/App/PartFeaturePy.h", + FatherNamespace="Part", +) +class BodyBase(PartFeature): + """ + Base class of all Body objects + + Author: Juergen Riegel (FreeCAD@juergen-riegel.net) + Licence: LGPL + """ + + ... diff --git a/src/Mod/Part/App/BodyBasePy.xml b/src/Mod/Part/App/BodyBasePy.xml deleted file mode 100644 index 5f7f9f0487..0000000000 --- a/src/Mod/Part/App/BodyBasePy.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - Base class of all Body objects - - - diff --git a/src/Mod/Part/App/BoundedCurve.pyi b/src/Mod/Part/App/BoundedCurve.pyi new file mode 100644 index 0000000000..205bfff72e --- /dev/null +++ b/src/Mod/Part/App/BoundedCurve.pyi @@ -0,0 +1,26 @@ +from Base.Metadata import export +from GeometryCurve import GeometryCurve +from typing import Any, Final + + +@export( + Twin="GeomBoundedCurve", + TwinPointer="GeomBoundedCurve", + PythonName="Part.BoundedCurve", + FatherInclude="Mod/Part/App/GeometryCurvePy.h", + Include="Mod/Part/App/Geometry.h", + Constructor=True, +) +class BoundedCurve(GeometryCurve): + """ + The abstract class BoundedCurve is the root class of all bounded curve objects. + + Author: Abdullah Tahiri (abdullah.tahiri.yo@gmail.com) + Licence: LGPL + """ + + StartPoint: Final[Any] = ... + """Returns the starting point of the bounded curve.""" + + EndPoint: Final[Any] = ... + """Returns the end point of the bounded curve.""" diff --git a/src/Mod/Part/App/BoundedCurvePy.xml b/src/Mod/Part/App/BoundedCurvePy.xml deleted file mode 100644 index 3cf02df0fb..0000000000 --- a/src/Mod/Part/App/BoundedCurvePy.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - The abstract class BoundedCurve is the root class of all bounded curve objects. - - - - Returns the starting point of the bounded curve. - - - - - - Returns the end point of the bounded curve. - - - - - diff --git a/src/Mod/Part/App/CMakeLists.txt b/src/Mod/Part/App/CMakeLists.txt index 0ad53f24ad..47bb011512 100644 --- a/src/Mod/Part/App/CMakeLists.txt +++ b/src/Mod/Part/App/CMakeLists.txt @@ -36,61 +36,62 @@ if(FREETYPE_FOUND) ) endif(FREETYPE_FOUND) -generate_from_xml(ArcPy) -generate_from_xml(ArcOfConicPy) -generate_from_xml(ArcOfCirclePy) -generate_from_xml(ArcOfParabolaPy) -generate_from_xml(BodyBasePy) -generate_from_xml(ConicPy) -generate_from_xml(CirclePy) -generate_from_xml(ArcOfEllipsePy) -generate_from_xml(EllipsePy) -generate_from_xml(HyperbolaPy) -generate_from_xml(ArcOfHyperbolaPy) -generate_from_xml(ParabolaPy) -generate_from_xml(OffsetCurvePy) -generate_from_xml(GeometryPy) -generate_from_xml(GeometryExtensionPy) -generate_from_xml(GeometryIntExtensionPy) -generate_from_xml(GeometryStringExtensionPy) -generate_from_xml(GeometryBoolExtensionPy) -generate_from_xml(GeometryDoubleExtensionPy) -generate_from_xml(GeometryCurvePy) -generate_from_xml(BoundedCurvePy) -generate_from_xml(TrimmedCurvePy) -generate_from_xml(GeometrySurfacePy) -generate_from_xml(LinePy) -generate_from_xml(LineSegmentPy) -generate_from_xml(PointPy) -generate_from_xml(BezierCurvePy) -generate_from_xml(BSplineCurvePy) -generate_from_xml(PlanePy) -generate_from_xml(ConePy) -generate_from_xml(CylinderPy) -generate_from_xml(SpherePy) -generate_from_xml(ToroidPy) -generate_from_xml(BezierSurfacePy) -generate_from_xml(BSplineSurfacePy) -generate_from_xml(OffsetSurfacePy) -generate_from_xml(PlateSurfacePy) -generate_from_xml(RectangularTrimmedSurfacePy) -generate_from_xml(SurfaceOfExtrusionPy) -generate_from_xml(SurfaceOfRevolutionPy) -generate_from_xml(PartFeaturePy) -generate_from_xml(AttachExtensionPy) -generate_from_xml(Part2DObjectPy) -generate_from_xml(AttachEnginePy) -generate_from_xml(TopoShapePy) -generate_from_xml(TopoShapeCompoundPy) -generate_from_xml(TopoShapeCompSolidPy) -generate_from_xml(TopoShapeEdgePy) -generate_from_xml(TopoShapeFacePy) -generate_from_xml(TopoShapeShellPy) -generate_from_xml(TopoShapeSolidPy) -generate_from_xml(TopoShapeVertexPy) -generate_from_xml(TopoShapeWirePy) -generate_from_xml(BRepOffsetAPI_MakePipeShellPy) -generate_from_xml(BRepOffsetAPI_MakeFillingPy) +generate_from_py(Arc) +generate_from_py(ArcOfConic) +generate_from_py(ArcOfCircle) +generate_from_py(ArcOfParabola) +generate_from_py(BodyBase) +generate_from_py(Conic) +generate_from_py(Circle) +generate_from_py(ArcOfEllipse) +generate_from_py(Ellipse) +generate_from_py(Hyperbola) +generate_from_py(ArcOfHyperbola) +generate_from_py(Parabola) +generate_from_py(OffsetCurve) +generate_from_py(Geometry) +generate_from_py(GeometryExtension) +generate_from_py(GeometryIntExtension) +generate_from_py(GeometryStringExtension) +generate_from_py(GeometryBoolExtension) +generate_from_py(GeometryDoubleExtension) +generate_from_py(GeometryCurve) +generate_from_py(BoundedCurve) +generate_from_py(TrimmedCurve) +generate_from_py(GeometrySurface) +generate_from_py(Line) +generate_from_py(LineSegment) +generate_from_py(Point) +generate_from_py(BezierCurve) +generate_from_py(BSplineCurve) +generate_from_py(Plane) +generate_from_py(Cone) +generate_from_py(Cylinder) +generate_from_py(Sphere) +generate_from_py(Toroid) +generate_from_py(BezierSurface) +generate_from_py(BSplineSurface) +generate_from_py(OffsetSurface) +generate_from_py(PlateSurface) +generate_from_py(RectangularTrimmedSurface) +generate_from_py(SurfaceOfExtrusion) +generate_from_py(SurfaceOfRevolution) +generate_from_py(PartFeature) +generate_from_py(AttachExtension) +generate_from_py(Part2DObject) +generate_from_py(AttachEngine) +generate_from_py(TopoShape) +generate_from_py(TopoShapeCompound) +generate_from_py(TopoShapeCompSolid) +generate_from_py(TopoShapeEdge) +generate_from_py(TopoShapeFace) +generate_from_py(TopoShapeShell) +generate_from_py(TopoShapeSolid) +generate_from_py(TopoShapeVertex) +generate_from_py(TopoShapeWire) +generate_from_py(BRepOffsetAPI_MakePipeShell) +generate_from_py(BRepOffsetAPI_MakeFilling) + # make sure to create the directory at configure time file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/BRepFeat) @@ -101,59 +102,59 @@ file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/HLRBRep) file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/ShapeFix) file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/ShapeUpgrade) -generate_from_xml(BRepFeat/MakePrismPy) +generate_from_py(BRepFeat/MakePrism) -generate_from_xml(ChFi2d/ChFi2d_AnaFilletAlgoPy) -generate_from_xml(ChFi2d/ChFi2d_FilletAlgoPy) -generate_from_xml(ChFi2d/ChFi2d_ChamferAPIPy) -generate_from_xml(ChFi2d/ChFi2d_FilletAPIPy) +generate_from_py(ChFi2d/ChFi2d_AnaFilletAlgo) +generate_from_py(ChFi2d/ChFi2d_FilletAlgo) +generate_from_py(ChFi2d/ChFi2d_ChamferAPI) +generate_from_py(ChFi2d/ChFi2d_FilletAPI) -generate_from_xml(Geom2d/ArcOfCircle2dPy) -generate_from_xml(Geom2d/ArcOfConic2dPy) -generate_from_xml(Geom2d/ArcOfEllipse2dPy) -generate_from_xml(Geom2d/ArcOfHyperbola2dPy) -generate_from_xml(Geom2d/ArcOfParabola2dPy) -generate_from_xml(Geom2d/BezierCurve2dPy) -generate_from_xml(Geom2d/BSplineCurve2dPy) -generate_from_xml(Geom2d/Circle2dPy) -generate_from_xml(Geom2d/Conic2dPy) -generate_from_xml(Geom2d/Ellipse2dPy) -generate_from_xml(Geom2d/Geometry2dPy) -generate_from_xml(Geom2d/Hyperbola2dPy) -generate_from_xml(Geom2d/Curve2dPy) -generate_from_xml(Geom2d/Line2dSegmentPy) -generate_from_xml(Geom2d/Line2dPy) -generate_from_xml(Geom2d/OffsetCurve2dPy) -generate_from_xml(Geom2d/Parabola2dPy) +generate_from_py(Geom2d/ArcOfCircle2d) +generate_from_py(Geom2d/ArcOfConic2d) +generate_from_py(Geom2d/ArcOfEllipse2d) +generate_from_py(Geom2d/ArcOfHyperbola2d) +generate_from_py(Geom2d/ArcOfParabola2d) +generate_from_py(Geom2d/BezierCurve2d) +generate_from_py(Geom2d/BSplineCurve2d) +generate_from_py(Geom2d/Circle2d) +generate_from_py(Geom2d/Conic2d) +generate_from_py(Geom2d/Ellipse2d) +generate_from_py(Geom2d/Geometry2d) +generate_from_py(Geom2d/Hyperbola2d) +generate_from_py(Geom2d/Curve2d) +generate_from_py(Geom2d/Line2dSegment) +generate_from_py(Geom2d/Line2d) +generate_from_py(Geom2d/OffsetCurve2d) +generate_from_py(Geom2d/Parabola2d) -generate_from_xml(GeomPlate/BuildPlateSurfacePy) -generate_from_xml(GeomPlate/CurveConstraintPy) -generate_from_xml(GeomPlate/PointConstraintPy) +generate_from_py(GeomPlate/BuildPlateSurface) +generate_from_py(GeomPlate/CurveConstraint) +generate_from_py(GeomPlate/PointConstraint) -generate_from_xml(HLRBRep/HLRBRep_AlgoPy) -generate_from_xml(HLRBRep/HLRToShapePy) -generate_from_xml(HLRBRep/HLRBRep_PolyAlgoPy) -generate_from_xml(HLRBRep/PolyHLRToShapePy) +generate_from_py(HLRBRep/HLRBRep_Algo) +generate_from_py(HLRBRep/HLRToShape) +generate_from_py(HLRBRep/HLRBRep_PolyAlgo) +generate_from_py(HLRBRep/PolyHLRToShape) -generate_from_xml(ShapeFix/ShapeFix_RootPy) -generate_from_xml(ShapeFix/ShapeFix_EdgePy) -generate_from_xml(ShapeFix/ShapeFix_FacePy) -generate_from_xml(ShapeFix/ShapeFix_ShapePy) -generate_from_xml(ShapeFix/ShapeFix_ShellPy) -generate_from_xml(ShapeFix/ShapeFix_SolidPy) -generate_from_xml(ShapeFix/ShapeFix_WirePy) -generate_from_xml(ShapeFix/ShapeFix_WireframePy) -generate_from_xml(ShapeFix/ShapeFix_WireVertexPy) -generate_from_xml(ShapeFix/ShapeFix_EdgeConnectPy) -generate_from_xml(ShapeFix/ShapeFix_FaceConnectPy) -generate_from_xml(ShapeFix/ShapeFix_FixSmallFacePy) -generate_from_xml(ShapeFix/ShapeFix_FixSmallSolidPy) -generate_from_xml(ShapeFix/ShapeFix_FreeBoundsPy) -generate_from_xml(ShapeFix/ShapeFix_ShapeTolerancePy) -generate_from_xml(ShapeFix/ShapeFix_SplitCommonVertexPy) -generate_from_xml(ShapeFix/ShapeFix_SplitToolPy) +generate_from_py(ShapeFix/ShapeFix_Root) +generate_from_py(ShapeFix/ShapeFix_Edge) +generate_from_py(ShapeFix/ShapeFix_Face) +generate_from_py(ShapeFix/ShapeFix_Shape) +generate_from_py(ShapeFix/ShapeFix_Shell) +generate_from_py(ShapeFix/ShapeFix_Solid) +generate_from_py(ShapeFix/ShapeFix_Wire) +generate_from_py(ShapeFix/ShapeFix_Wireframe) +generate_from_py(ShapeFix/ShapeFix_WireVertex) +generate_from_py(ShapeFix/ShapeFix_EdgeConnect) +generate_from_py(ShapeFix/ShapeFix_FaceConnect) +generate_from_py(ShapeFix/ShapeFix_FixSmallFace) +generate_from_py(ShapeFix/ShapeFix_FixSmallSolid) +generate_from_py(ShapeFix/ShapeFix_FreeBounds) +generate_from_py(ShapeFix/ShapeFix_ShapeTolerance) +generate_from_py(ShapeFix/ShapeFix_SplitCommonVertex) +generate_from_py(ShapeFix/ShapeFix_SplitTool) -generate_from_xml(ShapeUpgrade/UnifySameDomainPy) +generate_from_py(ShapeUpgrade/UnifySameDomain) SET(Features_SRCS FeaturePartBoolean.cpp @@ -256,115 +257,115 @@ SET(FCBRepAlgoAPI_SRCS SOURCE_GROUP("FCBRepAlgoAPI-wrapper" FILES ${FCBRepAlgoAPI_SRCS}) SET(Python_SRCS - ArcPy.xml + Arc.pyi ArcPyImp.cpp - ArcOfConicPy.xml + ArcOfConic.pyi ArcOfConicPyImp.cpp - ArcOfCirclePy.xml + ArcOfCircle.pyi ArcOfCirclePyImp.cpp - ArcOfParabolaPy.xml + ArcOfParabola.pyi ArcOfParabolaPyImp.cpp - BodyBasePy.xml + BodyBase.pyi BodyBasePyImp.cpp - ConicPy.xml + Conic.pyi ConicPyImp.cpp - CirclePy.xml + Circle.pyi CirclePyImp.cpp - ArcOfEllipsePy.xml + ArcOfEllipse.pyi ArcOfEllipsePyImp.cpp - EllipsePy.xml + Ellipse.pyi EllipsePyImp.cpp - HyperbolaPy.xml + Hyperbola.pyi HyperbolaPyImp.cpp - ArcOfHyperbolaPy.xml + ArcOfHyperbola.pyi ArcOfHyperbolaPyImp.cpp - ParabolaPy.xml + Parabola.pyi ParabolaPyImp.cpp - OffsetCurvePy.xml + OffsetCurve.pyi OffsetCurvePyImp.cpp - GeometryPy.xml + Geometry.pyi GeometryPyImp.cpp - GeometryExtensionPy.xml + GeometryExtension.pyi GeometryExtensionPyImp.cpp - GeometryIntExtensionPy.xml + GeometryIntExtension.pyi GeometryIntExtensionPyImp.cpp - GeometryStringExtensionPy.xml + GeometryStringExtension.pyi GeometryStringExtensionPyImp.cpp - GeometryBoolExtensionPy.xml + GeometryBoolExtension.pyi GeometryBoolExtensionPyImp.cpp - GeometryDoubleExtensionPy.xml + GeometryDoubleExtension.pyi GeometryDoubleExtensionPyImp.cpp - GeometryCurvePy.xml + GeometryCurve.pyi GeometryCurvePyImp.cpp - BoundedCurvePy.xml + BoundedCurve.pyi BoundedCurvePyImp.cpp - TrimmedCurvePy.xml + TrimmedCurve.pyi TrimmedCurvePyImp.cpp - GeometrySurfacePy.xml + GeometrySurface.pyi GeometrySurfacePyImp.cpp - LinePy.xml + Line.pyi LinePyImp.cpp - LineSegmentPy.xml + LineSegment.pyi LineSegmentPyImp.cpp - PointPy.xml + Point.pyi PointPyImp.cpp - BezierCurvePy.xml + BezierCurve.pyi BezierCurvePyImp.cpp - BSplineCurvePy.xml + BSplineCurve.pyi BSplineCurvePyImp.cpp - PlanePy.xml + Plane.pyi PlanePyImp.cpp - ConePy.xml + Cone.pyi ConePyImp.cpp - CylinderPy.xml + Cylinder.pyi CylinderPyImp.cpp - SpherePy.xml + Sphere.pyi SpherePyImp.cpp - ToroidPy.xml + Toroid.pyi ToroidPyImp.cpp - BezierSurfacePy.xml + BezierSurface.pyi BezierSurfacePyImp.cpp - BSplineSurfacePy.xml + BSplineSurface.pyi BSplineSurfacePyImp.cpp - OffsetSurfacePy.xml + OffsetSurface.pyi OffsetSurfacePyImp.cpp - PlateSurfacePy.xml + PlateSurface.pyi PlateSurfacePyImp.cpp - RectangularTrimmedSurfacePy.xml + RectangularTrimmedSurface.pyi RectangularTrimmedSurfacePyImp.cpp - SurfaceOfExtrusionPy.xml + SurfaceOfExtrusion.pyi SurfaceOfExtrusionPyImp.cpp - SurfaceOfRevolutionPy.xml + SurfaceOfRevolution.pyi SurfaceOfRevolutionPyImp.cpp - PartFeaturePy.xml + PartFeature.pyi PartFeaturePyImp.cpp - AttachExtensionPy.xml + AttachExtension.pyi AttachExtensionPyImp.cpp - Part2DObjectPy.xml + Part2DObject.pyi Part2DObjectPyImp.cpp - AttachEnginePy.xml + AttachEngine.pyi AttachEnginePyImp.cpp - TopoShapePy.xml + TopoShape.pyi TopoShapePyImp.cpp - TopoShapeCompSolidPy.xml + TopoShapeCompSolid.pyi TopoShapeCompSolidPyImp.cpp - TopoShapeCompoundPy.xml + TopoShapeCompound.pyi TopoShapeCompoundPyImp.cpp - TopoShapeEdgePy.xml + TopoShapeEdge.pyi TopoShapeEdgePyImp.cpp - TopoShapeFacePy.xml + TopoShapeFace.pyi TopoShapeFacePyImp.cpp - TopoShapeShellPy.xml + TopoShapeShell.pyi TopoShapeShellPyImp.cpp - TopoShapeSolidPy.xml + TopoShapeSolid.pyi TopoShapeSolidPyImp.cpp - TopoShapeVertexPy.xml + TopoShapeVertex.pyi TopoShapeVertexPyImp.cpp - TopoShapeWirePy.xml + TopoShapeWire.pyi TopoShapeWirePyImp.cpp - BRepOffsetAPI_MakePipeShellPy.xml + BRepOffsetAPI_MakePipeShell.pyi BRepOffsetAPI_MakePipeShellPyImp.cpp - BRepOffsetAPI_MakeFillingPy.xml + BRepOffsetAPI_MakeFilling.pyi BRepOffsetAPI_MakeFillingPyImp.cpp PartPyCXX.cpp PartPyCXX.h @@ -373,59 +374,59 @@ SOURCE_GROUP("Python" FILES ${Python_SRCS}) # BRepFeat wrappers SET(BRepFeatPy_SRCS - BRepFeat/MakePrismPy.xml + BRepFeat/MakePrism.pyi BRepFeat/MakePrismPyImp.cpp ) SOURCE_GROUP("BRepFeat" FILES ${BRepFeatPy_SRCS}) # ChFi2d wrappers SET(ChFi2dPy_SRCS - ChFi2d/ChFi2d_AnaFilletAlgoPy.xml + ChFi2d/ChFi2d_AnaFilletAlgo.pyi ChFi2d/ChFi2d_AnaFilletAlgoPyImp.cpp - ChFi2d/ChFi2d_FilletAlgoPy.xml + ChFi2d/ChFi2d_FilletAlgo.pyi ChFi2d/ChFi2d_FilletAlgoPyImp.cpp - ChFi2d/ChFi2d_ChamferAPIPy.xml + ChFi2d/ChFi2d_ChamferAPI.pyi ChFi2d/ChFi2d_ChamferAPIPyImp.cpp - ChFi2d/ChFi2d_FilletAPIPy.xml + ChFi2d/ChFi2d_FilletAPI.pyi ChFi2d/ChFi2d_FilletAPIPyImp.cpp ) SOURCE_GROUP("ChFi2d" FILES ${ChFi2dPy_SRCS}) # Geom2d wrappers SET(Geom2dPy_SRCS - Geom2d/ArcOfCircle2dPy.xml + Geom2d/ArcOfCircle2d.pyi Geom2d/ArcOfCircle2dPyImp.cpp - Geom2d/ArcOfConic2dPy.xml + Geom2d/ArcOfConic2d.pyi Geom2d/ArcOfConic2dPyImp.cpp - Geom2d/ArcOfEllipse2dPy.xml + Geom2d/ArcOfEllipse2d.pyi Geom2d/ArcOfEllipse2dPyImp.cpp - Geom2d/ArcOfHyperbola2dPy.xml + Geom2d/ArcOfHyperbola2d.pyi Geom2d/ArcOfHyperbola2dPyImp.cpp - Geom2d/ArcOfParabola2dPy.xml + Geom2d/ArcOfParabola2d.pyi Geom2d/ArcOfParabola2dPyImp.cpp - Geom2d/BezierCurve2dPy.xml + Geom2d/BezierCurve2d.pyi Geom2d/BezierCurve2dPyImp.cpp - Geom2d/BSplineCurve2dPy.xml + Geom2d/BSplineCurve2d.pyi Geom2d/BSplineCurve2dPyImp.cpp - Geom2d/Circle2dPy.xml + Geom2d/Circle2d.pyi Geom2d/Circle2dPyImp.cpp - Geom2d/Conic2dPy.xml + Geom2d/Conic2d.pyi Geom2d/Conic2dPyImp.cpp - Geom2d/Ellipse2dPy.xml + Geom2d/Ellipse2d.pyi Geom2d/Ellipse2dPyImp.cpp - Geom2d/Geometry2dPy.xml + Geom2d/Geometry2d.pyi Geom2d/Geometry2dPyImp.cpp - Geom2d/Curve2dPy.xml + Geom2d/Curve2d.pyi Geom2d/Curve2dPyImp.cpp - Geom2d/Hyperbola2dPy.xml + Geom2d/Hyperbola2d.pyi Geom2d/Hyperbola2dPyImp.cpp - Geom2d/Line2dPy.xml + Geom2d/Line2d.pyi Geom2d/Line2dPyImp.cpp - Geom2d/Line2dSegmentPy.xml + Geom2d/Line2dSegment.pyi Geom2d/Line2dSegmentPyImp.cpp - Geom2d/OffsetCurve2dPy.xml + Geom2d/OffsetCurve2d.pyi Geom2d/OffsetCurve2dPyImp.cpp - Geom2d/Parabola2dPy.xml + Geom2d/Parabola2d.pyi Geom2d/Parabola2dPyImp.cpp ) @@ -433,11 +434,11 @@ SOURCE_GROUP("Geom2d" FILES ${Geom2dPy_SRCS}) # GeomPlate wrappers SET(GeomPlatePy_SRCS - GeomPlate/BuildPlateSurfacePy.xml + GeomPlate/BuildPlateSurface.pyi GeomPlate/BuildPlateSurfacePyImp.cpp - GeomPlate/CurveConstraintPy.xml + GeomPlate/CurveConstraint.pyi GeomPlate/CurveConstraintPyImp.cpp - GeomPlate/PointConstraintPy.xml + GeomPlate/PointConstraint.pyi GeomPlate/PointConstraintPyImp.cpp ) @@ -445,53 +446,53 @@ SOURCE_GROUP("GeomPlate" FILES ${GeomPlatePy_SRCS}) # HLRBRep wrappers SET(HLRBRepPy_SRCS - HLRBRep/HLRBRep_AlgoPy.xml + HLRBRep/HLRBRep_Algo.pyi HLRBRep/HLRBRep_AlgoPyImp.cpp - HLRBRep/HLRToShapePy.xml + HLRBRep/HLRToShape.pyi HLRBRep/HLRToShapePyImp.cpp - HLRBRep/HLRBRep_PolyAlgoPy.xml + HLRBRep/HLRBRep_PolyAlgo.pyi HLRBRep/HLRBRep_PolyAlgoPyImp.cpp - HLRBRep/PolyHLRToShapePy.xml + HLRBRep/PolyHLRToShape.pyi HLRBRep/PolyHLRToShapePyImp.cpp ) SOURCE_GROUP("HLRBRep" FILES ${HLRBRepPy_SRCS}) # ShapeFix wrappers SET(ShapeFixPy_SRCS - ShapeFix/ShapeFix_RootPy.xml + ShapeFix/ShapeFix_Root.pyi ShapeFix/ShapeFix_RootPyImp.cpp - ShapeFix/ShapeFix_EdgePy.xml + ShapeFix/ShapeFix_Edge.pyi ShapeFix/ShapeFix_EdgePyImp.cpp - ShapeFix/ShapeFix_FacePy.xml + ShapeFix/ShapeFix_Face.pyi ShapeFix/ShapeFix_FacePyImp.cpp - ShapeFix/ShapeFix_ShapePy.xml + ShapeFix/ShapeFix_Shape.pyi ShapeFix/ShapeFix_ShapePyImp.cpp - ShapeFix/ShapeFix_ShellPy.xml + ShapeFix/ShapeFix_Shell.pyi ShapeFix/ShapeFix_ShellPyImp.cpp - ShapeFix/ShapeFix_SolidPy.xml + ShapeFix/ShapeFix_Solid.pyi ShapeFix/ShapeFix_SolidPyImp.cpp - ShapeFix/ShapeFix_WirePy.xml + ShapeFix/ShapeFix_Wire.pyi ShapeFix/ShapeFix_WirePyImp.cpp - ShapeFix/ShapeFix_WireframePy.xml + ShapeFix/ShapeFix_Wireframe.pyi ShapeFix/ShapeFix_WireframePyImp.cpp - ShapeFix/ShapeFix_WireVertexPy.xml + ShapeFix/ShapeFix_WireVertex.pyi ShapeFix/ShapeFix_WireVertexPyImp.cpp - ShapeFix/ShapeFix_EdgeConnectPy.xml + ShapeFix/ShapeFix_EdgeConnect.pyi ShapeFix/ShapeFix_EdgeConnectPyImp.cpp - ShapeFix/ShapeFix_FaceConnectPy.xml + ShapeFix/ShapeFix_FaceConnect.pyi ShapeFix/ShapeFix_FaceConnectPyImp.cpp - ShapeFix/ShapeFix_FixSmallFacePy.xml + ShapeFix/ShapeFix_FixSmallFace.pyi ShapeFix/ShapeFix_FixSmallFacePyImp.cpp - ShapeFix/ShapeFix_FixSmallSolidPy.xml + ShapeFix/ShapeFix_FixSmallSolid.pyi ShapeFix/ShapeFix_FixSmallSolidPyImp.cpp - ShapeFix/ShapeFix_FreeBoundsPy.xml + ShapeFix/ShapeFix_FreeBounds.pyi ShapeFix/ShapeFix_FreeBoundsPyImp.cpp - ShapeFix/ShapeFix_ShapeTolerancePy.xml + ShapeFix/ShapeFix_ShapeTolerance.pyi ShapeFix/ShapeFix_ShapeTolerancePyImp.cpp - ShapeFix/ShapeFix_SplitCommonVertexPy.xml + ShapeFix/ShapeFix_SplitCommonVertex.pyi ShapeFix/ShapeFix_SplitCommonVertexPyImp.cpp - ShapeFix/ShapeFix_SplitToolPy.xml + ShapeFix/ShapeFix_SplitTool.pyi ShapeFix/ShapeFix_SplitToolPyImp.cpp ) @@ -499,7 +500,7 @@ SOURCE_GROUP("ShapeFix" FILES ${ShapeFixPy_SRCS}) # ShapeUpgrade wrappers SET(ShapeUpgradePy_SRCS - ShapeUpgrade/UnifySameDomainPy.xml + ShapeUpgrade/UnifySameDomain.pyi ShapeUpgrade/UnifySameDomainPyImp.cpp ) diff --git a/src/Mod/Part/App/ChFi2d/ChFi2d_AnaFilletAlgo.pyi b/src/Mod/Part/App/ChFi2d/ChFi2d_AnaFilletAlgo.pyi new file mode 100644 index 0000000000..1b891d20c2 --- /dev/null +++ b/src/Mod/Part/App/ChFi2d/ChFi2d_AnaFilletAlgo.pyi @@ -0,0 +1,40 @@ +from Metadata import export, constmethod +from Base.PyObjectBase import PyObjectBase +from typing import Tuple + +@export( + Name="ChFi2d_AnaFilletAlgoPy", + PythonName="Part.ChFi2d.AnaFilletAlgo", + Twin="ChFi2d_AnaFilletAlgo", + TwinPointer="ChFi2d_AnaFilletAlgo", + Include="ChFi2d_AnaFilletAlgo.hxx", + Constructor=True, + Delete=True, +) +class AnaFilletAlgo(PyObjectBase): + """ + An analytical algorithm for calculation of the fillets. + It is implemented for segments and arcs of circle only. + """ + + def init(self) -> None: + """ + Initializes a fillet algorithm: accepts a wire consisting of two edges in a plane + """ + ... + + def perform(self, radius: float) -> bool: + """ + perform(radius) -> bool + + Constructs a fillet edge + """ + ... + + def result(self) -> Tuple[PyObjectBase, PyObjectBase, PyObjectBase]: + """ + result() + + Returns result (fillet edge, modified edge1, modified edge2) + """ + ... diff --git a/src/Mod/Part/App/ChFi2d/ChFi2d_AnaFilletAlgoPy.xml b/src/Mod/Part/App/ChFi2d/ChFi2d_AnaFilletAlgoPy.xml deleted file mode 100644 index dd1e79ee9f..0000000000 --- a/src/Mod/Part/App/ChFi2d/ChFi2d_AnaFilletAlgoPy.xml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - An analytical algorithm for calculation of the fillets. -It is implemented for segments and arcs of circle only. - - - - Initializes a fillet algorithm: accepts a wire consisting of two edges in a plane - - - - - perform(radius) -> bool - -Constructs a fillet edge - - - - - result() - -Returns result (fillet edge, modified edge1, modified edge2) - - - - diff --git a/src/Mod/Part/App/ChFi2d/ChFi2d_ChamferAPI.pyi b/src/Mod/Part/App/ChFi2d/ChFi2d_ChamferAPI.pyi new file mode 100644 index 0000000000..88c891b63b --- /dev/null +++ b/src/Mod/Part/App/ChFi2d/ChFi2d_ChamferAPI.pyi @@ -0,0 +1,42 @@ +from Base.Metadata import export, constmethod +from typing import Tuple, overload + +from Base.PyObjectBase import PyObjectBase + +@export( + PythonName="Part.ChFi2d.ChamferAPI", + Twin="ChFi2d_ChamferAPI", + TwinPointer="ChFi2d_ChamferAPI", + Include="ChFi2d_ChamferAPI.hxx", + Constructor=True, + Delete=True, +) +class ChFi2d_ChamferAPI(PyObjectBase): + """ + Algorithm that creates a chamfer between two linear edges + + Author: Werner Mayer (wmayer[at]users.sourceforge.net) + Licence: LGPL + """ + + def init(self) -> None: + """ + Initializes a chamfer algorithm: accepts a wire consisting of two edges in a plane + """ + ... + + def perform(self, radius: float) -> bool: + """ + perform(radius) -> bool + + Constructs a chamfer edge + """ + ... + + def result(self, point: object, solution: int = -1) -> Tuple[object, object, object]: + """ + result(point, solution=-1) + + Returns result (chamfer edge, modified edge1, modified edge2) + """ + ... diff --git a/src/Mod/Part/App/ChFi2d/ChFi2d_ChamferAPIPy.xml b/src/Mod/Part/App/ChFi2d/ChFi2d_ChamferAPIPy.xml deleted file mode 100644 index 83322d70c6..0000000000 --- a/src/Mod/Part/App/ChFi2d/ChFi2d_ChamferAPIPy.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - Algorithm that creates a chamfer between two linear edges - - - - Initializes a chamfer algorithm: accepts a wire consisting of two edges in a plane - - - - - perform(radius) -> bool - -Constructs a chamfer edge - - - - - result(point, solution=-1) - -Returns result (chamfer edge, modified edge1, modified edge2) - - - - diff --git a/src/Mod/Part/App/ChFi2d/ChFi2d_FilletAPI.pyi b/src/Mod/Part/App/ChFi2d/ChFi2d_FilletAPI.pyi new file mode 100644 index 0000000000..da521f94d7 --- /dev/null +++ b/src/Mod/Part/App/ChFi2d/ChFi2d_FilletAPI.pyi @@ -0,0 +1,50 @@ +from Base.Metadata import export, constmethod +from Base.PyObjectBase import PyObjectBase +from typing import overload +from Part.TopoShapeEdgePy import TopoShapeEdge +from Part.PointPy import Point + + +@export( + PythonName="Part.ChFi2d.FilletAPI", + Twin="ChFi2d_FilletAPI", + TwinPointer="ChFi2d_FilletAPI", + Include="ChFi2d_FilletAPI.hxx", + Constructor=True, + Delete=True, +) +class ChFi2d_FilletAPI(PyObjectBase): + """ + Algorithm that creates fillet edge + + Author: Werner Mayer (wmayer[at]users.sourceforge.net) + Licence: LGPL + """ + + def init(self) -> None: + """ + Initializes a fillet algorithm: accepts a wire consisting of two edges in a plane + """ + ... + + def perform(self, radius: float) -> bool: + """ + perform(radius) -> bool + + Constructs a fillet edge + """ + ... + + def numberOfResults(self) -> int: + """ + Returns number of possible solutions + """ + ... + + def result(self, point: Point, solution: int = -1) -> tuple[TopoShapeEdge, TopoShapeEdge, TopoShapeEdge]: + """ + result(point, solution=-1) + + Returns result (fillet edge, modified edge1, modified edge2) + """ + ... diff --git a/src/Mod/Part/App/ChFi2d/ChFi2d_FilletAPIPy.xml b/src/Mod/Part/App/ChFi2d/ChFi2d_FilletAPIPy.xml deleted file mode 100644 index 3b1781c119..0000000000 --- a/src/Mod/Part/App/ChFi2d/ChFi2d_FilletAPIPy.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - Algorithm that creates fillet edge - - - - Initializes a fillet algorithm: accepts a wire consisting of two edges in a plane - - - - - perform(radius) -> bool - -Constructs a fillet edge - - - - - Returns number of possible solutions - - - - - result(point, solution=-1) - -Returns result (fillet edge, modified edge1, modified edge2) - - - - diff --git a/src/Mod/Part/App/ChFi2d/ChFi2d_FilletAlgo.pyi b/src/Mod/Part/App/ChFi2d/ChFi2d_FilletAlgo.pyi new file mode 100644 index 0000000000..a17726ca0e --- /dev/null +++ b/src/Mod/Part/App/ChFi2d/ChFi2d_FilletAlgo.pyi @@ -0,0 +1,50 @@ +from Metadata import export, constmethod +from Base.PyObjectBase import PyObjectBase +from Base.Vector import Vector +from typing import Final + + +@export( + Name="ChFi2d_FilletAlgoPy", + PythonName="Part.ChFi2d.FilletAlgo", + Twin="ChFi2d_FilletAlgo", + TwinPointer="ChFi2d_FilletAlgo", + Include="ChFi2d_FilletAlgo.hxx", + Constructor=True, + Delete=True, +) +class FilletAlgo(PyObjectBase): + """ + Algorithm that creates fillet edge + + Author: Werner Mayer (wmayer[at]users.sourceforge.net) + Licence: LGPL + """ + + def init(self) -> None: + """ + Initializes a fillet algorithm: accepts a wire consisting of two edges in a plane + """ + ... + + def perform(self, radius: float) -> bool: + """ + perform(radius) -> bool + + Constructs a fillet edge + """ + ... + + def numberOfResults(self) -> int: + """ + Returns number of possible solutions + """ + ... + + def result(self, point: Vector, solution: int = -1) -> tuple[object, object, object]: + """ + result(point, solution=-1) + + Returns result (fillet edge, modified edge1, modified edge2) + """ + ... diff --git a/src/Mod/Part/App/ChFi2d/ChFi2d_FilletAlgoPy.xml b/src/Mod/Part/App/ChFi2d/ChFi2d_FilletAlgoPy.xml deleted file mode 100644 index 770660852c..0000000000 --- a/src/Mod/Part/App/ChFi2d/ChFi2d_FilletAlgoPy.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - Algorithm that creates fillet edge - - - - Initializes a fillet algorithm: accepts a wire consisting of two edges in a plane - - - - - perform(radius) -> bool - -Constructs a fillet edge - - - - - Returns number of possible solutions - - - - - result(point, solution=-1) - -Returns result (fillet edge, modified edge1, modified edge2) - - - - diff --git a/src/Mod/Part/App/Circle.pyi b/src/Mod/Part/App/Circle.pyi new file mode 100644 index 0000000000..1fd7f5f28b --- /dev/null +++ b/src/Mod/Part/App/Circle.pyi @@ -0,0 +1,53 @@ +from Base.Metadata import export +from Base.Vector import Vector +from Conic import Conic +from Point import Point +from typing import overload + + +@export( + PythonName="Part.Circle", + Twin="GeomCircle", + TwinPointer="GeomCircle", + Include="Mod/Part/App/Geometry.h", + FatherInclude="Mod/Part/App/ConicPy.h", + FatherNamespace="Part", + Constructor=True, +) +class Circle(Conic): + """ + Describes a circle in 3D space + To create a circle there are several ways: + Part.Circle() + Creates a default circle with center (0,0,0) and radius 1 + + Part.Circle(Circle) + Creates a copy of the given circle + + Part.Circle(Circle, Distance) + Creates a circle parallel to given circle at a certain distance + + Part.Circle(Center,Normal,Radius) + Creates a circle defined by center, normal direction and radius + + Part.Circle(Point1,Point2,Point3) + Creates a circle defined by three non-linear points + """ + + Radius: float = 0.0 + """The radius of the circle.""" + + @overload + def __init__(self) -> None: ... + + @overload + def __init__(self, circle: "Circle") -> None: ... + + @overload + def __init__(self, circle: "Circle", distance: float) -> None: ... + + @overload + def __init__(self, center: Point, normal: Vector, radius: float) -> None: ... + + @overload + def __init__(self, point1: Point, point2: Point, point3: Point) -> None: ... diff --git a/src/Mod/Part/App/CirclePy.xml b/src/Mod/Part/App/CirclePy.xml deleted file mode 100644 index 488734b7b7..0000000000 --- a/src/Mod/Part/App/CirclePy.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - Describes a circle in 3D space -To create a circle there are several ways: -Part.Circle() - Creates a default circle with center (0,0,0) and radius 1 - -Part.Circle(Circle) - Creates a copy of the given circle - -Part.Circle(Circle, Distance) - Creates a circle parallel to given circle at a certain distance - -Part.Circle(Center,Normal,Radius) - Creates a circle defined by center, normal direction and radius - -Part.Circle(Point1,Point2,Point3) - Creates a circle defined by three non-linear points - - - - - The radius of the circle. - - - - - diff --git a/src/Mod/Part/App/Cone.pyi b/src/Mod/Part/App/Cone.pyi new file mode 100644 index 0000000000..f9c119ced7 --- /dev/null +++ b/src/Mod/Part/App/Cone.pyi @@ -0,0 +1,64 @@ +from Base.Metadata import export +from Base.Vector import Vector +from Base.Axis import Axis as AxisPy +from GeometrySurface import GeometrySurface +from typing import Final + + +@export( + PythonName="Part.Cone", + Twin="GeomCone", + TwinPointer="GeomCone", + Include="Mod/Part/App/Geometry.h", + FatherInclude="Mod/Part/App/GeometrySurfacePy.h", + Constructor=True, +) +class Cone(GeometrySurface): + """ + Describes a cone in 3D space + + To create a cone there are several ways: + + Part.Cone() + Creates a default cone with radius 1 + + Part.Cone(Cone) + Creates a copy of the given cone + + Part.Cone(Cone, Distance) + Creates a cone parallel to given cone at a certain distance + + Part.Cone(Point1,Point2,Radius1,Radius2) + Creates a cone defined by two points and two radii + The axis of the cone is the line passing through + Point1 and Poin2. + Radius1 is the radius of the section passing through + Point1 and Radius2 the radius of the section passing + through Point2. + + Part.Cone(Point1,Point2,Point3,Point4) + Creates a cone passing through three points Point1, + Point2 and Point3. + Its axis is defined by Point1 and Point2 and the radius of + its base is the distance between Point3 and its axis. + The distance between Point and the axis is the radius of + the section passing through Point4. + + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + Apex: Final[Vector] = Vector() + """Compute the apex of the cone.""" + + Radius: float = 0.0 + """The radius of the cone.""" + + SemiAngle: float = 0.0 + """The semi-angle of the cone.""" + + Center: Vector = Vector() + """Center of the cone.""" + + Axis: AxisPy = AxisPy() + """The axis direction of the cone""" diff --git a/src/Mod/Part/App/ConePy.xml b/src/Mod/Part/App/ConePy.xml deleted file mode 100644 index 5e5151a08a..0000000000 --- a/src/Mod/Part/App/ConePy.xml +++ /dev/null @@ -1,77 +0,0 @@ - - - - - - Describes a cone in 3D space - -To create a cone there are several ways: - -Part.Cone() - Creates a default cone with radius 1 - -Part.Cone(Cone) - Creates a copy of the given cone - -Part.Cone(Cone, Distance) - Creates a cone parallel to given cone at a certain distance - -Part.Cone(Point1,Point2,Radius1,Radius2) - Creates a cone defined by two points and two radii - The axis of the cone is the line passing through - Point1 and Poin2. - Radius1 is the radius of the section passing through - Point1 and Radius2 the radius of the section passing - through Point2. - -Part.Cone(Point1,Point2,Point3,Point4) - Creates a cone passing through three points Point1, - Point2 and Point3. - Its axis is defined by Point1 and Point2 and the radius of - its base is the distance between Point3 and its axis. - The distance between Point and the axis is the radius of - the section passing through Point4. - - - - - Compute the apex of the cone. - - - - - - The radius of the cone. - - - - - - The semi-angle of the cone. - - - - - - Center of the cone. - - - - - - The axis direction of the cone - - - - - diff --git a/src/Mod/Part/App/Conic.pyi b/src/Mod/Part/App/Conic.pyi new file mode 100644 index 0000000000..619f1823e7 --- /dev/null +++ b/src/Mod/Part/App/Conic.pyi @@ -0,0 +1,47 @@ +from Base.Metadata import export +from GeometryCurve import GeometryCurve +from typing import Final + + +@export( + PythonName="Part.Conic", + Twin="GeomConic", + TwinPointer="GeomConic", + Include="Mod/Part/App/Geometry.h", + FatherInclude="Mod/Part/App/GeometryCurvePy.h", + Constructor=True, +) +class Conic(GeometryCurve): + """ + Describes an abstract conic in 3d space + + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + Location: object = ... + """Location of the conic.""" + + Center: object = ... + """Deprecated -- use Location.""" + + Eccentricity: Final[float] = ... + """ + Returns the eccentricity value of the conic e. + e = 0 for a circle + 0 < e < 1 for an ellipse (e = 0 if MajorRadius = MinorRadius) + e > 1 for a hyperbola + e = 1 for a parabola + """ + + AngleXU: float = ... + """The angle between the X axis and the major axis of the conic.""" + + Axis: object = ... + """The axis direction of the circle""" + + XAxis: object = ... + """The X axis direction of the circle""" + + YAxis: object = ... + """The Y axis direction of the circle""" diff --git a/src/Mod/Part/App/ConicPy.xml b/src/Mod/Part/App/ConicPy.xml deleted file mode 100644 index 675c5310ff..0000000000 --- a/src/Mod/Part/App/ConicPy.xml +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - Describes an abstract conic in 3d space - - - - Location of the conic. - - - - - - Deprecated -- use Location. - - - - - - Returns the eccentricity value of the conic e. -e = 0 for a circle -0 < e < 1 for an ellipse (e = 0 if MajorRadius = MinorRadius) -e > 1 for a hyperbola -e = 1 for a parabola - - - - - - - The angle between the X axis and the major axis of the conic. - - - - - - The axis direction of the circle - - - - - - The X axis direction of the circle - - - - - - The Y axis direction of the circle - - - - - diff --git a/src/Mod/Part/App/Cylinder.pyi b/src/Mod/Part/App/Cylinder.pyi new file mode 100644 index 0000000000..b3638c36db --- /dev/null +++ b/src/Mod/Part/App/Cylinder.pyi @@ -0,0 +1,63 @@ +from Base.Metadata import export +from Base.Vector import Vector +from Circle import Circle +from GeometrySurface import GeometrySurface +from typing import overload + + +@export( + PythonName="Part.Cylinder", + Twin="GeomCylinder", + TwinPointer="GeomCylinder", + Include="Mod/Part/App/Geometry.h", + FatherInclude="Mod/Part/App/GeometrySurfacePy.h", + Constructor=True, +) +class Cylinder(GeometrySurface): + """ + Describes a cylinder in 3D space + + To create a cylinder there are several ways: + + Part.Cylinder() + Creates a default cylinder with center (0,0,0) and radius 1 + + Part.Cylinder(Cylinder) + Creates a copy of the given cylinder + + Part.Cylinder(Cylinder, Distance) + Creates a cylinder parallel to given cylinder at a certain distance + + Part.Cylinder(Point1, Point2, Point2) + Creates a cylinder defined by three non-linear points + + Part.Cylinder(Circle) + Creates a cylinder by a circular base + + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + Radius: float + """The radius of the cylinder.""" + + Center: Vector + """Center of the cylinder.""" + + Axis: Vector + """The axis direction of the cylinder""" + + @overload + def __init__(self) -> None: ... + + @overload + def __init__(self, cylinder: "Cylinder") -> None: ... + + @overload + def __init__(self, cylinder: "Cylinder", distance: float) -> None: ... + + @overload + def __init__(self, point1: Vector, point2: Vector, point3: Vector) -> None: ... + + @overload + def __init__(self, circle: Circle) -> None: ... diff --git a/src/Mod/Part/App/CylinderPy.xml b/src/Mod/Part/App/CylinderPy.xml deleted file mode 100644 index 499e22f5e1..0000000000 --- a/src/Mod/Part/App/CylinderPy.xml +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - Describes a cylinder in 3D space - -To create a cylinder there are several ways: - -Part.Cylinder() - Creates a default cylinder with center (0,0,0) and radius 1 - -Part.Cylinder(Cylinder) - Creates a copy of the given cylinder - -Part.Cylinder(Cylinder, Distance) - Creates a cylinder parallel to given cylinder at a certain distance - -Part.Cylinder(Point1, Point2, Point2) - Creates a cylinder defined by three non-linear points - -Part.Cylinder(Circle) - Creates a cylinder by a circular base - - - - The radius of the cylinder. - - - - - - Center of the cylinder. - - - - - - The axis direction of the cylinder - - - - - diff --git a/src/Mod/Part/App/Ellipse.pyi b/src/Mod/Part/App/Ellipse.pyi new file mode 100644 index 0000000000..8e94479a8d --- /dev/null +++ b/src/Mod/Part/App/Ellipse.pyi @@ -0,0 +1,54 @@ +from Base.Metadata import export +from Base.Vector import Vector +from Conic import Conic +from typing import Final + + +@export( + Twin="GeomEllipse", + TwinPointer="GeomEllipse", + PythonName="Part.Ellipse", + FatherInclude="Mod/Part/App/ConicPy.h", + Include="Mod/Part/App/Geometry.h", + Constructor=True, +) +class Ellipse(Conic): + """ + Describes an ellipse in 3D space + + To create an ellipse there are several ways: + + Part.Ellipse() + Creates an ellipse with major radius 2 and minor radius 1 with the + center in (0,0,0) + + Part.Ellipse(Ellipse) + Create a copy of the given ellipse + + Part.Ellipse(S1,S2,Center) + Creates an ellipse centered on the point Center, where + the plane of the ellipse is defined by Center, S1 and S2, + its major axis is defined by Center and S1, + its major radius is the distance between Center and S1, and + its minor radius is the distance between S2 and the major axis. + + Part.Ellipse(Center,MajorRadius,MinorRadius) + Creates an ellipse with major and minor radii MajorRadius and + MinorRadius, and located in the plane defined by Center and + the normal (0,0,1) + """ + + MajorRadius: float = 0.0 + """The major radius of the ellipse.""" + + MinorRadius: float = 0.0 + """The minor radius of the ellipse.""" + + Focal: Final[float] = 0.0 + """The focal distance of the ellipse.""" + + Focus1: Final[Vector] = ... + """The first focus is on the positive side of the major axis of the ellipse.""" + + Focus2: Final[Vector] = ... + """The second focus is on the negative side of the major axis of the ellipse.""" diff --git a/src/Mod/Part/App/EllipsePy.xml b/src/Mod/Part/App/EllipsePy.xml deleted file mode 100644 index 9dea884aa1..0000000000 --- a/src/Mod/Part/App/EllipsePy.xml +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - Describes an ellipse in 3D space - -To create an ellipse there are several ways: - -Part.Ellipse() - Creates an ellipse with major radius 2 and minor radius 1 with the - center in (0,0,0) - -Part.Ellipse(Ellipse) - Create a copy of the given ellipse - -Part.Ellipse(S1,S2,Center) - Creates an ellipse centered on the point Center, where - the plane of the ellipse is defined by Center, S1 and S2, - its major axis is defined by Center and S1, - its major radius is the distance between Center and S1, and - its minor radius is the distance between S2 and the major axis. - -Part.Ellipse(Center,MajorRadius,MinorRadius) - Creates an ellipse with major and minor radii MajorRadius and - MinorRadius, and located in the plane defined by Center and - the normal (0,0,1) - - - - - The major radius of the ellipse. - - - - - - The minor radius of the ellipse. - - - - - - The focal distance of the ellipse. - - - - - - The first focus is on the positive side of the major axis of the ellipse. - - - - - - The second focus is on the negative side of the major axis of the ellipse. - - - - - diff --git a/src/Mod/Part/App/FT2FC.cpp b/src/Mod/Part/App/FT2FC.cpp index 31fecd7422..76ca6959cf 100644 --- a/src/Mod/Part/App/FT2FC.cpp +++ b/src/Mod/Part/App/FT2FC.cpp @@ -76,16 +76,14 @@ using namespace Part; -using UNICHAR = unsigned long; // ul is FT2's codepoint type <=> Py_UNICODE2/4 - // Private function prototypes -PyObject* getGlyphContours(FT_Face FTFace, UNICHAR currchar, double PenPos, double Scale,int charNum, double tracking); -FT_Vector getKerning(FT_Face FTFace, UNICHAR lc, UNICHAR rc); +PyObject* getGlyphContours(FT_Face FTFace, FT_ULong currchar, double PenPos, double Scale,int charNum, double tracking); +FT_Vector getKerning(FT_Face FTFace, FT_ULong lc, FT_ULong rc); TopoDS_Wire edgesToWire(std::vector Edges); int calcClockDir(std::vector points); // for compatibility with old version - separate path & filename -PyObject* FT2FC(const Py_UNICODE *PyUString, +PyObject* FT2FC(const Py_UCS4 *PyUString, const size_t length, const char *FontPath, const char *FontName, @@ -99,7 +97,7 @@ PyObject* FT2FC(const Py_UNICODE *PyUString, } // get string's wires (contours) in FC/OCC coords -PyObject* FT2FC(const Py_UNICODE *PyUString, +PyObject* FT2FC(const Py_UCS4 *PyUString, const size_t length, const char *FontSpec, const double stringheight, // fc coords @@ -114,7 +112,7 @@ PyObject* FT2FC(const Py_UNICODE *PyUString, std::stringstream ErrorMsg; double PenPos = 0, scalefactor; - UNICHAR prevchar = 0, currchar = 0; + FT_ULong prevchar = 0, currchar = 0; int cadv; size_t i; Py::List CharList; @@ -215,7 +213,7 @@ struct FTDC_Ctx { std::vector wDir; std::vector Edges; std::vector polyPoints; - UNICHAR currchar; + FT_ULong currchar; FT_Vector LastVert; Handle(Geom_Surface) surf; }; @@ -321,7 +319,7 @@ static FT_Outline_Funcs FTcbFuncs = { //********** FT2FC Helpers // get glyph outline in wires -PyObject* getGlyphContours(FT_Face FTFace, UNICHAR currchar, double PenPos, double Scale, int charNum, double tracking) { +PyObject* getGlyphContours(FT_Face FTFace, FT_ULong currchar, double PenPos, double Scale, int charNum, double tracking) { FT_Error error = 0; std::stringstream ErrorMsg; gp_Pnt origin = gp_Pnt(0.0,0.0,0.0); @@ -393,7 +391,7 @@ PyObject* getGlyphContours(FT_Face FTFace, UNICHAR currchar, double PenPos, doub // get kerning values for this char pair //TODO: should check FT_HASKERNING flag? returns (0,0) if no kerning? -FT_Vector getKerning(FT_Face FTFace, UNICHAR lc, UNICHAR rc) { +FT_Vector getKerning(FT_Face FTFace, FT_ULong lc, FT_ULong rc) { FT_Vector retXY; FT_Error error; std::stringstream ErrorMsg; diff --git a/src/Mod/Part/App/FT2FC.h b/src/Mod/Part/App/FT2FC.h index f7b8eb4968..907039496d 100644 --- a/src/Mod/Part/App/FT2FC.h +++ b/src/Mod/Part/App/FT2FC.h @@ -30,14 +30,14 @@ #ifndef FT2FC_H #define FT2FC_H // public functions -PyObject* FT2FC(const Py_UNICODE *unichars, +PyObject* FT2FC(const Py_UCS4 *unichars, const size_t length, const char *FontPath, const char *FontName, const double stringheight, const double tracking); -PyObject* FT2FC(const Py_UNICODE *unichars, +PyObject* FT2FC(const Py_UCS4 *unichars, const size_t length, const char *FontSpec, const double stringheight, diff --git a/src/Mod/Part/App/FeatureExtrusion.cpp b/src/Mod/Part/App/FeatureExtrusion.cpp index 800199733c..abffc334bd 100644 --- a/src/Mod/Part/App/FeatureExtrusion.cpp +++ b/src/Mod/Part/App/FeatureExtrusion.cpp @@ -200,6 +200,8 @@ bool Extrusion::fetchAxisLink(const App::PropertyLinkSub& axisLink, Base::Vector ExtrusionParameters Extrusion::computeFinalParameters() { + using std::numbers::pi; + ExtrusionParameters result; Base::Vector3d dir; switch (this->DirMode.getValue()) { @@ -244,11 +246,11 @@ ExtrusionParameters Extrusion::computeFinalParameters() result.solid = this->Solid.getValue(); - result.taperAngleFwd = this->TaperAngle.getValue() * M_PI / 180.0; - if (fabs(result.taperAngleFwd) > M_PI * 0.5 - Precision::Angular()) + result.taperAngleFwd = this->TaperAngle.getValue() * pi / 180.0; + if (fabs(result.taperAngleFwd) > pi * 0.5 - Precision::Angular()) throw Base::ValueError("Magnitude of taper angle matches or exceeds 90 degrees. That is too much."); - result.taperAngleRev = this->TaperAngleRev.getValue() * M_PI / 180.0; - if (fabs(result.taperAngleRev) > M_PI * 0.5 - Precision::Angular()) + result.taperAngleRev = this->TaperAngleRev.getValue() * pi / 180.0; + if (fabs(result.taperAngleRev) > pi * 0.5 - Precision::Angular()) throw Base::ValueError("Magnitude of taper angle matches or exceeds 90 degrees. That is too much."); result.faceMakerClass = this->FaceMakerClass.getValue(); diff --git a/src/Mod/Part/App/FeatureRevolution.cpp b/src/Mod/Part/App/FeatureRevolution.cpp index e27007f98a..2a59f99bce 100644 --- a/src/Mod/Part/App/FeatureRevolution.cpp +++ b/src/Mod/Part/App/FeatureRevolution.cpp @@ -143,7 +143,7 @@ App::DocumentObjectExecReturn *Revolution::execute() gp_Ax1 revAx(pnt, dir); //read out revolution angle - double angle = Angle.getValue()/180.0f*M_PI; + double angle = Angle.getValue()/180.0f * std::numbers::pi; if (fabs(angle) < Precision::Angular()) angle = angle_edge; diff --git a/src/Mod/Part/App/Geom2d/ArcOfCircle2d.pyi b/src/Mod/Part/App/Geom2d/ArcOfCircle2d.pyi new file mode 100644 index 0000000000..09edd36b7c --- /dev/null +++ b/src/Mod/Part/App/Geom2d/ArcOfCircle2d.pyi @@ -0,0 +1,40 @@ +from Metadata import export, constmethod +from typing import Final, overload +from Part.Geom2d import ArcOfConic2d + +@export( + PythonName="Part.Geom2d.ArcOfCircle2d", + Twin="Geom2dArcOfCircle", + TwinPointer="Geom2dArcOfCircle", + Include="Mod/Part/App/Geometry2d.h", + FatherInclude="Mod/Part/App/Geom2d/ArcOfConic2dPy.h", + Constructor=True, +) +class ArcOfCircle2d(ArcOfConic2d): + """ + Describes a portion of a circle + + Author: Werner Mayer (wmayer[at]users.sourceforge.net) + Licence: LGPL + """ + + Radius: float = ... + """The radius of the circle.""" + + Circle: Final[object] = ... + """The internal circle representation""" + + @overload + def __init__(self, Radius: float, Circle: object) -> None: ... + """ + ArcOfCircle2d(Radius, Circle) -> None + + Constructor for ArcOfCircle2d. + + Parameters: + Radius : float + The radius of the circle. + Circle : object + The internal circle representation. + """ + ... \ No newline at end of file diff --git a/src/Mod/Part/App/Geom2d/ArcOfCircle2dPy.xml b/src/Mod/Part/App/Geom2d/ArcOfCircle2dPy.xml deleted file mode 100644 index 670eaf5346..0000000000 --- a/src/Mod/Part/App/Geom2d/ArcOfCircle2dPy.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - Describes a portion of a circle - - - - The radius of the circle. - - - - - - The internal circle representation - - - - - diff --git a/src/Mod/Part/App/Geom2d/ArcOfConic2d.pyi b/src/Mod/Part/App/Geom2d/ArcOfConic2d.pyi new file mode 100644 index 0000000000..a937287f37 --- /dev/null +++ b/src/Mod/Part/App/Geom2d/ArcOfConic2d.pyi @@ -0,0 +1,38 @@ +from Base.Metadata import export, constmethod, overload +from typing import Final +from Part.Geom2d import Curve2d + +@export( + PythonName="Part.Geom2d.ArcOfConic2d", + Twin="Geom2dArcOfConic", + TwinPointer="Geom2dArcOfConic", + Include="Mod/Part/App/Geometry2d.h", + Namespace="Part", + FatherInclude="Mod/Part/App/Geom2d/Curve2dPy.h", + Constructor=True, +) +class ArcOfConic2d(Curve2d): + """ + Describes an abstract arc of conic in 2d space. + + Author: Werner Mayer (wmayer[at]users.sourceforge.net) + Licence: LGPL + """ + + Location: object = ... + """Location of the conic.""" + + Eccentricity: Final[float] = ... + """ + returns the eccentricity value of the conic e. + e = 0 for a circle + 0 < e < 1 for an ellipse (e = 0 if MajorRadius = MinorRadius) + e > 1 for a hyperbola + e = 1 for a parabola + """ + + XAxis: object = ... + """The X axis direction of the circle.""" + + YAxis: object = ... + """The Y axis direction of the circle.""" diff --git a/src/Mod/Part/App/Geom2d/ArcOfConic2dPy.xml b/src/Mod/Part/App/Geom2d/ArcOfConic2dPy.xml deleted file mode 100644 index aeceac6e0a..0000000000 --- a/src/Mod/Part/App/Geom2d/ArcOfConic2dPy.xml +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - Describes an abstract arc of conic in 2d space. - - - - Location of the conic. - - - - - - - returns the eccentricity value of the conic e. - e = 0 for a circle - 0 < e < 1 for an ellipse (e = 0 if MajorRadius = MinorRadius) - e > 1 for a hyperbola - e = 1 for a parabola - - - - - - - The X axis direction of the circle. - - - - - - The Y axis direction of the circle. - - - - - diff --git a/src/Mod/Part/App/Geom2d/ArcOfEllipse2d.pyi b/src/Mod/Part/App/Geom2d/ArcOfEllipse2d.pyi new file mode 100644 index 0000000000..62a4613216 --- /dev/null +++ b/src/Mod/Part/App/Geom2d/ArcOfEllipse2d.pyi @@ -0,0 +1,31 @@ +from Metadata import export, constmethod +from typing import Final, overload +from Part import ArcOfConic2d + +@export( + PythonName="Part.Geom2d.ArcOfEllipse2d", + Twin="Geom2dArcOfEllipse", + TwinPointer="Geom2dArcOfEllipse", + Include="Mod/Part/App/Geometry2d.h", + FatherInclude="Mod/Part/App/Geom2d/ArcOfConic2dPy.h", + Constructor=True, +) +class ArcOfEllipse2d(ArcOfConic2d): + """ + Describes a portion of an ellipse + Author: Werner Mayer (wmayer[at]users.sourceforge.net) + Licence: LGPL + """ + + MajorRadius: float = ... + """The major radius of the ellipse.""" + + MinorRadius: float = ... + """The minor radius of the ellipse.""" + + Ellipse: Final[object] = ... + """The internal ellipse representation""" + + @overload + def __init__(self) -> None: + ... diff --git a/src/Mod/Part/App/Geom2d/ArcOfEllipse2dPy.xml b/src/Mod/Part/App/Geom2d/ArcOfEllipse2dPy.xml deleted file mode 100644 index 6a9773a683..0000000000 --- a/src/Mod/Part/App/Geom2d/ArcOfEllipse2dPy.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - Describes a portion of an ellipse - - - - The major radius of the ellipse. - - - - - - The minor radius of the ellipse. - - - - - - The internal ellipse representation - - - - - diff --git a/src/Mod/Part/App/Geom2d/ArcOfHyperbola2d.pyi b/src/Mod/Part/App/Geom2d/ArcOfHyperbola2d.pyi new file mode 100644 index 0000000000..84dc9e95e8 --- /dev/null +++ b/src/Mod/Part/App/Geom2d/ArcOfHyperbola2d.pyi @@ -0,0 +1,30 @@ +from Base.Metadata import export, constmethod, overload +from typing import Final +from Part.Geom2d import ArcOfConic2d + +@export( + PythonName="Part.Geom2d.ArcOfHyperbola2d", + Twin="Geom2dArcOfHyperbola", + TwinPointer="Geom2dArcOfHyperbola", + Include="Mod/Part/App/Geometry2d.h", + FatherInclude="Mod/Part/App/Geom2d/ArcOfConic2dPy.h", + Constructor=True, +) +class ArcOfHyperbola2d(ArcOfConic2d): + """ + Describes a portion of an hyperbola + Author: Werner Mayer (wmayer@users.sourceforge.net) Licence: LGPL + """ + + MajorRadius: float = ... + """The major radius of the hyperbola.""" + + MinorRadius: float = ... + """The minor radius of the hyperbola.""" + + Hyperbola: Final[object] = ... + """The internal hyperbola representation""" + + @overload + def __init__(self) -> None: + ... diff --git a/src/Mod/Part/App/Geom2d/ArcOfHyperbola2dPy.xml b/src/Mod/Part/App/Geom2d/ArcOfHyperbola2dPy.xml deleted file mode 100644 index 25755bae6e..0000000000 --- a/src/Mod/Part/App/Geom2d/ArcOfHyperbola2dPy.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - Describes a portion of an hyperbola - - - - The major radius of the hyperbola. - - - - - - The minor radius of the hyperbola. - - - - - - The internal hyperbola representation - - - - - diff --git a/src/Mod/Part/App/Geom2d/ArcOfParabola2d.pyi b/src/Mod/Part/App/Geom2d/ArcOfParabola2d.pyi new file mode 100644 index 0000000000..1e545310b9 --- /dev/null +++ b/src/Mod/Part/App/Geom2d/ArcOfParabola2d.pyi @@ -0,0 +1,29 @@ +from Metadata import export, constmethod, overload +from typing import Final +from Part.Geom2d import ArcOfConic2d + +@export( + PythonName="Part.Geom2d.ArcOfParabola2d", + Twin="Geom2dArcOfParabola", + TwinPointer="Geom2dArcOfParabola", + Include="Mod/Part/App/Geometry2d.h", + FatherInclude="Mod/Part/App/Geom2d/ArcOfConic2dPy.h", + Constructor=True, +) +class ArcOfParabola2d(ArcOfConic2d): + """ + Describes a portion of a parabola. + + Author: Werner Mayer + Licence: LGPL + """ + + Focal: float = ... + """The focal length of the parabola.""" + + Parabola: Final[object] = ... + """The internal parabola representation.""" + + @overload + def __init__(self) -> None: + ... diff --git a/src/Mod/Part/App/Geom2d/ArcOfParabola2dPy.xml b/src/Mod/Part/App/Geom2d/ArcOfParabola2dPy.xml deleted file mode 100644 index 9cb9d752a0..0000000000 --- a/src/Mod/Part/App/Geom2d/ArcOfParabola2dPy.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - Describes a portion of a parabola. - - - - The focal length of the parabola. - - - - - - The internal parabola representation. - - - - - diff --git a/src/Mod/Part/App/Geom2d/BSplineCurve2d.pyi b/src/Mod/Part/App/Geom2d/BSplineCurve2d.pyi new file mode 100644 index 0000000000..5c452b8e0d --- /dev/null +++ b/src/Mod/Part/App/Geom2d/BSplineCurve2d.pyi @@ -0,0 +1,437 @@ +from Base.Metadata import export, constmethod +from typing import Final, overload +from Part.Curve2d import Curve2d +from Base.Vector import Vector + +@export( + Twin="Geom2dBSplineCurve", + TwinPointer="Geom2dBSplineCurve", + PythonName="Part.Geom2d.BSplineCurve2d", + FatherInclude="Mod/Part/App/Geom2d/Curve2dPy.h", + Include="Mod/Part/App/Geometry2d.h", + Constructor=True, +) +class BSplineCurve2d(Curve2d): + """ + Describes a B-Spline curve in 3D space + + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + Degree: Final[int] = ... + """Returns the polynomial degree of this B-Spline curve.""" + + MaxDegree: Final[int] = ... + """Returns the value of the maximum polynomial degree of any + B-Spline curve curve. This value is 25.""" + + NbPoles: Final[int] = ... + """Returns the number of poles of this B-Spline curve.""" + + NbKnots: Final[int] = ... + """Returns the number of knots of this B-Spline curve.""" + + StartPoint: Final[object] = ... + """Returns the start point of this B-Spline curve.""" + + EndPoint: Final[object] = ... + """Returns the end point of this B-Spline curve.""" + + FirstUKnotIndex: Final[object] = ... + """Returns the index in the knot array of the knot + corresponding to the first or last parameter + of this B-Spline curve.""" + + LastUKnotIndex: Final[object] = ... + """Returns the index in the knot array of the knot + corresponding to the first or last parameter + of this B-Spline curve.""" + + KnotSequence: Final[list] = ... + """Returns the knots sequence of this B-Spline curve.""" + + def isRational(self) -> bool: + """ + Returns true if this B-Spline curve is rational. + A B-Spline curve is rational if, at the time of construction, the weight table has been initialized. + """ + ... + + def isPeriodic(self) -> bool: + """ + Returns true if this BSpline curve is periodic. + """ + ... + + def isClosed(self) -> bool: + """ + Returns true if the distance between the start point and end point of + this B-Spline curve is less than or equal to gp::Resolution(). + """ + ... + + def increaseDegree(self, Degree: int) -> None: + """ + increaseDegree(Int=Degree) + + Increases the degree of this B-Spline curve to Degree. + As a result, the poles, weights and multiplicities tables + are modified; the knots table is not changed. Nothing is + done if Degree is less than or equal to the current degree. + """ + ... + + @overload + def increaseMultiplicity(self, index: int, mult: int) -> None: + ... + + @overload + def increaseMultiplicity(self, start: int, end: int, mult: int) -> None: + ... + + def increaseMultiplicity(self, *args, **kwargs) -> None: + """ + increaseMultiplicity(int index, int mult) + increaseMultiplicity(int start, int end, int mult) + Increases multiplicity of knots up to mult. + + index: the index of a knot to modify (1-based) + start, end: index range of knots to modify. + If mult is lower or equal to the current multiplicity nothing is done. + If mult is higher than the degree the degree is used. + """ + ... + + def incrementMultiplicity(self, start: int, end: int, mult: int) -> None: + """ + incrementMultiplicity(int start, int end, int mult) + Raises multiplicity of knots by mult. + + start, end: index range of knots to modify. + """ + ... + + def insertKnot(self, u: float, mult: int = 1, tol: float = 0.0) -> None: + """ + insertKnot(u, mult = 1, tol = 0.0) + + Inserts a knot value in the sequence of knots. If u is an existing knot the multiplicity is increased by mult. + """ + ... + + def insertKnots(self, list_of_floats: list[float], list_of_ints: list[int], tol: float = 0.0, bool_add: bool = True) -> None: + """ + insertKnots(list_of_floats, list_of_ints, tol = 0.0, bool_add = True) + + Inserts a set of knots values in the sequence of knots. + + For each u = list_of_floats[i], mult = list_of_ints[i] + + If u is an existing knot the multiplicity is increased by mult if bool_add is + True, otherwise increased to mult. + + If u is not on the parameter range nothing is done. + + If the multiplicity is negative or null nothing is done. The new multiplicity + is limited to the degree. + + The tolerance criterion for knots equality is the max of Epsilon(U) and ParametricTolerance. + """ + ... + + def removeKnot(self, Index: int, M: int, tol: float) -> None: + """ + removeKnot(Index, M, tol) + + Reduces the multiplicity of the knot of index Index to M. + If M is equal to 0, the knot is removed. + With a modification of this type, the array of poles is also modified. + Two different algorithms are systematically used to compute the new + poles of the curve. If, for each pole, the distance between the pole + calculated using the first algorithm and the same pole calculated using + the second algorithm, is less than Tolerance, this ensures that the curve + is not modified by more than Tolerance. Under these conditions, true is + returned; otherwise, false is returned. + + A low tolerance is used to prevent modification of the curve. + A high tolerance is used to 'smooth' the curve. + """ + ... + + def segment(self, u1: float, u2: float) -> None: + """ + segment(u1,u2) + Modifies this B-Spline curve by segmenting it. + """ + ... + + def setKnot(self, value: float) -> None: + """ + Set a knot of the B-Spline curve. + """ + ... + + def getKnot(self, index: int) -> float: + """ + Get a knot of the B-Spline curve. + """ + ... + + def setKnots(self, knots: list[float]) -> None: + """ + Set knots of the B-Spline curve. + """ + ... + + def getKnots(self) -> list[float]: + """ + Get all knots of the B-Spline curve. + """ + ... + + def setPole(self, P: Vector, Index: int) -> None: + """ + Modifies this B-Spline curve by assigning P to the pole of index Index in the poles table. + """ + ... + + def getPole(self, Index: int) -> Vector: + """ + Get a pole of the B-Spline curve. + """ + ... + + def getPoles(self) -> list[Vector]: + """ + Get all poles of the B-Spline curve. + """ + ... + + def setWeight(self, weight: float, Index: int) -> None: + """ + Set a weight of the B-Spline curve. + """ + ... + + def getWeight(self, Index: int) -> float: + """ + Get a weight of the B-Spline curve. + """ + ... + + def getWeights(self) -> list[float]: + """ + Get all weights of the B-Spline curve. + """ + ... + + def getPolesAndWeights(self) -> tuple[list[Vector], list[float]]: + """ + Returns the table of poles and weights in homogeneous coordinates. + """ + ... + + @constmethod + def getResolution(self) -> float: + """ + Computes for this B-Spline curve the parametric tolerance (UTolerance) + for a given 3D tolerance (Tolerance3D). + If f(t) is the equation of this B-Spline curve, the parametric tolerance ensures that: + |t1-t0| < UTolerance =""==> |f(t1)-f(t0)| < Tolerance3D + """ + ... + + def movePoint(self, U: float, P: Vector, Index1: int, Index2: int) -> tuple[int, int]: + """ + movePoint(U, P, Index1, Index2) + + Moves the point of parameter U of this B-Spline curve to P. + Index1 and Index2 are the indexes in the table of poles of this B-Spline curve + of the first and last poles designated to be moved. + + Returns: (FirstModifiedPole, LastModifiedPole). They are the indexes of the + first and last poles which are effectively modified. + """ + ... + + def setNotPeriodic(self) -> None: + """ + Changes this B-Spline curve into a non-periodic curve. + If this curve is already non-periodic, it is not modified. + """ + ... + + def setPeriodic(self) -> None: + """ + Changes this B-Spline curve into a periodic curve. + """ + ... + + def setOrigin(self, Index: int) -> None: + """ + Assigns the knot of index Index in the knots table as the origin of this periodic B-Spline curve. + As a consequence, the knots and poles tables are modified. + """ + ... + + def getMultiplicity(self, index: int) -> int: + """ + Returns the multiplicity of the knot of index from the knots table of this B-Spline curve. + """ + ... + + def getMultiplicities(self) -> list[int]: + """ + Returns the multiplicities table M of the knots of this B-Spline curve. + """ + ... + + def approximate(self, **kwargs) -> None: + """ + Replaces this B-Spline curve by approximating a set of points. + The function accepts keywords as arguments. + + approximate2(Points = list_of_points) + + Optional arguments : + + DegMin = integer (3) : Minimum degree of the curve. + DegMax = integer (8) : Maximum degree of the curve. + Tolerance = float (1e-3) : approximating tolerance. + Continuity = string ('C2') : Desired continuity of the curve. + Possible values : 'C0','G1','C1','G2','C2','C3','CN' + + LengthWeight = float, CurvatureWeight = float, TorsionWeight = float + If one of these arguments is not null, the functions approximates the + points using variational smoothing algorithm, which tries to minimize + additional criterium: + LengthWeight*CurveLength + CurvatureWeight*Curvature + TorsionWeight*Torsion + Continuity must be C0, C1 or C2, else defaults to C2. + + Parameters = list of floats : knot sequence of the approximated points. + This argument is only used if the weights above are all null. + + ParamType = string ('Uniform','Centripetal' or 'ChordLength') + Parameterization type. Only used if weights and Parameters above aren't specified. + + Note : Continuity of the spline defaults to C2. However, it may not be applied if + it conflicts with other parameters ( especially DegMax ). + """ + ... + + def getCardinalSplineTangents(self, **kwargs) -> None: + """ + Compute the tangents for a Cardinal spline + """ + ... + + def interpolate(self, **kwargs) -> None: + """ + Replaces this B-Spline curve by interpolating a set of points. + The function accepts keywords as arguments. + + interpolate(Points = list_of_points) + + Optional arguments : + + PeriodicFlag = bool (False) : Sets the curve closed or opened. + Tolerance = float (1e-6) : interpolating tolerance + + Parameters : knot sequence of the interpolated points. + If not supplied, the function defaults to chord-length parameterization. + If PeriodicFlag == True, one extra parameter must be appended. + + EndPoint Tangent constraints : + + InitialTangent = vector, FinalTangent = vector + specify tangent vectors for starting and ending points + of the BSpline. Either none, or both must be specified. + + Full Tangent constraints : + + Tangents = list_of_vectors, TangentFlags = list_of_bools + Both lists must have the same length as Points list. + Tangents specifies the tangent vector of each point in Points list. + TangentFlags (bool) activates or deactivates the corresponding tangent. + These arguments will be ignored if EndPoint Tangents (above) are also defined. + + Note : Continuity of the spline defaults to C2. However, if periodic, or tangents + are supplied, the continuity will drop to C1. + """ + ... + + def buildFromPoles(self, poles: list[Vector]) -> None: + """ + Builds a B-Spline by a list of poles. + """ + ... + + @overload + def buildFromPolesMultsKnots(self, poles: list[Vector], mults: tuple[int, ...], knots: tuple[float, ...], periodic: bool, degree: int) -> None: + ... + + @overload + def buildFromPolesMultsKnots(self, poles: list[Vector], mults: tuple[int, ...], knots: tuple[float, ...], periodic: bool, degree: int, weights: tuple[float, ...], CheckRational: bool) -> None: + ... + + def buildFromPolesMultsKnots(self, **kwargs) -> None: + """ + Builds a B-Spline by a lists of Poles, Mults, Knots. + arguments: poles (sequence of Base.Vector), + [mults , knots, periodic, degree, weights (sequence of float), CheckRational] + + Examples: + from FreeCAD import Base + import Part + V=Base.Vector + poles=[V(-10,-10),V(10,-10),V(10,10),V(-10,10)] + + # non-periodic spline + n=Part.BSplineCurve() + n.buildFromPolesMultsKnots(poles,(3,1,3),(0,0.5,1),False,2) + Part.show(n.toShape()) + + # periodic spline + p=Part.BSplineCurve() + p.buildFromPolesMultsKnots(poles,(1,1,1,1,1),(0,0.25,0.5,0.75,1),True,2) + Part.show(p.toShape()) + + # periodic and rational spline + r=Part.BSplineCurve() + r.buildFromPolesMultsKnots(poles,(1,1,1,1,1),(0,0.25,0.5,0.75,1),True,2,(1,0.8,0.7,0.2)) + Part.show(r.toShape()) + """ + ... + + def toBezier(self) -> list: + """ + Build a list of Bezier splines. + """ + ... + + def toBiArcs(self, tolerance: float) -> list: + """ + toBiArcs(tolerance) -> list. + Build a list of arcs and lines to approximate the B-spline. + """ + ... + + def join(self, other: "BSplineCurve2d") -> "BSplineCurve2d": + """ + Build a new spline by joining this and a second spline. + """ + ... + + def makeC1Continuous(self, tol: float = 1e-6, ang_tol: float = 1e-7) -> "BSplineCurve2d": + """ + makeC1Continuous(tol = 1e-6, ang_tol = 1e-7) + + Reduces as far as possible the multiplicities of the knots of this BSpline + (keeping the geometry). It returns a new BSpline, which could still be C0. + tol is a geometrical tolerance. + The tol_ang is angular tolerance, in radians. It sets tolerable angle mismatch + of the tangents on the left and on the right to decide if the curve is G1 or + not at a given point. + """ + ... \ No newline at end of file diff --git a/src/Mod/Part/App/Geom2d/BSplineCurve2dPy.xml b/src/Mod/Part/App/Geom2d/BSplineCurve2dPy.xml deleted file mode 100644 index 9901aacdbd..0000000000 --- a/src/Mod/Part/App/Geom2d/BSplineCurve2dPy.xml +++ /dev/null @@ -1,411 +0,0 @@ - - - - - - Describes a B-Spline curve in 3D space - - - - Returns the polynomial degree of this B-Spline curve. - - - - - - Returns the value of the maximum polynomial degree of any - B-Spline curve curve. This value is 25. - - - - - - Returns the number of poles of this B-Spline curve. - - - - - - Returns the number of knots of this B-Spline curve. - - - - - - Returns the start point of this B-Spline curve. - - - - - - Returns the end point of this B-Spline curve. - - - - - - Returns the index in the knot array of the knot - corresponding to the first or last parameter - of this B-Spline curve. - - - - - - Returns the index in the knot array of the knot - corresponding to the first or last parameter - of this B-Spline curve. - - - - - - Returns the knots sequence of this B-Spline curve. - - - - - - Returns true if this B-Spline curve is rational. - A B-Spline curve is rational if, at the time of construction, - the weight table has been initialized. - - - - - Returns true if this BSpline curve is periodic. - - - - - Returns true if the distance between the start point and end point of - this B-Spline curve is less than or equal to gp::Resolution(). - - - - - increase(Int=Degree) - Increases the degree of this B-Spline curve to Degree. - As a result, the poles, weights and multiplicities tables - are modified; the knots table is not changed. Nothing is - done if Degree is less than or equal to the current degree. - - - - - increaseMultiplicity(int index, int mult) - increaseMultiplicity(int start, int end, int mult) - Increases multiplicity of knots up to mult. - - index: the index of a knot to modify (1-based) - start, end: index range of knots to modify. - If mult is lower or equal to the current multiplicity nothing is done. - If mult is higher than the degree the degree is used. - - - - - incrementMultiplicity(int start, int end, int mult) - Raises multiplicity of knots by mult. - - start, end: index range of knots to modify. - - - - - insertKnot(u, mult = 1, tol = 0.0) - Inserts a knot value in the sequence of knots. If u is an existing knot the - multiplicity is increased by mult. - - - - - insertKnots(list_of_floats, list_of_ints, tol = 0.0, bool_add = True) - - Inserts a set of knots values in the sequence of knots. - - For each u = list_of_floats[i], mult = list_of_ints[i] - - If u is an existing knot the multiplicity is increased by mult if bool_add is - True, otherwise increased to mult. - - If u is not on the parameter range nothing is done. - - If the multiplicity is negative or null nothing is done. The new multiplicity - is limited to the degree. - - The tolerance criterion for knots equality is the max of Epsilon(U) and ParametricTolerance. - - - - - removeKnot(Index, M, tol) - - Reduces the multiplicity of the knot of index Index to M. - If M is equal to 0, the knot is removed. - With a modification of this type, the array of poles is also modified. - Two different algorithms are systematically used to compute the new - poles of the curve. If, for each pole, the distance between the pole - calculated using the first algorithm and the same pole calculated using - the second algorithm, is less than Tolerance, this ensures that the curve - is not modified by more than Tolerance. Under these conditions, true is - returned; otherwise, false is returned. - - A low tolerance is used to prevent modification of the curve. - A high tolerance is used to 'smooth' the curve. - - - - - segment(u1,u2) - Modifies this B-Spline curve by segmenting it. - - - - - Set a knot of the B-Spline curve. - - - - - Get a knot of the B-Spline curve. - - - - - Set knots of the B-Spline curve. - - - - - Get all knots of the B-Spline curve. - - - - - Modifies this B-Spline curve by assigning P - to the pole of index Index in the poles table. - - - - - Get a pole of the B-Spline curve. - - - - - Get all poles of the B-Spline curve. - - - - - Set a weight of the B-Spline curve. - - - - - Get a weight of the B-Spline curve. - - - - - Get all weights of the B-Spline curve. - - - - - Returns the table of poles and weights in homogeneous coordinates. - - - - - Computes for this B-Spline curve the parametric tolerance (UTolerance) - for a given 3D tolerance (Tolerance3D). - If f(t) is the equation of this B-Spline curve, the parametric tolerance - ensures that: - |t1-t0| < UTolerance =""==> |f(t1)-f(t0)| < Tolerance3D - - - - - movePoint(U, P, Index1, Index2) - Moves the point of parameter U of this B-Spline curve to P. - Index1 and Index2 are the indexes in the table of poles of this B-Spline curve - of the first and last poles designated to be moved. - - Returns: (FirstModifiedPole, LastModifiedPole). They are the indexes of the - first and last poles which are effectively modified. - - - - - Changes this B-Spline curve into a non-periodic curve. - If this curve is already non-periodic, it is not modified. - - - - - Changes this B-Spline curve into a periodic curve. - - - - - Assigns the knot of index Index in the knots table - as the origin of this periodic B-Spline curve. As a consequence, - the knots and poles tables are modified. - - - - - Returns the multiplicity of the knot of index - from the knots table of this B-Spline curve. - - - - - Returns the multiplicities table M of the knots of this B-Spline curve. - - - - - Replaces this B-Spline curve by approximating a set of points. - The function accepts keywords as arguments. - - approximate2(Points = list_of_points) - - Optional arguments : - - DegMin = integer (3) : Minimum degree of the curve. - DegMax = integer (8) : Maximum degree of the curve. - Tolerance = float (1e-3) : approximating tolerance. - Continuity = string ('C2') : Desired continuity of the curve. - Possible values : 'C0','G1','C1','G2','C2','C3','CN' - - LengthWeight = float, CurvatureWeight = float, TorsionWeight = float - If one of these arguments is not null, the functions approximates the - points using variational smoothing algorithm, which tries to minimize - additional criterium: - LengthWeight*CurveLength + CurvatureWeight*Curvature + TorsionWeight*Torsion - Continuity must be C0, C1 or C2, else defaults to C2. - - Parameters = list of floats : knot sequence of the approximated points. - This argument is only used if the weights above are all null. - - ParamType = string ('Uniform','Centripetal' or 'ChordLength') - Parameterization type. Only used if weights and Parameters above aren't specified. - - Note : Continuity of the spline defaults to C2. However, it may not be applied if - it conflicts with other parameters ( especially DegMax ). - - - - - Compute the tangents for a Cardinal spline - - - - - Replaces this B-Spline curve by interpolating a set of points. - The function accepts keywords as arguments. - - interpolate(Points = list_of_points) - - Optional arguments : - - PeriodicFlag = bool (False) : Sets the curve closed or opened. - Tolerance = float (1e-6) : interpolating tolerance - - Parameters : knot sequence of the interpolated points. - If not supplied, the function defaults to chord-length parameterization. - If PeriodicFlag == True, one extra parameter must be appended. - - EndPoint Tangent constraints : - - InitialTangent = vector, FinalTangent = vector - specify tangent vectors for starting and ending points - of the BSpline. Either none, or both must be specified. - - Full Tangent constraints : - - Tangents = list_of_vectors, TangentFlags = list_of_bools - Both lists must have the same length as Points list. - Tangents specifies the tangent vector of each point in Points list. - TangentFlags (bool) activates or deactivates the corresponding tangent. - These arguments will be ignored if EndPoint Tangents (above) are also defined. - - Note : Continuity of the spline defaults to C2. However, if periodic, or tangents - are supplied, the continuity will drop to C1. - - - - - Builds a B-Spline by a list of poles. - - - - - Builds a B-Spline by a lists of Poles, Mults, Knots. - arguments: poles (sequence of Base.Vector), - [mults , knots, periodic, degree, weights (sequence of float), CheckRational] - - Examples: - from FreeCAD import Base - import Part - V=Base.Vector - poles=[V(-10,-10),V(10,-10),V(10,10),V(-10,10)] - - # non-periodic spline - n=Part.BSplineCurve() - n.buildFromPolesMultsKnots(poles,(3,1,3),(0,0.5,1),False,2) - Part.show(n.toShape()) - - # periodic spline - p=Part.BSplineCurve() - p.buildFromPolesMultsKnots(poles,(1,1,1,1,1),(0,0.25,0.5,0.75,1),True,2) - Part.show(p.toShape()) - - # periodic and rational spline - r=Part.BSplineCurve() - r.buildFromPolesMultsKnots(poles,(1,1,1,1,1),(0,0.25,0.5,0.75,1),True,2,(1,0.8,0.7,0.2)) - Part.show(r.toShape()) - - - - - Build a list of Bezier splines. - - - - - Build a list of arcs and lines to approximate the B-spline. - toBiArcs(tolerance) -> list. - - - - - Build a new spline by joining this and a second spline. - - - - - makeC1Continuous(tol = 1e-6, ang_tol = 1e-7) - Reduces as far as possible the multiplicities of the knots of this BSpline - (keeping the geometry). It returns a new BSpline, which could still be C0. - tol is a geometrical tolerance. - The tol_ang is angular tolerance, in radians. It sets tolerable angle mismatch - of the tangents on the left and on the right to decide if the curve is G1 or - not at a given point. - - - - diff --git a/src/Mod/Part/App/Geom2d/BezierCurve2d.pyi b/src/Mod/Part/App/Geom2d/BezierCurve2d.pyi new file mode 100644 index 0000000000..dd80a1a75c --- /dev/null +++ b/src/Mod/Part/App/Geom2d/BezierCurve2d.pyi @@ -0,0 +1,141 @@ +from Base.Metadata import export, constmethod +from Part.Curve2d import Curve2d +from typing import Final, List + +@export( + Twin="Geom2dBezierCurve", + TwinPointer="Geom2dBezierCurve", + PythonName="Part.Geom2d.BezierCurve2d", + FatherInclude="Mod/Part/App/Geom2d/Curve2dPy.h", + Include="Mod/Part/App/Geometry2d.h", + Constructor=True, +) +class BezierCurve2d(Curve2d): + """ + Describes a rational or non-rational Bezier curve in 2d space: + -- a non-rational Bezier curve is defined by a table of poles (also called control points) + -- a rational Bezier curve is defined by a table of poles with varying weights + + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + Degree: Final[int] = ... + """Returns the polynomial degree of this Bezier curve, which is equal to the number of poles minus 1.""" + + MaxDegree: Final[int] = ... + """Returns the value of the maximum polynomial degree of any Bezier curve curve. This value is 25.""" + + NbPoles: Final[int] = ... + """Returns the number of poles of this Bezier curve.""" + + StartPoint: Final[object] = ... + """Returns the start point of this Bezier curve.""" + + EndPoint: Final[object] = ... + """Returns the end point of this Bezier curve.""" + + def isRational(self) -> bool: + """ + Returns false if the weights of all the poles of this Bezier curve are equal. + """ + ... + + def isPeriodic(self) -> bool: + """ + Returns false. + """ + ... + + def isClosed(self) -> bool: + """ + Returns true if the distance between the start point and end point of this Bezier curve + is less than or equal to gp::Resolution(). + """ + ... + + def increase(self, Degree: int) -> None: + """ + increase(Int=Degree) + Increases the degree of this Bezier curve to Degree. + As a result, the poles and weights tables are modified. + """ + ... + + def insertPoleAfter(self, index: int) -> None: + """ + Inserts after the pole of index. + """ + ... + + def insertPoleBefore(self, index: int) -> None: + """ + Inserts before the pole of index. + """ + ... + + def removePole(self, index: int) -> None: + """ + Removes the pole of index Index from the table of poles of this Bezier curve. + If this Bezier curve is rational, it can become non-rational. + """ + ... + + def segment(self) -> None: + """ + Modifies this Bezier curve by segmenting it. + """ + ... + + def setPole(self, index: int, pole: object) -> None: + """ + Set a pole of the Bezier curve. + """ + ... + + def getPole(self, index: int) -> object: + """ + Get a pole of the Bezier curve. + """ + ... + + def getPoles(self) -> List[object]: + """ + Get all poles of the Bezier curve. + """ + ... + + def setPoles(self, poles: List[object]) -> None: + """ + Set the poles of the Bezier curve. + """ + ... + + def setWeight(self, index: int, weight: float) -> None: + """ + Set a weight of the Bezier curve. + """ + ... + + def getWeight(self, index: int) -> float: + """ + Get a weight of the Bezier curve. + """ + ... + + def getWeights(self) -> List[float]: + """ + Get all weights of the Bezier curve. + """ + ... + + @constmethod + def getResolution(self, Tolerance3D: float) -> float: + """ + Computes for this Bezier curve the parametric tolerance (UTolerance) + for a given 3D tolerance (Tolerance3D). + If f(t) is the equation of this Bezier curve, + the parametric tolerance ensures that: + |t1-t0| < UTolerance =\"\"==> |f(t1)-f(t0)| < Tolerance3D + """ + ... diff --git a/src/Mod/Part/App/Geom2d/BezierCurve2dPy.xml b/src/Mod/Part/App/Geom2d/BezierCurve2dPy.xml deleted file mode 100644 index 55869d5553..0000000000 --- a/src/Mod/Part/App/Geom2d/BezierCurve2dPy.xml +++ /dev/null @@ -1,141 +0,0 @@ - - - - - - Describes a rational or non-rational Bezier curve in 2d space: - -- a non-rational Bezier curve is defined by a table of poles (also called control points) - -- a rational Bezier curve is defined by a table of poles with varying weights - - - - Returns the polynomial degree of this Bezier curve, - which is equal to the number of poles minus 1. - - - - - - Returns the value of the maximum polynomial degree of any - Bezier curve curve. This value is 25. - - - - - - Returns the number of poles of this Bezier curve. - - - - - - Returns the start point of this Bezier curve. - - - - - - Returns the end point of this Bezier curve. - - - - - - Returns false if the weights of all the poles of this Bezier curve are equal. - - - - - Returns false. - - - - - Returns true if the distance between the start point and end point of - this Bezier curve is less than or equal to gp::Resolution(). - - - - - increase(Int=Degree) - Increases the degree of this Bezier curve to Degree. - As a result, the poles and weights tables are modified. - - - - - Inserts after the pole of index. - - - - - Inserts before the pole of index. - - - - - Removes the pole of index Index from the table of poles of this Bezier curve. - If this Bezier curve is rational, it can become non-rational. - - - - - Modifies this Bezier curve by segmenting it. - - - - - Set a pole of the Bezier curve. - - - - - Get a pole of the Bezier curve. - - - - - Get all poles of the Bezier curve. - - - - - Set the poles of the Bezier curve. - - - - - Set a weight of the Bezier curve. - - - - - Get a weight of the Bezier curve. - - - - - Get all weights of the Bezier curve. - - - - - Computes for this Bezier curve the parametric tolerance (UTolerance) - for a given 3D tolerance (Tolerance3D). - If f(t) is the equation of this Bezier curve, - the parametric tolerance ensures that: - |t1-t0| < UTolerance =""==> |f(t1)-f(t0)| < Tolerance3D - - - - diff --git a/src/Mod/Part/App/Geom2d/Circle2d.pyi b/src/Mod/Part/App/Geom2d/Circle2d.pyi new file mode 100644 index 0000000000..8b661f3565 --- /dev/null +++ b/src/Mod/Part/App/Geom2d/Circle2d.pyi @@ -0,0 +1,81 @@ +from Metadata import export, constmethod +from typing import Final, overload, Tuple +from Part.Geom2d import Conic2d + +@export( + PythonName="Part.Geom2d.Circle2d", + Twin="Geom2dCircle", + TwinPointer="Geom2dCircle", + Include="Mod/Part/App/Geometry2d.h", + FatherInclude="Mod/Part/App/Geom2d/Conic2dPy.h", + Constructor=True, +) +class Circle2d(Conic2d): + """ + Describes a circle in 3D space + To create a circle there are several ways: + Part.Geom2d.Circle2d() + Creates a default circle with center (0,0) and radius 1 + + Part.Geom2d.Circle2d(circle) + Creates a copy of the given circle + + Part.Geom2d.Circle2d(circle, Distance) + Creates a circle parallel to given circle at a certain distance + + Part.Geom2d.Circle2d(Center,Radius) + Creates a circle defined by center and radius + + Part.Geom2d.Circle2d(Point1,Point2,Point3) + Creates a circle defined by three non-linear points + + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + Radius: float = ... + """The radius of the circle.""" + + @overload + def __init__(self) -> None: ... + + @overload + def __init__(self, circle: "Circle2d") -> None: ... + + @overload + def __init__(self, circle: "Circle2d", Distance: float) -> None: ... + + @overload + def __init__(self, Center: Tuple[float, float], Radius: float) -> None: ... + + @overload + def __init__(self, Point1: Tuple[float, float], Point2: Tuple[float, float], Point3: Tuple[float, float]) -> None: ... + + @overload + def __init__(self, *args, **kwargs) -> None: + """ + Describes a circle in 3D space + To create a circle there are several ways: + Part.Geom2d.Circle2d() + Creates a default circle with center (0,0) and radius 1 + + Part.Geom2d.Circle2d(circle) + Creates a copy of the given circle + + Part.Geom2d.Circle2d(circle, Distance) + Creates a circle parallel to given circle at a certain distance + + Part.Geom2d.Circle2d(Center,Radius) + Creates a circle defined by center and radius + + Part.Geom2d.Circle2d(Point1,Point2,Point3) + Creates a circle defined by three non-linear points + """ + ... + + @staticmethod + def getCircleCenter() -> Tuple[float, float]: + """ + Get the circle center defined by three points + """ + ... \ No newline at end of file diff --git a/src/Mod/Part/App/Geom2d/Circle2dPy.xml b/src/Mod/Part/App/Geom2d/Circle2dPy.xml deleted file mode 100644 index 11d6c8d525..0000000000 --- a/src/Mod/Part/App/Geom2d/Circle2dPy.xml +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - Describes a circle in 3D space -To create a circle there are several ways: -Part.Geom2d.Circle2d() - Creates a default circle with center (0,0) and radius 1 - -Part.Geom2d.Circle2d(circle) - Creates a copy of the given circle - -Part.Geom2d.Circle2d(circle, Distance) - Creates a circle parallel to given circle at a certain distance - -Part.Geom2d.Circle2d(Center,Radius) - Creates a circle defined by center and radius - -Part.Geom2d.Circle2d(Point1,Point2,Point3) - Creates a circle defined by three non-linear points - - - - - Get the circle center defined by three points - - - - - The radius of the circle. - - - - - diff --git a/src/Mod/Part/App/Geom2d/Conic2d.pyi b/src/Mod/Part/App/Geom2d/Conic2d.pyi new file mode 100644 index 0000000000..482ddc8158 --- /dev/null +++ b/src/Mod/Part/App/Geom2d/Conic2d.pyi @@ -0,0 +1,37 @@ +from Base.Metadata import export +from typing import Final +from Part.Geom2d import Curve2d + +@export( + PythonName="Part.Geom2d.Conic2d", + Twin="Geom2dConic", + TwinPointer="Geom2dConic", + Include="Mod/Part/App/Geometry2d.h", + FatherInclude="Mod/Part/App/Geom2d/Curve2dPy.h", + Constructor=True, +) +class Conic2d(Curve2d): + """ + Describes an abstract conic in 2d space + + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + Location: object = ... + """Location of the conic.""" + + Eccentricity: Final[float] = ... + """ + returns the eccentricity value of the conic e. + e = 0 for a circle + 0 < e < 1 for an ellipse (e = 0 if MajorRadius = MinorRadius) + e > 1 for a hyperbola + e = 1 for a parabola + """ + + XAxis: object = ... + """The X axis direction of the circle""" + + YAxis: object = ... + """The Y axis direction of the circle""" diff --git a/src/Mod/Part/App/Geom2d/Conic2dPy.xml b/src/Mod/Part/App/Geom2d/Conic2dPy.xml deleted file mode 100644 index f86e5f12ea..0000000000 --- a/src/Mod/Part/App/Geom2d/Conic2dPy.xml +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - Describes an abstract conic in 2d space - - - - Location of the conic. - - - - - - - returns the eccentricity value of the conic e. - e = 0 for a circle - 0 < e < 1 for an ellipse (e = 0 if MajorRadius = MinorRadius) - e > 1 for a hyperbola - e = 1 for a parabola - - - - - - - The X axis direction of the circle - - - - - - The Y axis direction of the circle - - - - - diff --git a/src/Mod/Part/App/Geom2d/Curve2d.pyi b/src/Mod/Part/App/Geom2d/Curve2d.pyi new file mode 100644 index 0000000000..e80da55efb --- /dev/null +++ b/src/Mod/Part/App/Geom2d/Curve2d.pyi @@ -0,0 +1,223 @@ +from Base.Metadata import export, constmethod +from Base.Vector import Vector +from Part.App.Geom2d.Geometry2d import Geometry2d +from Part.App.Geom2d.BSplineCurve import BSplineCurve +from typing import Final, overload, List + +@export( + Include="Mod/Part/App/Geometry2d.h", + FatherInclude="Mod/Part/App/Geom2d/Geometry2dPy.h", + Twin="Geom2dCurve", + TwinPointer="Geom2dCurve", + PythonName="Part.Geom2d.Curve2d", + Constructor=True, +) +class Curve2d(Geometry2d): + """ + The abstract class Geom2dCurve is the root class of all curve objects. + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + Continuity: Final[str] = ... + """Returns the global continuity of the curve.""" + + Closed: Final[bool] = ... + """Returns true if the curve is closed.""" + + Periodic: Final[bool] = ... + """Returns true if the curve is periodic.""" + + FirstParameter: Final[float] = ... + """Returns the value of the first parameter.""" + + LastParameter: Final[float] = ... + """Returns the value of the last parameter.""" + + def reverse(self) -> None: + """ + Changes the direction of parametrization of the curve. + """ + ... + + @constmethod + def toShape(self) -> object: + """ + Return the shape for the geometry. + """ + ... + + @overload + @constmethod + def discretize(self, *, Number: int) -> List[Vector]: + ... + + @overload + @constmethod + def discretize(self, *, QuasiNumber: int) -> List[Vector]: + ... + + @overload + @constmethod + def discretize(self, *, Distance: float) -> List[Vector]: + ... + + @overload + @constmethod + def discretize(self, *, Deflection: float) -> List[Vector]: + ... + + @overload + @constmethod + def discretize(self, *, QuasiDeflection: float) -> List[Vector]: + ... + + @overload + @constmethod + def discretize(self, *, Angular: float, Curvature: float, Minimum: int = 2) -> List[Vector]: + ... + + @constmethod + def discretize(self, **kwargs) -> List[Vector]: + """ + Discretizes the curve and returns a list of points. + The function accepts keywords as argument: + discretize(Number=n) => gives a list of 'n' equidistant points. + discretize(QuasiNumber=n) => gives a list of 'n' quasi-equidistant points (is faster than the method above). + discretize(Distance=d) => gives a list of equidistant points with distance 'd'. + discretize(Deflection=d) => gives a list of points with a maximum deflection 'd' to the curve. + discretize(QuasiDeflection=d) => gives a list of points with a maximum deflection 'd' to the curve (faster). + discretize(Angular=a,Curvature=c,[Minimum=m]) => gives a list of points with an angular deflection of 'a' + and a curvature deflection of 'c'. Optionally a minimum number of points + can be set, which by default is set to 2. + + Optionally you can set the keywords 'First' and 'Last' to define + a sub-range of the parameter range of the curve. + + If no keyword is given, then it depends on whether the argument is an int or float. + If it's an int then the behaviour is as if using the keyword 'Number', + if it's a float then the behaviour is as if using the keyword 'Distance'. + + Example: + + import Part + c=PartGeom2d.Circle2d() + c.Radius=5 + p=c.discretize(Number=50,First=3.14) + s=Part.Compound([Part.Vertex(i) for i in p]) + Part.show(s) + + + p=c.discretize(Angular=0.09,Curvature=0.01,Last=3.14,Minimum=100) + s=Part.Compound([Part.Vertex(i) for i in p]) + Part.show(s) + """ + ... + + @overload + def length(self) -> float: + ... + + @overload + def length(self, uMin: float) -> float: + ... + + @overload + def length(self, uMin: float, uMax: float) -> float: + ... + + @overload + def length(self, uMin: float, uMax: float, Tol: float) -> float: + ... + + def length(self, *args: float) -> float: + """ + Computes the length of a curve + length([uMin,uMax,Tol]) -> Float + """ + ... + + @overload + def parameterAtDistance(self, abscissa: float) -> float: + ... + + @overload + def parameterAtDistance(self, abscissa: float, startingParameter: float) -> float: + ... + + def parameterAtDistance(self, *args: float) -> float: + """ + Returns the parameter on the curve of a point at + the given distance from a starting parameter. + parameterAtDistance([abscissa, startingParameter]) -> Float + """ + ... + + def value(self, u: float) -> Vector: + """ + Computes the point of parameter u on this curve + """ + ... + + def tangent(self, u: float) -> Vector: + """ + Computes the tangent of parameter u on this curve + """ + ... + + def parameter(self, point: Vector) -> float: + """ + Returns the parameter on the curve of the + nearest orthogonal projection of the point. + """ + ... + + @constmethod + def normal(self, pos: float) -> Vector: + """ + Vector = normal(pos) - Get the normal vector at the given parameter [First|Last] if defined. + """ + ... + + @constmethod + def curvature(self, pos: float) -> float: + """ + Float = curvature(pos) - Get the curvature at the given parameter [First|Last] if defined. + """ + ... + + @constmethod + def centerOfCurvature(self, pos: float) -> Vector: + """ + Vector = centerOfCurvature(float pos) - Get the center of curvature at the given parameter [First|Last] if defined. + """ + ... + + @constmethod + def intersectCC(self, other: "Curve2d") -> List[Vector]: + """ + Returns all intersection points between this curve and the given curve. + """ + ... + + @overload + def toBSpline(self) -> BSplineCurve: + ... + + @overload + def toBSpline(self, First: float, Last: float) -> BSplineCurve: + ... + + def toBSpline(self, *args: float) -> BSplineCurve: + """ + Converts a curve of any type (only part from First to Last) + toBSpline([Float=First, Float=Last]) -> B-Spline curve + """ + ... + + def approximateBSpline(self, Tolerance: float, MaxSegments: int, MaxDegree: int, Order: str = "C2") -> BSplineCurve: + """ + Approximates a curve of any type to a B-Spline curve + approximateBSpline(Tolerance, MaxSegments, MaxDegree, [Order='C2']) -> B-Spline curve + """ + ... \ No newline at end of file diff --git a/src/Mod/Part/App/Geom2d/Curve2dPy.xml b/src/Mod/Part/App/Geom2d/Curve2dPy.xml deleted file mode 100644 index 33f5050ec1..0000000000 --- a/src/Mod/Part/App/Geom2d/Curve2dPy.xml +++ /dev/null @@ -1,175 +0,0 @@ - - - - - - The abstract class Geom2dCurve is the root class of all curve objects. - - - - Changes the direction of parametrization of the curve. - - - - - Return the shape for the geometry. - - - - - -Discretizes the curve and returns a list of points. -The function accepts keywords as argument: -discretize(Number=n) => gives a list of 'n' equidistant points. -discretize(QuasiNumber=n) => gives a list of 'n' quasi-equidistant points (is faster than the method above). -discretize(Distance=d) => gives a list of equidistant points with distance 'd'. -discretize(Deflection=d) => gives a list of points with a maximum deflection 'd' to the curve. -discretize(QuasiDeflection=d) => gives a list of points with a maximum deflection 'd' to the curve (faster). -discretize(Angular=a,Curvature=c,[Minimum=m]) => gives a list of points with an angular deflection of 'a' - and a curvature deflection of 'c'. Optionally a minimum number of points - can be set, which by default is set to 2. - -Optionally you can set the keywords 'First' and 'Last' to define - a sub-range of the parameter range of the curve. - -If no keyword is given, then it depends on whether the argument is an int or float. -If it's an int then the behaviour is as if using the keyword 'Number', -if it's a float then the behaviour is as if using the keyword 'Distance'. - -Example: - -import Part -c=PartGeom2d.Circle2d() -c.Radius=5 -p=c.discretize(Number=50,First=3.14) -s=Part.Compound([Part.Vertex(i) for i in p]) -Part.show(s) - - -p=c.discretize(Angular=0.09,Curvature=0.01,Last=3.14,Minimum=100) -s=Part.Compound([Part.Vertex(i) for i in p]) -Part.show(s) - - - - - - - Computes the length of a curve - length([uMin,uMax,Tol]) -> Float - - - - - - - Returns the parameter on the curve of a point at - the given distance from a starting parameter. - parameterAtDistance([abscissa, startingParameter]) -> Float - - - - - - Computes the point of parameter u on this curve - - - - - Computes the tangent of parameter u on this curve - - - - - - Returns the parameter on the curve of the - nearest orthogonal projection of the point. - - - - - - - Vector = normal(pos) - Get the normal vector at the given parameter [First|Last] if defined. - - - - - - - Float = curvature(pos) - Get the curvature at the given parameter [First|Last] if defined. - - - - - - - Vector = centerOfCurvature(float pos) - Get the center of curvature at the given parameter [First|Last] if defined. - - - - - - - Returns all intersection points between this curve and the given curve. - - - - - - - Converts a curve of any type (only part from First to Last) - toBSpline([Float=First, Float=Last]) -> B-Spline curve - - - - - - - Approximates a curve of any type to a B-Spline curve - approximateBSpline(Tolerance, MaxSegments, MaxDegree, [Order='C2']) -> B-Spline curve - - - - - - Returns the global continuity of the curve. - - - - - - Returns true if the curve is closed. - - - - - - Returns true if the curve is periodic. - - - - - - Returns the value of the first parameter. - - - - - - Returns the value of the last parameter. - - - - - diff --git a/src/Mod/Part/App/Geom2d/Ellipse2d.pyi b/src/Mod/Part/App/Geom2d/Ellipse2d.pyi new file mode 100644 index 0000000000..6375b76656 --- /dev/null +++ b/src/Mod/Part/App/Geom2d/Ellipse2d.pyi @@ -0,0 +1,67 @@ +from typing import Final, overload +from Base.Metadata import export, constmethod +from Part.Conic2d import Conic2d + +@export( + Twin="Geom2dEllipse", + TwinPointer="Geom2dEllipse", + PythonName="Part.Geom2d.Ellipse2d", + FatherInclude="Mod/Part/App/Geom2d/Conic2dPy.h", + Include="Mod/Part/App/Geometry2d.h", + Constructor=True, +) +class Ellipse2d(Conic2d): + """ + Describes an ellipse in 2D space + To create an ellipse there are several ways: + Part.Geom2d.Ellipse2d() + Creates an ellipse with major radius 2 and minor radius 1 with the + center in (0,0) + + Part.Geom2d.Ellipse2d(Ellipse) + Create a copy of the given ellipse + + Part.Geom2d.Ellipse2d(S1,S2,Center) + Creates an ellipse centered on the point Center, + its major axis is defined by Center and S1, + its major radius is the distance between Center and S1, and + its minor radius is the distance between S2 and the major axis. + + Part.Geom2d.Ellipse2d(Center,MajorRadius,MinorRadius) + Creates an ellipse with major and minor radii MajorRadius and + MinorRadius + + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + MajorRadius: float = ... + """The major radius of the ellipse.""" + + MinorRadius: float = ... + """The minor radius of the ellipse.""" + + Focal: Final[float] = ... + """The focal distance of the ellipse.""" + + Focus1: Final[object] = ... + """The first focus is on the positive side of the major axis of the ellipse.""" + + Focus2: Final[object] = ... + """The second focus is on the negative side of the major axis of the ellipse.""" + + @overload + def __init__(self) -> None: ... + + @overload + def __init__(self, Ellipse: "Ellipse2d") -> None: ... + + @overload + def __init__(self, S1: object, S2: object, Center: object) -> None: ... + + @overload + def __init__(self, Center: object, MajorRadius: float, MinorRadius: float) -> None: ... + + @overload + def __init__(self, *args, **kwargs) -> None: + ... diff --git a/src/Mod/Part/App/Geom2d/Ellipse2dPy.xml b/src/Mod/Part/App/Geom2d/Ellipse2dPy.xml deleted file mode 100644 index 2412cdd68b..0000000000 --- a/src/Mod/Part/App/Geom2d/Ellipse2dPy.xml +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - - Describes an ellipse in 2D space - To create an ellipse there are several ways: - Part.Geom2d.Ellipse2d() - Creates an ellipse with major radius 2 and minor radius 1 with the - center in (0,0) - - Part.Geom2d.Ellipse2d(Ellipse) - Create a copy of the given ellipse - - Part.Geom2d.Ellipse2d(S1,S2,Center) - Creates an ellipse centered on the point Center, - its major axis is defined by Center and S1, - its major radius is the distance between Center and S1, and - its minor radius is the distance between S2 and the major axis. - - Part.Geom2d.Ellipse2d(Center,MajorRadius,MinorRadius) - Creates an ellipse with major and minor radii MajorRadius and - MinorRadius - - - - The major radius of the ellipse. - - - - - - The minor radius of the ellipse. - - - - - - The focal distance of the ellipse. - - - - - - The first focus is on the positive side of the major axis of the ellipse. - - - - - - The second focus is on the negative side of the major axis of the ellipse. - - - - - diff --git a/src/Mod/Part/App/Geom2d/Geometry2d.pyi b/src/Mod/Part/App/Geom2d/Geometry2d.pyi new file mode 100644 index 0000000000..3998aaf2fe --- /dev/null +++ b/src/Mod/Part/App/Geom2d/Geometry2d.pyi @@ -0,0 +1,59 @@ +from Base.Metadata import export, constmethod +from Base.PyObjectBase import PyObjectBase +from typing import overload + +@export( + Twin="Geometry2d", + TwinPointer="Geometry2d", + PythonName="Part.Geom2d.Geometry2d", + Include="Mod/Part/App/Geometry2d.h", + Constructor=True, + Delete=True, +) +class Geometry2d(PyObjectBase): + """ + The abstract class Geometry for 2D space is the root class of all geometric objects. + It describes the common behavior of these objects when: + - applying geometric transformations to objects, and + - constructing objects by geometric transformation (including copying). + + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + def mirror(self) -> None: + """ + Performs the symmetrical transformation of this geometric object. + """ + ... + + def rotate(self) -> None: + """ + Rotates this geometric object at angle Ang (in radians) around a point. + """ + ... + + def scale(self) -> None: + """ + Applies a scaling transformation on this geometric object with a center and scaling factor. + """ + ... + + def transform(self) -> None: + """ + Applies a transformation to this geometric object. + """ + ... + + def translate(self) -> None: + """ + Translates this geometric object. + """ + ... + + @constmethod + def copy(self) -> "Geometry2d": + """ + Create a copy of this geometry. + """ + ... diff --git a/src/Mod/Part/App/Geom2d/Geometry2dPy.xml b/src/Mod/Part/App/Geom2d/Geometry2dPy.xml deleted file mode 100644 index 743b2f3c80..0000000000 --- a/src/Mod/Part/App/Geom2d/Geometry2dPy.xml +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - The abstract class Geometry for 2D space is the root class of all geometric objects. - It describes the common behavior of these objects when: - - applying geometric transformations to objects, and - - constructing objects by geometric transformation (including copying). - - - - Performs the symmetrical transformation of this geometric object. - - - - - Rotates this geometric object at angle Ang (in radians) around a point. - - - - - Applies a scaling transformation on this geometric object with a center and scaling factor. - - - - - Applies a transformation to this geometric object. - - - - - Translates this geometric object. - - - - - Create a copy of this geometry. - - - - diff --git a/src/Mod/Part/App/Geom2d/Hyperbola2d.pyi b/src/Mod/Part/App/Geom2d/Hyperbola2d.pyi new file mode 100644 index 0000000000..a27fa36323 --- /dev/null +++ b/src/Mod/Part/App/Geom2d/Hyperbola2d.pyi @@ -0,0 +1,66 @@ +from Base.Metadata import export +from Part.Conic2d import Conic2d +from typing import Final, overload + +@export( + Twin="Geom2dHyperbola", + TwinPointer="Geom2dHyperbola", + PythonName="Part.Geom2d.Hyperbola2d", + FatherInclude="Mod/Part/App/Geom2d/Conic2dPy.h", + Include="Mod/Part/App/Geometry2d.h", + Constructor=True +) +class Hyperbola2d(Conic2d): + """ + Describes a hyperbola in 2D space + To create a hyperbola there are several ways: + Part.Geom2d.Hyperbola2d() + Creates a hyperbola with major radius 2 and minor radius 1 with the + center in (0,0) + + Part.Geom2d.Hyperbola2d(Hyperbola) + Create a copy of the given hyperbola + + Part.Geom2d.Hyperbola2d(S1,S2,Center) + Creates a hyperbola centered on the point Center, S1 and S2, + its major axis is defined by Center and S1, + its major radius is the distance between Center and S1, and + its minor radius is the distance between S2 and the major axis. + + Part.Geom2d.Hyperbola2d(Center,MajorRadius,MinorRadius) + Creates a hyperbola with major and minor radii MajorRadius and + MinorRadius and located at Center + + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + MajorRadius: float = ... + """The major radius of the hyperbola.""" + + MinorRadius: float = ... + """The minor radius of the hyperbola.""" + + Focal: Final[float] = ... + """The focal distance of the hyperbola.""" + + Focus1: Final[object] = ... + """ + The first focus is on the positive side of the major axis of the hyperbola; + the second focus is on the negative side. + """ + + Focus2: Final[object] = ... + """ + The first focus is on the positive side of the major axis of the hyperbola; + the second focus is on the negative side. + """ + + @overload + def __init__(self) -> None: ... + @overload + def __init__(self, Hyperbola: "Hyperbola2d") -> None: ... + @overload + def __init__(self, S1: object, S2: object, Center: object) -> None: ... + @overload + def __init__(self, Center: object, MajorRadius: float, MinorRadius: float) -> None: ... diff --git a/src/Mod/Part/App/Geom2d/Hyperbola2dPy.xml b/src/Mod/Part/App/Geom2d/Hyperbola2dPy.xml deleted file mode 100644 index 24c8bfcf1e..0000000000 --- a/src/Mod/Part/App/Geom2d/Hyperbola2dPy.xml +++ /dev/null @@ -1,70 +0,0 @@ - - - - - - Describes a hyperbola in 2D space - To create a hyperbola there are several ways: - Part.Geom2d.Hyperbola2d() - Creates a hyperbola with major radius 2 and minor radius 1 with the - center in (0,0) - - Part.Geom2d.Hyperbola2d(Hyperbola) - Create a copy of the given hyperbola - - Part.Geom2d.Hyperbola2d(S1,S2,Center) - Creates a hyperbola centered on the point Center, S1 and S2, - its major axis is defined by Center and S1, - its major radius is the distance between Center and S1, and - its minor radius is the distance between S2 and the major axis. - - Part.Geom2d.Hyperbola2d(Center,MajorRadius,MinorRadius) - Creates a hyperbola with major and minor radii MajorRadius and - MinorRadius and located at Center - - - - The major radius of the hyperbola. - - - - - - The minor radius of the hyperbola. - - - - - - The focal distance of the hyperbola. - - - - - - The first focus is on the positive side of the major axis of the hyperbola; - the second focus is on the negative side. - - - - - - - The first focus is on the positive side of the major axis of the hyperbola; - the second focus is on the negative side. - - - - - - diff --git a/src/Mod/Part/App/Geom2d/Line2d.pyi b/src/Mod/Part/App/Geom2d/Line2d.pyi new file mode 100644 index 0000000000..11ef90d158 --- /dev/null +++ b/src/Mod/Part/App/Geom2d/Line2d.pyi @@ -0,0 +1,34 @@ +from Base.Metadata import export, constmethod +from Part.Geom2d.Curve2d import Curve2d +from typing import overload + +@export( + PythonName="Part.Geom2d.Line2d", + Twin="Geom2dLine", + TwinPointer="Geom2dLine", + Include="Mod/Part/App/Geometry2d.h", + FatherInclude="Mod/Part/App/Geom2d/Curve2dPy.h", + Constructor=True, +) +class Line2d(Curve2d): + """ + Describes an infinite line in 2D space + To create a line there are several ways: + Part.Geom2d.Line2d() + Creates a default line. + + Part.Geom2d.Line2d(Line) + Creates a copy of the given line. + + Part.Geom2d.Line2d(Point,Dir) + Creates a line that goes through two given points. + + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + Location: object = ... + """Returns the location of this line.""" + + Direction: object = ... + """Returns the direction of this line.""" diff --git a/src/Mod/Part/App/Geom2d/Line2dPy.xml b/src/Mod/Part/App/Geom2d/Line2dPy.xml deleted file mode 100644 index 90b5ac87f5..0000000000 --- a/src/Mod/Part/App/Geom2d/Line2dPy.xml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - Describes an infinite line in 2D space - To create a line there are several ways: - Part.Geom2d.Line2d() - Creates a default line. - - Part.Geom2d.Line2d(Line) - Creates a copy of the given line. - - Part.Geom2d.Line2d(Point,Dir) - Creates a line that goes through two given points. - - - - Returns the location of this line. - - - - - - Returns the direction of this line. - - - - - diff --git a/src/Mod/Part/App/Geom2d/Line2dSegment.pyi b/src/Mod/Part/App/Geom2d/Line2dSegment.pyi new file mode 100644 index 0000000000..bf193170c7 --- /dev/null +++ b/src/Mod/Part/App/Geom2d/Line2dSegment.pyi @@ -0,0 +1,47 @@ +from Base.Metadata import export, constmethod +from typing import Final, overload +from Part.Geom2d import Curve2d + +@export( + PythonName="Part.Geom2d.Line2dSegment", + Twin="Geom2dLineSegment", + TwinPointer="Geom2dLineSegment", + Include="Mod/Part/App/Geometry2d.h", + FatherInclude="Mod/Part/App/Geom2d/Curve2dPy.h", + Constructor=True, +) +class Line2dSegment(Curve2d): + """ + Describes a line segment in 2D space. + +To create a line there are several ways: +Part.Geom2d.Line2dSegment() + Creates a default line + +Part.Geom2d.Line2dSegment(Line) + Creates a copy of the given line + +Part.Geom2d.Line2dSegment(Point1,Point2) + Creates a line that goes through two given points. + """ + + StartPoint: object = ... + """Returns the start point of this line segment.""" + + EndPoint: object = ... + """Returns the end point of this line segment.""" + + @overload + def __init__(self) -> None: ... + + @overload + def __init__(self, Line: "Line2dSegment") -> None: ... + + @overload + def __init__(self, Point1: object, Point2: object) -> None: ... + + def setParameterRange(self) -> None: + """ + Set the parameter range of the underlying line segment geometry. + """ + ... diff --git a/src/Mod/Part/App/Geom2d/Line2dSegmentPy.xml b/src/Mod/Part/App/Geom2d/Line2dSegmentPy.xml deleted file mode 100644 index b915ca80fc..0000000000 --- a/src/Mod/Part/App/Geom2d/Line2dSegmentPy.xml +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - Describes a line segment in 2D space. - - To create a line there are several ways: - Part.Geom2d.Line2dSegment() - Creates a default line - - Part.Geom2d.Line2dSegment(Line) - Creates a copy of the given line - - Part.Geom2d.Line2dSegment(Point1,Point2) - Creates a line that goes through two given points. - - - - Set the parameter range of the underlying line segment geometry. - - - - - Returns the start point of this line segment. - - - - - - Returns the end point of this line segment. - - - - - diff --git a/src/Mod/Part/App/Geom2d/OffsetCurve2d.pyi b/src/Mod/Part/App/Geom2d/OffsetCurve2d.pyi new file mode 100644 index 0000000000..1363db5d1c --- /dev/null +++ b/src/Mod/Part/App/Geom2d/OffsetCurve2d.pyi @@ -0,0 +1,27 @@ +from Metadata import export +from typing import Final +from Part.Geom2d import Curve2d + +@export( + Name="OffsetCurve2dPy", + Namespace="Part", + Twin="Geom2dOffsetCurve", + TwinPointer="Geom2dOffsetCurve", + PythonName="Part.Geom2d.OffsetCurve2d", + FatherInclude="Mod/Part/App/Geom2d/Curve2dPy.h", + Include="Mod/Part/App/Geometry2d.h", + Father="Curve2dPy", + FatherNamespace="Part", + Constructor=True, +) +class OffsetCurve2d(Curve2d): + """ + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + OffsetValue: float = ... + """Sets or gets the offset value to offset the underlying curve.""" + + BasisCurve: object = ... + """Sets or gets the basic curve.""" \ No newline at end of file diff --git a/src/Mod/Part/App/Geom2d/OffsetCurve2dPy.xml b/src/Mod/Part/App/Geom2d/OffsetCurve2dPy.xml deleted file mode 100644 index 6b33e20d76..0000000000 --- a/src/Mod/Part/App/Geom2d/OffsetCurve2dPy.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - Sets or gets the offset value to offset the underlying curve. - - - - - - Sets or gets the basic curve. - - - - - diff --git a/src/Mod/Part/App/Geom2d/Parabola2d.pyi b/src/Mod/Part/App/Geom2d/Parabola2d.pyi new file mode 100644 index 0000000000..490929dcfc --- /dev/null +++ b/src/Mod/Part/App/Geom2d/Parabola2d.pyi @@ -0,0 +1,37 @@ +from Base.Metadata import export, constmethod +from Part.App.Geom2d.Conic2d import Conic2d +from typing import Final + + +@export( + Include="Mod/Part/App/Geometry2d.h", + FatherInclude="Mod/Part/App/Geom2d/Conic2dPy.h", + Twin="Geom2dParabola", + TwinPointer="Geom2dParabola", + PythonName="Part.Geom2d.Parabola2d", + Constructor=True, +) +class Parabola2d(Conic2d): + """ + Describes a parabola in 2D space + + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + Focal: float = ... + """ + The focal distance is the distance between the apex and the focus of the parabola. + """ + + Focus: Final[object] = ... + """ + The focus is on the positive side of the + 'X Axis' of the local coordinate system of the parabola. + """ + + Parameter: Final[float] = ... + """ + Compute the parameter of this parabola which is the distance between its focus + and its directrix. This distance is twice the focal length. + """ diff --git a/src/Mod/Part/App/Geom2d/Parabola2dPy.xml b/src/Mod/Part/App/Geom2d/Parabola2dPy.xml deleted file mode 100644 index 32cb935c06..0000000000 --- a/src/Mod/Part/App/Geom2d/Parabola2dPy.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - Describes a parabola in 2D space - - - - The focal distance is the distance between - the apex and the focus of the parabola. - - - - - - The focus is on the positive side of the - 'X Axis' of the local coordinate system of the parabola. - - - - - - Compute the parameter of this parabola - which is the distance between its focus - and its directrix. This distance is twice the focal length. - - - - - diff --git a/src/Mod/Part/App/GeomPlate/BuildPlateSurface.pyi b/src/Mod/Part/App/GeomPlate/BuildPlateSurface.pyi new file mode 100644 index 0000000000..bd2bd6b097 --- /dev/null +++ b/src/Mod/Part/App/GeomPlate/BuildPlateSurface.pyi @@ -0,0 +1,139 @@ +from Base.Metadata import export, constmethod +from Base.PyObjectBase import PyObjectBase +from typing import List + +@export( + PythonName="Part.GeomPlate.BuildPlateSurfacePy", + Twin="GeomPlate_BuildPlateSurface", + TwinPointer="GeomPlate_BuildPlateSurface", + Include="GeomPlate_BuildPlateSurface.hxx", + Constructor=True, + Delete=True, +) +class BuildPlateSurface(PyObjectBase): + """ + This class provides an algorithm for constructing such a plate surface. + + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + def init(self) -> None: + """ + Resets all constraints + """ + ... + + def setNbBounds(self) -> None: + """ + Sets the number of bounds + """ + ... + + def loadInitSurface(self) -> None: + """ + Loads the initial surface + """ + ... + + @constmethod + def surfInit(self) -> object: + """ + Returns the initial surface + """ + ... + + @constmethod + def surface(self) -> object: + """ + Returns the plate surface + """ + ... + + def add(self) -> None: + """ + Adds a linear or point constraint + """ + ... + + def perform(self) -> None: + """ + Calls the algorithm and computes the plate surface + """ + ... + + @constmethod + def isDone(self) -> bool: + """ + Tests whether computation of the plate has been completed + """ + ... + + @constmethod + def sense(self) -> object: + """ + Returns the orientation of the curves in the array returned by curves2d + """ + ... + + @constmethod + def order(self) -> int: + """ + Returns the order of the curves in the array returned by curves2d + """ + ... + + @constmethod + def curves2d(self) -> List[object]: + """ + Extracts the array of curves on the plate surface which + correspond to the curve constraints set in add() + """ + ... + + @constmethod + def curveConstraint(self) -> object: + """ + Returns the curve constraint of order + """ + ... + + @constmethod + def pointConstraint(self) -> object: + """ + Returns the point constraint of order + """ + ... + + def disc2dContour(self) -> object: + """ + Returns the 2D contour of the plate surface + """ + ... + + def disc3dContour(self) -> object: + """ + Returns the 3D contour of the plate surface + """ + ... + + @constmethod + def G0Error(self) -> float: + """ + Returns the max distance between the result and the constraints + """ + ... + + @constmethod + def G1Error(self) -> float: + """ + Returns the max angle between the result and the constraints + """ + ... + + @constmethod + def G2Error(self) -> float: + """ + Returns the max difference of curvature between the result and the constraints + """ + ... diff --git a/src/Mod/Part/App/GeomPlate/BuildPlateSurfacePy.xml b/src/Mod/Part/App/GeomPlate/BuildPlateSurfacePy.xml deleted file mode 100644 index 4575e85cd2..0000000000 --- a/src/Mod/Part/App/GeomPlate/BuildPlateSurfacePy.xml +++ /dev/null @@ -1,112 +0,0 @@ - - - - - - This class provides an algorithm for constructing such a plate surface. - - - - Resets all constraints - - - - - Sets the number of bounds - - - - - Loads the initial surface - - - - - Returns the initial surface - - - - - Returns the plate surface - - - - - Adds a linear or point constraint - - - - - Calls the algorithm and computes the plate surface - - - - - Tests whether computation of the plate has been completed - - - - - Returns the orientation of the curves in the array returned by curves2d - - - - - Returns the order of the curves in the array returned by curves2d - - - - - Extracts the array of curves on the plate surface which - correspond to the curve constraints set in add() - - - - - - Returns the curve constraint of order - - - - - Returns the point constraint of order - - - - - Returns the 2D contour of the plate surface - - - - - Returns the 3D contour of the plate surface - - - - - Returns the max distance between the result and the constraints - - - - - Returns the max angle between the result and the constraints - - - - - Returns the max difference of curvature between the result and the constraints - - - - diff --git a/src/Mod/Part/App/GeomPlate/CurveConstraint.pyi b/src/Mod/Part/App/GeomPlate/CurveConstraint.pyi new file mode 100644 index 0000000000..afc98326f6 --- /dev/null +++ b/src/Mod/Part/App/GeomPlate/CurveConstraint.pyi @@ -0,0 +1,144 @@ +from Base.Metadata import export, constmethod +from Base.PyObjectBase import PyObjectBase +from typing import Final, overload + +@export( + PythonName="Part.GeomPlate.CurveConstraintPy", + Twin="GeomPlate_CurveConstraint", + TwinPointer="GeomPlate_CurveConstraint", + Include="GeomPlate_CurveConstraint.hxx", + Constructor=True, + Delete=True, +) +class CurveConstraint(PyObjectBase): + """ + Defines curves as constraints to be used to deform a surface + + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + NbPoints: int = ... + """ + The number of points on the curve used as a + constraint. The default setting is 10. This parameter + affects computation time, which increases by the cube of + the number of points. + """ + + FirstParameter: Final[float] = ... + """ + This function returns the first parameter of the curve. + The first parameter is the lowest parametric value for the curve, which defines the starting point of the curve. + """ + + LastParameter: Final[float] = ... + """ + This function returns the last parameter of the curve. + The last parameter is the highest parametric value for the curve, which defines the ending point of the curve. + """ + + Length: Final[float] = ... + """ + This function returns the length of the curve. + The length of the curve is a geometric property that indicates how long the curve is in the space. + """ + + def setOrder(self) -> None: + """ + Allows you to set the order of continuity required for the constraints: G0, G1, and G2, controlled + respectively by G0Criterion G1Criterion and G2Criterion. + """ + ... + + def order(self) -> None: + """ + Returns the order of constraint, one of G0, G1 or G2 + """ + ... + + def G0Criterion(self) -> None: + """ + Returns the G0 criterion at the parametric point U on the curve. + This is the greatest distance allowed between the constraint and the target surface at U. + """ + ... + + def G1Criterion(self) -> None: + """ + Returns the G1 criterion at the parametric point U on the curve. + This is the greatest angle allowed between the constraint and the target surface at U. + Raises an exception if the curve is not on a surface. + """ + ... + + def G2Criterion(self) -> None: + """ + Returns the G2 criterion at the parametric point U on the curve. + This is the greatest difference in curvature allowed between the constraint and the target surface at U. + Raises an exception if the curve is not on a surface. + """ + ... + + def setG0Criterion(self) -> None: + """ + Allows you to set the G0 criterion. This is the law + defining the greatest distance allowed between the + constraint and the target surface for each point of the + constraint. If this criterion is not set, TolDist, the + distance tolerance from the constructor, is used. + """ + ... + + def setG1Criterion(self) -> None: + """ + Allows you to set the G1 criterion. This is the law + defining the greatest angle allowed between the + constraint and the target surface. If this criterion is not + set, TolAng, the angular tolerance from the constructor, is used. + Raises an exception if the curve is not on a surface. + """ + ... + + def setG2Criterion(self) -> None: + """ + Allows you to set the G2 criterion. This is the law + defining the greatest difference in curvature allowed + between the constraint and the target surface. If this + criterion is not set, TolCurv, the curvature tolerance from + the constructor, is used. + Raises ConstructionError if the point is not on the surface. + """ + ... + + def curve3d(self) -> None: + """ + Returns a 3d curve associated the surface resulting of the constraints + """ + ... + + def setCurve2dOnSurf(self) -> None: + """ + Loads a 2d curve associated the surface resulting of the constraints + """ + ... + + def curve2dOnSurf(self) -> None: + """ + Returns a 2d curve associated the surface resulting of the constraints + """ + ... + + def setProjectedCurve(self) -> None: + """ + Loads a 2d curve resulting from the normal projection of + the curve on the initial surface + """ + ... + + def projectedCurve(self) -> None: + """ + Returns the projected curve resulting from the normal projection of the + curve on the initial surface + """ + ... diff --git a/src/Mod/Part/App/GeomPlate/CurveConstraintPy.xml b/src/Mod/Part/App/GeomPlate/CurveConstraintPy.xml deleted file mode 100644 index 941d20c07a..0000000000 --- a/src/Mod/Part/App/GeomPlate/CurveConstraintPy.xml +++ /dev/null @@ -1,148 +0,0 @@ - - - - - - Defines curves as constraints to be used to deform a surface - - - - Allows you to set the order of continuity required for -the constraints: G0, G1, and G2, controlled -respectively by G0Criterion G1Criterion and G2Criterion. - - - - - - Returns the order of constraint, one of G0, G1 or G2 - - - - - Returns the G0 criterion at the parametric point U on -the curve. This is the greatest distance allowed between -the constraint and the target surface at U. - - - - - - Returns the G1 criterion at the parametric point U on -the curve. This is the greatest angle allowed between -the constraint and the target surface at U. -Raises an exception if the curve is not on a surface. - - - - - - Returns the G2 criterion at the parametric point U on -the curve. This is the greatest difference in curvature -allowed between the constraint and the target surface at U. -Raises an exception if the curve is not on a surface. - - - - - - Allows you to set the G0 criterion. This is the law -defining the greatest distance allowed between the -constraint and the target surface for each point of the -constraint. If this criterion is not set, TolDist, the -distance tolerance from the constructor, is used. - - - - - - Allows you to set the G1 criterion. This is the law -defining the greatest angle allowed between the -constraint and the target surface. If this criterion is not -set, TolAng, the angular tolerance from the constructor, is used. -Raises an exception if the curve is not on a surface - - - - - - Allows you to set the G2 criterion. This is the law - defining the greatest difference in curvature allowed - between the constraint and the target surface. If this - criterion is not set, TolCurv, the curvature tolerance from - the constructor, is used. - Raises ConstructionError if the point is not on the surface - - - - - - Returns a 3d curve associated the surface resulting of the constraints - - - - - loads a 2d curve associated the surface resulting of the constraints - - - - - - Returns a 2d curve associated the surface resulting of the constraints - - - - - loads a 2d curve resulting from the normal projection of - the curve on the initial surface - - - - - Returns the projected curve resulting from the normal projection of the - curve on the initial surface - - - - - The number of points on the curve used as a -constraint. The default setting is 10. This parameter -affects computation time, which increases by the cube of -the number of points. - - - - - - This function returns the first parameter of the curve. -The first parameter is the lowest parametric value for the curve, which defines the starting point of the curve. - - - - - - This function returns the last parameter of the curve. -The last parameter is the highest parametric value for the curve, which defines the ending point of the curve. - - - - - - This function returns the length of the curve. -The length of the curve is a geometric property that indicates how long the curve is in the space. - - - - - diff --git a/src/Mod/Part/App/GeomPlate/PointConstraint.pyi b/src/Mod/Part/App/GeomPlate/PointConstraint.pyi new file mode 100644 index 0000000000..b2a8c218fc --- /dev/null +++ b/src/Mod/Part/App/GeomPlate/PointConstraint.pyi @@ -0,0 +1,107 @@ +from Base.Metadata import export, constmethod +from Base.PyObjectBase import PyObjectBase +from typing import Final, overload + +@export( + PythonName="Part.GeomPlate.PointConstraintPy", + Twin="GeomPlate_PointConstraint", + TwinPointer="GeomPlate_PointConstraint", + Include="GeomPlate_PointConstraint.hxx", + Constructor=True, + Delete=True, +) +class PointConstraint(PyObjectBase): + """ + Defines points as constraints to be used to deform a surface + + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + def setOrder(self, order: str) -> None: + """ + Allows you to set the order of continuity required for + the constraints: G0, G1, and G2, controlled + respectively by G0Criterion G1Criterion and G2Criterion. + """ + ... + + def order(self) -> str: + """ + Returns the order of constraint, one of G0, G1 or G2 + """ + ... + + def G0Criterion(self, U: float) -> float: + """ + Returns the G0 criterion at the parametric point U on + the curve. This is the greatest distance allowed between + the constraint and the target surface at U. + """ + ... + + def G1Criterion(self, U: float) -> float: + """ + Returns the G1 criterion at the parametric point U on + the curve. This is the greatest angle allowed between + the constraint and the target surface at U. + Raises an exception if the curve is not on a surface. + """ + ... + + def G2Criterion(self, U: float) -> float: + """ + Returns the G2 criterion at the parametric point U on + the curve. This is the greatest difference in curvature + allowed between the constraint and the target surface at U. + Raises an exception if the curve is not on a surface. + """ + ... + + def setG0Criterion(self, value: float) -> None: + """ + Allows you to set the G0 criterion. This is the law + defining the greatest distance allowed between the + constraint and the target surface for each point of the + constraint. If this criterion is not set, TolDist, the + distance tolerance from the constructor, is used. + """ + ... + + def setG1Criterion(self, value: float) -> None: + """ + Allows you to set the G1 criterion. This is the law + defining the greatest angle allowed between the + constraint and the target surface. If this criterion is not + set, TolAng, the angular tolerance from the constructor, is used. + Raises an exception if the curve is not on a surface + """ + ... + + def setG2Criterion(self, value: float) -> None: + """ + Allows you to set the G2 criterion. This is the law + defining the greatest difference in curvature allowed between the + constraint and the target surface. If this criterion is not + set, TolCurv, the curvature tolerance from the constructor, is used. + Raises ConstructionError if the curve is not on a surface + """ + ... + + def hasPnt2dOnSurf(self) -> bool: + """ + Checks if there is a 2D point associated with the surface. It returns a boolean indicating whether such a point exists. + """ + ... + + def setPnt2dOnSurf(self, p: "gp_Pnt2d") -> None: + """ + Allows you to set a 2D point on the surface. It takes a gp_Pnt2d as an argument, representing the 2D point to be associated with the surface. + """ + ... + + def pnt2dOnSurf(self) -> "gp_Pnt2d": + """ + Returns the 2D point on the surface. It returns a gp_Pnt2d representing the associated 2D point. + """ + ... diff --git a/src/Mod/Part/App/GeomPlate/PointConstraintPy.xml b/src/Mod/Part/App/GeomPlate/PointConstraintPy.xml deleted file mode 100644 index 60c9699e3f..0000000000 --- a/src/Mod/Part/App/GeomPlate/PointConstraintPy.xml +++ /dev/null @@ -1,103 +0,0 @@ - - - - - - Defines points as constraints to be used to deform a surface - - - - Allows you to set the order of continuity required for -the constraints: G0, G1, and G2, controlled -respectively by G0Criterion G1Criterion and G2Criterion. - - - - - - Returns the order of constraint, one of G0, G1 or G2 - - - - - Returns the G0 criterion at the parametric point U on -the curve. This is the greatest distance allowed between -the constraint and the target surface at U. - - - - - - Returns the G1 criterion at the parametric point U on -the curve. This is the greatest angle allowed between -the constraint and the target surface at U. -Raises an exception if the curve is not on a surface. - - - - - - Returns the G2 criterion at the parametric point U on -the curve. This is the greatest difference in curvature -allowed between the constraint and the target surface at U. -Raises an exception if the curve is not on a surface. - - - - - - Allows you to set the G0 criterion. This is the law -defining the greatest distance allowed between the -constraint and the target surface for each point of the -constraint. If this criterion is not set, TolDist, the -distance tolerance from the constructor, is used. - - - - - - Allows you to set the G1 criterion. This is the law -defining the greatest angle allowed between the -constraint and the target surface. If this criterion is not -set, TolAng, the angular tolerance from the constructor, is used. -Raises an exception if the curve is not on a surface - - - - - - Allows you to set the G2 criterion. This is the law - defining the greatest difference in curvature allowed between the - constraint and the target surface. If this criterion is not - set, TolCurv, the curvature tolerance from the constructor, is used. - Raises ConstructionError if the curve is not on a surface - - - - - Checks if there is a 2D point associated with the surface. It returns a boolean indicating whether such a point exists. - - - - - Allows you to set a 2D point on the surface. It takes a gp_Pnt2d as an argument, representing the 2D point to be associated with the surface. - - - - - Returns the 2D point on the surface. It returns a gp_Pnt2d representing the associated 2D point. - - - - diff --git a/src/Mod/Part/App/Geometry.cpp b/src/Mod/Part/App/Geometry.cpp index bb3d7d2a8d..571b91cd18 100644 --- a/src/Mod/Part/App/Geometry.cpp +++ b/src/Mod/Part/App/Geometry.cpp @@ -2640,7 +2640,7 @@ GeomCurve* GeomCircle::createArc(double first, double last) const GeomBSplineCurve* GeomCircle::toNurbs(double first, double last) const { // for an arc of circle use the generic method - if (first != 0 || last != 2*M_PI) { + if (first != 0 || last != 2 * std::numbers::pi) { return GeomConic::toNurbs(first, last); } @@ -2674,8 +2674,8 @@ GeomBSplineCurve* GeomCircle::toNurbs(double first, double last) const TColStd_Array1OfReal knots(1, 3); knots(1) = 0; - knots(2) = M_PI; - knots(3) = 2*M_PI; + knots(2) = std::numbers::pi; + knots(3) = 2 * std::numbers::pi; Handle(Geom_BSplineCurve) spline = new Geom_BSplineCurve(poles, weights,knots, mults, 3, Standard_False, Standard_True); @@ -2906,9 +2906,9 @@ void GeomArcOfCircle::getRange(double& u, double& v, bool emulateCCWXY) const } if (v < u) - v += 2*M_PI; - if (v-u > 2*M_PI) - v -= 2*M_PI; + v += 2 * std::numbers::pi; + if (v-u > 2 * std::numbers::pi) + v -= 2 * std::numbers::pi; } } @@ -3086,7 +3086,7 @@ GeomCurve* GeomEllipse::createArc(double first, double last) const GeomBSplineCurve* GeomEllipse::toNurbs(double first, double last) const { // for an arc of ellipse use the generic method - if (first != 0 || last != 2*M_PI) { + if (first != 0 || last != 2 * std::numbers::pi) { return GeomConic::toNurbs(first, last); } @@ -3465,9 +3465,9 @@ void GeomArcOfEllipse::getRange(double& u, double& v, bool emulateCCWXY) const std::swap(u,v); u = -u; v = -v; if (v < u) - v += 2*M_PI; - if (v-u > 2*M_PI) - v -= 2*M_PI; + v += 2 * std::numbers::pi; + if (v-u > 2 * std::numbers::pi) + v -= 2 * std::numbers::pi; } } } @@ -4683,11 +4683,12 @@ void GeomLineSegment::Restore (Base::XMLReader &reader) // for other objects, the best effort may be just to leave default values. reader.setPartialRestore(true); + constexpr double increment{std::numeric_limits::epsilon()}; if(start.x == 0) { - end = start + Base::Vector3d(DBL_EPSILON,0,0); + end = start + Base::Vector3d(increment, 0, 0); } else { - end = start + Base::Vector3d(start.x*DBL_EPSILON,0,0); + end = start + Base::Vector3d(start.x * increment, 0, 0); } setPoints(start, end); @@ -5409,7 +5410,7 @@ gp_Vec GeomCone::getDN(double u, double v, int Nu, int Nv) const { gp_XYZ Xdir = Pos.XDirection().XYZ(); gp_XYZ Ydir = Pos.YDirection().XYZ(); - Standard_Real Um = U + Nu * M_PI_2; // M_PI * 0.5 + Standard_Real Um = U + Nu * std::numbers::pi/2; Xdir.Multiply(cos(Um)); Ydir.Multiply(sin(Um)); Xdir.Add(Ydir); @@ -6228,11 +6229,11 @@ GeomArcOfCircle *createFilletGeometry(const GeomLineSegment *lineSeg1, const Geo if (endAngle < startAngle) std::swap(startAngle, endAngle); - if (endAngle > 2*M_PI ) - endAngle -= 2*M_PI; + if (endAngle > 2 * std::numbers::pi) + endAngle -= 2 * std::numbers::pi; - if (startAngle < 0 ) - endAngle += 2*M_PI; + if (startAngle < 0) + endAngle += 2 * std::numbers::pi; // Create Arc Segment GeomArcOfCircle *arc = new GeomArcOfCircle(); diff --git a/src/Mod/Part/App/Geometry.pyi b/src/Mod/Part/App/Geometry.pyi new file mode 100644 index 0000000000..18d0f6b341 --- /dev/null +++ b/src/Mod/Part/App/Geometry.pyi @@ -0,0 +1,129 @@ +from Base.Metadata import export, constmethod +from Base.Persistence import Persistence +from App.Extension import Extension +from Base.Vector import Vector +from Base.Matrix import Matrix +from typing import Final, List, Optional + + +@export( + Include="Mod/Part/App/Geometry.h", + Constructor=True, + Delete=True, +) +class Geometry(Persistence): + """ + The abstract class Geometry for 3D space is the root class of all geometric objects. + It describes the common behavior of these objects when: + - applying geometric transformations to objects, and + - constructing objects by geometric transformation (including copying). + """ + + Tag: Final[str] + """Gives the tag of the geometry as string.""" + + def mirror(self, geometry: "Geometry") -> None: + """ + Performs the symmetrical transformation of this geometric object + """ + ... + + def rotate(self, angle: float, axis: Vector) -> None: + """ + Rotates this geometric object at angle Ang (in radians) about axis + """ + ... + + def scale(self, center: Vector, factor: float) -> None: + """ + Applies a scaling transformation on this geometric object with a center and scaling factor + """ + ... + + def transform(self, transformation: Matrix) -> None: + """ + Applies a transformation to this geometric object + """ + ... + + def translate(self, vector: Vector) -> None: + """ + Translates this geometric object + """ + ... + + @constmethod + def copy(self) -> "Geometry": + """ + Create a copy of this geometry + """ + ... + + @constmethod + def clone(self) -> "Geometry": + """ + Create a clone of this geometry with the same Tag + """ + ... + + @constmethod + def isSame(self, geom: "Geometry", tol: float, angulartol: float) -> bool: + """ + isSame(geom, tol, angulartol) -> boolean + + Compare this geometry to another one + """ + ... + + @constmethod + def hasExtensionOfType(self, type_name: str) -> bool: + """ + Returns a boolean indicating whether a geometry extension of the type indicated as a string exists. + """ + ... + + @constmethod + def hasExtensionOfName(self, name: str) -> bool: + """ + Returns a boolean indicating whether a geometry extension with the name indicated as a string exists. + """ + ... + + @constmethod + def getExtensionOfType(self, type_name: str) -> Optional[Extension]: + """ + Gets the first geometry extension of the type indicated by the string. + """ + ... + + @constmethod + def getExtensionOfName(self, name: str) -> Optional[Extension]: + """ + Gets the first geometry extension of the name indicated by the string. + """ + ... + + def setExtension(self, extension: Extension) -> None: + """ + Sets a geometry extension of the indicated type. + """ + ... + + def deleteExtensionOfType(self, type_name: str) -> None: + """ + Deletes all extensions of the indicated type. + """ + ... + + def deleteExtensionOfName(self, name: str) -> None: + """ + Deletes all extensions of the indicated name. + """ + ... + + @constmethod + def getExtensions(self) -> List[Extension]: + """ + Returns a list with information about the geometry extensions. + """ + ... diff --git a/src/Mod/Part/App/GeometryBoolExtension.pyi b/src/Mod/Part/App/GeometryBoolExtension.pyi new file mode 100644 index 0000000000..7f3db293b4 --- /dev/null +++ b/src/Mod/Part/App/GeometryBoolExtension.pyi @@ -0,0 +1,17 @@ +from Base.Metadata import export +from GeometryExtension import GeometryExtension + + +@export( + PythonName="Part.GeometryBoolExtension", + Include="Mod/Part/App/GeometryDefaultExtension.h", + FatherInclude="Mod/Part/App/GeometryExtensionPy.h", + Constructor=True, +) +class GeometryBoolExtension(GeometryExtension): + """ + A GeometryExtension extending geometry objects with a boolean. + """ + + Value: bool = ... + """Returns the value of the GeometryBoolExtension.""" diff --git a/src/Mod/Part/App/GeometryBoolExtensionPy.xml b/src/Mod/Part/App/GeometryBoolExtensionPy.xml deleted file mode 100644 index 5cfb7fa71c..0000000000 --- a/src/Mod/Part/App/GeometryBoolExtensionPy.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - A GeometryExtension extending geometry objects with a boolean. - - - - - Returns the value of the GeometryBoolExtension. - - - - - - diff --git a/src/Mod/Part/App/GeometryCurve.pyi b/src/Mod/Part/App/GeometryCurve.pyi new file mode 100644 index 0000000000..993c1014ae --- /dev/null +++ b/src/Mod/Part/App/GeometryCurve.pyi @@ -0,0 +1,446 @@ +from Base.Metadata import export, constmethod +from Base.Vector import Vector +from Base.Rotation import Rotation as RotationPy +from Geometry import Geometry +from typing import Final, overload, List, Union, Optional, Tuple + + +@export( + Twin="GeomCurve", + TwinPointer="GeomCurve", + PythonName="Part.Curve", + FatherInclude="Mod/Part/App/GeometryPy.h", + Include="Mod/Part/App/Geometry.h", + Constructor=True, +) +class GeometryCurve(Geometry): + """ + The abstract class GeometryCurve is the root class of all curve objects. + + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + Continuity: Final[str] = "" + """Returns the global continuity of the curve.""" + + FirstParameter: Final[float] = 0.0 + """Returns the value of the first parameter.""" + + LastParameter: Final[float] = 0.0 + """Returns the value of the last parameter.""" + + Rotation: Final[RotationPy] = ... + """Returns a rotation object to describe the orientation for curve that supports it""" + + @constmethod + def toShape(self) -> object: + """ + Return the shape for the geometry. + """ + ... + + @overload + @constmethod + def discretize( + self, + Number: int, + *, + First: Optional[float] = None, + Last: Optional[float] = None + ) -> List[Vector]: + """ + Discretizes the curve and returns a list of points. + """ + ... + + @overload + @constmethod + def discretize( + self, + QuasiNumber: int, + *, + First: Optional[float] = None, + Last: Optional[float] = None + ) -> List[Vector]: + """ + Discretizes the curve and returns a list of quasi equidistant points. + """ + ... + + @overload + @constmethod + def discretize( + self, + Distance: float, + *, + First: Optional[float] = None, + Last: Optional[float] = None + ) -> List[Vector]: + """ + Discretizes the curve and returns a list of equidistant points with distance 'd'. + """ + ... + + @overload + @constmethod + def discretize( + self, + Deflection: float, + *, + First: Optional[float] = None, + Last: Optional[float] = None + ) -> List[Vector]: + """ + Discretizes the curve and returns a list of points with a maximum deflection 'd' to the curve. + """ + ... + + @overload + @constmethod + def discretize( + self, + QuasiDeflection: float, + *, + First: Optional[float] = None, + Last: Optional[float] = None + ) -> List[Vector]: + """ + Discretizes the curve and returns a list of points with a maximum deflection 'd' to the curve (faster). + """ + ... + + @overload + @constmethod + def discretize( + self, + Angular: float, + Curvature: float, + Minimum: int = 2, + *, + First: Optional[float] = None, + Last: Optional[float] = None + ) -> List[Vector]: + """ + Discretizes the curve and returns a list of points with an angular deflection of 'a' and a curvature deflection of 'c'. + Optionally a minimum number of points can be set. + """ + ... + + @constmethod + def discretize(self, **kwargs) -> List[Vector]: + """ + Discretizes the curve and returns a list of points. + + The function accepts keywords as argument: + + discretize(Number=n) => gives a list of 'n' equidistant points + discretize(QuasiNumber=n) => gives a list of 'n' quasi equidistant points (is faster than the method above) + discretize(Distance=d) => gives a list of equidistant points with distance 'd' + discretize(Deflection=d) => gives a list of points with a maximum deflection 'd' to the curve + discretize(QuasiDeflection=d) => gives a list of points with a maximum deflection 'd' to the curve (faster) + discretize(Angular=a,Curvature=c,[Minimum=m]) => gives a list of points with an angular deflection of 'a' + and a curvature deflection of 'c'. Optionally a minimum number of points + can be set which by default is set to 2. + + Optionally you can set the keywords 'First' and 'Last' to define a sub-range of the parameter range + of the curve. + + If no keyword is given then it depends on whether the argument is an int or float. + If it's an int then the behaviour is as if using the keyword 'Number', if it's float + then the behaviour is as if using the keyword 'Distance'. + + Example: + + import Part + c=Part.Circle() + c.Radius=5 + p=c.discretize(Number=50,First=3.14) + s=Part.Compound([Part.Vertex(i) for i in p]) + Part.show(s) + + + p=c.discretize(Angular=0.09,Curvature=0.01,Last=3.14,Minimum=100) + s=Part.Compound([Part.Vertex(i) for i in p]) + Part.show(s) + """ + ... + + @constmethod + def getD0(self, parameter: float) -> Vector: + """ + Returns the point of given parameter + """ + ... + + @constmethod + def getD1(self, parameter: float) -> Vector: + """ + Returns the point and first derivative of given parameter + """ + ... + + @constmethod + def getD2(self, parameter: float) -> Vector: + """ + Returns the point, first and second derivatives + """ + ... + + @constmethod + def getD3(self, parameter: float) -> Vector: + """ + Returns the point, first, second and third derivatives + """ + ... + + @constmethod + def getDN(self, n: int, parameter: float) -> Vector: + """ + Returns the n-th derivative + """ + ... + + @constmethod + def length( + self, + uMin: Optional[float] = None, + uMax: Optional[float] = None, + Tol: Optional[float] = None, + ) -> float: + """ + Computes the length of a curve + length([uMin, uMax, Tol]) -> float + """ + ... + + @constmethod + def parameterAtDistance( + self, + abscissa: Optional[float] = None, + startingParameter: Optional[float] = None, + ) -> float: + """ + Returns the parameter on the curve of a point at the given distance from a starting parameter. + parameterAtDistance([abscissa, startingParameter]) -> float + """ + ... + + @constmethod + def value(self, u: float) -> Vector: + """ + Computes the point of parameter u on this curve + """ + ... + + @constmethod + def tangent(self, u: float) -> Vector: + """ + Computes the tangent of parameter u on this curve + """ + ... + + @constmethod + def makeRuledSurface(self, otherCurve: "GeometryCurve") -> object: + """ + Make a ruled surface of this and the given curves + """ + ... + + @constmethod + def intersect2d(self, otherCurve: "GeometryCurve") -> List[Vector]: + """ + Get intersection points with another curve lying on a plane. + """ + ... + + @constmethod + def continuityWith(self, otherCurve: "GeometryCurve") -> str: + """ + Computes the continuity of two curves + """ + ... + + @constmethod + def parameter(self, point: Vector) -> float: + """ + Returns the parameter on the curve of the nearest orthogonal projection of the point. + """ + ... + + @constmethod + def normal(self, pos: float) -> Vector: + """ + Vector = normal(pos) - Get the normal vector at the given parameter [First|Last] if defined + """ + ... + + @overload + @constmethod + def projectPoint(self, Point: Vector, Method: str = "NearestPoint") -> Vector: + """ + projectPoint(Point=Vector, Method="NearestPoint") -> Vector + """ + ... + + @overload + @constmethod + def projectPoint(self, Point: Vector, Method: str = "LowerDistance") -> float: + """ + projectPoint(Vector, "LowerDistance") -> float. + """ + ... + + @overload + @constmethod + def projectPoint( + self, Point: Vector, Method: str = "LowerDistanceParameter" + ) -> float: + """ + projectPoint(Vector, "LowerDistanceParameter") -> float. + """ + ... + + @overload + @constmethod + def projectPoint(self, Point: Vector, Method: str = "Distance") -> List[float]: + """ + projectPoint(Vector, "Distance") -> list of floats. + """ + ... + + @overload + @constmethod + def projectPoint(self, Point: Vector, Method: str = "Parameter") -> List[float]: + """ + projectPoint(Vector, "Parameter") -> list of floats. + """ + ... + + @overload + @constmethod + def projectPoint(self, Point: Vector, Method: str = "Point") -> List[Vector]: + """ + projectPoint(Vector, "Point") -> list of points. + """ + ... + + @constmethod + def projectPoint(self, **kwargs) -> Union[Vector, float, List[float], List[Vector]]: + """ + Computes the projection of a point on the curve + + projectPoint(Point=Vector,[Method="NearestPoint"]) + projectPoint(Vector,"NearestPoint") -> Vector + projectPoint(Vector,"LowerDistance") -> float + projectPoint(Vector,"LowerDistanceParameter") -> float + projectPoint(Vector,"Distance") -> list of floats + projectPoint(Vector,"Parameter") -> list of floats + projectPoint(Vector,"Point") -> list of points + """ + ... + + @constmethod + def curvature(self, pos: float) -> float: + """ + Float = curvature(pos) - Get the curvature at the given parameter [First|Last] if defined + """ + ... + + @constmethod + def centerOfCurvature(self, pos: float) -> Vector: + """ + Vector = centerOfCurvature(float pos) - Get the center of curvature at the given parameter [First|Last] if defined + """ + ... + + @constmethod + def intersect(self, curve_or_surface: object, precision: float) -> object: + """ + Returns all intersection points and curve segments between the curve and the curve/surface. + + arguments: curve/surface (for the intersection), precision (float) + """ + ... + + @constmethod + def intersectCS(self, surface: object) -> object: + """ + Returns all intersection points and curve segments between the curve and the surface. + """ + ... + + @constmethod + def intersectCC(self, otherCurve: "GeometryCurve") -> List[Vector]: + """ + Returns all intersection points between this curve and the given curve. + """ + ... + + @constmethod + def toBSpline(self, points: Tuple[float, float]) -> "BSplineCurve": + """ + Converts a curve of any type (only part from First to Last) to BSpline curve. + toBSpline((first: float, last: float)) -> BSplineCurve + """ + ... + + @constmethod + def toNurbs(self, points: Tuple[float, float]) -> "NurbsCurve": + """ + Converts a curve of any type (only part from First to Last) to NURBS curve. + toNurbs((first: float, last: float)) -> NurbsCurve + """ + ... + + @constmethod + def trim(self, points: Tuple[float, float]) -> "TrimmedCurve": + """ + Returns a trimmed curve defined in the given parameter range. + trim((first: float, last: float)) -> TrimmedCurve + """ + ... + + @constmethod + def approximateBSpline( + self, Tolerance: float, MaxSegments: int, MaxDegree: int, Order: str = "C2" + ) -> "BSplineCurve": + """ + Approximates a curve of any type to a B-Spline curve. + approximateBSpline(Tolerance, MaxSegments, MaxDegree, [Order='C2']) -> BSplineCurve + """ + ... + + def reverse(self) -> None: + """ + Changes the direction of parametrization of the curve. + """ + ... + + @constmethod + def reversedParameter(self, U: float) -> float: + """ + Returns the parameter on the reversed curve for the point of parameter U on this curve. + """ + ... + + @constmethod + def isPeriodic(self) -> bool: + """ + Returns true if this curve is periodic. + """ + ... + + @constmethod + def period(self) -> float: + """ + Returns the period of this curve or raises an exception if it is not periodic. + """ + ... + + @constmethod + def isClosed(self) -> bool: + """ + Returns true if the curve is closed. + """ + ... diff --git a/src/Mod/Part/App/GeometryCurvePy.xml b/src/Mod/Part/App/GeometryCurvePy.xml deleted file mode 100644 index 63a5ca126e..0000000000 --- a/src/Mod/Part/App/GeometryCurvePy.xml +++ /dev/null @@ -1,246 +0,0 @@ - - - - - - The abstract class GeometryCurve is the root class of all curve objects. - - - - Return the shape for the geometry. - - - - - Discretizes the curve and returns a list of points. - -The function accepts keywords as argument: - -discretize(Number=n) => gives a list of 'n' equidistant points -discretize(QuasiNumber=n) => gives a list of 'n' quasi equidistant points (is faster than the method above) -discretize(Distance=d) => gives a list of equidistant points with distance 'd' -discretize(Deflection=d) => gives a list of points with a maximum deflection 'd' to the curve -discretize(QuasiDeflection=d) => gives a list of points with a maximum deflection 'd' to the curve (faster) -discretize(Angular=a,Curvature=c,[Minimum=m]) => gives a list of points with an angular deflection of 'a' - and a curvature deflection of 'c'. Optionally a minimum number of points - can be set which by default is set to 2. - -Optionally you can set the keywords 'First' and 'Last' to define a sub-range of the parameter range -of the curve. - -If no keyword is given then it depends on whether the argument is an int or float. -If it's an int then the behaviour is as if using the keyword 'Number', if it's float -then the behaviour is as if using the keyword 'Distance'. - -Example: - -import Part -c=Part.Circle() -c.Radius=5 -p=c.discretize(Number=50,First=3.14) -s=Part.Compound([Part.Vertex(i) for i in p]) -Part.show(s) - - -p=c.discretize(Angular=0.09,Curvature=0.01,Last=3.14,Minimum=100) -s=Part.Compound([Part.Vertex(i) for i in p]) -Part.show(s) - - - - - Returns the point of given parameter - - - - - Returns the point and first derivative of given parameter - - - - - Returns the point, first and second derivatives - - - - - Returns the point, first, second and third derivatives - - - - - Returns the n-th derivative - - - - - Computes the length of a curve -length([uMin, uMax, Tol]) -> float - - - - - Returns the parameter on the curve of a point at the given distance from a starting parameter. -parameterAtDistance([abscissa, startingParameter]) -> float - - - - - Computes the point of parameter u on this curve - - - - - Computes the tangent of parameter u on this curve - - - - - Make a ruled surface of this and the given curves - - - - - Get intersection points with another curve lying on a plane. - - - - - Computes the continuity of two curves - - - - - Returns the parameter on the curve of the nearest orthogonal projection of the point. - - - - - Vector = normal(pos) - Get the normal vector at the given parameter [First|Last] if defined - - - - - Computes the projection of a point on the curve - -projectPoint(Point=Vector,[Method="NearestPoint"]) -projectPoint(Vector,"NearestPoint") -> Vector -projectPoint(Vector,"LowerDistance") -> float -projectPoint(Vector,"LowerDistanceParameter") -> float -projectPoint(Vector,"Distance") -> list of floats -projectPoint(Vector,"Parameter") -> list of floats -projectPoint(Vector,"Point") -> list of points - - - - - Float = curvature(pos) - Get the curvature at the given parameter [First|Last] if defined - - - - - Vector = centerOfCurvature(float pos) - Get the center of curvature at the given parameter [First|Last] if defined - - - - - Returns all intersection points and curve segments between the curve and the curve/surface. - -arguments: curve/surface (for the intersection), precision (float) - - - - - Returns all intersection points and curve segments between the curve and the surface. - - - - - Returns all intersection points between this curve and the given curve. - - - - - Converts a curve of any type (only part from First to Last) to BSpline curve. -toBSpline((first: float, last: float)) -> BSplineCurve - - - - - Converts a curve of any type (only part from First to Last) to NURBS curve. -toNurbs((first: float, last: float)) -> NurbsCurve - - - - - Returns a trimmed curve defined in the given parameter range. -trim((first: float, last: float)) -> TrimmedCurve - - - - - Approximates a curve of any type to a B-Spline curve. -approximateBSpline(Tolerance, MaxSegments, MaxDegree, [Order='C2']) -> BSplineCurve - - - - - Changes the direction of parametrization of the curve. - - - - - Returns the parameter on the reversed curve for the point of parameter U on this curve. - - - - - Returns true if this curve is periodic. - - - - - Returns the period of this curve or raises an exception if it is not periodic. - - - - - Returns true if the curve is closed. - - - - - Returns the global continuity of the curve. - - - - - - Returns the value of the first parameter. - - - - - - Returns the value of the last parameter. - - - - - - Returns a rotation object to describe the orientation for curve that supports it - - - - - diff --git a/src/Mod/Part/App/GeometryDoubleExtension.pyi b/src/Mod/Part/App/GeometryDoubleExtension.pyi new file mode 100644 index 0000000000..64a63673f4 --- /dev/null +++ b/src/Mod/Part/App/GeometryDoubleExtension.pyi @@ -0,0 +1,20 @@ +from Base.Metadata import export +from GeometryExtension import GeometryExtension + + +@export( + PythonName="Part.GeometryDoubleExtension", + Include="Mod/Part/App/GeometryDefaultExtension.h", + FatherInclude="Mod/Part/App/GeometryExtensionPy.h", + Constructor=True, +) +class GeometryDoubleExtension(GeometryExtension): + """ + A GeometryExtension extending geometry objects with a double. + + Author: Abdullah Tahiri (abdullah.tahiri.yo@gmail.com) + Licence: LGPL + """ + + Value: float = ... + """Returns the value of the GeometryDoubleExtension.""" diff --git a/src/Mod/Part/App/GeometryDoubleExtensionPy.xml b/src/Mod/Part/App/GeometryDoubleExtensionPy.xml deleted file mode 100644 index b11ae02798..0000000000 --- a/src/Mod/Part/App/GeometryDoubleExtensionPy.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - A GeometryExtension extending geometry objects with a double. - - - - - Returns the value of the GeometryDoubleExtension. - - - - - - diff --git a/src/Mod/Part/App/GeometryExtension.pyi b/src/Mod/Part/App/GeometryExtension.pyi new file mode 100644 index 0000000000..7d582844cb --- /dev/null +++ b/src/Mod/Part/App/GeometryExtension.pyi @@ -0,0 +1,24 @@ +from Base.Metadata import export, constmethod +from Base.PyObjectBase import PyObjectBase +from typing import Final + + +@export( + Include="Mod/Part/App/GeometryExtension.h", + Constructor=True, + Delete=True, +) +class GeometryExtension(PyObjectBase): + """ + The abstract class GeometryExtension enables to extend geometry objects with application specific data. + Author: Abdullah Tahiri (abdullah.tahiri.yo@gmail.com) + Licence: LGPL + """ + + Name: str = "" + """Sets/returns the name of this extension.""" + + @constmethod + def copy(self) -> "GeometryExtension": + """Create a copy of this geometry extension.""" + ... diff --git a/src/Mod/Part/App/GeometryExtensionPy.xml b/src/Mod/Part/App/GeometryExtensionPy.xml deleted file mode 100644 index 3e17b9f680..0000000000 --- a/src/Mod/Part/App/GeometryExtensionPy.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - The abstract class GeometryExtension enables to extend geometry objects with application specific data. - - - - Create a copy of this geometry extension. - - - - - Sets/returns the name of this extension. - - - - - diff --git a/src/Mod/Part/App/GeometryIntExtension.pyi b/src/Mod/Part/App/GeometryIntExtension.pyi new file mode 100644 index 0000000000..f8a306e3dc --- /dev/null +++ b/src/Mod/Part/App/GeometryIntExtension.pyi @@ -0,0 +1,27 @@ +from Base.Metadata import export +from Part.GeometryExtension import GeometryExtension +from typing import Final + + +@export( + Father="GeometryExtensionPy", + Name="GeometryIntExtensionPy", + PythonName="Part.GeometryIntExtension", + Twin="GeometryIntExtension", + TwinPointer="GeometryIntExtension", + Include="Mod/Part/App/GeometryDefaultExtension.h", + Namespace="Part", + FatherInclude="Mod/Part/App/GeometryExtensionPy.h", + FatherNamespace="Part", + Constructor=True, +) +class GeometryIntExtension(GeometryExtension): + """ + A GeometryExtension extending geometry objects with an int. + + Author: Abdullah Tahiri (abdullah.tahiri.yo@gmail.com) + Licence: LGPL + """ + + Value: int = ... + """returns the value of the GeometryIntExtension.""" diff --git a/src/Mod/Part/App/GeometryIntExtensionPy.xml b/src/Mod/Part/App/GeometryIntExtensionPy.xml deleted file mode 100644 index 694f1c2659..0000000000 --- a/src/Mod/Part/App/GeometryIntExtensionPy.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - A GeometryExtension extending geometry objects with an int. - - - - - returns the value of the GeometryIntExtension. - - - - - - diff --git a/src/Mod/Part/App/GeometryMigrationExtension.cpp b/src/Mod/Part/App/GeometryMigrationExtension.cpp index 2ecebe6298..43388eeecc 100644 --- a/src/Mod/Part/App/GeometryMigrationExtension.cpp +++ b/src/Mod/Part/App/GeometryMigrationExtension.cpp @@ -52,11 +52,7 @@ std::unique_ptr GeometryMigrationExtension::copy() cons copyAttributes(cpy.get()); -#if defined (__GNUC__) && (__GNUC__ <=4) - return std::move(cpy); -#else return cpy; -#endif } PyObject * GeometryMigrationExtension::getPyObject() diff --git a/src/Mod/Part/App/GeometryPy.xml b/src/Mod/Part/App/GeometryPy.xml deleted file mode 100644 index e56c95fe88..0000000000 --- a/src/Mod/Part/App/GeometryPy.xml +++ /dev/null @@ -1,110 +0,0 @@ - - - - - - The abstract class Geometry for 3D space is the root class of all geometric objects. -It describes the common behavior of these objects when: -- applying geometric transformations to objects, and -- constructing objects by geometric transformation (including copying). - - - - Performs the symmetrical transformation of this geometric object - - - - - Rotates this geometric object at angle Ang (in radians) about axis - - - - - Applies a scaling transformation on this geometric object with a center and scaling factor - - - - - Applies a transformation to this geometric object - - - - - Translates this geometric object - - - - - Create a copy of this geometry - - - - - Create a clone of this geometry with the same Tag - - - - - isSame(geom, tol, angulartol) -> boolean - -Compare this geometry to another one - - - - - Returns a boolean indicating whether a geometry extension of the type indicated as a string exists. - - - - - Returns a boolean indicating whether a geometry extension with the name indicated as a string exists. - - - - - Gets the first geometry extension of the type indicated by the string. - - - - - Gets the first geometry extension of the name indicated by the string. - - - - - Sets a geometry extension of the indicated type. - - - - - Deletes all extensions of the indicated type. - - - - - Deletes all extensions of the indicated name. - - - - - Returns a list with information about the geometry extensions. - - - - - Gives the tag of the geometry as string. - - - - - diff --git a/src/Mod/Part/App/GeometryStringExtension.pyi b/src/Mod/Part/App/GeometryStringExtension.pyi new file mode 100644 index 0000000000..732f60c2f3 --- /dev/null +++ b/src/Mod/Part/App/GeometryStringExtension.pyi @@ -0,0 +1,20 @@ +from Base.Metadata import export +from GeometryExtension import GeometryExtension + + +@export( + PythonName="Part.GeometryStringExtension", + Include="Mod/Part/App/GeometryDefaultExtension.h", + FatherInclude="Mod/Part/App/GeometryExtensionPy.h", + Constructor=True, +) +class GeometryStringExtension(GeometryExtension): + """ + A GeometryExtension extending geometry objects with a string. + + Author: Abdullah Tahiri (abdullah.tahiri.yo@gmail.com) + Licence: LGPL + """ + + Value: str = ... + """returns the value of the GeometryStringExtension.""" diff --git a/src/Mod/Part/App/GeometryStringExtensionPy.xml b/src/Mod/Part/App/GeometryStringExtensionPy.xml deleted file mode 100644 index 0f371b9401..0000000000 --- a/src/Mod/Part/App/GeometryStringExtensionPy.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - A GeometryExtension extending geometry objects with a string. - - - - - returns the value of the GeometryStringExtension. - - - - - - diff --git a/src/Mod/Part/App/GeometrySurface.pyi b/src/Mod/Part/App/GeometrySurface.pyi new file mode 100644 index 0000000000..a5e0f621f4 --- /dev/null +++ b/src/Mod/Part/App/GeometrySurface.pyi @@ -0,0 +1,288 @@ +from Base.Metadata import export, constmethod +from Base.Rotation import Rotation as RotationPy +from Base.Vector import Vector +from Geometry import Geometry +from GeometryCurve import GeometryCurve +from Line import Line +from typing import Final, overload, Any, Tuple, List, Literal, Union + + +@export( + Twin="GeomSurface", + TwinPointer="GeomSurface", + PythonName="Part.GeometrySurface", + FatherInclude="Mod/Part/App/GeometryPy.h", + Include="Mod/Part/App/Geometry.h", + Constructor=True, +) +class GeometrySurface(Geometry): + """ + The abstract class GeometrySurface is the root class of all surface objects. + + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + Continuity: Final[str] = "" + """Returns the global continuity of the surface.""" + + Rotation: Final[RotationPy] = ... + """Returns a rotation object to describe the orientation for surface that supports it""" + + @constmethod + def toShape(self) -> Any: + """ + Return the shape for the geometry. + """ + ... + + @constmethod + def toShell(self, *, Bounds: object, Segment: object) -> Any: + """ + Make a shell of the surface. + """ + ... + + @constmethod + def getD0(self, param: float) -> Vector: + """ + Returns the point of given parameter + """ + ... + + @constmethod + def getDN(self, n: int) -> Any: + """ + Returns the n-th derivative + """ + ... + + @constmethod + def value(self, u: float, v: float) -> Vector: + """ + value(u,v) -> Point + Computes the point of parameter (u,v) on this surface + """ + ... + + @constmethod + def tangent(self, u: float, v: float) -> Tuple[Vector, Vector]: + """ + tangent(u,v) -> (Vector,Vector) + Computes the tangent of parameter (u,v) on this geometry + """ + ... + + @constmethod + def normal(self, u: float, v: float) -> Vector: + """ + normal(u,v) -> Vector + Computes the normal of parameter (u,v) on this geometry + """ + ... + + @overload + def projectPoint( + self, Point: Vector, Method: Literal["NearestPoint"] = "NearestPoint" + ) -> Vector: ... + + @overload + def projectPoint( + self, Point: Vector, Method: Literal["LowerDistance"] + ) -> float: ... + + @overload + def projectPoint( + self, Point: Vector, Method: Literal["LowerDistanceParameters"] + ) -> Tuple[float, float]: ... + + @overload + def projectPoint( + self, Point: Vector, Method: Literal["Distance"] + ) -> List[float]: ... + + @overload + def projectPoint( + self, Point: Vector, Method: Literal["Parameters"] + ) -> List[Tuple[float, float]]: ... + + @overload + def projectPoint(self, Point: Vector, Method: Literal["Point"]) -> List[Vector]: ... + + @constmethod + def projectPoint(self, *, Point: Vector, Method: str = ...) -> Any: + """ + Computes the projection of a point on the surface + + projectPoint(Point=Vector,[Method="NearestPoint"]) + projectPoint(Vector,"NearestPoint") -> Vector + projectPoint(Vector,"LowerDistance") -> float + projectPoint(Vector,"LowerDistanceParameters") -> tuple of floats (u,v) + projectPoint(Vector,"Distance") -> list of floats + projectPoint(Vector,"Parameters") -> list of tuples of floats + projectPoint(Vector,"Point") -> list of points + """ + ... + + @constmethod + def isUmbillic(self, u: float, v: float) -> bool: + """ + isUmbillic(u,v) -> bool + Check if the geometry on parameter is an umbillic point, + i.e. maximum and minimum curvature are equal. + """ + ... + + @constmethod + def curvature(self, u: float, v: float, type: str) -> float: + """ + curvature(u,v,type) -> float + The value of type must be one of this: Max, Min, Mean or Gauss + Computes the curvature of parameter (u,v) on this geometry + """ + ... + + @constmethod + def curvatureDirections(self, u: float, v: float) -> Tuple[Vector, Vector]: + """ + curvatureDirections(u,v) -> (Vector,Vector) + Computes the directions of maximum and minimum curvature + of parameter (u,v) on this geometry. + The first vector corresponds to the maximum curvature, + the second vector corresponds to the minimum curvature. + """ + ... + + @constmethod + def bounds(self) -> Tuple[float, float, float, float]: + """ + Returns the parametric bounds (U1, U2, V1, V2) of this trimmed surface. + """ + ... + + @constmethod + def isPlanar(self, tolerance: float = 0.0) -> bool: + """ + isPlanar([float]) -> Bool + Checks if the surface is planar within a certain tolerance. + """ + ... + + @constmethod + def uIso(self, u: Tuple) -> Union[GeometryCurve, Line]: + """ + Builds the U isoparametric curve + """ + ... + + @constmethod + def vIso(self, v: Tuple) -> Union[GeometryCurve, Line]: + """ + Builds the V isoparametric curve + """ + ... + + @constmethod + def isUPeriodic(self) -> bool: + """ + Returns true if this patch is periodic in the given parametric direction. + """ + ... + + @constmethod + def isVPeriodic(self) -> bool: + """ + Returns true if this patch is periodic in the given parametric direction. + """ + ... + + @constmethod + def isUClosed(self) -> bool: + """ + Checks if this surface is closed in the u parametric direction. + """ + ... + + @constmethod + def isVClosed(self) -> bool: + """ + Checks if this surface is closed in the v parametric direction. + """ + ... + + @constmethod + def UPeriod(self) -> float: + """ + Returns the period of this patch in the u parametric direction. + """ + ... + + @constmethod + def VPeriod(self) -> float: + """ + Returns the period of this patch in the v parametric direction. + """ + ... + + @constmethod + def parameter(self) -> float: + """ + Returns the parameter on the curve + of the nearest orthogonal projection of the point. + """ + ... + + @overload + def toBSpline( + self, + tolerance: float = 1e-7, + continuity_u: Literal["C0", "G0", "G1", "C1", "G2", "C3", "CN"] = "C1", + continuity_v: Literal["C0", "G0", "G1", "C1", "G2", "C3", "CN"] = "C1", + max_degree_u: int = 25, + max_degree_v: int = 25, + max_segments: int = 1000, + precision_code: int = 0, + ) -> Any: ... + + @constmethod + def toBSpline( + self, + *, + tolerance: float = 1e-7, + continuity_u: str = "C1", + continuity_v: str = "C1", + max_degree_u: int = 25, + max_degree_v: int = 25, + max_segments: int = 1000, + precision_code: int = 0 + ) -> Any: + """ + Returns a B-Spline representation of this surface. + The optional arguments are: + * tolerance (default=1e-7) + * continuity in u (as string e.g. C0, G0, G1, C1, G2, C3, CN) (default='C1') + * continuity in v (as string e.g. C0, G0, G1, C1, G2, C3, CN) (default='C1') + * maximum degree in u (default=25) + * maximum degree in v (default=25) + * maximum number of segments (default=1000) + * precision code (default=0) + Will raise an exception if surface is infinite in U or V (like planes, cones or cylinders) + """ + ... + + @constmethod + def intersect(self) -> Any: + """ + Returns all intersection points/curves between the surface and the curve/surface. + """ + ... + + @constmethod + def intersectSS(self, SecondSurface: Any, precision_code: int = 0) -> Any: + """ + Returns all intersection curves of this surface and the given surface. + The required arguments are: + * Second surface + * precision code (optional, default=0) + """ + ... diff --git a/src/Mod/Part/App/GeometrySurfacePy.xml b/src/Mod/Part/App/GeometrySurfacePy.xml deleted file mode 100644 index 45c8a4022d..0000000000 --- a/src/Mod/Part/App/GeometrySurfacePy.xml +++ /dev/null @@ -1,189 +0,0 @@ - - - - - - The abstract class GeometrySurface is the root class of all surface objects. - - - - Return the shape for the geometry. - - - - - Make a shell of the surface. - - - - - Returns the point of given parameter - - - - - Returns the n-th derivative - - - - - value(u,v) -> Point -Computes the point of parameter (u,v) on this surface - - - - - tangent(u,v) -> (Vector,Vector) -Computes the tangent of parameter (u,v) on this geometry - - - - - normal(u,v) -> Vector -Computes the normal of parameter (u,v) on this geometry - - - - - Computes the projection of a point on the surface - -projectPoint(Point=Vector,[Method="NearestPoint"]) -projectPoint(Vector,"NearestPoint") -> Vector -projectPoint(Vector,"LowerDistance") -> float -projectPoint(Vector,"LowerDistanceParameters") -> tuple of floats (u,v) -projectPoint(Vector,"Distance") -> list of floats -projectPoint(Vector,"Parameters") -> list of tuples of floats -projectPoint(Vector,"Point") -> list of points - - - - - isUmbillic(u,v) -> bool -Check if the geometry on parameter is an umbillic point, -i.e. maximum and minimum curvature are equal. - - - - - curvature(u,v,type) -> float -The value of type must be one of this: Max, Min, Mean or Gauss -Computes the curvature of parameter (u,v) on this geometry - - - - - curvatureDirections(u,v) -> (Vector,Vector) -Computes the directions of maximum and minimum curvature -of parameter (u,v) on this geometry. -The first vector corresponds to the maximum curvature, -the second vector corresponds to the minimum curvature. - - - - - Returns the parametric bounds (U1, U2, V1, V2) of this trimmed surface. - - - - - isPlanar([float]) -> Bool -Checks if the surface is planar within a certain tolerance. - - - - - Returns the global continuity of the surface. - - - - - - Returns a rotation object to describe the orientation for surface that supports it - - - - - - Builds the U isoparametric curve - - - - - Builds the V isoparametric curve - - - - - Returns true if this patch is periodic in the given parametric direction. - - - - - Returns true if this patch is periodic in the given parametric direction. - - - - - Checks if this surface is closed in the u parametric direction. - - - - - Checks if this surface is closed in the v parametric direction. - - - - - Returns the period of this patch in the u parametric direction. - - - - - Returns the period of this patch in the v parametric direction. - - - - - Returns the parameter on the curve -of the nearest orthogonal projection of the point. - - - - - Returns a B-Spline representation of this surface. -The optional arguments are: -* tolerance (default=1e-7) -* continuity in u (as string e.g. C0, G0, G1, C1, G2, C3, CN) (default='C1') -* continuity in v (as string e.g. C0, G0, G1, C1, G2, C3, CN) (default='C1') -* maximum degree in u (default=25) -* maximum degree in v (default=25) -* maximum number of segments (default=1000) -* precision code (default=0) -Will raise an exception if surface is infinite in U or V (like planes, cones or cylinders) - - - - - Returns all intersection points/curves between the surface and the curve/surface. - - - - - Returns all intersection curves of this surface and the given surface. -The required arguments are: -* Second surface -* precision code (optional, default=0) - - - - diff --git a/src/Mod/Part/App/HLRBRep/HLRBRep_Algo.pyi b/src/Mod/Part/App/HLRBRep/HLRBRep_Algo.pyi new file mode 100644 index 0000000000..99ea10de99 --- /dev/null +++ b/src/Mod/Part/App/HLRBRep/HLRBRep_Algo.pyi @@ -0,0 +1,198 @@ +from Base.Metadata import export, constmethod, class_declarations +from Base.PyObjectBase import PyObjectBase +from typing import Final + +@export( + Twin="HLRBRep_Algo", + TwinPointer="HLRBRep_Algo", + Include="HLRBRep_Algo.hxx", + Constructor=True, +) +@class_declarations(""" +private: + Handle(HLRBRep_Algo) hAlgo; + +public: + Handle(HLRBRep_Algo) handle() { + return hAlgo; + } +""") +class HLRBRep_Algo(PyObjectBase): + """ + Algo() -> HLRBRep_Algo + + A framework to compute a shape as seen in a projection + plane. This is done by calculating the visible and the hidden parts + of the shape. HLRBRep_Algo works with three types of entity: + + - shapes to be visualized + - edges in these shapes (these edges are the basic entities which will be + visualized or hidden), and + - faces in these shapes which hide the edges. + + HLRBRep_Algo is based on the principle of comparing each edge of the shape to + be visualized with each of its faces, and calculating the visible and the + hidden parts of each edge. For a given projection, HLRBRep_Algo calculates a + set of lines characteristic of the object being represented. It is also used in + conjunction with the HLRBRep_HLRToShape extraction utilities, which reconstruct + a new, simplified shape from a selection of calculation results. This new shape + is made up of edges, which represent the shape visualized in the + projection. HLRBRep_Algo takes the shape itself into account whereas + HLRBRep_PolyAlgo works with a polyhedral simplification of the shape. When you + use HLRBRep_Algo, you obtain an exact result, whereas, when you use + HLRBRep_PolyAlgo, you reduce computation time but obtain polygonal segments. In + the case of complicated shapes, HLRBRep_Algo may be time-consuming. An + HLRBRep_Algo object provides a framework for: + + - defining the point of view + - identifying the shape or shapes to be visualized + - calculating the outlines + - calculating the visible and hidden lines of the shape. Warning + - Superimposed lines are not eliminated by this algorithm. + - There must be no unfinished objects inside the shape you wish to visualize. + - Points are not treated. + - Note that this is not the sort of algorithm used in generating shading, which + calculates the visible and hidden parts of each face in a shape to be + visualized by comparing each face in the shape with every other face in the + same shape. + + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + def add(self, S, nbIso: int = 0) -> None: + """ + add(S, nbIso=0) + + Adds the shape S to this framework, and specifies the number of isoparameters + nbiso desired in visualizing S. You may add as many shapes as you wish. Use + the function add once for each shape. + """ + ... + + def remove(self, i: int) -> None: + """ + remove(i) + + Remove the shape of index i from this framework. + """ + ... + + def index(self, S) -> int: + """ + index(S) -> int + + Return the index of the Shape S and return 0 if the Shape S is not found. + """ + ... + + def outLinedShapeNullify(self) -> None: + """ + outlinedShapeNullify() + + Nullify all the results of OutLiner from HLRTopoBRep. + """ + ... + + def setProjector( + self, + *, + Origin: tuple[float, float, float] = (0, 0, 0), + ZDir: tuple[float, float, float] = (0, 0, 0), + XDir: tuple[float, float, float] = (0, 0, 0), + focus: float = float("nan"), + ) -> None: + """ + setProjector(Origin=(0, 0, 0), ZDir=(0,0,0), XDir=(0,0,0), focus=NaN) + + Set the projector. With focus left to NaN, an axonometric projector is + created. Otherwise, a perspective projector is created with focus focus. + """ + ... + + def nbShapes(self) -> int: + """ + nbShapes() + + Returns the number of shapes in the collection. It does not modify the + object's state and is used to retrieve the count of shapes. + """ + ... + + def showAll(self, i: int = -1) -> None: + """ + showAll(i=-1) + + If i < 1, then set all the edges to visible. + Otherwise, set to visible all the edges of the shape of index i. + """ + ... + + def hide(self, i: int = -1, j: int = -1) -> None: + """ + hide(i=-1, j=-1) + + If i < 1, hide all of the datastructure. + Otherwise, if j < 1, hide the shape of index i. + Otherwise, hide the shape of index i by the shape of index j. + """ + ... + + def hideAll(self, i: int = -1) -> None: + """ + hideAll(i=-1) + + If i < 1, hide all the edges. + Otherwise, hide all the edges of shape of index i. + """ + ... + + def partialHide(self) -> None: + """ + partialHide() + + Own hiding of all the shapes of the DataStructure without hiding by each other. + """ + ... + + def select(self, i: int = -1) -> None: + """ + select(i=-1) + + If i < 1, select all the DataStructure. + Otherwise, only select the shape of index i. + """ + ... + + def selectEdge(self, i: int) -> None: + """ + selectEdge(i) + + Select only the edges of the shape of index i. + """ + ... + + def selectFace(self, i: int) -> None: + """ + selectFace(i) + + Select only the faces of the shape of index i. + """ + ... + + def initEdgeStatus(self) -> None: + """ + initEdgeStatus() + + Init the status of the selected edges depending of the back faces of a closed + shell. + """ + ... + + def update(self) -> None: + """ + update() + + Update the DataStructure. + """ + ... diff --git a/src/Mod/Part/App/HLRBRep/HLRBRep_AlgoPy.xml b/src/Mod/Part/App/HLRBRep/HLRBRep_AlgoPy.xml deleted file mode 100644 index 22f810bfa4..0000000000 --- a/src/Mod/Part/App/HLRBRep/HLRBRep_AlgoPy.xml +++ /dev/null @@ -1,195 +0,0 @@ - - - - - - Algo() -> HLRBRep_Algo - -A framework to compute a shape as seen in a projection -plane. This is done by calculating the visible and the hidden parts -of the shape. HLRBRep_Algo works with three types of entity: - -- shapes to be visualized -- edges in these shapes (these edges are the basic entities which will be - visualized or hidden), and -- faces in these shapes which hide the edges. - -HLRBRep_Algo is based on the principle of comparing each edge of the shape to -be visualized with each of its faces, and calculating the visible and the -hidden parts of each edge. For a given projection, HLRBRep_Algo calculates a -set of lines characteristic of the object being represented. It is also used in -conjunction with the HLRBRep_HLRToShape extraction utilities, which reconstruct -a new, simplified shape from a selection of calculation results. This new shape -is made up of edges, which represent the shape visualized in the -projection. HLRBRep_Algo takes the shape itself into account whereas -HLRBRep_PolyAlgo works with a polyhedral simplification of the shape. When you -use HLRBRep_Algo, you obtain an exact result, whereas, when you use -HLRBRep_PolyAlgo, you reduce computation time but obtain polygonal segments. In -the case of complicated shapes, HLRBRep_Algo may be time-consuming. An -HLRBRep_Algo object provides a framework for: - -- defining the point of view -- identifying the shape or shapes to be visualized -- calculating the outlines -- calculating the visible and hidden lines of the shape. Warning -- Superimposed lines are not eliminated by this algorithm. -- There must be no unfinished objects inside the shape you wish to visualize. -- Points are not treated. -- Note that this is not the sort of algorithm used in generating shading, which - calculates the visible and hidden parts of each face in a shape to be - visualized by comparing each face in the shape with every other face in the - same shape. - - - - - add(S, nbIso=0) - -Adds the shape S to this framework, and specifies the number of isoparameters -nbiso desired in visualizing S. You may add as many shapes as you wish. Use -the function add once for each shape. - - - - - - remove(i) - -Remove the shape of index i from this framework. - - - - - - index(S) -> int - -Return the index of the Shape S and return 0 if the Shape S is not found. - - - - - - outlinedShapeNullify() - -Nullify all the results of OutLiner from HLRTopoBRep. - - - - - - setProjector(Origin=(0, 0, 0), ZDir=(0,0,0), XDir=(0,0,0), focus=NaN) - -Set the projector. With focus left to NaN, an axonometric projector is -created. Otherwise, a perspective projector is created with focus focus. - - - - - - nbShapes() - -Returns the number of shapes in the collection. It does not modify the -object's state and is used to retrieve the count of shapes. - - - - - - showAll(i=-1) - -If i < 1, then set all the edges to visible. -Otherwise, set to visible all the edges of the shape of index i. - - - - - - hide(i=-1, j=-1) - -If i < 1, hide all of the datastructure. -Otherwise, if j < 1, hide the shape of index i. -Otherwise, hide the shape of index i by the shape of index j. - - - - - - hideAll(i=-1) - -If i < 1, hide all the edges. -Otherwise, hide all the edges of shape of index i. - - - - - - partialHide() - -Own hiding of all the shapes of the DataStructure without hiding by each other. - - - - - - select(i=-1) - -If i < 1, select all the DataStructure. -Otherwise, only select the shape of index i. - - - - - - selectEdge(i) - -Select only the edges of the shape of index i. - - - - - - selectFace(i) - -Select only the faces of the shape of index i. - - - - - - initEdgeStatus() - -Init the status of the selected edges depending of the back faces of a closed -shell. - - - - - - update() - -Update the DataStructure. - - - - -private: - Handle(HLRBRep_Algo) hAlgo; - -public: - Handle(HLRBRep_Algo) handle() { - return hAlgo; - } - - - diff --git a/src/Mod/Part/App/HLRBRep/HLRBRep_PolyAlgo.pyi b/src/Mod/Part/App/HLRBRep/HLRBRep_PolyAlgo.pyi new file mode 100644 index 0000000000..0ae16f6935 --- /dev/null +++ b/src/Mod/Part/App/HLRBRep/HLRBRep_PolyAlgo.pyi @@ -0,0 +1,172 @@ +from Base.Metadata import export, constmethod, class_declarations +from Base.PyObjectBase import PyObjectBase +from Part.TopoShapePy import TopoShape +from typing import Final, overload + +@export( + PythonName="Part.HLRBRep_PolyAlgo", + Twin="HLRBRep_PolyAlgo", + TwinPointer="HLRBRep_PolyAlgo", + Include="HLRBRep_PolyAlgo.hxx", + Constructor=True, +) +@class_declarations(""" +private: + Handle(HLRBRep_PolyAlgo) hAlgo; + +public: + Handle(HLRBRep_PolyAlgo) handle() { + return hAlgo; + } +""") +class HLRBRep_PolyAlgo(PyObjectBase): + """ + PolyAlgo() -> HLRBRep_PolyAlgo + + A framework to compute the shape as seen in a projection + plane. This is done by calculating the visible and the hidden parts of the + shape. HLRBRep_PolyAlgo works with three types of entity: + + - shapes to be visualized (these shapes must have already been triangulated.) + - edges in these shapes (these edges are defined as polygonal lines on the + triangulation of the shape, and are the basic entities which will be visualized + or hidden), and + - triangles in these shapes which hide the edges. + + HLRBRep_PolyAlgo is based on the principle of comparing each edge of the shape + to be visualized with each of the triangles produced by the triangulation of + the shape, and calculating the visible and the hidden parts of each edge. For a + given projection, HLRBRep_PolyAlgo calculates a set of lines characteristic of + the object being represented. It is also used in conjunction with the + HLRBRep_PolyHLRToShape extraction utilities, which reconstruct a new, + simplified shape from a selection of calculation results. This new shape is + made up of edges, which represent the shape visualized in the + projection. HLRBRep_PolyAlgo works with a polyhedral simplification of the + shape whereas HLRBRep_Algo takes the shape itself into account. When you use + HLRBRep_Algo, you obtain an exact result, whereas, when you use + HLRBRep_PolyAlgo, you reduce computation time but obtain polygonal segments. An + HLRBRep_PolyAlgo object provides a framework for: + + - defining the point of view + - identifying the shape or shapes to be visualized + - calculating the outlines + - calculating the visible and hidden lines of the shape. Warning + - Superimposed lines are not eliminated by this algorithm. + - There must be no unfinished objects inside the shape you wish to visualize. + - Points are not treated. + - Note that this is not the sort of algorithm used in generating shading, which + calculates the visible and hidden parts of each face in a shape to be + visualized by comparing each face in the shape with every other face in the + same shape. + """ + + def load(self, S: TopoShape) -> None: + """ + load(S) + + Loads the shape S into this framework. Warning S must have already been triangulated. + """ + ... + + def remove(self, i: int) -> None: + """ + remove(i) + + Remove the shape of index i from this framework. + """ + ... + + def nbShapes(self) -> int: + """ + nbShapes() + + Returns the number of shapes in the collection. It does not modify the + object's state and is used to retrieve the count of shapes. + """ + ... + + def shape(self, i: int) -> TopoShape: + """ + shape(i) -> TopoShape + + Return the shape of index i. + """ + ... + + def index(self, S: TopoShape) -> int: + """ + index(S) -> int + + Return the index of the Shape S. + """ + ... + + def setProjector(self, *, Origin: tuple[float, float, float] = (0.0, 0.0, 0.0), + ZDir: tuple[float, float, float] = (0.0, 0.0, 0.0), + XDir: tuple[float, float, float] = (0.0, 0.0, 0.0), + focus: float = float("nan")) -> None: + """ + setProjector(Origin=(0, 0, 0), ZDir=(0,0,0), XDir=(0,0,0), focus=NaN) + + Set the projector. With focus left to NaN, an axonometric projector is + created. Otherwise, a perspective projector is created with focus focus. + """ + ... + + def update(self) -> None: + """ + update() + + Launches calculation of outlines of the shape visualized by this + framework. Used after setting the point of view and defining the shape or + shapes to be visualized. + """ + ... + + def initHide(self) -> None: + """ + initHide() + """ + ... + + def moreHide(self) -> None: + """ + moreHide() + """ + ... + + def nextHide(self) -> None: + """ + nextHide() + """ + ... + + def initShow(self) -> None: + """ + initShow() + """ + ... + + def moreShow(self) -> None: + """ + moreShow() + """ + ... + + def nextShow(self) -> None: + """ + nextShow() + """ + ... + + def outLinedShape(self, S: TopoShape) -> TopoShape: + """ + outLinedShape(S) -> TopoShape + + Make a shape with the internal outlines in each face of shape S. + """ + ... + + TolAngular: float = ... + + TolCoef: float = ... diff --git a/src/Mod/Part/App/HLRBRep/HLRBRep_PolyAlgoPy.xml b/src/Mod/Part/App/HLRBRep/HLRBRep_PolyAlgoPy.xml deleted file mode 100644 index 253c2ffe25..0000000000 --- a/src/Mod/Part/App/HLRBRep/HLRBRep_PolyAlgoPy.xml +++ /dev/null @@ -1,183 +0,0 @@ - - - - - - PolyAlgo() -> HLRBRep_PolyAlgo - -A framework to compute the shape as seen in a projection -plane. This is done by calculating the visible and the hidden parts of the -shape. HLRBRep_PolyAlgo works with three types of entity: - -- shapes to be visualized (these shapes must have already been triangulated.) -- edges in these shapes (these edges are defined as polygonal lines on the - triangulation of the shape, and are the basic entities which will be visualized - or hidden), and -- triangles in these shapes which hide the edges. - -HLRBRep_PolyAlgo is based on the principle of comparing each edge of the shape -to be visualized with each of the triangles produced by the triangulation of -the shape, and calculating the visible and the hidden parts of each edge. For a -given projection, HLRBRep_PolyAlgo calculates a set of lines characteristic of -the object being represented. It is also used in conjunction with the -HLRBRep_PolyHLRToShape extraction utilities, which reconstruct a new, -simplified shape from a selection of calculation results. This new shape is -made up of edges, which represent the shape visualized in the -projection. HLRBRep_PolyAlgo works with a polyhedral simplification of the -shape whereas HLRBRep_Algo takes the shape itself into account. When you use -HLRBRep_Algo, you obtain an exact result, whereas, when you use -HLRBRep_PolyAlgo, you reduce computation time but obtain polygonal segments. An -HLRBRep_PolyAlgo object provides a framework for: - -- defining the point of view -- identifying the shape or shapes to be visualized -- calculating the outlines -- calculating the visible and hidden lines of the shape. Warning -- Superimposed lines are not eliminated by this algorithm. -- There must be no unfinished objects inside the shape you wish to visualize. -- Points are not treated. -- Note that this is not the sort of algorithm used in generating shading, which - calculates the visible and hidden parts of each face in a shape to be - visualized by comparing each face in the shape with every other face in the - same shape. - - - - - - load(S) - -Loads the shape S into this framework. Warning S must have already been triangulated. - - - - - - remove(i) - -Remove the shape of index i from this framework. - - - - - - nbShapes() - -Returns the number of shapes in the collection. It does not modify the -object's state and is used to retrieve the count of shapes. - - - - - - shape(i) -> TopoShape - -Return the shape of index i. - - - - - - index(S) -> int - -Return the index of the Shape S. - - - - - - setProjector(Origin=(0, 0, 0), ZDir=(0,0,0), XDir=(0,0,0), focus=NaN) - -Set the projector. With focus left to NaN, an axonometric projector is -created. Otherwise, a perspective projector is created with focus focus. - - - - - - update() - -Launches calculation of outlines of the shape visualized by this -framework. Used after setting the point of view and defining the shape or -shapes to be visualized. - - - - - - initHide() - - - - - - moreHide() - - - - - - nextHide() - - - - - - initShow() - - - - - - moreShow() - - - - - - nextShow() - - - - - - outLinedShape(S) -> TopoShape - -Make a shape with the internal outlines in each face of shape S. - - - - - - - - - - - - - - - - -private: - Handle(HLRBRep_PolyAlgo) hAlgo; - -public: - Handle(HLRBRep_PolyAlgo) handle() { - return hAlgo; - } - - - diff --git a/src/Mod/Part/App/HLRBRep/HLRToShape.pyi b/src/Mod/Part/App/HLRBRep/HLRToShape.pyi new file mode 100644 index 0000000000..b5b1fef975 --- /dev/null +++ b/src/Mod/Part/App/HLRBRep/HLRToShape.pyi @@ -0,0 +1,151 @@ +from Base.Metadata import export, constmethod +from Part.TopoShapePy import TopoShape +from Base.PyObjectBase import PyObjectBase +from typing import Optional, overload + +@export( + PythonName="Part.HLRToShapePy", + Twin="HLRBRep_HLRToShape", + TwinPointer="HLRBRep_HLRToShape", + Include="HLRBRep_HLRToShape.hxx", + Constructor=True, + Delete=True, +) +class HLRToShape(PyObjectBase): + """ + HLRToShape(algo: HLRBRep_Algo) -> HLRBRep_HLRToShape + + A framework for filtering the computation results of an HLRBRep_Algo algorithm + by extraction. From the results calculated by the algorithm on a shape, a + filter returns the type of edge you want to identify. You can choose any of the + following types of output: + - visible sharp edges + - hidden sharp edges + - visible smooth edges + - hidden smooth edges + - visible sewn edges + - hidden sewn edges + - visible outline edges + - hidden outline edges + - visible isoparameters and + - hidden isoparameters. + + Sharp edges present a C0 continuity (non G1). Smooth edges present a G1 + continuity (non G2). Sewn edges present a C2 continuity. The result is composed + of 2D edges in the projection plane of the view which the algorithm has worked + with. These 2D edges are not included in the data structure of the visualized + shape. In order to obtain a complete image, you must combine the shapes given + by each of the chosen filters. The construction of the shape does not call a + new computation of the algorithm, but only reads its internal results. The + methods of this shape are almost identic to those of the HLRBrep_PolyHLRToShape + class. + """ + + def vCompound(self, Shape: Optional[TopoShape] = None) -> TopoShape: + """ + vCompound(Shape=None) -> TopoShape + + Sets the extraction filter for visible sharp edges for either shape Shape or + for all added shapes (Shape=None). + """ + ... + + def Rg1LineVCompound(self, Shape: Optional[TopoShape] = None) -> TopoShape: + """ + Rg1LineVCompound(Shape=None) -> TopoShape + + Sets the extraction filter for visible smooth edges for either shape Shape or + for all added shapes (Shape=None). + """ + ... + + def RgNLineVCompound(self, Shape: Optional[TopoShape] = None) -> TopoShape: + """ + RgNLineVCompound(Shape=None) -> TopoShape + + Sets the extraction filter for visible sewn edges for either shape Shape or for + all added shapes (Shape=None). + """ + ... + + def outLineVCompound(self, Shape: Optional[TopoShape] = None) -> TopoShape: + """ + outLineVCompound(Shape=None) -> TopoShape + + Sets the extraction filter for visible outline edges for either shape Shape or + for all added shapes (Shape=None). + """ + ... + + def outLineVCompound3d(self, Shape: Optional[TopoShape] = None) -> TopoShape: + """ + outLineVCompound(Shape=None) -> TopoShape + + Sets the extraction filter for visible outline edges in 3D for either shape + Shape or for all added shapes (Shape=None). + """ + ... + + def isoLineVCompound(self, Shape: Optional[TopoShape] = None) -> TopoShape: + """ + isoLineVCompound(Shape=None) -> TopoShape + + Sets the extraction filter for visible isoparameters for either shape Shape or + for all added shapes (Shape=None). + """ + ... + + def hCompound(self, Shape: Optional[TopoShape] = None) -> TopoShape: + """ + hCompound(Shape=None) -> TopoShape + + Sets the extraction filter for hidden sharp edges for either shape Shape or for + all added shapes (Shape=None). + """ + ... + + def Rg1LineHCompound(self, Shape: Optional[TopoShape] = None) -> TopoShape: + """ + Rg1LineHCompound(Shape=None) -> TopoShape + + Sets the extraction filter for hidden smooth edges for either shape Shape or + for all added shapes (Shape=None). + """ + ... + + def RgNLineHCompound(self, Shape: Optional[TopoShape] = None) -> TopoShape: + """ + RgNLineHCompound(Shape=None) -> TopoShape + + Sets the extraction filter for hidden sewn edges for either shape Shape or for + all added shapes (Shape=None). + """ + ... + + def outLineHCompound(self, Shape: Optional[TopoShape] = None) -> TopoShape: + """ + outLineHCompound(Shape=None) -> TopoShape + + Sets the extraction filter for hidden outline edges for either shape Shape or + for all added shapes (Shape=None). + """ + ... + + def isoLineHCompound(self, Shape: Optional[TopoShape] = None) -> TopoShape: + """ + isoLineHCompound(Shape=None) -> TopoShape + + Sets the extraction filter for hidden isoparameters for either shape Shape or + for all added shapes (Shape=None). + """ + ... + + def compoundOfEdges(self, Type: int, Visible: bool, In3D: bool, *, Shape: Optional[TopoShape] = None) -> TopoShape: + """ + compoundOfEdges(Type: int, Visible: bool, In3D: bool, Shape=None) -> TopoShape + + Returns compound of resulting edges of required type and visibility, taking + into account the kind of space (2d or 3d). If Shape=None, return it for all + added shapes, otherwise return it for shape Shape. + """ + ... \ No newline at end of file diff --git a/src/Mod/Part/App/HLRBRep/HLRToShapePy.xml b/src/Mod/Part/App/HLRBRep/HLRToShapePy.xml deleted file mode 100644 index 994d0084d0..0000000000 --- a/src/Mod/Part/App/HLRBRep/HLRToShapePy.xml +++ /dev/null @@ -1,155 +0,0 @@ - - - - - - HLRToShape(algo: HLRBRep_Algo) -> HLRBRep_HLRToShape - -A framework for filtering the computation results of an HLRBRep_Algo algorithm -by extraction. From the results calculated by the algorithm on a shape, a -filter returns the type of edge you want to identify. You can choose any of the -following types of output: -- visible sharp edges -- hidden sharp edges -- visible smooth edges -- hidden smooth edges -- visible sewn edges -- hidden sewn edges -- visible outline edges -- hidden outline edges -- visible isoparameters and -- hidden isoparameters. - -Sharp edges present a C0 continuity (non G1). Smooth edges present a G1 -continuity (non G2). Sewn edges present a C2 continuity. The result is composed -of 2D edges in the projection plane of the view which the algorithm has worked -with. These 2D edges are not included in the data structure of the visualized -shape. In order to obtain a complete image, you must combine the shapes given -by each of the chosen filters. The construction of the shape does not call a -new computation of the algorithm, but only reads its internal results. The -methods of this shape are almost identic to those of the HLRBrep_PolyHLRToShape -class. - - - - - vCompound(Shape=None) -> TopoShape - -Sets the extraction filter for visible sharp edges for either shape Shape or -for all added shapes (Shape=None). - - - - - - Rg1LineVCompound(Shape=None) -> TopoShape - -Sets the extraction filter for visible smooth edges for either shape Shape or -for all added shapes (Shape=None). - - - - - - RgNLineVCompound(Shape=None) -> TopoShape - -Sets the extraction filter for visible sewn edges for either shape Shape or for -all added shapes (Shape=None). - - - - - - outLineVCompound(Shape=None) -> TopoShape - -Sets the extraction filter for visible outline edges for either shape Shape or -for all added shapes (Shape=None). - - - - - - outLineVCompound(Shape=None) -> TopoShape - -Sets the extraction filter for visible outline edges in 3D for either shape -Shape or for all added shapes (Shape=None). - - - - - - isoLineVCompound(Shape=None) -> TopoShape - -Sets the extraction filter for visible isoparameters for either shape Shape or -for all added shapes (Shape=None). - - - - - - hCompound(Shape=None) -> TopoShape - -Sets the extraction filter for hidden sharp edges for either shape Shape or for -all added shapes (Shape=None). - - - - - - Rg1LineHCompound(Shape=None) -> TopoShape - -Sets the extraction filter for hidden smooth edges for either shape Shape or -for all added shapes (Shape=None). - - - - - - RgNLineHCompound(Shape=None) -> TopoShape - -Sets the extraction filter for hidden sewn edges for either shape Shape or for -all added shapes (Shape=None). - - - - - - outLineHCompound(Shape=None) -> TopoShape - -Sets the extraction filter for hidden outline edges for either shape Shape or -for all added shapes (Shape=None). - - - - - - isoLineHCompound(Shape=None) -> TopoShape - -Sets the extraction filter for hidden isoparameters for either shape Shape or -for all added shapes (Shape=None). - - - - - - compoundOfEdges(Type: int, Visible: bool, In3D: bool, Shape=None) -> TopoShape - -Returns compound of resulting edges of required type and visibility, taking -into account the kind of space (2d or 3d). If Shape=None, return it for all -added shapes, otherwise return it for shape Shape. - - - - - diff --git a/src/Mod/Part/App/HLRBRep/PolyHLRToShape.pyi b/src/Mod/Part/App/HLRBRep/PolyHLRToShape.pyi new file mode 100644 index 0000000000..3a4d946bd9 --- /dev/null +++ b/src/Mod/Part/App/HLRBRep/PolyHLRToShape.pyi @@ -0,0 +1,131 @@ +from Base.Metadata import export, constmethod +from Base.PyObjectBase import PyObjectBase +from Part.HLRBRep_PolyAlgo import HLRBRep_PolyAlgo +from Part.TopoShapePy import TopoShape +from typing import Optional, overload + +@export( + PythonName="Part.PolyHLRToShapePy", + Twin="HLRBRep_PolyHLRToShape", + TwinPointer="HLRBRep_PolyHLRToShape", + Include="HLRBRep_PolyHLRToShape.hxx", + Constructor=True, + Delete=True, +) +class PolyHLRToShape(PyObjectBase): + """ + PolyHLRToShape(algo: HLRBRep_PolyAlgo) -> HLRBRep_PolyHLRToShape + + A framework for filtering the computation results of an HLRBRep_PolyAlgo + algorithm by extraction. From the results calculated by the algorithm on a + shape, a filter returns the type of edge you want to identify. You can choose + any of the following types of output: + - visible sharp edges + - hidden sharp edges + - visible smooth edges + - hidden smooth edges + - visible sewn edges + - hidden sewn edges + - visible outline edges + - hidden outline edges + - visible isoparameters and + - hidden isoparameters. + + Sharp edges present a C0 continuity (non G1). Smooth edges present a G1 + continuity (non G2). Sewn edges present a C2 continuity. The result is composed + of 2D edges in the projection plane of the view which the algorithm has worked + with. These 2D edges are not included in the data structure of the visualized + shape. In order to obtain a complete image, you must combine the shapes given + by each of the chosen filters. The construction of the shape does not call a + new computation of the algorithm, but only reads its internal results. + """ + + def update(self, algo: HLRBRep_PolyAlgo) -> None: + """ + update(algo: HLRBRep_PolyAlgo) + """ + ... + + def show(self) -> None: + """ + show() + """ + ... + + def hide(self) -> None: + """ + hide() + """ + ... + + def vCompound(self, Shape: Optional[TopoShape] = None) -> TopoShape: + """ + vCompound(Shape=None) -> TopoShape + + Sets the extraction filter for visible sharp edges for either shape Shape or + for all added shapes (Shape=None). + """ + ... + + def Rg1LineVCompound(self, Shape: Optional[TopoShape] = None) -> TopoShape: + """ + Rg1LineVCompound(Shape=None) -> TopoShape + + Sets the extraction filter for visible smooth edges for either shape Shape or + for all added shapes (Shape=None). + """ + ... + + def RgNLineVCompound(self, Shape: Optional[TopoShape] = None) -> TopoShape: + """ + RgNLineVCompound(Shape=None) -> TopoShape + + Sets the extraction filter for visible sewn edges for either shape Shape or for + all added shapes (Shape=None). + """ + ... + + def outLineVCompound(self, Shape: Optional[TopoShape] = None) -> TopoShape: + """ + outLineVCompound(Shape=None) -> TopoShape + + Sets the extraction filter for visible outline edges for either shape Shape or + for all added shapes (Shape=None). + """ + ... + + def hCompound(self, Shape: Optional[TopoShape] = None) -> TopoShape: + """ + hCompound(Shape=None) -> TopoShape + + Sets the extraction filter for hidden sharp edges for either shape Shape or for + all added shapes (Shape=None). + """ + ... + + def Rg1LineHCompound(self, Shape: Optional[TopoShape] = None) -> TopoShape: + """ + Rg1LineHCompound(Shape=None) -> TopoShape + + Sets the extraction filter for hidden smooth edges for either shape Shape or + for all added shapes (Shape=None). + """ + ... + + def RgNLineHCompound(self, Shape: Optional[TopoShape] = None) -> TopoShape: + """ + RgNLineHCompound(Shape=None) -> TopoShape + + Sets the extraction filter for hidden sewn edges for either shape Shape or for + all added shapes (Shape=None). + """ + ... + + def outLineHCompound(self, Shape: Optional[TopoShape] = None) -> TopoShape: + """ + outLineHCompound(Shape=None) -> TopoShape + + Sets the extraction filter for hidden outline edges for either shape Shape or + for all added shapes (Shape=None). + """ + ... diff --git a/src/Mod/Part/App/HLRBRep/PolyHLRToShapePy.xml b/src/Mod/Part/App/HLRBRep/PolyHLRToShapePy.xml deleted file mode 100644 index 021f71e5a3..0000000000 --- a/src/Mod/Part/App/HLRBRep/PolyHLRToShapePy.xml +++ /dev/null @@ -1,134 +0,0 @@ - - - - - - PolyHLRToShape(algo: HLRBRep_PolyAlgo) -> HLRBRep_PolyHLRToShape - -A framework for filtering the computation results of an HLRBRep_PolyAlgo -algorithm by extraction. From the results calculated by the algorithm on a -shape, a filter returns the type of edge you want to identify. You can choose -any of the following types of output: -- visible sharp edges -- hidden sharp edges -- visible smooth edges -- hidden smooth edges -- visible sewn edges -- hidden sewn edges -- visible outline edges -- hidden outline edges -- visible isoparameters and -- hidden isoparameters. - -Sharp edges present a C0 continuity (non G1). Smooth edges present a G1 -continuity (non G2). Sewn edges present a C2 continuity. The result is composed -of 2D edges in the projection plane of the view which the algorithm has worked -with. These 2D edges are not included in the data structure of the visualized -shape. In order to obtain a complete image, you must combine the shapes given -by each of the chosen filters. The construction of the shape does not call a -new computation of the algorithm, but only reads its internal results. - - - - - update(algo: HLRBRep_PolyAlgo) - - - - - - show() - - - - - - hide() - - - - - - vCompound(Shape=None) -> TopoShape - -Sets the extraction filter for visible sharp edges for either shape Shape or -for all added shapes (Shape=None). - - - - - - Rg1LineVCompound(Shape=None) -> TopoShape - -Sets the extraction filter for visible smooth edges for either shape Shape or -for all added shapes (Shape=None). - - - - - - RgNLineVCompound(Shape=None) -> TopoShape - -Sets the extraction filter for visible sewn edges for either shape Shape or for -all added shapes (Shape=None). - - - - - - outLineVCompound(Shape=None) -> TopoShape - -Sets the extraction filter for visible outline edges for either shape Shape or -for all added shapes (Shape=None). - - - - - - hCompound(Shape=None) -> TopoShape - -Sets the extraction filter for hidden sharp edges for either shape Shape or for -all added shapes (Shape=None). - - - - - - Rg1LineHCompound(Shape=None) -> TopoShape - -Sets the extraction filter for hidden smooth edges for either shape Shape or -for all added shapes (Shape=None). - - - - - - RgNLineHCompound(Shape=None) -> TopoShape - -Sets the extraction filter for hidden sewn edges for either shape Shape or for -all added shapes (Shape=None). - - - - - - outLineHCompound(Shape=None) -> TopoShape - -Sets the extraction filter for hidden outline edges for either shape Shape or -for all added shapes (Shape=None). - - - - - diff --git a/src/Mod/Part/App/Hyperbola.pyi b/src/Mod/Part/App/Hyperbola.pyi new file mode 100644 index 0000000000..d3423f3ab2 --- /dev/null +++ b/src/Mod/Part/App/Hyperbola.pyi @@ -0,0 +1,72 @@ +from Base.Metadata import export, constmethod +from Base.Vector import Vector +from Conic import Conic +from typing import Final, overload + + +@export( + Name="HyperbolaPy", + Namespace="Part", + Twin="GeomHyperbola", + TwinPointer="GeomHyperbola", + PythonName="Part.Hyperbola", + FatherInclude="Mod/Part/App/ConicPy.h", + Include="Mod/Part/App/Geometry.h", + Father="ConicPy", + FatherNamespace="Part", + Constructor=True, +) +class Hyperbola(Conic): + """ + Describes an hyperbola in 3D space + + To create a hyperbola there are several ways: + + Part.Hyperbola() + Creates an hyperbola with major radius 2 and minor radius 1 with the + center in (0,0,0) + + Part.Hyperbola(Hyperbola) + Create a copy of the given hyperbola + + Part.Hyperbola(S1,S2,Center) + Creates an hyperbola centered on the point Center, where + the plane of the hyperbola is defined by Center, S1 and S2, + its major axis is defined by Center and S1, + its major radius is the distance between Center and S1, and + its minor radius is the distance between S2 and the major axis. + + Part.Hyperbola(Center,MajorRadius,MinorRadius) + Creates an hyperbola with major and minor radii MajorRadius and + MinorRadius, and located in the plane defined by Center and + the normal (0,0,1) + """ + + MajorRadius: float + """The major radius of the hyperbola.""" + + MinorRadius: float + """The minor radius of the hyperbola.""" + + Focal: Final[float] + """The focal distance of the hyperbola.""" + + Focus1: Final[Vector] + """The first focus is on the positive side of the major axis of the hyperbola.""" + + Focus2: Final[Vector] + """The second focus is on the negative side of the major axis of the hyperbola.""" + + @overload + def __init__(self) -> None: ... + + @overload + def __init__(self, hyperbola: "Hyperbola") -> None: ... + + @overload + def __init__(self, S1: Vector, S2: Vector, Center: Vector) -> None: ... + + @overload + def __init__( + self, Center: Vector, MajorRadius: float, MinorRadius: float + ) -> None: ... diff --git a/src/Mod/Part/App/HyperbolaPy.xml b/src/Mod/Part/App/HyperbolaPy.xml deleted file mode 100644 index abc76b9a4c..0000000000 --- a/src/Mod/Part/App/HyperbolaPy.xml +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - Describes an hyperbola in 3D space - -To create a hyperbola there are several ways: - -Part.Hyperbola() - Creates an hyperbola with major radius 2 and minor radius 1 with the - center in (0,0,0) - -Part.Hyperbola(Hyperbola) - Create a copy of the given hyperbola - -Part.Hyperbola(S1,S2,Center) - Creates an hyperbola centered on the point Center, where - the plane of the hyperbola is defined by Center, S1 and S2, - its major axis is defined by Center and S1, - its major radius is the distance between Center and S1, and - its minor radius is the distance between S2 and the major axis. - -Part.Hyperbola(Center,MajorRadius,MinorRadius) - Creates an hyperbola with major and minor radii MajorRadius and - MinorRadius, and located in the plane defined by Center and - the normal (0,0,1) - - - - - The major radius of the hyperbola. - - - - - - The minor radius of the hyperbola. - - - - - - The focal distance of the hyperbola. - - - - - - The first focus is on the positive side of the major axis of the hyperbola. - - - - - - The second focus is on the negative side of the major axis of the hyperbola. - - - - - diff --git a/src/Mod/Part/App/Line.pyi b/src/Mod/Part/App/Line.pyi new file mode 100644 index 0000000000..1d6351999b --- /dev/null +++ b/src/Mod/Part/App/Line.pyi @@ -0,0 +1,45 @@ +from Base.Metadata import export +from Base.Vector import Vector +from GeometryCurve import GeometryCurve +from typing import overload, Final + + +@export( + PythonName="Part.Line", + Twin="GeomLine", + TwinPointer="GeomLine", + Include="Mod/Part/App/Geometry.h", + FatherInclude="Mod/Part/App/GeometryCurvePy.h", + Constructor=True, +) +class Line(GeometryCurve): + """ + Describes an infinite line + To create a line there are several ways: + Part.Line() + Creates a default line + + Part.Line(Line) + Creates a copy of the given line + + Part.Line(Point1,Point2) + Creates a line that goes through two given points + + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + Location: Vector = ... + """Returns the location of this line.""" + + Direction: Vector = ... + """Returns the direction of this line.""" + + @overload + def __init__(self) -> None: ... + + @overload + def __init__(self, line: "Line") -> None: ... + + @overload + def __init__(self, point1: Vector, point2: Vector) -> None: ... diff --git a/src/Mod/Part/App/LinePy.xml b/src/Mod/Part/App/LinePy.xml deleted file mode 100644 index 279d6e3e19..0000000000 --- a/src/Mod/Part/App/LinePy.xml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - Describes an infinite line -To create a line there are several ways: -Part.Line() - Creates a default line - -Part.Line(Line) - Creates a copy of the given line - -Part.Line(Point1,Point2) - Creates a line that goes through two given points - - - - Returns the location of this line. - - - - - - Returns the direction of this line. - - - - - diff --git a/src/Mod/Part/App/LineSegment.pyi b/src/Mod/Part/App/LineSegment.pyi new file mode 100644 index 0000000000..0e4c17faf6 --- /dev/null +++ b/src/Mod/Part/App/LineSegment.pyi @@ -0,0 +1,49 @@ +from Base.Metadata import export +from Base.Type import Type +from TrimmedCurve import TrimmedCurve +from Point import Point +from typing import Final, overload + + +@export( + PythonName="Part.LineSegment", + Twin="GeomLineSegment", + TwinPointer="GeomLineSegment", + Include="Mod/Part/App/Geometry.h", + FatherInclude="Mod/Part/App/TrimmedCurvePy.h", + Constructor=True, +) +class LineSegment(TrimmedCurve): + """ + Describes a line segment + To create a line segment there are several ways: + Part.LineSegment() + Creates a default line segment + + Part.LineSegment(LineSegment) + Creates a copy of the given line segment + + Part.LineSegment(Point1,Point2) + Creates a line segment that goes through two given points + """ + + StartPoint: Type = ... + """Returns the start point of this line.""" + + EndPoint: Type = ... + """Returns the end point point of this line.""" + + @overload + def __init__(self) -> None: ... + + @overload + def __init__(self, line_segment: "LineSegment") -> None: ... + + @overload + def __init__(self, point1: Point, point2: Point) -> None: ... + + def setParameterRange(self) -> None: + """ + Set the parameter range of the underlying line geometry + """ + ... diff --git a/src/Mod/Part/App/LineSegmentPy.xml b/src/Mod/Part/App/LineSegmentPy.xml deleted file mode 100644 index 4408592c6f..0000000000 --- a/src/Mod/Part/App/LineSegmentPy.xml +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - Describes a line segment -To create a line segment there are several ways: -Part.LineSegment() - Creates a default line segment - -Part.LineSegment(LineSegment) - Creates a copy of the given line segment - -Part.LineSegment(Point1,Point2) - Creates a line segment that goes through two given points - - - - Set the parameter range of the underlying line geometry - - - - - Returns the start point of this line. - - - - - - Returns the end point point of this line. - - - - - diff --git a/src/Mod/Part/App/OffsetCurve.pyi b/src/Mod/Part/App/OffsetCurve.pyi new file mode 100644 index 0000000000..367e9e7e9a --- /dev/null +++ b/src/Mod/Part/App/OffsetCurve.pyi @@ -0,0 +1,28 @@ +from Base.Metadata import export +from Base.Vector import Vector +from GeometryCurve import GeometryCurve +from typing import Final + + +@export( + PythonName="Part.OffsetCurve", + Twin="GeomOffsetCurve", + TwinPointer="GeomOffsetCurve", + Include="Mod/Part/App/Geometry.h", + FatherInclude="Mod/Part/App/GeometryCurvePy.h", + Constructor=True, +) +class OffsetCurve(GeometryCurve): + """ + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + OffsetValue: float = ... + """Sets or gets the offset value to offset the underlying curve.""" + + OffsetDirection: Vector = ... + """Sets or gets the offset direction to offset the underlying curve.""" + + BasisCurve: GeometryCurve = ... + """Sets or gets the basic curve.""" diff --git a/src/Mod/Part/App/OffsetCurvePy.xml b/src/Mod/Part/App/OffsetCurvePy.xml deleted file mode 100644 index b4b8357ce7..0000000000 --- a/src/Mod/Part/App/OffsetCurvePy.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - Sets or gets the offset value to offset the underlying curve. - - - - - - Sets or gets the offset direction to offset the underlying curve. - - - - - - Sets or gets the basic curve. - - - - - diff --git a/src/Mod/Part/App/OffsetSurface.pyi b/src/Mod/Part/App/OffsetSurface.pyi new file mode 100644 index 0000000000..4c25fe54a5 --- /dev/null +++ b/src/Mod/Part/App/OffsetSurface.pyi @@ -0,0 +1,23 @@ +from Base.Metadata import export +from GeometrySurface import GeometrySurface + + +@export( + Twin="GeomOffsetSurface", + TwinPointer="GeomOffsetSurface", + PythonName="Part.OffsetSurface", + FatherInclude="Mod/Part/App/GeometrySurfacePy.h", + Include="Mod/Part/App/Geometry.h", + Constructor=True, +) +class OffsetSurface(GeometrySurface): + """ + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + OffsetValue: float = 0.0 + """Sets or gets the offset value to offset the underlying surface.""" + + BasisSurface: object = ... + """Sets or gets the basic surface.""" diff --git a/src/Mod/Part/App/OffsetSurfacePy.xml b/src/Mod/Part/App/OffsetSurfacePy.xml deleted file mode 100644 index 4618984db5..0000000000 --- a/src/Mod/Part/App/OffsetSurfacePy.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - Sets or gets the offset value to offset the underlying surface. - - - - - - Sets or gets the basic surface. - - - - - diff --git a/src/Mod/Part/App/Parabola.pyi b/src/Mod/Part/App/Parabola.pyi new file mode 100644 index 0000000000..fc95a37bc1 --- /dev/null +++ b/src/Mod/Part/App/Parabola.pyi @@ -0,0 +1,45 @@ +from Base.Metadata import export, constmethod +from Base.Vector import Vector +from Conic import Conic +from typing import Final + + +@export( + Twin="GeomParabola", + TwinPointer="GeomParabola", + PythonName="Part.Parabola", + FatherInclude="Mod/Part/App/ConicPy.h", + Include="Mod/Part/App/Geometry.h", + Constructor=True, +) +class Parabola(Conic): + """ + Describes a parabola in 3D space + """ + + Focal: float = ... + """ + The focal distance is the distance between + the apex and the focus of the parabola. + """ + + Focus: Final[Vector] = ... + """ + The focus is on the positive side of the + 'X Axis' of the local coordinate system of the parabola. + """ + + Parameter: Final[float] = ... + """ + Compute the parameter of this parabola + which is the distance between its focus + and its directrix. This distance is twice the focal length. + """ + + def compute(self, p1: Vector, p2: Vector, p3: Vector) -> None: + """ + compute(p1,p2,p3) -> None + + The three points must lie on a plane parallel to xy plane and must not be collinear + """ + ... diff --git a/src/Mod/Part/App/ParabolaPy.xml b/src/Mod/Part/App/ParabolaPy.xml deleted file mode 100644 index e59f6f6160..0000000000 --- a/src/Mod/Part/App/ParabolaPy.xml +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - Describes a parabola in 3D space - - - - compute(p1,p2,p3) -> None - -The three points must lie on a plane parallel to xy plane and must not be collinear - - - - - The focal distance is the distance between -the apex and the focus of the parabola. - - - - - - The focus is on the positive side of the -'X Axis' of the local coordinate system of the parabola. - - - - - - Compute the parameter of this parabola -which is the distance between its focus -and its directrix. This distance is twice the focal length. - - - - - diff --git a/src/Mod/Part/App/Part2DObject.pyi b/src/Mod/Part/App/Part2DObject.pyi new file mode 100644 index 0000000000..606e74daf2 --- /dev/null +++ b/src/Mod/Part/App/Part2DObject.pyi @@ -0,0 +1,18 @@ +from Base.Metadata import export +from PartFeature import PartFeature + + +@export( + Twin="Part2DObject", + TwinPointer="Part2DObject", + Include="Mod/Part/App/Part2DObject.h", + FatherInclude="Mod/Part/App/PartFeaturePy.h", +) +class Part2DObject(PartFeature): + """ + This object represents a 2D Shape in a 3D World + + Author: Juergen Riegel (FreeCAD@juergen-riegel.net) + Licence: LGPL + """ + ... diff --git a/src/Mod/Part/App/Part2DObjectPy.xml b/src/Mod/Part/App/Part2DObjectPy.xml deleted file mode 100644 index a08fbfd1c6..0000000000 --- a/src/Mod/Part/App/Part2DObjectPy.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - This object represents a 2D Shape in a 3D World - - - diff --git a/src/Mod/Part/App/PartFeature.pyi b/src/Mod/Part/App/PartFeature.pyi new file mode 100644 index 0000000000..e1a52920b4 --- /dev/null +++ b/src/Mod/Part/App/PartFeature.pyi @@ -0,0 +1,44 @@ +from Base.Metadata import export, constmethod +from App.GeoFeature import GeoFeature +from App.DocumentObject import DocumentObject +from typing import List, Tuple, Union + + +@export( + Twin="Feature", + TwinPointer="Feature", + Include="Mod/Part/App/PartFeature.h", + FatherInclude="App/GeoFeaturePy.h", +) +class PartFeature(GeoFeature): + """ + This is the father of all shape object classes + + Author: Juergen Riegel (FreeCAD@juergen-riegel.net) + Licence: LGPL + """ + + @constmethod + def getElementHistory( + self, + name: str, + *, + recursive: bool = True, + sameType: bool = False, + showName: bool = False, + ) -> Union[ + Tuple[DocumentObject, str, List[str]], + List[Tuple[DocumentObject, str, List[str]]], + ]: + """ + getElementHistory(name,recursive=True,sameType=False,showName=False) - returns the element mapped name history + + name: mapped element name belonging to this shape + recursive: if True, then track back the history through other objects till the origin + sameType: if True, then stop trace back when element type changes + showName: if False, return the owner object, or else return a tuple of object name and label + + If not recursive, then return tuple(sourceObject, sourceElementName, [intermediateNames...]), + otherwise return a list of tuple. + """ + ... diff --git a/src/Mod/Part/App/PartFeaturePy.xml b/src/Mod/Part/App/PartFeaturePy.xml deleted file mode 100644 index 34a67570c6..0000000000 --- a/src/Mod/Part/App/PartFeaturePy.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - This is the father of all shape object classes - - - - -getElementHistory(name,recursive=True,sameType=False,showName=False) - returns the element mapped name history - -name: mapped element name belonging to this shape -recursive: if True, then track back the history through other objects till the origin -sameType: if True, then stop trace back when element type changes -showName: if False, return the owner object, or else return a tuple of object name and label - -If not recursive, then return tuple(sourceObject, sourceElementName, [intermediateNames...]), -otherwise return a list of tuple. - - - - - diff --git a/src/Mod/Part/App/Plane.pyi b/src/Mod/Part/App/Plane.pyi new file mode 100644 index 0000000000..3f808757bc --- /dev/null +++ b/src/Mod/Part/App/Plane.pyi @@ -0,0 +1,41 @@ +from Base.Metadata import export +from GeometrySurface import GeometrySurface + + +@export( + PythonName="Part.Plane", + Twin="GeomPlane", + TwinPointer="GeomPlane", + Include="Mod/Part/App/Geometry.h", + FatherInclude="Mod/Part/App/GeometrySurfacePy.h", + Constructor=True, +) +class Plane(GeometrySurface): + """ + Describes an infinite plane + To create a plane there are several ways: + Part.Plane() + Creates a default plane with base (0,0,0) and normal (0,0,1) + + Part.Plane(Plane) + Creates a copy of the given plane + + Part.Plane(Plane, Distance) + Creates a plane parallel to given plane at a certain distance + + Part.Plane(Location,Normal) + Creates a plane with a given location and normal + + Part.Plane(Point1,Point2,Point3) + Creates a plane defined by three non-linear points + + Part.Plane(A,B,C,D) + Creates a plane from its cartesian equation + Ax+By+Cz+D=0 + """ + + Position: object = ... + """Returns the position point of this plane.""" + + Axis: object = ... + """Returns the axis of this plane.""" diff --git a/src/Mod/Part/App/PlanePy.xml b/src/Mod/Part/App/PlanePy.xml deleted file mode 100644 index 29d5ce4dd7..0000000000 --- a/src/Mod/Part/App/PlanePy.xml +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - Describes an infinite plane -To create a plane there are several ways: -Part.Plane() - Creates a default plane with base (0,0,0) and normal (0,0,1) - -Part.Plane(Plane) - Creates a copy of the given plane - -Part.Plane(Plane, Distance) - Creates a plane parallel to given plane at a certain distance - -Part.Plane(Location,Normal) - Creates a plane with a given location and normal - -Part.Plane(Point1,Point2,Point3) - Creates a plane defined by three non-linear points - -Part.Plane(A,B,C,D) - Creates a plane from its cartesian equation - Ax+By+Cz+D=0 - - - - - Returns the position point of this plane. - - - - - - Returns the axis of this plane. - - - - - diff --git a/src/Mod/Part/App/PlateSurface.pyi b/src/Mod/Part/App/PlateSurface.pyi new file mode 100644 index 0000000000..ba3eaeb5e8 --- /dev/null +++ b/src/Mod/Part/App/PlateSurface.pyi @@ -0,0 +1,32 @@ +from Base.Metadata import export +from GeometrySurface import GeometrySurface + + +@export( + Twin="GeomPlateSurface", + TwinPointer="GeomPlateSurface", + PythonName="Part.PlateSurface", + FatherInclude="Mod/Part/App/GeometrySurfacePy.h", + Include="Mod/Part/App/Geometry.h", + Constructor=True, +) +class PlateSurface(GeometrySurface): + """ + Represents a plate surface in FreeCAD. Plate surfaces can be defined by specifying points or curves as constraints, and they can also be approximated to B-spline surfaces using the makeApprox method. This class is commonly used in CAD modeling for creating surfaces that represent flat or curved plates, such as sheet metal components or structural elements. + """ + + def makeApprox( + self, + *, + Tol3d: float = 0, + MaxSegments: int = 0, + MaxDegree: int = 0, + MaxDistance: float = 0, + CritOrder: int = 0, + Continuity: str = "", + EnlargeCoeff: float = 0 + ) -> None: + """ + Approximate the plate surface to a B-Spline surface + """ + ... diff --git a/src/Mod/Part/App/PlateSurfacePy.xml b/src/Mod/Part/App/PlateSurfacePy.xml deleted file mode 100644 index 97480092ec..0000000000 --- a/src/Mod/Part/App/PlateSurfacePy.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - Represents a plate surface in FreeCAD. Plate surfaces can be defined by specifying points or curves as constraints, and they can also be approximated to B-spline surfaces using the makeApprox method. This class is commonly used in CAD modeling for creating surfaces that represent flat or curved plates, such as sheet metal components or structural elements. - - - - Approximate the plate surface to a B-Spline surface - - - - diff --git a/src/Mod/Part/App/Point.pyi b/src/Mod/Part/App/Point.pyi new file mode 100644 index 0000000000..0856e66525 --- /dev/null +++ b/src/Mod/Part/App/Point.pyi @@ -0,0 +1,55 @@ +from Base.Metadata import export, constmethod +from Base.Vector import Vector +from Geometry import Geometry +from typing import overload + + +@export( + PythonName="Part.Point", + Twin="GeomPoint", + TwinPointer="GeomPoint", + Include="Mod/Part/App/Geometry.h", + FatherInclude="Mod/Part/App/GeometryPy.h", + Constructor=True, +) +class Point(Geometry): + """ + Describes a point + To create a point there are several ways: + Part.Point() + Creates a default point + + Part.Point(Point) + Creates a copy of the given point + + Part.Point(Vector) + Creates a line for the given coordinates + + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + @overload + def __init__(self) -> None: ... + + @overload + def __init__(self, other: "Point") -> None: ... + + @overload + def __init__(self, coordinates: Vector) -> None: ... + + @constmethod + def toShape(self) -> object: + """ + Create a vertex from this point. + """ + ... + + X: float = ... + """X component of this point.""" + + Y: float = ... + """Y component of this point.""" + + Z: float = ... + """Z component of this point.""" diff --git a/src/Mod/Part/App/PointPy.xml b/src/Mod/Part/App/PointPy.xml deleted file mode 100644 index 6a3ece5adf..0000000000 --- a/src/Mod/Part/App/PointPy.xml +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - Describes a point -To create a point there are several ways: -Part.Point() - Creates a default point - -Part.Point(Point) - Creates a copy of the given point - -Part.Point(Vector) - Creates a line for the given coordinates - - - - Create a vertex from this point. - - - - - X component of this point. - - - - - - Y component of this point. - - - - - - Z component of this point. - - - - - diff --git a/src/Mod/Part/App/PreCompiled.h b/src/Mod/Part/App/PreCompiled.h index ca53f56d20..a537ca9c03 100644 --- a/src/Mod/Part/App/PreCompiled.h +++ b/src/Mod/Part/App/PreCompiled.h @@ -43,6 +43,7 @@ #include #include #include +#include // STL #include @@ -52,6 +53,7 @@ #include #include #include +#include #include #include #include diff --git a/src/Mod/Part/App/PrimitiveFeature.cpp b/src/Mod/Part/App/PrimitiveFeature.cpp index 095ce83a3c..02d13f8825 100644 --- a/src/Mod/Part/App/PrimitiveFeature.cpp +++ b/src/Mod/Part/App/PrimitiveFeature.cpp @@ -22,7 +22,7 @@ #include "PreCompiled.h" #ifndef _PreComp_ -# include +# include # include # include # include @@ -57,7 +57,7 @@ namespace Part { const App::PropertyQuantityConstraint::Constraints torusRangeV = {-180.0, 180.0, 1.0}; const App::PropertyQuantityConstraint::Constraints angleRangeU = {0.0, 360.0, 1.0}; const App::PropertyQuantityConstraint::Constraints angleRangeV = {-90.0, 90.0, 1.0}; - const App::PropertyQuantityConstraint::Constraints quantityRange = {0.0, FLT_MAX, 0.1}; + const App::PropertyQuantityConstraint::Constraints quantityRange = {0.0, std::numeric_limits::max(), 0.1}; } using namespace Part; diff --git a/src/Mod/Part/App/RectangularTrimmedSurface.pyi b/src/Mod/Part/App/RectangularTrimmedSurface.pyi new file mode 100644 index 0000000000..086111f2df --- /dev/null +++ b/src/Mod/Part/App/RectangularTrimmedSurface.pyi @@ -0,0 +1,41 @@ +from Base.Metadata import export +from GeometrySurface import GeometrySurface +from typing import Any, Final, Tuple + + +@export( + Twin="GeomTrimmedSurface", + TwinPointer="GeomTrimmedSurface", + PythonName="Part.RectangularTrimmedSurface", + FatherInclude="Mod/Part/App/GeometrySurfacePy.h", + Include="Mod/Part/App/Geometry.h", + Constructor=True, +) +class RectangularTrimmedSurface(GeometrySurface): + """ + Describes a portion of a surface (a patch) limited by two values of the + u parameter in the u parametric direction, and two values of the v parameter in the v parametric + direction. The domain of the trimmed surface must be within the domain of the surface being trimmed. + + The trimmed surface is defined by: + - the basis surface, and + - the values (umin, umax) and (vmin, vmax) which limit it in the u and v parametric directions. + + The trimmed surface is built from a copy of the basis surface. Therefore, when the basis surface + is modified the trimmed surface is not changed. Consequently, the trimmed surface does not + necessarily have the same orientation as the basis surface. + + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + BasisSurface: Final[Any] = None + """Represents the basis surface from which the trimmed surface is derived.""" + + def setTrim(self, params: Tuple[float, float, float, float]) -> None: + """ + setTrim(self, params: (u1, u2, v1, v2)) -> None + + Modifies this patch by changing the trim values applied to the original surface + """ + ... diff --git a/src/Mod/Part/App/RectangularTrimmedSurfacePy.xml b/src/Mod/Part/App/RectangularTrimmedSurfacePy.xml deleted file mode 100644 index 060cd78cf9..0000000000 --- a/src/Mod/Part/App/RectangularTrimmedSurfacePy.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - Describes a portion of a surface (a patch) limited by two values of the -u parameter in the u parametric direction, and two values of the v parameter in the v parametric -direction. The domain of the trimmed surface must be within the domain of the surface being trimmed. - -The trimmed surface is defined by: -- the basis surface, and -- the values (umin, umax) and (vmin, vmax) which limit it in the u and v parametric directions. - -The trimmed surface is built from a copy of the basis surface. Therefore, when the basis surface -is modified the trimmed surface is not changed. Consequently, the trimmed surface does not -necessarily have the same orientation as the basis surface. - - - - -setTrim(self, params: (u1, u2, v1, v2)) -> None - -Modifies this patch by changing the trim values applied to the original surface - - - - - - Represents the basis surface from which the trimmed surface is derived. - - - - - diff --git a/src/Mod/Part/App/ShapeFix/ShapeFix_Edge.pyi b/src/Mod/Part/App/ShapeFix/ShapeFix_Edge.pyi new file mode 100644 index 0000000000..82ebf40d97 --- /dev/null +++ b/src/Mod/Part/App/ShapeFix/ShapeFix_Edge.pyi @@ -0,0 +1,149 @@ +from Base.Metadata import export, constmethod, class_declarations +from Base.PyObjectBase import PyObjectBase +from typing import Final + +@export( + PythonName="Part.ShapeFix.Edge", + Include="ShapeFix_Edge.hxx", + Constructor=True, +) +@class_declarations(""" +private: + Handle(ShapeFix_Edge) hEdge; + +public: + void setHandle(Handle(ShapeFix_Edge) handle) { + setTwinPointer(handle.get()); + hEdge = handle; + } +""") +class ShapeFix_Edge(PyObjectBase): + """ + Fixing invalid edge + + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + def fixRemovePCurve(self) -> bool: + """ + Removes the pcurve(s) of the edge if it does not match the + vertices + Check is done + Use : It is to be called when pcurve of an edge can be wrong + (e.g., after import from IGES) + Returns: True, if does not match, removed (status DONE) + False, (status OK) if matches or (status FAIL) if no pcurve, + nothing done. + """ + ... + + def fixRemoveCurve3d(self) -> bool: + """ + Removes 3d curve of the edge if it does not match the vertices + Returns: True, if does not match, removed (status DONE) + False, (status OK) if matches or (status FAIL) if no 3d curve, + nothing done. + """ + ... + + def fixAddPCurve(self) -> bool: + """ + Adds pcurve(s) of the edge if missing (by projecting 3d curve) + Parameter isSeam indicates if the edge is a seam. + The parameter 'prec' defines the precision for calculations. + If it is 0 (default), the tolerance of the edge is taken. + Remark : This method is rather for internal use since it accepts parameter + 'surfana' for optimization of computations + Use : It is to be called after FixRemovePCurve (if removed) or in any + case when edge can have no pcurve + Returns: True if pcurve was added, else False + Status : + OK : Pcurve exists + FAIL1: No 3d curve + FAIL2: fail during projecting + DONE1: Pcurve was added + DONE2: specific case of pcurve going through degenerated point on + sphere encountered during projection (see class + ShapeConstruct_ProjectCurveOnSurface for more info). + """ + ... + + def fixAddCurve3d(self) -> bool: + """ + Tries to build 3d curve of the edge if missing + Use : It is to be called after FixRemoveCurve3d (if removed) or in any + case when edge can have no 3d curve + Returns: True if 3d curve was added, else False + Status : + OK : 3d curve exists + FAIL1: BRepLib::BuildCurve3d() has failed + DONE1: 3d curve was added. + """ + ... + + def fixVertexTolerance(self) -> bool: + """ + Increases the tolerances of the edge vertices to comprise + the ends of 3d curve and pcurve on the given face + (first method) or all pcurves stored in an edge (second one) + Returns: True, if tolerances have been increased, otherwise False + Status: + OK : the original tolerances have not been changed + DONE1: the tolerance of first vertex has been increased + DONE2: the tolerance of last vertex has been increased. + """ + ... + + def fixReversed2d(self) -> bool: + """ + Fixes edge if pcurve is directed opposite to 3d curve + Check is done by call to the function + ShapeAnalysis_Edge::CheckCurve3dWithPCurve() + Warning: For seam edge this method will check and fix the pcurve in only + one direction. Hence, it should be called twice for seam edge: + once with edge orientation FORWARD and once with REVERSED. + Returns: False if nothing done, True if reversed (status DONE) + Status: OK - pcurve OK, nothing done + FAIL1 - no pcurve + FAIL2 - no 3d curve + DONE1 - pcurve was reversed. + """ + ... + + def fixSameParameter(self) -> bool: + """ + Tries to make edge SameParameter and sets corresponding + tolerance and SameParameter flag. + First, it makes edge same range if SameRange flag is not set. + If flag SameParameter is set, this method calls the + function ShapeAnalysis_Edge::CheckSameParameter() that + calculates the maximal deviation of pcurves of the edge from + its 3d curve. If deviation > tolerance, the tolerance of edge + is increased to a value of deviation. If deviation < tolerance + nothing happens. + + If flag SameParameter is not set, this method chooses the best + variant (one that has minimal tolerance), either + a. only after computing deviation (as above) or + b. after calling standard procedure BRepLib::SameParameter + and computing deviation (as above). If 'tolerance' > 0, it is + used as parameter for BRepLib::SameParameter, otherwise, + tolerance of the edge is used. + + Use : Is to be called after all pcurves and 3d curve of the edge are + correctly computed + Remark : SameParameter flag is always set to True after this method + Returns: True, if something done, else False + Status : OK - edge was initially SameParameter, nothing is done + FAIL1 - computation of deviation of pcurves from 3d curve has failed + FAIL2 - BRepLib::SameParameter() has failed + DONE1 - tolerance of the edge was increased + DONE2 - flag SameParameter was set to True (only if + BRepLib::SameParameter() did not set it) + DONE3 - edge was modified by BRepLib::SameParameter() to SameParameter + DONE4 - not used anymore + DONE5 - if the edge resulting from BRepLib has been chosen, i.e. variant b. above + (only for edges with not set SameParameter). + """ + ... diff --git a/src/Mod/Part/App/ShapeFix/ShapeFix_EdgeConnect.pyi b/src/Mod/Part/App/ShapeFix/ShapeFix_EdgeConnect.pyi new file mode 100644 index 0000000000..de5ca5a732 --- /dev/null +++ b/src/Mod/Part/App/ShapeFix/ShapeFix_EdgeConnect.pyi @@ -0,0 +1,49 @@ +from Base.Metadata import export, constmethod +from Base.PyObjectBase import PyObjectBase +from Part.App.TopoShapeEdge import TopoShapeEdge +from Part.App.TopoShape import TopoShape +from typing import overload + +@export( + PythonName="Part.ShapeFix.EdgeConnect", + Include="ShapeFix_EdgeConnect.hxx", + Constructor=True, + Delete=True, +) +class ShapeFix_EdgeConnect(PyObjectBase): + """ + Root class for fixing operations + + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + @overload + def add(self, edge1: TopoShapeEdge, edge2: TopoShapeEdge) -> None: ... + + @overload + def add(self, shape: TopoShape) -> None: ... + + def add(self, *args, **kwargs) -> None: + """ + add(edge, edge) + Adds information on connectivity between start vertex + of second edge and end vertex of first edge taking + edges orientation into account + + add(shape) + Adds connectivity information for the whole shape. + """ + ... + + def build(self) -> None: + """ + Builds shared vertices, updates their positions and tolerances + """ + ... + + def clear(self) -> None: + """ + Clears internal data structure + """ + ... diff --git a/src/Mod/Part/App/ShapeFix/ShapeFix_EdgeConnectPy.xml b/src/Mod/Part/App/ShapeFix/ShapeFix_EdgeConnectPy.xml deleted file mode 100644 index 0b9267faba..0000000000 --- a/src/Mod/Part/App/ShapeFix/ShapeFix_EdgeConnectPy.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - Root class for fixing operations - - - - add(edge, edge) -Adds information on connectivity between start vertex -of second edge and end vertex of first edge taking -edges orientation into account - -add(shape) -Adds connectivity information for the whole shape. - - - - - - Builds shared vertices, updates their positions and tolerances - - - - - Clears internal data structure - - - - diff --git a/src/Mod/Part/App/ShapeFix/ShapeFix_EdgePy.xml b/src/Mod/Part/App/ShapeFix/ShapeFix_EdgePy.xml deleted file mode 100644 index 602230b643..0000000000 --- a/src/Mod/Part/App/ShapeFix/ShapeFix_EdgePy.xml +++ /dev/null @@ -1,146 +0,0 @@ - - - - - - Fixing invalid edge - - - - Removes the pcurve(s) of the edge if it does not match the - vertices - Check is done - Use : It is to be called when pcurve of an edge can be wrong - (e.g., after import from IGES) - Returns: True, if does not match, removed (status DONE) - False, (status OK) if matches or (status FAIL) if no pcurve, - nothing done. - - - - - Removes 3d curve of the edge if it does not match the vertices - Returns: True, if does not match, removed (status DONE) - False, (status OK) if matches or (status FAIL) if no 3d curve, - nothing done. - - - - - Adds pcurve(s) of the edge if missing (by projecting 3d curve) - Parameter isSeam indicates if the edge is a seam. - The parameter 'prec' defines the precision for calculations. - If it is 0 (default), the tolerance of the edge is taken. - Remark : This method is rather for internal use since it accepts parameter - 'surfana' for optimization of computations - Use : It is to be called after FixRemovePCurve (if removed) or in any - case when edge can have no pcurve - Returns: True if pcurve was added, else False - Status : - OK : Pcurve exists - FAIL1: No 3d curve - FAIL2: fail during projecting - DONE1: Pcurve was added - DONE2: specific case of pcurve going through degenerated point on - sphere encountered during projection (see class - ShapeConstruct_ProjectCurveOnSurface for more info). - - - - - Tries to build 3d curve of the edge if missing - Use : It is to be called after FixRemoveCurve3d (if removed) or in any - case when edge can have no 3d curve - Returns: True if 3d curve was added, else False - Status : - OK : 3d curve exists - FAIL1: BRepLib::BuildCurve3d() has failed - DONE1: 3d curve was added. - - - - - Increases the tolerances of the edge vertices to comprise - the ends of 3d curve and pcurve on the given face - (first method) or all pcurves stored in an edge (second one) - Returns: True, if tolerances have been increased, otherwise False - Status: - OK : the original tolerances have not been changed - DONE1: the tolerance of first vertex has been increased - DONE2: the tolerance of last vertex has been increased. - - - - - Fixes edge if pcurve is directed opposite to 3d curve - Check is done by call to the function - ShapeAnalysis_Edge::CheckCurve3dWithPCurve() - Warning: For seam edge this method will check and fix the pcurve in only - one direction. Hence, it should be called twice for seam edge: - once with edge orientation FORWARD and once with REVERSED. - Returns: False if nothing done, True if reversed (status DONE) - Status: OK - pcurve OK, nothing done - FAIL1 - no pcurve - FAIL2 - no 3d curve - DONE1 - pcurve was reversed. - - - - - Tries to make edge SameParameter and sets corresponding - tolerance and SameParameter flag. - First, it makes edge same range if SameRange flag is not set. - If flag SameParameter is set, this method calls the - function ShapeAnalysis_Edge::CheckSameParameter() that - calculates the maximal deviation of pcurves of the edge from - its 3d curve. If deviation > tolerance, the tolerance of edge - is increased to a value of deviation. If deviation < tolerance - nothing happens. - - If flag SameParameter is not set, this method chooses the best - variant (one that has minimal tolerance), either - a. only after computing deviation (as above) or - b. after calling standard procedure BRepLib::SameParameter - and computing deviation (as above). If 'tolerance' > 0, it is - used as parameter for BRepLib::SameParameter, otherwise, - tolerance of the edge is used. - - Use : Is to be called after all pcurves and 3d curve of the edge are - correctly computed - Remark : SameParameter flag is always set to True after this method - Returns: True, if something done, else False - Status : OK - edge was initially SameParameter, nothing is done - FAIL1 - computation of deviation of pcurves from 3d curve has failed - FAIL2 - BRepLib::SameParameter() has failed - DONE1 - tolerance of the edge was increased - DONE2 - flag SameParameter was set to True (only if - BRepLib::SameParameter() did not set it) - DONE3 - edge was modified by BRepLib::SameParameter() to SameParameter - DONE4 - not used anymore - DONE5 - if the edge resulting from BRepLib has been chosen, i.e. variant b. above - (only for edges with not set SameParameter). - - - -private: - Handle(ShapeFix_Edge) hEdge; - -public: - void setHandle(Handle(ShapeFix_Edge) handle) { - setTwinPointer(handle.get()); - hEdge = handle; - } - - - diff --git a/src/Mod/Part/App/ShapeFix/ShapeFix_Face.pyi b/src/Mod/Part/App/ShapeFix/ShapeFix_Face.pyi new file mode 100644 index 0000000000..eae87dc7e8 --- /dev/null +++ b/src/Mod/Part/App/ShapeFix/ShapeFix_Face.pyi @@ -0,0 +1,174 @@ +from Base.Metadata import export, constmethod +from Part.ShapeFix_Root import ShapeFix_Root +from Part.TopoShapeFace import TopoShapeFace +from Part.TopoShapeShell import TopoShapeShell +from typing import Final, Union, overload + +@export( + PythonName="Part.ShapeFix.Face", + Include="ShapeFix_Face.hxx", + FatherInclude="Mod/Part/App/ShapeFix/ShapeFix_RootPy.h", + Constructor=True, +) +class ShapeFix_Face(ShapeFix_Root): + """ + Class for fixing operations on faces + + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + FixWireMode: bool = ... + """Mode for applying fixes of ShapeFix_Wire""" + + FixOrientationMode: bool = ... + """ + Mode for applying fixes of orientation + If True, wires oriented to border limited square + """ + + FixAddNaturalBoundMode: bool = ... + """ + If true, natural boundary is added on faces that miss them. + Default is False for faces with single wire (they are + handled by FixOrientation in that case) and True for others. + """ + + FixMissingSeamMode: bool = ... + """If True, tries to insert seam if missing""" + + FixSmallAreaWireMode: bool = ... + """If True, drops small wires""" + + RemoveSmallAreaFaceMode: bool = ... + """If True, drops small wires""" + + FixIntersectingWiresMode: bool = ... + """Mode for applying fixes of intersecting wires""" + + FixLoopWiresMode: bool = ... + """Mode for applying fixes of loop wires""" + + FixSplitFaceMode: bool = ... + """Mode for applying fixes of split face""" + + AutoCorrectPrecisionMode: bool = ... + """Mode for applying auto-corrected precision""" + + FixPeriodicDegeneratedMode: bool = ... + """Mode for applying periodic degeneration""" + + def init(self) -> None: + """ + Initializes by face + """ + ... + + def fixWireTool(self): + """ + Returns tool for fixing wires + """ + ... + + def clearModes(self) -> None: + """ + Sets all modes to default + """ + ... + + def add(self) -> None: + """ + Add a wire to current face using BRep_Builder. + Wire is added without taking into account orientation of face + (as if face were FORWARD) + """ + ... + + def fixOrientation(self) -> bool: + """ + Fixes orientation of wires on the face + It tries to make all wires lie outside all others (according + to orientation) by reversing orientation of some of them. + If face lying on sphere or torus has single wire and + AddNaturalBoundMode is True, that wire is not reversed in + any case (supposing that natural bound will be added). + Returns True if wires were reversed + """ + ... + + def fixAddNaturalBound(self) -> bool: + """ + Adds natural boundary on face if it is missing. + Two cases are supported: + - face has no wires + - face lies on geometrically double-closed surface + (sphere or torus) and none of wires is left-oriented + Returns True if natural boundary was added + """ + ... + + def fixMissingSeam(self) -> bool: + """ + Detects and fixes the special case when face on a closed + surface is given by two wires closed in 3d but with gap in 2d. + In that case it creates a new wire from the two, and adds a + missing seam edge + Returns True if missing seam was added + """ + ... + + def fixSmallAreaWire(self) -> bool: + """ + Detects wires with small area (that is less than + 100*Precision.PConfusion(). Removes these wires if they are internal. + Returns True if at least one small wire removed, False nothing is done. + """ + ... + + def fixLoopWire(self) -> None: + """ + Detects if wire has a loop and fixes this situation by splitting on the few parts. + """ + ... + + def fixIntersectingWires(self) -> None: + """ + Detects and fixes the special case when face has more than one wire + and this wires have intersection point + """ + ... + + def fixWiresTwoCoincidentEdges(self) -> None: + """ + If wire contains two coincidence edges it must be removed + """ + ... + + def fixPeriodicDegenerated(self) -> None: + """ + Fixes topology for a specific case when face is composed + by a single wire belting a periodic surface. In that case + a degenerated edge is reconstructed in the degenerated pole + of the surface. Initial wire gets consistent orientation. + Must be used in couple and before FixMissingSeam routine + """ + ... + + def perform(self) -> None: + """ + Iterates on subshapes and performs fixes + """ + ... + + def face(self) -> TopoShapeFace: + """ + Returns a face which corresponds to the current state + """ + ... + + def result(self) -> Union[TopoShapeFace, TopoShapeShell]: + """ + Returns resulting shape (Face or Shell if split) + To be used instead of face() if FixMissingSeam involved + """ + ... diff --git a/src/Mod/Part/App/ShapeFix/ShapeFix_FaceConnect.pyi b/src/Mod/Part/App/ShapeFix/ShapeFix_FaceConnect.pyi new file mode 100644 index 0000000000..e36d79abd7 --- /dev/null +++ b/src/Mod/Part/App/ShapeFix/ShapeFix_FaceConnect.pyi @@ -0,0 +1,35 @@ +from Base.Metadata import export, constmethod +from Base.PyObjectBase import PyObjectBase + + +@export( + PythonName="Part.ShapeFix.FaceConnect", + Include="ShapeFix_FaceConnect.hxx", + Constructor=True, + Delete=True, +) +class ShapeFix_FaceConnect(PyObjectBase): + """ + Rebuilds connectivity between faces in shell + + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + def add(self, face) -> None: + """ + add(face, face) + """ + ... + + def build(self, shell, sewtolerance, fixtolerance) -> None: + """ + build(shell, sewtolerance, fixtolerance) + """ + ... + + def clear(self) -> None: + """ + Clears internal data structure + """ + ... diff --git a/src/Mod/Part/App/ShapeFix/ShapeFix_FaceConnectPy.xml b/src/Mod/Part/App/ShapeFix/ShapeFix_FaceConnectPy.xml deleted file mode 100644 index a646a06d53..0000000000 --- a/src/Mod/Part/App/ShapeFix/ShapeFix_FaceConnectPy.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - Rebuilds connectivity between faces in shell - - - - add(face, face) - - - - - build(shell, sewtolerance, fixtolerance) - - - - - Clears internal data structure - - - - diff --git a/src/Mod/Part/App/ShapeFix/ShapeFix_FacePy.xml b/src/Mod/Part/App/ShapeFix/ShapeFix_FacePy.xml deleted file mode 100644 index 6721767837..0000000000 --- a/src/Mod/Part/App/ShapeFix/ShapeFix_FacePy.xml +++ /dev/null @@ -1,208 +0,0 @@ - - - - - - Class for fixing operations on faces - - - - Initializes by face - - - - - Returns tool for fixing wires - - - - - Sets all modes to default - - - - - Add a wire to current face using BRep_Builder. -Wire is added without taking into account orientation of face -(as if face were FORWARD) - - - - - - -Fixes orientation of wires on the face -It tries to make all wires lie outside all others (according -to orientation) by reversing orientation of some of them. -If face lying on sphere or torus has single wire and -AddNaturalBoundMode is True, that wire is not reversed in -any case (supposing that natural bound will be added). -Returns True if wires were reversed - - - - - - -Adds natural boundary on face if it is missing. -Two cases are supported: - - face has no wires - - face lies on geometrically double-closed surface -(sphere or torus) and none of wires is left-oriented -Returns True if natural boundary was added - - - - - - -Detects and fixes the special case when face on a closed -surface is given by two wires closed in 3d but with gap in 2d. -In that case it creates a new wire from the two, and adds a -missing seam edge -Returns True if missing seam was added - - - - - - -Detects wires with small area (that is less than -100*Precision.PConfusion(). Removes these wires if they are internal. -Returns True if at least one small wire removed, False nothing is done. - - - - - - -Detects if wire has a loop and fixes this situation by splitting on the few parts. - - - - - - -Detects and fixes the special case when face has more than one wire -and this wires have intersection point - - - - - - -If wire contains two coincidence edges it must be removed - - - - - - -Fixes topology for a specific case when face is composed -by a single wire belting a periodic surface. In that case -a degenerated edge is reconstructed in the degenerated pole -of the surface. Initial wire gets consistent orientation. -Must be used in couple and before FixMissingSeam routine - - - - - - Iterates on subshapes and performs fixes - - - - - Returns a face which corresponds to the current state - - - - - Returns resulting shape (Face or Shell if split) -To be used instead of face() if FixMissingSeam involved - - - - - - Mode for applying fixes of ShapeFix_Wire - - - - - - Mode for applying fixes of orientation -If True, wires oriented to border limited square - - - - - - - If true, natural boundary is added on faces that miss them. -Default is False for faces with single wire (they are -handled by FixOrientation in that case) and True for others. - - - - - - - If True, tries to insert seam if missing - - - - - - If True, drops small wires - - - - - - If True, drops small wires - - - - - - Mode for applying fixes of intersecting wires - - - - - - Mode for applying fixes of loop wires - - - - - - Mode for applying fixes of split face - - - - - - Mode for applying auto-corrected precision - - - - - - Mode for applying periodic degeneration - - - - - diff --git a/src/Mod/Part/App/ShapeFix/ShapeFix_FixSmallFace.pyi b/src/Mod/Part/App/ShapeFix/ShapeFix_FixSmallFace.pyi new file mode 100644 index 0000000000..cd9e7d0748 --- /dev/null +++ b/src/Mod/Part/App/ShapeFix/ShapeFix_FixSmallFace.pyi @@ -0,0 +1,86 @@ +from Base.Metadata import export +from Part.App.ShapeFix.ShapeFix_Root import ShapeFix_Root +from Part.App.TopoShape import TopoShape +from typing import Final, overload + +@export( + PythonName="Part.ShapeFix.FixSmallFace", + Include="ShapeFix_FixSmallFace.hxx", + FatherInclude="Mod/Part/App/ShapeFix/ShapeFix_RootPy.h", + Constructor=True +) +class ShapeFix_FixSmallFace(ShapeFix_Root): + """ + Class for fixing operations on faces + + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + def init(self) -> None: + """ + Initializes by shape + """ + ... + + def perform(self) -> None: + """ + Fixing case of spot face + """ + ... + + def fixSpotFace(self) -> None: + """ + Fixing case of spot face, if tol = -1 used local tolerance + """ + ... + + def replaceVerticesInCaseOfSpot(self) -> None: + """ + Compute average vertex and replacing vertices by new one + """ + ... + + def removeFacesInCaseOfSpot(self) -> None: + """ + Remove spot face from compound + """ + ... + + def fixStripFace(self) -> None: + """ + Fixing case of strip face, if tol = -1 used local tolerance + """ + ... + + def removeFacesInCaseOfStrip(self) -> None: + """ + Remove strip face from compound + """ + ... + + def fixSplitFace(self) -> TopoShape: + """ + Fixes cases related to split faces within the given shape. + It may return a modified shape after fixing the issues. + """ + ... + + def fixFace(self) -> None: + """ + Fixes issues related to the specified face and returns the modified face. + """ + ... + + def fixShape(self) -> None: + """ + Fixes issues in the overall geometric shape. + This function likely encapsulates higher-level fixes that involve multiple faces or elements. + """ + ... + + def shape(self) -> TopoShape: + """ + Returns the current state of the geometric shape after potential modifications. + """ + ... diff --git a/src/Mod/Part/App/ShapeFix/ShapeFix_FixSmallFacePy.xml b/src/Mod/Part/App/ShapeFix/ShapeFix_FixSmallFacePy.xml deleted file mode 100644 index c13a42057d..0000000000 --- a/src/Mod/Part/App/ShapeFix/ShapeFix_FixSmallFacePy.xml +++ /dev/null @@ -1,96 +0,0 @@ - - - - - - Class for fixing operations on faces - - - - Initializes by shape - - - - - Fixing case of spot face - - - - - Fixing case of spot face, if tol = -1 used local tolerance - - - - - Compute average vertex and replacing vertices by new one - - - - - Remove spot face from compound - - - - - Fixing case of strip face, if tol = -1 used local tolerance - - - - - - Remove strip face from compound - - - - - - Fixes cases related to split faces within the given shape. - It may return a modified shape after fixing the issues. - - - - - - Fixes issues related to the specified face and returns the modified face. - - - - - Fixes issues in the overall geometric shape. - This function likely encapsulates higher-level fixes that involve multiple faces or elements. - - - - - Returns the current state of the geometric shape after potential modifications. - - - - - diff --git a/src/Mod/Part/App/ShapeFix/ShapeFix_FixSmallSolid.pyi b/src/Mod/Part/App/ShapeFix/ShapeFix_FixSmallSolid.pyi new file mode 100644 index 0000000000..a9baae65f6 --- /dev/null +++ b/src/Mod/Part/App/ShapeFix/ShapeFix_FixSmallSolid.pyi @@ -0,0 +1,50 @@ +from Base.Metadata import export +from Part.App.ShapeFix.ShapeFix_Root import ShapeFix_Root +from typing import overload + +@export( + PythonName="Part.ShapeFix.FixSmallSolid", + Include="ShapeFix_FixSmallSolid.hxx", + FatherInclude="Mod/Part/App/ShapeFix/ShapeFix_RootPy.h", + Constructor=True, +) +class ShapeFix_FixSmallSolid(ShapeFix_Root): + """ + Fixing solids with small size + + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + def setFixMode(self, theMode: int) -> None: + """ + Set working mode for operator: + - theMode = 0 use both WidthFactorThreshold and VolumeThreshold parameters + - theMode = 1 use only WidthFactorThreshold parameter + - theMode = 2 use only VolumeThreshold parameter + """ + ... + + def setVolumeThreshold(self) -> None: + """ + Set or clear volume threshold for small solids + """ + ... + + def setWidthFactorThreshold(self) -> None: + """ + Set or clear width factor threshold for small solids + """ + ... + + def remove(self) -> None: + """ + Remove small solids from the given shape + """ + ... + + def merge(self) -> None: + """ + Merge small solids in the given shape to adjacent non-small ones + """ + ... diff --git a/src/Mod/Part/App/ShapeFix/ShapeFix_FixSmallSolidPy.xml b/src/Mod/Part/App/ShapeFix/ShapeFix_FixSmallSolidPy.xml deleted file mode 100644 index 7c71b2ac58..0000000000 --- a/src/Mod/Part/App/ShapeFix/ShapeFix_FixSmallSolidPy.xml +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - Fixing solids with small size - - - - -Set working mode for operator: -- theMode = 0 use both WidthFactorThreshold and VolumeThreshold parameters -- theMode = 1 use only WidthFactorThreshold parameter -- theMode = 2 use only VolumeThreshold parameter - - - - - - Set or clear volume threshold for small solids - - - - - Set or clear width factor threshold for small solids - - - - - Remove small solids from the given shape - - - - - Merge small solids in the given shape to adjacent non-small ones - - - - diff --git a/src/Mod/Part/App/ShapeFix/ShapeFix_FreeBounds.pyi b/src/Mod/Part/App/ShapeFix/ShapeFix_FreeBounds.pyi new file mode 100644 index 0000000000..588f26ab04 --- /dev/null +++ b/src/Mod/Part/App/ShapeFix/ShapeFix_FreeBounds.pyi @@ -0,0 +1,36 @@ +from Base.Metadata import export, constmethod +from Base.PyObjectBase import PyObjectBase +from Part.App.TopoShapeCompound import TopoShapeCompound +from Part.App.TopoShape import TopoShape + +@export( + PythonName="Part.ShapeFix.FreeBounds", + Include="ShapeFix_FreeBounds.hxx", + Constructor=True, + Delete=True, +) +class ShapeFix_FreeBounds(PyObjectBase): + """ + This class is intended to output free bounds of the shape + + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + def closedWires(self) -> TopoShapeCompound: + """ + Returns compound of closed wires out of free edges + """ + ... + + def openWires(self) -> TopoShapeCompound: + """ + Returns compound of open wires out of free edges + """ + ... + + def shape(self) -> TopoShape: + """ + Returns modified source shape + """ + ... \ No newline at end of file diff --git a/src/Mod/Part/App/ShapeFix/ShapeFix_FreeBoundsPy.xml b/src/Mod/Part/App/ShapeFix/ShapeFix_FreeBoundsPy.xml deleted file mode 100644 index 35d5fac04f..0000000000 --- a/src/Mod/Part/App/ShapeFix/ShapeFix_FreeBoundsPy.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - This class is intended to output free bounds of the shape - - - - Returns compound of closed wires out of free edges - - - - - Returns compound of open wires out of free edges - - - - - Returns modified source shape - - - - diff --git a/src/Mod/Part/App/ShapeFix/ShapeFix_Root.pyi b/src/Mod/Part/App/ShapeFix/ShapeFix_Root.pyi new file mode 100644 index 0000000000..2f9fd413c9 --- /dev/null +++ b/src/Mod/Part/App/ShapeFix/ShapeFix_Root.pyi @@ -0,0 +1,42 @@ +from Base.Metadata import export, constmethod +from Base.PyObjectBase import PyObjectBase +from typing import Final, overload + +@export( + PythonName="Part.ShapeFix.Root", + Include="ShapeFix_Root.hxx", + Constructor=True, +) +@class_declarations(""" +private: + Handle(ShapeFix_Root) hRoot; + +public: + void setHandle(Handle(ShapeFix_Root) handle) { + setTwinPointer(handle.get()); + hRoot = handle; + } +""") +class ShapeFix_Root(PyObjectBase): + """ + Root class for fixing operations + + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + Precision: float = ... + """Basic precision value""" + + MinTolerance: float = ... + """Minimal allowed tolerance""" + + MaxTolerance: float = ... + """Maximal allowed tolerance""" + + @constmethod + def limitTolerance(self) -> float: + """ + Returns tolerance limited by [MinTolerance,MaxTolerance] + """ + ... diff --git a/src/Mod/Part/App/ShapeFix/ShapeFix_RootPy.xml b/src/Mod/Part/App/ShapeFix/ShapeFix_RootPy.xml deleted file mode 100644 index 38e09d73e4..0000000000 --- a/src/Mod/Part/App/ShapeFix/ShapeFix_RootPy.xml +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - Root class for fixing operations - - - - Returns tolerance limited by [MinTolerance,MaxTolerance] - - - - - Basic precision value - - - - - - Minimal allowed tolerance - - - - - - Maximal allowed tolerance - - - - -private: - Handle(ShapeFix_Root) hRoot; - -public: - void setHandle(Handle(ShapeFix_Root) handle) { - setTwinPointer(handle.get()); - hRoot = handle; - } - - - diff --git a/src/Mod/Part/App/ShapeFix/ShapeFix_Shape.pyi b/src/Mod/Part/App/ShapeFix/ShapeFix_Shape.pyi new file mode 100644 index 0000000000..e66d193891 --- /dev/null +++ b/src/Mod/Part/App/ShapeFix/ShapeFix_Shape.pyi @@ -0,0 +1,88 @@ +from Base.Metadata import export, constmethod +from Part.App.ShapeFix.ShapeFix_Root import ShapeFix_Root +from Part.TopoShape import TopoShape +from typing import Final, overload + + +@export( + PythonName="Part.ShapeFix.Shape", + Include="ShapeFix_Shape.hxx", + FatherInclude="Mod/Part/App/ShapeFix/ShapeFix_RootPy.h", + Constructor=True, +) +class ShapeFix_Shape(ShapeFix_Root): + """ + Class for fixing operations on shapes + + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + FixSolidMode: bool = ... + """Mode for applying fixes of ShapeFix_Solid""" + + FixFreeShellMode: bool = ... + """Mode for applying fixes of ShapeFix_Shell""" + + FixFreeFaceMode: bool = ... + """Mode for applying fixes of ShapeFix_Face""" + + FixFreeWireMode: bool = ... + """Mode for applying fixes of ShapeFix_Wire""" + + FixSameParameterMode: bool = ... + """Mode for applying ShapeFix::SameParameter after all fixes""" + + FixVertexPositionMode: bool = ... + """Mode for applying ShapeFix::FixVertexPosition before all fixes""" + + FixVertexTolMode: bool = ... + """Mode for fixing tolerances of vertices on whole shape""" + + def init(self) -> None: + """ + Initializes by shape + """ + ... + + def perform(self) -> None: + """ + Iterates on sub- shape and performs fixes + """ + ... + + def shape(self) -> TopoShape: + """ + Returns resulting shape + """ + ... + + def fixSolidTool(self) -> object: + """ + Returns tool for fixing solids + """ + ... + + def fixShellTool(self) -> object: + """ + Returns tool for fixing shells + """ + ... + + def fixFaceTool(self) -> object: + """ + Returns tool for fixing faces + """ + ... + + def fixWireTool(self) -> object: + """ + Returns tool for fixing wires + """ + ... + + def fixEdgeTool(self) -> object: + """ + Returns tool for fixing edges + """ + ... diff --git a/src/Mod/Part/App/ShapeFix/ShapeFix_ShapePy.xml b/src/Mod/Part/App/ShapeFix/ShapeFix_ShapePy.xml deleted file mode 100644 index 158dfbea35..0000000000 --- a/src/Mod/Part/App/ShapeFix/ShapeFix_ShapePy.xml +++ /dev/null @@ -1,101 +0,0 @@ - - - - - - Class for fixing operations on shapes - - - - Initializes by shape - - - - - Iterates on sub- shape and performs fixes - - - - - Returns resulting shape - - - - - Returns tool for fixing solids - - - - - Returns tool for fixing shells - - - - - Returns tool for fixing faces - - - - - Returns tool for fixing wires - - - - - Returns tool for fixing edges - - - - - Mode for applying fixes of ShapeFix_Solid - - - - - - Mode for applying fixes of ShapeFix_Shell - - - - - - Mode for applying fixes of ShapeFix_Face - - - - - - Mode for applying fixes of ShapeFix_Wire - - - - - - Mode for applying ShapeFix::SameParameter after all fixes - - - - - - Mode for applying ShapeFix::FixVertexPosition before all fixes - - - - - - Mode for fixing tolerances of vertices on whole shape - - - - - diff --git a/src/Mod/Part/App/ShapeFix/ShapeFix_ShapeTolerance.pyi b/src/Mod/Part/App/ShapeFix/ShapeFix_ShapeTolerance.pyi new file mode 100644 index 0000000000..ef0c0adf06 --- /dev/null +++ b/src/Mod/Part/App/ShapeFix/ShapeFix_ShapeTolerance.pyi @@ -0,0 +1,42 @@ +from Base.Metadata import export, constmethod +from Base.PyObjectBase import PyObjectBase +from Part.App.TopoShape import TopoShape +from typing import Final, overload + +@export( + PythonName="Part.ShapeFix.ShapeTolerance", + Include="ShapeFix_ShapeTolerance.hxx", + Constructor=True, + Delete=True, +) +class ShapeFix_ShapeTolerance(PyObjectBase): + """ + Modifies tolerances of sub-shapes (vertices, edges, faces) + + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + @overload + def limitTolerance(self, shape: TopoShape, tmin: float) -> None: ... + + @overload + def limitTolerance(self, shape: TopoShape, tmin: float, tmax: float, ShapeEnum: str = None) -> None: ... + + def limitTolerance(self, shape: TopoShape, tmin: float, tmax: float = 0, ShapeEnum: str = None) -> None: + """ + limitTolerance(shape, tmin, [tmax=0, ShapeEnum=SHAPE]) + """ + ... + + @overload + def setTolerance(self, shape: TopoShape, precision: float) -> None: ... + + @overload + def setTolerance(self, shape: TopoShape, precision: float, ShapeEnum: str = None) -> None: ... + + def setTolerance(self, shape: TopoShape, precision: float, ShapeEnum: str = None) -> None: + """ + setTolerance(shape, precision, [ShapeEnum=SHAPE]) + """ + ... diff --git a/src/Mod/Part/App/ShapeFix/ShapeFix_ShapeTolerancePy.xml b/src/Mod/Part/App/ShapeFix/ShapeFix_ShapeTolerancePy.xml deleted file mode 100644 index 08f09c8489..0000000000 --- a/src/Mod/Part/App/ShapeFix/ShapeFix_ShapeTolerancePy.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - Modifies tolerances of sub-shapes (vertices, edges, faces) - - - - limitTolerance(shape, tmin, [tmax=0, ShapeEnum=SHAPE]) - - - - - setTolerance(shape, precision, [ShapeEnum=SHAPE]) - - - - diff --git a/src/Mod/Part/App/ShapeFix/ShapeFix_Shell.pyi b/src/Mod/Part/App/ShapeFix/ShapeFix_Shell.pyi new file mode 100644 index 0000000000..84d04b2195 --- /dev/null +++ b/src/Mod/Part/App/ShapeFix/ShapeFix_Shell.pyi @@ -0,0 +1,92 @@ +from Metadata import export, constmethod +from Part.App.ShapeFix.ShapeFix_Root import ShapeFix_Root +from typing import Final, overload + +@export( + PythonName="Part.ShapeFix.Shell", + Twin="ShapeFix_Shell", + TwinPointer="ShapeFix_Shell", + Include="ShapeFix_Shell.hxx", + FatherInclude="Mod/Part/App/ShapeFix/ShapeFix_RootPy.h", + Constructor=True, +) +class ShapeFix_Shell(ShapeFix_Root): + """ + Root class for fixing operations + + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + FixOrientationMode: bool = ... + """Mode for applying fixes of orientation of faces""" + + FixFaceMode: bool = ... + """Mode for applying fixes using ShapeFix_Face""" + + def init(self) -> None: + """ + Initializes by shell + """ + ... + + def fixFaceTool(self) -> None: + """ + Returns tool for fixing faces + """ + ... + + def perform(self) -> None: + """ + Iterates on subshapes and performs fixes + """ + ... + + def shell(self) -> None: + """ + Returns fixed shell (or subset of oriented faces) + """ + ... + + def numberOfShells(self) -> None: + """ + Returns the number of obtained shells + """ + ... + + def shape(self) -> None: + """ + In case of multiconnexity returns compound of fixed shells and one shell otherwise + """ + ... + + def errorFaces(self) -> None: + """ + Returns not oriented subset of faces + """ + ... + + def fixFaceOrientation(self) -> None: + """ + Fixes orientation of faces in shell. + Changes orientation of face in the shell, if it is oriented opposite + to neighbouring faces. If it is not possible to orient all faces in the + shell (like in case of mebious band), this method orients only subset + of faces. Other faces are stored in Error compound. + Modes : + isAccountMultiConex - mode for account cases of multiconnexity. + If this mode is equal to Standard_True, separate shells will be created + in the cases of multiconnexity. If this mode is equal to Standard_False, + one shell will be created without account of multiconnexity. By default - Standard_True; + NonManifold - mode for creation of non-manifold shells. + If this mode is equal to Standard_True one non-manifold will be created from shell + contains multishared edges. Else if this mode is equal to Standard_False only + manifold shells will be created. By default - Standard_False. + """ + ... + + def setNonManifoldFlag(self) -> None: + """ + Sets NonManifold flag + """ + ... \ No newline at end of file diff --git a/src/Mod/Part/App/ShapeFix/ShapeFix_ShellPy.xml b/src/Mod/Part/App/ShapeFix/ShapeFix_ShellPy.xml deleted file mode 100644 index 76159957e0..0000000000 --- a/src/Mod/Part/App/ShapeFix/ShapeFix_ShellPy.xml +++ /dev/null @@ -1,91 +0,0 @@ - - - - - - Root class for fixing operations - - - - Initializes by shell - - - - - Returns tool for fixing faces - - - - - Iterates on subshapes and performs fixes - - - - - Returns fixed shell (or subset of oriented faces) - - - - - Returns the number of obtained shells - - - - - In case of multiconnexity returns compound of fixed shells and one shell otherwise - - - - - Returns not oriented subset of faces - - - - - -Fixes orientation of faces in shell. -Changes orientation of face in the shell, if it is oriented opposite -to neighbouring faces. If it is not possible to orient all faces in the -shell (like in case of mebious band), this method orients only subset -of faces. Other faces are stored in Error compound. -Modes : -isAccountMultiConex - mode for account cases of multiconnexity. -If this mode is equal to Standard_True, separate shells will be created -in the cases of multiconnexity. If this mode is equal to Standard_False, -one shell will be created without account of multiconnexity. By default - Standard_True; -NonManifold - mode for creation of non-manifold shells. -If this mode is equal to Standard_True one non-manifold will be created from shell -contains multishared edges. Else if this mode is equal to Standard_False only -manifold shells will be created. By default - Standard_False. - - - - - - Sets NonManifold flag - - - - - Mode for applying fixes of orientation of faces - - - - - - Mode for applying fixes using ShapeFix_Face - - - - - diff --git a/src/Mod/Part/App/ShapeFix/ShapeFix_Solid.pyi b/src/Mod/Part/App/ShapeFix/ShapeFix_Solid.pyi new file mode 100644 index 0000000000..2a468d75a9 --- /dev/null +++ b/src/Mod/Part/App/ShapeFix/ShapeFix_Solid.pyi @@ -0,0 +1,68 @@ +from Base.Metadata import export +from Part.App.ShapeFix.ShapeFix_Root import ShapeFix_Root +from typing import Final + +@export( + PythonName="Part.ShapeFix.Solid", + Twin="ShapeFix_Solid", + TwinPointer="ShapeFix_Solid", + Include="ShapeFix_Solid.hxx", + FatherInclude="Mod/Part/App/ShapeFix/ShapeFix_RootPy.h", + Constructor=True, +) +class ShapeFix_Solid(ShapeFix_Root): + """ + Root class for fixing operations + + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + FixShellMode: bool = ... + """Mode for applying fixes of ShapeFix_Shell""" + + FixShellOrientationMode: bool = ... + """ + Mode for applying analysis and fixes of + orientation of shells in the solid + """ + + CreateOpenSolidMode: bool = ... + """Mode for creation of solids""" + + def init(self) -> None: + """ + Initializes by solid + """ + ... + + def perform(self) -> None: + """ + Iterates on subshapes and performs fixes + """ + ... + + def solidFromShell(self) -> None: + """ + Calls MakeSolid and orients the solid to be not infinite + """ + ... + + def solid(self) -> None: + """ + Returns resulting solid + """ + ... + + def shape(self) -> None: + """ + In case of multiconnexity returns compound of fixed solids + else returns one solid + """ + ... + + def fixShellTool(self) -> None: + """ + Returns tool for fixing shells + """ + ... diff --git a/src/Mod/Part/App/ShapeFix/ShapeFix_SolidPy.xml b/src/Mod/Part/App/ShapeFix/ShapeFix_SolidPy.xml deleted file mode 100644 index 18cafdb36f..0000000000 --- a/src/Mod/Part/App/ShapeFix/ShapeFix_SolidPy.xml +++ /dev/null @@ -1,69 +0,0 @@ - - - - - - Root class for fixing operations - - - - Initializes by solid - - - - - Iterates on subshapes and performs fixes - - - - - Calls MakeSolid and orients the solid to be not infinite - - - - - Returns resulting solid - - - - - In case of multiconnexity returns compound of fixed solids -else returns one solid - - - - - Returns tool for fixing shells - - - - - Mode for applying fixes of ShapeFix_Shell - - - - - - Mode for applying analysis and fixes of -orientation of shells in the solid - - - - - - Mode for creation of solids - - - - - diff --git a/src/Mod/Part/App/ShapeFix/ShapeFix_SplitCommonVertex.pyi b/src/Mod/Part/App/ShapeFix/ShapeFix_SplitCommonVertex.pyi new file mode 100644 index 0000000000..b0d1d10624 --- /dev/null +++ b/src/Mod/Part/App/ShapeFix/ShapeFix_SplitCommonVertex.pyi @@ -0,0 +1,35 @@ +from Base.Metadata import export +from Part.App.ShapeFix.ShapeFix_Root import ShapeFix_Root +from typing import overload + +@export( + PythonName="Part.ShapeFix.SplitCommonVertex", + Include="ShapeFix_SplitCommonVertex.hxx", + FatherInclude="Mod/Part/App/ShapeFix/ShapeFix_RootPy.h", + Constructor=True, +) +class ShapeFix_SplitCommonVertex(ShapeFix_Root): + """ + Class for fixing operations on shapes + + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + def init(self) -> None: + """ + Initializes by shape + """ + ... + + def perform(self) -> None: + """ + Iterates on sub- shape and performs fixes + """ + ... + + def shape(self) -> object: + """ + Returns resulting shape + """ + ... diff --git a/src/Mod/Part/App/ShapeFix/ShapeFix_SplitCommonVertexPy.xml b/src/Mod/Part/App/ShapeFix/ShapeFix_SplitCommonVertexPy.xml deleted file mode 100644 index 3470f55e25..0000000000 --- a/src/Mod/Part/App/ShapeFix/ShapeFix_SplitCommonVertexPy.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - Class for fixing operations on shapes - - - - Initializes by shape - - - - - Iterates on sub- shape and performs fixes - - - - - Returns resulting shape - - - - diff --git a/src/Mod/Part/App/ShapeFix/ShapeFix_SplitTool.pyi b/src/Mod/Part/App/ShapeFix/ShapeFix_SplitTool.pyi new file mode 100644 index 0000000000..bd7c0896df --- /dev/null +++ b/src/Mod/Part/App/ShapeFix/ShapeFix_SplitTool.pyi @@ -0,0 +1,31 @@ +from Base.Metadata import export, constmethod +from Base.PyObjectBase import PyObjectBase +from typing import overload + + +@export( + PythonName="Part.ShapeFix.SplitTool", + Include="ShapeFix_SplitTool.hxx", + FatherInclude="Base/PyObjectBase.h", + Constructor=True, + Delete=True, +) +class ShapeFix_SplitTool(PyObjectBase): + """ + Tool for splitting and cutting edges + + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + def splitEdge(self) -> None: + """ + Split edge on two new edges using new vertex + """ + ... + + def cutEdge(self) -> None: + """ + Cut edge by parameters pend and cut + """ + ... \ No newline at end of file diff --git a/src/Mod/Part/App/ShapeFix/ShapeFix_SplitToolPy.xml b/src/Mod/Part/App/ShapeFix/ShapeFix_SplitToolPy.xml deleted file mode 100644 index dd347e32d9..0000000000 --- a/src/Mod/Part/App/ShapeFix/ShapeFix_SplitToolPy.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - Tool for splitting and cutting edges - - - - Split edge on two new edges using new vertex - - - - - Cut edge by parameters pend and cut - - - - diff --git a/src/Mod/Part/App/ShapeFix/ShapeFix_Wire.pyi b/src/Mod/Part/App/ShapeFix/ShapeFix_Wire.pyi new file mode 100644 index 0000000000..75dcf7c8de --- /dev/null +++ b/src/Mod/Part/App/ShapeFix/ShapeFix_Wire.pyi @@ -0,0 +1,370 @@ +from Base.Metadata import export, constmethod +from Part.App.ShapeFix.ShapeFix_Root import ShapeFix_Root +from typing import Final + +@export( + PythonName="Part.ShapeFix.Wire", + Twin="ShapeFix_Wire", + TwinPointer="ShapeFix_Wire", + Include="ShapeFix_Wire.hxx", + FatherInclude="Mod/Part/App/ShapeFix/ShapeFix_RootPy.h", + Constructor=True, +) +class ShapeFix_Wire(ShapeFix_Root): + """ + Class for fixing operations on wires + + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + def init(self) -> None: + """ + Initializes by wire, face, precision + """ + pass + + def fixEdgeTool(self) -> None: + """ + Returns tool for fixing wires + """ + pass + + def clearModes(self) -> None: + """ + Sets all modes to default + """ + pass + + def clearStatuses(self) -> None: + """ + Clears all statuses + """ + pass + + def load(self) -> None: + """ + Load data for the wire, and drops all fixing statuses + """ + pass + + def setFace(self) -> None: + """ + Set working face for the wire + """ + pass + + def setSurface(self, surface: object, Placement: object = ...) -> None: + """ + setSurface(surface, [Placement]) + Set surface for the wire + """ + pass + + def setMaxTailAngle(self) -> None: + """ + Sets the maximal allowed angle of the tails in radians + """ + pass + + def setMaxTailWidth(self) -> None: + """ + Sets the maximal allowed width of the tails + """ + pass + + def isLoaded(self) -> None: + """ + Tells if the wire is loaded + """ + pass + + def isReady(self) -> None: + """ + Tells if the wire and face are loaded + """ + pass + + def numberOfEdges(self) -> None: + """ + Returns number of edges in the working wire + """ + pass + + def wire(self) -> None: + """ + Makes the resulting Wire (by basic Brep_Builder) + """ + pass + + def wireAPIMake(self) -> None: + """ + Makes the resulting Wire (by BRepAPI_MakeWire) + """ + pass + + def face(self) -> None: + """ + Returns working face + """ + pass + + def perform(self) -> None: + """ + Iterates on subshapes and performs fixes + """ + pass + + def fixReorder(self) -> None: + """ + Performs an analysis and reorders edges in the wire + """ + pass + + def fixSmall(self) -> None: + """ + Applies fixSmall(...) to all edges in the wire + """ + pass + + def fixConnected(self, num: int) -> None: + """ + Applies fixConnected(num) to all edges in the wire + Connection between first and last edges is treated only if + flag ClosedMode is True + If prec is -1 then maxTolerance() is taken. + """ + pass + + def fixEdgeCurves(self) -> None: + """ + Groups the fixes dealing with 3d and pcurves of the edges + """ + pass + + def fixDegenerated(self) -> None: + """ + Applies fixDegenerated(...) to all edges in the wire + """ + pass + + def fixSelfIntersection(self) -> None: + """ + Applies FixSelfIntersectingEdge(num) and + FixIntersectingEdges(num) to all edges in the wire and + FixIntersectingEdges(num1, num2) for all pairs num1 and num2 + and removes wrong edges if any + """ + pass + + def fixLacking(self) -> None: + """ + Applies FixLacking(num) to all edges in the wire + Connection between first and last edges is treated only if + flag ClosedMode is True + If 'force' is False (default), test for connectness is done with + precision of vertex between edges, else it is done with minimal + value of vertex tolerance and Analyzer.Precision(). + Hence, 'force' will lead to inserting lacking edges in replacement + of vertices which have big tolerances. + """ + pass + + def fixClosed(self) -> None: + """ + Fixes a wire to be well closed + """ + pass + + def fixGaps3d(self, num: int) -> None: + """ + Fixes gaps between ends of 3d curves on adjacent edges + """ + pass + + def fixGaps2d(self, num: int) -> None: + """ + Fixes gaps between ends of pcurves on adjacent edges + """ + pass + + def fixSeam(self) -> None: + """ + Fixes seam edges + """ + pass + + def fixShifted(self) -> None: + """ + Fixes edges which have pcurves shifted by whole parameter + range on the closed surface + """ + pass + + def fixNotchedEdges(self) -> None: + """ + Fixes Notch edges.Check if there are notch edges in 2d and fix it + """ + pass + + def fixGap3d(self, num: int) -> None: + """ + Fixes gap between ends of 3d curves on num-1 and num-th edges + """ + pass + + def fixGap2d(self, num: int) -> None: + """ + Fixes gap between ends of pcurves on num-1 and num-th edges + """ + pass + + def fixTails(self) -> None: + """ + Fixes issues related to 'tails' in the geometry. + Tails are typically small, undesired protrusions or deviations in the curves or edges that need correction. + This method examines the geometry and applies corrective actions to eliminate or reduce the presence of tails. + """ + pass + + ModifyTopologyMode: bool = ... + """Mode for modifying topology of the wire""" + + ModifyGeometryMode: bool = ... + """Mode for modifying geometry of vertexes and edges""" + + ModifyRemoveLoopMode: bool = ... + """Mode for modifying edges""" + + ClosedWireMode: bool = ... + """ + Mode which defines whether the wire + is to be closed (by calling methods like fixDegenerated() + and fixConnected() for last and first edges) + """ + + PreferencePCurveMode: bool = ... + """ + Mode which defines whether the 2d 'True' + representation of the wire is preferable over 3d one in the + case of ambiguity in FixEdgeCurves + """ + + FixGapsByRangesMode: bool = ... + """ + Mode which defines whether tool + tries to fix gaps first by changing curves ranges (i.e. + using intersection, extrema, projections) or not + """ + + FixReorderMode: bool = ... + """ + Mode which performs an analysis and reorders edges in the wire using class WireOrder. + Flag 'theModeBoth' determines the use of miscible mode if necessary. + """ + + FixSmallMode: bool = ... + """Mode which applies FixSmall(num) to all edges in the wire""" + + FixConnectedMode: bool = ... + """ + Mode which applies FixConnected(num) to all edges in the wire + Connection between first and last edges is treated only if + flag ClosedMode is True + If 'prec' is -1 then MaxTolerance() is taken. + """ + + FixEdgeCurvesMode: bool = ... + """ + Mode which groups the fixes dealing with 3d and pcurves of the edges. + The order of the fixes and the default behaviour are: + ShapeFix_Edge::FixReversed2d + ShapeFix_Edge::FixRemovePCurve (only if forced) + ShapeFix_Edge::FixAddPCurve + ShapeFix_Edge::FixRemoveCurve3d (only if forced) + ShapeFix_Edge::FixAddCurve3d + FixSeam, + FixShifted, + ShapeFix_Edge::FixSameParameter + """ + + FixDegeneratedMode: bool = ... + """ + Mode which applies FixDegenerated(num) to all edges in the wire + Connection between first and last edges is treated only if + flag ClosedMode is True + """ + + FixSelfIntersectionMode: bool = ... + """ + Mode which applies FixSelfIntersectingEdge(num) and + FixIntersectingEdges(num) to all edges in the wire and + FixIntersectingEdges(num1, num2) for all pairs num1 and num2 + and removes wrong edges if any + """ + + FixLackingMode: bool = ... + """ + Mode which applies FixLacking(num) to all edges in the wire + Connection between first and last edges is treated only if + flag ClosedMode is True + If 'force' is False (default), test for connectness is done with + precision of vertex between edges, else it is done with minimal + value of vertex tolerance and Analyzer.Precision(). + Hence, 'force' will lead to inserting lacking edges in replacement + of vertices which have big tolerances. + """ + + FixGaps3dMode: bool = ... + """ + Mode which fixes gaps between ends of 3d curves on adjacent edges + myPrecision is used to detect the gaps. + """ + + FixGaps2dMode: bool = ... + """ + Mode whixh fixes gaps between ends of pcurves on adjacent edges + myPrecision is used to detect the gaps. + """ + + FixReversed2dMode: bool = ... + """Mode which fixes the reversed in 2d""" + + FixRemovePCurveMode: bool = ... + """Mode which removePCurve in 2d""" + + FixAddPCurveMode: bool = ... + """Mode which fixes addCurve in 2d""" + + FixRemoveCurve3dMode: bool = ... + """Mode which fixes removeCurve in 3d """ + + FixAddCurve3dMode: bool = ... + """Mode which fixes addCurve in 3d""" + + FixSeamMode: bool = ... + """Mode which fixes Seam """ + + FixShiftedMode: bool = ... + """Mode which fixes Shifted""" + + FixSameParameterMode: bool = ... + """Mode which fixes sameParameter in 2d""" + + FixVertexToleranceMode: bool = ... + """Mode which fixes VertexTolerence in 2d""" + + FixNotchedEdgesMode: bool = ... + """Mode which fixes NotchedEdges in 2d""" + + FixSelfIntersectingEdgeMode: bool = ... + """Mode which fixes SelfIntersectionEdge in 2d""" + + FixIntersectingEdgesMode: bool = ... + """Mode which fixes IntersectingEdges in 2d""" + + FixNonAdjacentIntersectingEdgesMode: bool = ... + """Mode which fixes NonAdjacentIntersectingEdges in 2d""" + + FixTailMode: bool = ... + """Mode which fixes Tails in 2d""" diff --git a/src/Mod/Part/App/ShapeFix/ShapeFix_WirePy.xml b/src/Mod/Part/App/ShapeFix/ShapeFix_WirePy.xml deleted file mode 100644 index 4f85c02b16..0000000000 --- a/src/Mod/Part/App/ShapeFix/ShapeFix_WirePy.xml +++ /dev/null @@ -1,403 +0,0 @@ - - - - - - Class for fixing operations on wires - - - - Initializes by wire, face, precision - - - - - Returns tool for fixing wires - - - - - Sets all modes to default - - - - - Clears all statuses - - - - - Load data for the wire, and drops all fixing statuses - - - - - Set working face for the wire - - - - - setSurface(surface, [Placement]) -Set surface for the wire - - - - - Sets the maximal allowed angle of the tails in radians - - - - - Sets the maximal allowed width of the tails - - - - - Tells if the wire is loaded - - - - - Tells if the wire and face are loaded - - - - - Returns number of edges in the working wire - - - - - Makes the resulting Wire (by basic Brep_Builder) - - - - - Makes the resulting Wire (by BRepAPI_MakeWire) - - - - - Returns working face - - - - - Iterates on subshapes and performs fixes - - - - - Performs an analysis and reorders edges in the wire - - - - - Applies fixSmall(...) to all edges in the wire - - - - - Applies fixConnected(num) to all edges in the wire -Connection between first and last edges is treated only if -flag ClosedMode is True -If prec is -1 then maxTolerance() is taken. - - - - - Groups the fixes dealing with 3d and pcurves of the edges - - - - - Applies fixDegenerated(...) to all edges in the wire - - - - - Applies FixSelfIntersectingEdge(num) and - FixIntersectingEdges(num) to all edges in the wire and - FixIntersectingEdges(num1, num2) for all pairs num1 and num2 - and removes wrong edges if any - - - - - Applies FixLacking(num) to all edges in the wire - Connection between first and last edges is treated only if - flag ClosedMode is True - If 'force' is False (default), test for connectness is done with - precision of vertex between edges, else it is done with minimal - value of vertex tolerance and Analyzer.Precision(). - Hence, 'force' will lead to inserting lacking edges in replacement - of vertices which have big tolerances. - - - - - Fixes a wire to be well closed - - - - - Fixes gaps between ends of 3d curves on adjacent edges - - - - - Fixes gaps between ends of pcurves on adjacent edges - - - - - Fixes seam edges - - - - - Fixes edges which have pcurves shifted by whole parameter -range on the closed surface - - - - - Fixes Notch edges.Check if there are notch edges in 2d and fix it - - - - - Fixes gap between ends of 3d curves on num-1 and num-th edges - - - - - Fixes gap between ends of pcurves on num-1 and num-th edges - - - - - Fixes issues related to 'tails' in the geometry. - Tails are typically small, undesired protrusions or deviations in the curves or edges that need correction. - This method examines the geometry and applies corrective actions to eliminate or reduce the presence of tails. - - - - - Mode for modifying topology of the wire - - - - - - Mode for modifying geometry of vertexes and edges - - - - - - Mode for modifying edges - - - - - - Mode which defines whether the wire -is to be closed (by calling methods like fixDegenerated() -and fixConnected() for last and first edges) - - - - - - Mode which defines whether the 2d 'True' -representation of the wire is preferable over 3d one in the -case of ambiguity in FixEdgeCurves - - - - - - Mode which defines whether tool -tries to fix gaps first by changing curves ranges (i.e. -using intersection, extrema, projections) or not - - - - - - Mode which performs an analysis and reorders edges in the wire using class WireOrder. -Flag 'theModeBoth' determines the use of miscible mode if necessary. - - - - - - Mode which applies FixSmall(num) to all edges in the wire - - - - - - Mode which applies FixConnected(num) to all edges in the wire - Connection between first and last edges is treated only if - flag ClosedMode is True - If 'prec' is -1 then MaxTolerance() is taken. - - - - - - Mode which groups the fixes dealing with 3d and pcurves of the edges. - The order of the fixes and the default behaviour are: - ShapeFix_Edge::FixReversed2d - ShapeFix_Edge::FixRemovePCurve (only if forced) - ShapeFix_Edge::FixAddPCurve - ShapeFix_Edge::FixRemoveCurve3d (only if forced) - ShapeFix_Edge::FixAddCurve3d - FixSeam, - FixShifted, - ShapeFix_Edge::FixSameParameter - - - - - - Mode which applies FixDegenerated(num) to all edges in the wire - Connection between first and last edges is treated only if - flag ClosedMode is True - - - - - - Mode which applies FixSelfIntersectingEdge(num) and - FixIntersectingEdges(num) to all edges in the wire and - FixIntersectingEdges(num1, num2) for all pairs num1 and num2 - and removes wrong edges if any - - - - - - Mode which applies FixLacking(num) to all edges in the wire - Connection between first and last edges is treated only if - flag ClosedMode is True - If 'force' is False (default), test for connectness is done with - precision of vertex between edges, else it is done with minimal - value of vertex tolerance and Analyzer.Precision(). - Hence, 'force' will lead to inserting lacking edges in replacement - of vertices which have big tolerances. - - - - - - Mode which fixes gaps between ends of 3d curves on adjacent edges - myPrecision is used to detect the gaps. - - - - - - Mode whixh fixes gaps between ends of pcurves on adjacent edges - myPrecision is used to detect the gaps. - - - - - - Mode which fixes the reversed in 2d - - - - - - Mode which removePCurve in 2d - - - - - - Mode which fixes addCurve in 2d - - - - - - Mode which fixes removeCurve in 3d - - - - - - Mode which fixes addCurve in 3d - - - - - - Mode which fixes Seam - - - - - - Mode which fixes Shifted - - - - - - Mode which fixes sameParameter in 2d - - - - - - Mode which fixes VertexTolerence in 2d - - - - - - Mode which fixes NotchedEdges in 2d - - - - - - Mode which fixes SelfIntersectionEdge in 2d - - - - - - Mode which fixes IntersectingEdges in 2d - - - - - - Mode which fixes NonAdjacentIntersectingEdges in 2d - - - - - - Mode which fixes Tails in 2d - - - - - diff --git a/src/Mod/Part/App/ShapeFix/ShapeFix_WireVertex.pyi b/src/Mod/Part/App/ShapeFix/ShapeFix_WireVertex.pyi new file mode 100644 index 0000000000..c213988ed5 --- /dev/null +++ b/src/Mod/Part/App/ShapeFix/ShapeFix_WireVertex.pyi @@ -0,0 +1,42 @@ +from Base.Metadata import export, constmethod +from Base.PyObjectBase import PyObjectBase + +@export( + PythonName="Part.ShapeFix.WireVertex", + Include="ShapeFix_WireVertex.hxx", + Constructor=True, + Delete=True, +) +class ShapeFix_WireVertex(PyObjectBase): + """ + Fixing disconnected edges in the wire + + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + def init(self) -> None: + """ + Loads the wire, ininializes internal analyzer with the given precision + """ + ... + + def wire(self) -> object: + """ + Returns resulting wire + """ + ... + + def fixSame(self) -> int: + """ + Returns the count of fixed vertices, 0 if none + """ + ... + + def fix(self) -> int: + """ + Fixes all statuses except Disjoined, i.e. the cases in which a + common value has been set, with or without changing parameters + Returns the count of fixed vertices, 0 if none + """ + ... diff --git a/src/Mod/Part/App/ShapeFix/ShapeFix_WireVertexPy.xml b/src/Mod/Part/App/ShapeFix/ShapeFix_WireVertexPy.xml deleted file mode 100644 index a6af305339..0000000000 --- a/src/Mod/Part/App/ShapeFix/ShapeFix_WireVertexPy.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - Fixing disconnected edges in the wire - - - - Loads the wire, ininializes internal analyzer with the given precision - - - - - Returns resulting wire - - - - - Returns the count of fixed vertices, 0 if none - - - - - Fixes all statuses except Disjoined, i.e. the cases in which a -common value has been set, with or without changing parameters -Returns the count of fixed vertices, 0 if none - - - - diff --git a/src/Mod/Part/App/ShapeFix/ShapeFix_Wireframe.pyi b/src/Mod/Part/App/ShapeFix/ShapeFix_Wireframe.pyi new file mode 100644 index 0000000000..ca9bbbf09c --- /dev/null +++ b/src/Mod/Part/App/ShapeFix/ShapeFix_Wireframe.pyi @@ -0,0 +1,52 @@ +from Base.Metadata import export, constmethod +from Part.App.ShapeFix.ShapeFix_Root import ShapeFix_Root +from typing import Final + +@export( + PythonName="Part.ShapeFix.Wireframe", + Twin="ShapeFix_Wireframe", + TwinPointer="ShapeFix_Wireframe", + Include="ShapeFix_Wireframe.hxx", + FatherInclude="Mod/Part/App/ShapeFix/ShapeFix_RootPy.h", + Constructor=True, +) +class ShapeFix_Wireframe(ShapeFix_Root): + """ + Provides methods for fixing wireframe of shape + + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + ModeDropSmallEdges: bool = ... + """Returns mode managing removing small edges""" + + LimitAngle: float = ... + """Limit angle for merging edges""" + + def clearStatuses(self) -> None: + """ + Clears all statuses + """ + ... + + def load(self) -> None: + """ + Loads a shape, resets statuses + """ + ... + + def fixWireGaps(self) -> None: + """ + Fixes gaps between ends of curves of adjacent edges + """ + ... + + def fixSmallEdges(self) -> None: + """ + Fixes small edges in shape by merging adjacent edges + """ + ... + + def shape(self) -> None: + ... diff --git a/src/Mod/Part/App/ShapeFix/ShapeFix_WireframePy.xml b/src/Mod/Part/App/ShapeFix/ShapeFix_WireframePy.xml deleted file mode 100644 index 756d242ef0..0000000000 --- a/src/Mod/Part/App/ShapeFix/ShapeFix_WireframePy.xml +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - Provides methods for fixing wireframe of shape - - - - Clears all statuses - - - - - Loads a shape, resets statuses - - - - - Fixes gaps between ends of curves of adjacent edges - - - - - Fixes small edges in shape by merging adjacent edges - - - - - - - - - - Returns mode managing removing small edges - - - - - - Limit angle for merging edges - - - - - diff --git a/src/Mod/Part/App/ShapeMapHasher.h b/src/Mod/Part/App/ShapeMapHasher.h index 6b0fe94c4c..24ab2a5b30 100644 --- a/src/Mod/Part/App/ShapeMapHasher.h +++ b/src/Mod/Part/App/ShapeMapHasher.h @@ -38,7 +38,7 @@ public: size_t operator()(const TopoDS_Shape& theShape) const { #if OCC_VERSION_HEX < 0x070800 - return theShape.HashCode(INT_MAX); + return theShape.HashCode(std::numeric_limits::max()); #else return std::hash{}(theShape); #endif diff --git a/src/Mod/Part/App/ShapeUpgrade/UnifySameDomain.pyi b/src/Mod/Part/App/ShapeUpgrade/UnifySameDomain.pyi new file mode 100644 index 0000000000..ae42ef0a47 --- /dev/null +++ b/src/Mod/Part/App/ShapeUpgrade/UnifySameDomain.pyi @@ -0,0 +1,80 @@ +from Base.Metadata import export, constmethod +from Base.PyObjectBase import PyObjectBase +from typing import overload + +@export( + PythonName="Part.ShapeUpgrade.UnifySameDomain", + Include="ShapeUpgrade_UnifySameDomain.hxx", + Twin="ShapeUpgrade_UnifySameDomain", + TwinPointer="ShapeUpgrade_UnifySameDomain", + Constructor=True, + Delete=True, +) +class UnifySameDomain(PyObjectBase): + """ + This tool tries to unify faces and edges of the shape which lie on the same geometry. + + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + def initialize(self, **kwargs) -> None: + """ + Initializes with a shape and necessary flags + """ + ... + + def allowInternalEdges(self) -> None: + """ + Sets the flag defining whether it is allowed to create + internal edges inside merged faces in the case of non-manifold + topology. Without this flag merging through multi connected edge + is forbidden. Default value is false. + """ + ... + + def keepShape(self) -> None: + """ + Sets the shape for avoid merging of the faces/edges. + """ + ... + + def keepShapes(self) -> None: + """ + Sets the map of shapes for avoid merging of the faces/edges. + """ + ... + + def setSafeInputMode(self) -> None: + """ + Sets the flag defining the behavior of the algorithm regarding + modification of input shape. + If this flag is equal to True then the input (original) shape can't be + modified during modification process. Default value is true. + """ + ... + + def setLinearTolerance(self) -> None: + """ + Sets the linear tolerance + """ + ... + + def setAngularTolerance(self) -> None: + """ + Sets the angular tolerance + """ + ... + + def build(self) -> None: + """ + Performs unification and builds the resulting shape + """ + ... + + @constmethod + def shape(self) -> None: + """ + Gives the resulting shape + """ + ... diff --git a/src/Mod/Part/App/ShapeUpgrade/UnifySameDomainPy.xml b/src/Mod/Part/App/ShapeUpgrade/UnifySameDomainPy.xml deleted file mode 100644 index c5cb04e921..0000000000 --- a/src/Mod/Part/App/ShapeUpgrade/UnifySameDomainPy.xml +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - This tool tries to unify faces and edges of the shape which lie on the same geometry. - - - - Initializes with a shape and necessary flags - - - - - Sets the flag defining whether it is allowed to create -internal edges inside merged faces in the case of non-manifold -topology. Without this flag merging through multi connected edge -is forbidden. Default value is false. - - - - - Sets the shape for avoid merging of the faces/edges. - - - - - Sets the map of shapes for avoid merging of the faces/edges. - - - - - Sets the flag defining the behavior of the algorithm regarding -modification of input shape. -If this flag is equal to True then the input (original) shape can't be -modified during modification process. Default value is true. - - - - - Sets the linear tolerance - - - - - Sets the angular tolerance - - - - - Performs unification and builds the resulting shape - - - - - Gives the resulting shape - - - - diff --git a/src/Mod/Part/App/Sphere.pyi b/src/Mod/Part/App/Sphere.pyi new file mode 100644 index 0000000000..83241c40ed --- /dev/null +++ b/src/Mod/Part/App/Sphere.pyi @@ -0,0 +1,38 @@ +from Base.Metadata import export, constmethod +from Base.BaseClass import BaseClass +from Base.Vector import Vector +from Base.Axis import Axis as AxisPy +from GeometrySurface import GeometrySurface +from typing import Final + + +@export( + Twin="GeomSphere", + TwinPointer="GeomSphere", + PythonName="Part.Sphere", + FatherInclude="Mod/Part/App/GeometrySurfacePy.h", + Include="Mod/Part/App/Geometry.h", + Constructor=True, +) +class Sphere(GeometrySurface): + """ + Describes a sphere in 3D space + + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + Radius: float = ... + """The radius of the sphere.""" + + Area: Final[float] = 0.0 + """Compute the area of the sphere.""" + + Volume: Final[float] = 0.0 + """Compute the volume of the sphere.""" + + Center: Vector = ... + """Center of the sphere.""" + + Axis: AxisPy = ... + """The axis direction of the circle""" diff --git a/src/Mod/Part/App/SpherePy.xml b/src/Mod/Part/App/SpherePy.xml deleted file mode 100644 index adb4e13e87..0000000000 --- a/src/Mod/Part/App/SpherePy.xml +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - Describes a sphere in 3D space - - - - The radius of the sphere. - - - - - - Compute the area of the sphere. - - - - - - Compute the volume of the sphere. - - - - - - Center of the sphere. - - - - - - The axis direction of the circle - - - - - diff --git a/src/Mod/Part/App/SurfaceOfExtrusion.pyi b/src/Mod/Part/App/SurfaceOfExtrusion.pyi new file mode 100644 index 0000000000..8cdc6e15b6 --- /dev/null +++ b/src/Mod/Part/App/SurfaceOfExtrusion.pyi @@ -0,0 +1,26 @@ +from Base.Metadata import export +from GeometrySurface import GeometrySurface +from GeometryCurve import GeometryCurve +from Base.Vector import Vector + + +@export( + Twin="GeomSurfaceOfExtrusion", + TwinPointer="GeomSurfaceOfExtrusion", + PythonName="Part.SurfaceOfExtrusion", + FatherInclude="Mod/Part/App/GeometrySurfacePy.h", + Include="Mod/Part/App/Geometry.h", + Constructor=True, +) +class SurfaceOfExtrusion(GeometrySurface): + """ + Describes a surface of linear extrusion + Author: Werner Mayer () + Licence: LGPL + """ + + Direction: Vector = ... + """Sets or gets the direction of revolution.""" + + BasisCurve: GeometryCurve = ... + """Sets or gets the basic curve.""" diff --git a/src/Mod/Part/App/SurfaceOfExtrusionPy.xml b/src/Mod/Part/App/SurfaceOfExtrusionPy.xml deleted file mode 100644 index d7951b7b68..0000000000 --- a/src/Mod/Part/App/SurfaceOfExtrusionPy.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - Describes a surface of linear extrusion - - - - Sets or gets the direction of revolution. - - - - - - Sets or gets the basic curve. - - - - - diff --git a/src/Mod/Part/App/SurfaceOfRevolution.pyi b/src/Mod/Part/App/SurfaceOfRevolution.pyi new file mode 100644 index 0000000000..9b318a5bfb --- /dev/null +++ b/src/Mod/Part/App/SurfaceOfRevolution.pyi @@ -0,0 +1,37 @@ +from Base.Metadata import export +from Base.Placement import Placement +from Base.Vector import Vector +from GeometrySurface import GeometrySurface +from GeometryCurve import GeometryCurve +from typing import overload + + +@export( + Twin="GeomSurfaceOfRevolution", + TwinPointer="GeomSurfaceOfRevolution", + PythonName="Part.SurfaceOfRevolution", + FatherInclude="Mod/Part/App/GeometrySurfacePy.h", + Include="Mod/Part/App/Geometry.h", + Constructor=True, +) +class SurfaceOfRevolution(GeometrySurface): + """ + Describes a surface of revolution + + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + Location: Placement + """Sets or gets the location of revolution.""" + + Direction: Vector + """Sets or gets the direction of revolution.""" + + BasisCurve: GeometryCurve + """Sets or gets the basic curve.""" + + @overload + def __init__( + self, location: Placement, direction: Vector, basis_curve: GeometryCurve + ) -> None: ... diff --git a/src/Mod/Part/App/SurfaceOfRevolutionPy.xml b/src/Mod/Part/App/SurfaceOfRevolutionPy.xml deleted file mode 100644 index 5eb0de87a8..0000000000 --- a/src/Mod/Part/App/SurfaceOfRevolutionPy.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - Describes a surface of revolution - - - - Sets or gets the location of revolution. - - - - - - Sets or gets the direction of revolution. - - - - - - Sets or gets the basic curve. - - - - - diff --git a/src/Mod/Part/App/Tools.cpp b/src/Mod/Part/App/Tools.cpp index 93ce969ec9..12cfe3f949 100644 --- a/src/Mod/Part/App/Tools.cpp +++ b/src/Mod/Part/App/Tools.cpp @@ -773,9 +773,9 @@ bool Part::Tools::isConcave(const TopoDS_Face &face, const gp_Pnt &pointOfVue, c // check normals orientation gp_Dir dirdU(dU); - result = (dirdU.Angle(direction) - M_PI_2) <= Precision::Confusion(); + result = (dirdU.Angle(direction) - std::numbers::pi/2) <= Precision::Confusion(); gp_Dir dirdV(dV); - result = result || ((dirdV.Angle(direction) - M_PI_2) <= Precision::Confusion()); + result = result || ((dirdV.Angle(direction) - std::numbers::pi/2) <= Precision::Confusion()); return result; } diff --git a/src/Mod/Part/App/TopoShape.cpp b/src/Mod/Part/App/TopoShape.cpp index 8d70e4c3c1..951b6086dc 100644 --- a/src/Mod/Part/App/TopoShape.cpp +++ b/src/Mod/Part/App/TopoShape.cpp @@ -2004,7 +2004,7 @@ TopoDS_Shape TopoShape::makeTube(double radius, double tol, int cont, int maxdeg //circular profile Handle(Geom_Circle) aCirc = new Geom_Circle (gp::XOY(), theRadius); - aCirc->Rotate (gp::OZ(), M_PI/2.); + aCirc->Rotate (gp::OZ(), std::numbers::pi/2.); //perpendicular section Handle(Law_Function) myEvol = ::CreateBsFunction (myPath->FirstParameter(), myPath->LastParameter(), theRadius); @@ -2116,6 +2116,8 @@ TopoDS_Shape TopoShape::makeHelix(Standard_Real pitch, Standard_Real height, Standard_Boolean leftHanded, Standard_Boolean newStyle) const { + using std::numbers::pi; + if (fabs(pitch) < Precision::Confusion()) Standard_Failure::Raise("Pitch of helix too small"); @@ -2140,24 +2142,24 @@ TopoDS_Shape TopoShape::makeHelix(Standard_Real pitch, Standard_Real height, } gp_Pnt2d aPnt(0, 0); - gp_Dir2d aDir(2. * M_PI, pitch); + gp_Dir2d aDir(2. * pi, pitch); Standard_Real coneDir = 1.0; if (leftHanded) { - aDir.SetCoord(-2. * M_PI, pitch); + aDir.SetCoord(-2. * pi, pitch); coneDir = -1.0; } gp_Ax2d aAx2d(aPnt, aDir); Handle(Geom2d_Line) line = new Geom2d_Line(aAx2d); gp_Pnt2d beg = line->Value(0); - gp_Pnt2d end = line->Value(sqrt(4.0*M_PI*M_PI+pitch*pitch)*(height/pitch)); + gp_Pnt2d end = line->Value(sqrt(4.0*pi*pi+pitch*pitch)*(height/pitch)); if (newStyle) { // See discussion at 0001247: Part Conical Helix Height/Pitch Incorrect if (angle >= Precision::Confusion()) { // calculate end point for conical helix Standard_Real v = height / cos(angle); - Standard_Real u = coneDir * (height/pitch) * 2.0 * M_PI; + Standard_Real u = coneDir * (height/pitch) * 2.0 * pi; gp_Pnt2d cend(u, v); end = cend; } @@ -2179,6 +2181,8 @@ TopoDS_Shape TopoShape::makeLongHelix(Standard_Real pitch, Standard_Real height, Standard_Real radius, Standard_Real angle, Standard_Boolean leftHanded) const { + using std::numbers::pi; + if (pitch < Precision::Confusion()) Standard_Failure::Raise("Pitch of helix too small"); @@ -2206,10 +2210,10 @@ TopoDS_Shape TopoShape::makeLongHelix(Standard_Real pitch, Standard_Real height, Standard_Real partTurn = turns - wholeTurns; gp_Pnt2d aPnt(0, 0); - gp_Dir2d aDir(2. * M_PI, pitch); + gp_Dir2d aDir(2. * pi, pitch); Standard_Real coneDir = 1.0; if (leftHanded) { - aDir.SetCoord(-2. * M_PI, pitch); + aDir.SetCoord(-2. * pi, pitch); coneDir = -1.0; } gp_Ax2d aAx2d(aPnt, aDir); @@ -2223,10 +2227,10 @@ TopoDS_Shape TopoShape::makeLongHelix(Standard_Real pitch, Standard_Real height, for (unsigned long i = 0; i < wholeTurns; i++) { if (isCylinder) { - end = line->Value(sqrt(4.0*M_PI*M_PI+pitch*pitch)*(i+1)); + end = line->Value(sqrt(4.0*pi*pi+pitch*pitch)*(i+1)); } else { - u = coneDir * (i+1) * 2.0 * M_PI; + u = coneDir * (i+1) * 2.0 * pi; v = ((i+1) * pitch) / cos(angle); end = gp_Pnt2d(u, v); } @@ -2238,10 +2242,10 @@ TopoDS_Shape TopoShape::makeLongHelix(Standard_Real pitch, Standard_Real height, if (partTurn > Precision::Confusion()) { if (isCylinder) { - end = line->Value(sqrt(4.0*M_PI*M_PI+pitch*pitch)*turns); + end = line->Value(sqrt(4.0*pi*pi+pitch*pitch)*turns); } else { - u = coneDir * turns * 2.0 * M_PI; + u = coneDir * turns * 2.0 * pi; v = height / cos(angle); end = gp_Pnt2d(u, v); } @@ -2283,9 +2287,9 @@ TopoDS_Shape TopoShape::makeSpiralHelix(Standard_Real radiusbottom, Standard_Rea gp_Pnt2d beg(0, 0); gp_Pnt2d end(0, 0); - gp_Vec2d dir(breakperiod * 2.0 * M_PI, 1 / nbPeriods); + gp_Vec2d dir(breakperiod * 2.0 * std::numbers::pi, 1 / nbPeriods); if (leftHanded == Standard_True) - dir = gp_Vec2d(-breakperiod * 2.0 * M_PI, 1 / nbPeriods); + dir = gp_Vec2d(-breakperiod * 2.0 * std::numbers::pi, 1 / nbPeriods); Handle(Geom2d_TrimmedCurve) segm; TopoDS_Edge edgeOnSurf; BRepBuilderAPI_MakeWire mkWire; @@ -2314,6 +2318,7 @@ TopoDS_Shape TopoShape::makeThread(Standard_Real pitch, Standard_Real height, Standard_Real radius) const { + using std::numbers::pi; if (pitch < Precision::Confusion()) Standard_Failure::Raise("Pitch of thread too small"); @@ -2332,21 +2337,21 @@ TopoDS_Shape TopoShape::makeThread(Standard_Real pitch, Handle(Geom_CylindricalSurface) aCyl2 = new Geom_CylindricalSurface(cylAx2 , radius+depth); //Threading : Define 2D Curves - gp_Pnt2d aPnt(2. * M_PI , height / 2.); - gp_Dir2d aDir(2. * M_PI , height / 4.); + gp_Pnt2d aPnt(2. * pi , height / 2.); + gp_Dir2d aDir(2. * pi , height / 4.); gp_Ax2d aAx2d(aPnt , aDir); - Standard_Real aMajor = 2. * M_PI; + Standard_Real aMajor = 2. * pi; Standard_Real aMinor = pitch; Handle(Geom2d_Ellipse) anEllipse1 = new Geom2d_Ellipse(aAx2d , aMajor , aMinor); Handle(Geom2d_Ellipse) anEllipse2 = new Geom2d_Ellipse(aAx2d , aMajor , aMinor / 4); - Handle(Geom2d_TrimmedCurve) aArc1 = new Geom2d_TrimmedCurve(anEllipse1 , 0 , M_PI); - Handle(Geom2d_TrimmedCurve) aArc2 = new Geom2d_TrimmedCurve(anEllipse2 , 0 , M_PI); + Handle(Geom2d_TrimmedCurve) aArc1 = new Geom2d_TrimmedCurve(anEllipse1 , 0 , pi); + Handle(Geom2d_TrimmedCurve) aArc2 = new Geom2d_TrimmedCurve(anEllipse2 , 0 , pi); gp_Pnt2d anEllipsePnt1 = anEllipse1->Value(0); - gp_Pnt2d anEllipsePnt2 = anEllipse1->Value(M_PI); + gp_Pnt2d anEllipsePnt2 = anEllipse1->Value(pi); Handle(Geom2d_TrimmedCurve) aSegment = GCE2d_MakeSegment(anEllipsePnt1 , anEllipsePnt2); diff --git a/src/Mod/Part/App/TopoShape.pyi b/src/Mod/Part/App/TopoShape.pyi new file mode 100644 index 0000000000..48004ed66a --- /dev/null +++ b/src/Mod/Part/App/TopoShape.pyi @@ -0,0 +1,1162 @@ +from Base.Metadata import export, constmethod +from Base.Vector import Vector +from Base.Matrix import Matrix +from Base.BoundBox import BoundBox +from App.ComplexGeoData import ComplexGeoData +from __future__ import annotations +from typing import Final, List, Tuple, Union, overload + +@export( + Include="Mod/Part/App/TopoShape.h", + FatherInclude="App/ComplexGeoDataPy.h", + FatherNamespace="Data", + Constructor=True, +) +class TopoShape(ComplexGeoData): + """ + TopoShape is the OpenCasCade topological shape wrapper. + Sub-elements such as vertices, edges or faces are accessible as: + * Vertex#, where # is in range(1, number of vertices) + * Edge#, where # is in range(1, number of edges) + * Face#, where # is in range(1, number of faces) + """ + + ShapeType: Final[str] = "" + """Returns the type of the shape.""" + + Orientation: str = "" + """Returns the orientation of the shape.""" + + Faces: Final[List] = [] + """List of faces in this shape.""" + + Vertexes: Final[List] = [] + """List of vertexes in this shape.""" + + Shells: Final[List] = [] + """List of subsequent shapes in this shape.""" + + Solids: Final[List] = [] + """List of subsequent shapes in this shape.""" + + CompSolids: Final[List] = [] + """List of subsequent shapes in this shape.""" + + Edges: Final[List] = [] + """List of Edges in this shape.""" + + Wires: Final[List] = [] + """List of wires in this shape.""" + + Compounds: Final[List] = [] + """List of compounds in this shape.""" + + SubShapes: Final[List] = [] + """List of sub-shapes in this shape.""" + + Length: Final[float] = 0.0 + """Total length of the edges of the shape.""" + + Area: Final[float] = 0.0 + """Total area of the faces of the shape.""" + + Volume: Final[float] = 0.0 + """Total volume of the solids of the shape.""" + + @constmethod + def dumps(self) -> str: + """ + Serialize the content of this shape to a string in BREP format. + """ + ... + + def loads(self, brep_str: str) -> None: + """ + Deserialize the content of this shape from a string in BREP format. + """ + ... + + def read(self, filename: str) -> None: + """ + Read in an IGES, STEP or BREP file. + read(filename) + """ + ... + + @constmethod + def writeInventor(self, *, Mode: int, Deviation: float, Angle: float, FaceColors: object) -> str: + """ + Write the mesh in OpenInventor format to a string. + writeInventor() -> string + """ + ... + + @constmethod + def exportIges(self, filename: str) -> None: + """ + Export the content of this shape to an IGES file. + exportIges(filename) + """ + ... + + @constmethod + def exportStep(self, filename: str) -> None: + """ + Export the content of this shape to an STEP file. + exportStep(filename) + """ + ... + + @constmethod + def exportBrep(self, filename: str) -> None: + """ + Export the content of this shape to an BREP file. + exportBrep(filename) + -- + BREP is an OpenCasCade native format. + """ + ... + + @constmethod + def exportBinary(self, filename: str) -> None: + """ + Export the content of this shape in binary format to a file. + exportBinary(filename) + """ + ... + + @constmethod + def exportBrepToString(self) -> str: + """ + Export the content of this shape to a string in BREP format. + exportBrepToString() -> string + -- + BREP is an OpenCasCade native format. + """ + ... + + @constmethod + def dumpToString(self) -> str: + """ + Dump information about the shape to a string. + dumpToString() -> string + """ + ... + + @constmethod + def exportStl(self, filename: str) -> None: + """ + Export the content of this shape to an STL mesh file. + exportStl(filename) + """ + ... + + def importBrep(self, filename: str) -> None: + """ + Load the shape from a file in BREP format. + importBrep(filename) + """ + ... + + def importBinary(self, filename: str) -> None: + """ + Import the content to this shape of a string in BREP format. + importBinary(filename) + """ + ... + + def importBrepFromString(self, s: str, displayProgressBar: bool = True) -> None: + """ + Load the shape from a string that keeps the content in BREP format. + importBrepFromString(string, [displayProgressBar=True]) + -- + importBrepFromString(str, False) to not display a progress bar. + """ + ... + + @constmethod + def extrude(self, vector: Vector) -> TopoShape: + """ + Extrude the shape along a vector. + extrude(vector) -> Shape + -- + Shp2 = Shp1.extrude(App.Vector(0,0,10)) - extrude the shape 10 mm in the +Z direction. + """ + ... + + @constmethod + def revolve(self, base: Vector, direction: Vector, angle: float) -> TopoShape: + """ + Revolve the shape around an Axis to a given degree. + revolve(base, direction, angle) + -- + Part.revolve(App.Vector(0,0,0),App.Vector(0,0,1),360) - revolves the shape around the Z Axis 360 degree. + + Hints: Sometimes you want to create a rotation body out of a closed edge or wire. + Example: + from FreeCAD import Base + import Part + V=Base.Vector + + e=Part.Ellipse() + s=e.toShape() + r=s.revolve(V(0,0,0),V(0,1,0), 360) + Part.show(r) + + However, you may possibly realize some rendering artifacts or that the mesh + creation seems to hang. This is because this way the surface is created twice. + Since the curve is a full ellipse it is sufficient to do a rotation of 180 degree + only, i.e. r=s.revolve(V(0,0,0),V(0,1,0), 180) + + Now when rendering this object you may still see some artifacts at the poles. Now the + problem seems to be that the meshing algorithm doesn't like to rotate around a point + where there is no vertex. + + The idea to fix this issue is that you create only half of the ellipse so that its shape + representation has vertexes at its start and end point. + + from FreeCAD import Base + import Part + V=Base.Vector + + e=Part.Ellipse() + s=e.toShape(e.LastParameter/4,3*e.LastParameter/4) + r=s.revolve(V(0,0,0),V(0,1,0), 360) + Part.show(r) + """ + ... + + @constmethod + def check(self, runBopCheck: bool = False) -> bool: + """ + Checks the shape and report errors in the shape structure. + check([runBopCheck = False]) + -- + This is a more detailed check as done in isValid(). + if runBopCheck is True, a BOPCheck analysis is also performed. + """ + ... + + @constmethod + def fuse(self, tools: Tuple[TopoShape, ...], tolerance: float = 0.0) -> TopoShape: + """ + Union of this and a given (list of) topo shape. + fuse(tool) -> Shape + or + fuse((tool1,tool2,...),[tolerance=0.0]) -> Shape + -- + Union of this and a given list of topo shapes. + + Supports (OCCT 6.9.0 and above): + - Fuzzy Boolean operations (global tolerance for a Boolean operation) + - Support of multiple arguments for a single Boolean operation + - Parallelization of Boolean Operations algorithm + + Beginning from OCCT 6.8.1 a tolerance value can be specified. + """ + ... + + @constmethod + def multiFuse(self, tools: Tuple[TopoShape, ...], tolerance: float = 0.0) -> TopoShape: + """ + Union of this and a given list of topo shapes. + multiFuse((tool1,tool2,...),[tolerance=0.0]) -> Shape + -- + Supports (OCCT 6.9.0 and above): + - Fuzzy Boolean operations (global tolerance for a Boolean operation) + - Support of multiple arguments for a single Boolean operation + - Parallelization of Boolean Operations algorithm + + Beginning from OCCT 6.8.1 a tolerance value can be specified. + Deprecated: use fuse() instead. + """ + ... + + @constmethod + def oldFuse(self, tool: TopoShape) -> TopoShape: + """ + Union of this and a given topo shape (old algorithm). + oldFuse(tool) -> Shape + """ + ... + + @constmethod + def common(self, tools: Tuple[TopoShape, ...], tolerance: float = 0.0) -> TopoShape: + """ + Intersection of this and a given (list of) topo shape. + common(tool) -> Shape + or + common((tool1,tool2,...),[tolerance=0.0]) -> Shape + -- + Supports: + - Fuzzy Boolean operations (global tolerance for a Boolean operation) + - Support of multiple arguments for a single Boolean operation (s1 AND (s2 OR s3)) + - Parallelization of Boolean Operations algorithm + + OCC 6.9.0 or later is required. + """ + ... + + @constmethod + def section(self, tool: Tuple[TopoShape, ...], tolerance: float = 0.0, approximation: bool = False) -> TopoShape: + """ + Section of this with a given (list of) topo shape. + section(tool,[approximation=False]) -> Shape + or + section((tool1,tool2,...),[tolerance=0.0, approximation=False]) -> Shape + -- + If approximation is True, section edges are approximated to a C1-continuous BSpline curve. + + Supports: + - Fuzzy Boolean operations (global tolerance for a Boolean operation) + - Support of multiple arguments for a single Boolean operation (s1 AND (s2 OR s3)) + - Parallelization of Boolean Operations algorithm + + OCC 6.9.0 or later is required. + """ + ... + + @constmethod + def slices(self, direction: Vector, distancesList: List[float]) -> List: + """ + Make slices of this shape. + slices(direction, distancesList) --> Wires + """ + ... + + @constmethod + def slice(self, direction: Vector, distance: float) -> List: + """ + Make single slice of this shape. + slice(direction, distance) --> Wires + """ + ... + + @constmethod + def cut(self, tool: Tuple[TopoShape, ...], tolerance: float = 0.0) -> TopoShape: + """ + Difference of this and a given (list of) topo shape + cut(tool) -> Shape + or + cut((tool1,tool2,...),[tolerance=0.0]) -> Shape + -- + Supports: + - Fuzzy Boolean operations (global tolerance for a Boolean operation) + - Support of multiple arguments for a single Boolean operation + - Parallelization of Boolean Operations algorithm + + OCC 6.9.0 or later is required. + """ + ... + + @constmethod + def generalFuse(self, shapes: Tuple[TopoShape, ...], fuzzy_value: float = 0.0) -> Tuple[TopoShape, List[List[TopoShape]]]: + """ + Run general fuse algorithm (GFA) between this and given shapes. + generalFuse(list_of_other_shapes, [fuzzy_value = 0.0]) -> (result, map) + -- + list_of_other_shapes: shapes to run the algorithm against (the list is + effectively prepended by 'self'). + + fuzzy_value: extra tolerance to apply when searching for interferences, in + addition to tolerances of the input shapes. + + Returns a tuple of 2: (result, map). + + result is a compound containing all the pieces generated by the algorithm + (e.g., for two spheres, the pieces are three touching solids). Pieces that + touch share elements. + + map is a list of lists of shapes, providing the info on which children of + result came from which argument. The length of list is equal to length of + list_of_other_shapes + 1. First element is a list of pieces that came from + shape of this, and the rest are those that come from corresponding shapes in + list_of_other_shapes. + hint: use isSame method to test shape equality + + Parallelization of Boolean Operations algorithm + + OCC 6.9.0 or later is required. + """ + ... + + def sewShape(self) -> None: + """ + Sew the shape if there is a gap. + sewShape() + """ + ... + + @constmethod + def childShapes(self, cumOri: bool = True, cumLoc: bool = True) -> List: + """ + Return a list of sub-shapes that are direct children of this shape. + childShapes([cumOri=True, cumLoc=True]) -> list + -- + * If cumOri is true, the function composes all + sub-shapes with the orientation of this shape. + * If cumLoc is true, the function multiplies all + sub-shapes by the location of this shape, i.e. it applies to + each sub-shape the transformation that is associated with this shape. + """ + ... + + @constmethod + def ancestorsOfType(self, shape: TopoShape, shape_type: str) -> List: + """ + For a sub-shape of this shape get its ancestors of a type. + ancestorsOfType(shape, shape type) -> list + """ + ... + + def removeInternalWires(self, minimalArea: float) -> bool: + """ + Removes internal wires (also holes) from the shape. + removeInternalWires(minimalArea) -> bool + """ + ... + + @constmethod + def mirror(self, base: Vector, norm: Vector) -> TopoShape: + """ + Mirror this shape on a given plane. + mirror(base, norm) -> Shape + -- + The plane is given with its base point and its normal direction. + """ + ... + + @constmethod + def transformGeometry(self, matrix: Matrix) -> TopoShape: + """ + Apply geometric transformation on this or a copy the shape. + transformGeometry(matrix) -> Shape + -- + This method returns a new shape. + The transformation to be applied is defined as a 4x4 matrix. + The underlying geometry of the following shapes may change: + - a curve which supports an edge of the shape, or + - a surface which supports a face of the shape; + + For example, a circle may be transformed into an ellipse when + applying an affinity transformation. It may also happen that + the circle then is represented as a B-spline curve. + + The transformation is applied to: + - all the curves which support edges of the shape, and + - all the surfaces which support faces of the shape. + + Note: If you want to transform a shape without changing the + underlying geometry then use the methods translate or rotate. + """ + ... + + def transformShape(self, matrix: Matrix, copy: bool = False, checkScale: bool = False) -> None: + """ + Apply transformation on a shape without changing the underlying geometry. + transformShape(Matrix, [boolean copy=False, checkScale=False]) -> None + -- + If checkScale is True, it will use transformGeometry if non-uniform + scaling is detected. + """ + ... + + @constmethod + def transformed(self, *, matrix: Matrix, copy: bool = False, checkScale: bool = False, op: str = None) -> TopoShape: + """ + Create a new transformed shape + transformed(Matrix,copy=False,checkScale=False,op=None) -> shape + """ + ... + + def translate(self, vector: Vector) -> None: + """ + Apply the translation to the current location of this shape. + translate(vector) + """ + ... + + @constmethod + def translated(self, vector: Vector) -> TopoShape: + """ + Create a new shape with translation + translated(vector) -> shape + """ + ... + + def rotate(self, base: Vector, dir: Vector, degree: float) -> None: + """ + Apply the rotation (base, dir, degree) to the current location of this shape + rotate(base, dir, degree) + -- + Shp.rotate(App.Vector(0,0,0), App.Vector(0,0,1), 180) - rotate the shape around the Z Axis 180 degrees. + """ + ... + + @constmethod + def rotated(self, base: Vector, dir: Vector, degree: float) -> TopoShape: + """ + Create a new shape with rotation. + rotated(base, dir, degree) -> shape + """ + ... + + def scale(self, factor: float, base: Vector = None) -> None: + """ + Apply scaling with point and factor to this shape. + scale(factor, [base=App.Vector(0,0,0)]) + """ + ... + + @constmethod + def scaled(self, factor: float, base: Vector = None) -> TopoShape: + """ + Create a new shape with scale. + scaled(factor, [base=App.Vector(0,0,0)]) -> shape + """ + ... + + @constmethod + def makeFillet(self, *args) -> TopoShape: + """ + Make fillet. + makeFillet(radius, edgeList) -> Shape + or + makeFillet(radius1, radius2, edgeList) -> Shape + """ + ... + + @overload + @constmethod + def makeFillet(self, radius: float, edgeList: List) -> TopoShape: ... + + @overload + @constmethod + def makeFillet(self, radius1: float, radius2: float, edgeList: List) -> TopoShape: ... + + @constmethod + def makeChamfer(self, *args) -> TopoShape: + """ + Make chamfer. + makeChamfer(radius, edgeList) -> Shape + or + makeChamfer(radius1, radius2, edgeList) -> Shape + """ + ... + + @overload + @constmethod + def makeChamfer(self, radius: float, edgeList: List) -> TopoShape: ... + + @overload + @constmethod + def makeChamfer(self, radius1: float, radius2: float, edgeList: List) -> TopoShape: ... + + @constmethod + def makeThickness(self, faces: List, offset: float, tolerance: float) -> TopoShape: + """ + Hollow a solid according to given thickness and faces. + makeThickness(List of faces, Offset (Float), Tolerance (Float)) -> Shape + -- + A hollowed solid is built from an initial solid and a set of faces on this solid, + which are to be removed. The remaining faces of the solid become the walls of + the hollowed solid, their thickness defined at the time of construction. + """ + ... + + @constmethod + def makeOffsetShape(self, offset: float, tolerance: float, *, inter: bool = False, self_inter: bool = False, offsetMode: int = 0, join: int = 0, fill: bool = False) -> TopoShape: + """ + Makes an offset shape (3d offsetting). + makeOffsetShape(offset, tolerance, [inter=False, self_inter=False, offsetMode=0, join=0, fill=False]) -> Shape + -- + The function supports keyword arguments. + + * offset: distance to expand the shape by. Negative value will shrink the shape. + + * tolerance: precision of approximation. + + * inter: (parameter to OCC routine; not implemented) + + * self_inter: (parameter to OCC routine; not implemented) + + * offsetMode: 0 = skin; 1 = pipe; 2 = recto-verso + + * join: method of offsetting non-tangent joints. 0 = arcs, 1 = tangent, 2 = + intersection + + * fill: if true, offsetting a shell is to yield a solid + + Returns: result of offsetting. + """ + ... + + @constmethod + def makeOffset2D(self, offset: float, *, join: int = 0, fill: bool = False, openResult: bool = False, intersection: bool = False) -> TopoShape: + """ + Makes an offset shape (2d offsetting). + makeOffset2D(offset, [join=0, fill=False, openResult=False, intersection=False]) -> Shape + -- + The function supports keyword arguments. + Input shape (self) can be edge, wire, face, or a compound of those. + + * offset: distance to expand the shape by. Negative value will shrink the shape. + + * join: method of offsetting non-tangent joints. 0 = arcs, 1 = tangent, 2 = intersection + + * fill: if true, the output is a face filling the space covered by offset. If + false, the output is a wire. + + * openResult: affects the way open wires are processed. If False, an open wire + is made. If True, a closed wire is made from a double-sided offset, with rounds + around open vertices. + + * intersection: affects the way compounds are processed. If False, all children + are offset independently. If True, and children are edges/wires, the children + are offset in a collective manner. If compounding is nested, collectiveness + does not spread across compounds (only direct children of a compound are taken + collectively). + + Returns: result of offsetting (wire or face or compound of those). Compounding + structure follows that of source shape. + """ + ... + + @constmethod + def makeEvolved(self, *, Profile: TopoShape, Join: int, AxeProf: bool, Solid: bool, + ProfOnSpine: bool, Tolerance: float) -> None: + """ + Profile along the spine + """ + ... + + @constmethod + def makeWires(self, op: str = None) -> TopoShape: + """ + Make wire(s) using the edges of this shape + makeWires([op=None]) + -- + The function will sort any edges inside the current shape, and connect them + into wire. If more than one wire is found, then it will make a compound out of + all found wires. + + This function is element mapping aware. If the input shape has non-zero Tag, + it will map any edge and vertex element name inside the input shape into the + itself. + + op: an optional string to be appended when auto generates element mapping. + """ + ... + + def reverse(self) -> None: + """ + Reverses the orientation of this shape. + reverse() + """ + ... + + @constmethod + def reversed(self) -> TopoShape: + """ + Reverses the orientation of a copy of this shape. + reversed() -> Shape + """ + ... + + def complement(self) -> None: + """ + Computes the complement of the orientation of this shape, + i.e. reverses the interior/exterior status of boundaries of this shape. + complement() + """ + ... + + def nullify(self) -> None: + """ + Destroys the reference to the underlying shape stored in this shape. + As a result, this shape becomes null. + nullify() + """ + ... + + @constmethod + def isClosed(self) -> bool: + """ + Checks if the shape is closed. + isClosed() -> bool + -- + If the shape is a shell it returns True if it has no free boundaries (edges). + If the shape is a wire it returns True if it has no free ends (vertices). + (Internal and External sub-shapes are ignored in these checks) + If the shape is an edge it returns True if its vertices are the same. + """ + ... + + @constmethod + def isPartner(self, shape: TopoShape) -> bool: + """ + Checks if both shapes share the same geometry. + Placement and orientation may differ. + isPartner(shape) -> bool + """ + ... + + @constmethod + def isSame(self, shape: TopoShape) -> bool: + """ + Checks if both shapes share the same geometry + and placement. Orientation may differ. + isSame(shape) -> bool + """ + ... + + @constmethod + def isEqual(self, shape: TopoShape) -> bool: + """ + Checks if both shapes are equal. + This means geometry, placement and orientation are equal. + isEqual(shape) -> bool + """ + ... + + @constmethod + def isNull(self) -> bool: + """ + Checks if the shape is null. + isNull() -> bool + """ + ... + + @constmethod + def isValid(self) -> bool: + """ + Checks if the shape is valid, i.e. neither null, nor empty nor corrupted. + isValid() -> bool + """ + ... + + @constmethod + def isCoplanar(self, shape: TopoShape, tol: float = None) -> bool: + """ + Checks if this shape is coplanar with the given shape. + isCoplanar(shape,tol=None) -> bool + """ + ... + + @constmethod + def isInfinite(self) -> bool: + """ + Checks if this shape has an infinite expansion. + isInfinite() -> bool + """ + ... + + @constmethod + def findPlane(self, tol: float = None) -> TopoShape: + """ + Returns a plane if the shape is planar + findPlane(tol=None) -> Shape + """ + ... + + def fix(self, working_precision: float, minimum_precision: float, maximum_precision: float) -> bool: + """ + Tries to fix a broken shape. + fix(working precision, minimum precision, maximum precision) -> bool + -- + True is returned if the operation succeeded, False otherwise. + """ + ... + + @constmethod + def hashCode(self) -> int: + """ + This value is computed from the value of the underlying shape reference and the location. + hashCode() -> int + -- + Orientation is not taken into account. + """ + ... + + @constmethod + def tessellate(self) -> Tuple[List[Vector], List]: + """ + Tessellate the shape and return a list of vertices and face indices + tessellate() -> (vertex,facets) + """ + ... + + @constmethod + def project(self, shapeList: List[TopoShape]) -> TopoShape: + """ + Project a list of shapes on this shape + project(shapeList) -> Shape + """ + ... + + @constmethod + def makeParallelProjection(self, shape: TopoShape, dir: Vector) -> TopoShape: + """ + Parallel projection of an edge or wire on this shape + makeParallelProjection(shape, dir) -> Shape + """ + ... + + @constmethod + def makePerspectiveProjection(self, shape: TopoShape, pnt: Vector) -> TopoShape: + """ + Perspective projection of an edge or wire on this shape + makePerspectiveProjection(shape, pnt) -> Shape + """ + ... + + @constmethod + def reflectLines(self, ViewDir: Vector, *, ViewPos: Vector = None, UpDir: Vector = None, + EdgeType: str = None, Visible: bool = True, OnShape: bool = False) -> TopoShape: + """ + Build projection or reflect lines of a shape according to a view direction. + reflectLines(ViewDir, [ViewPos, UpDir, EdgeType, Visible, OnShape]) -> Shape (Compound of edges) + -- + This algorithm computes the projection of the shape in the ViewDir direction. + If OnShape is False(default), the returned edges are flat on the XY plane defined by + ViewPos(origin) and UpDir(up direction). + If OnShape is True, the returned edges are the corresponding 3D reflect lines located on the shape. + EdgeType is a string defining the type of result edges : + - IsoLine : isoparametric line + - OutLine : outline (silhouette) edge + - Rg1Line : smooth edge of G1-continuity between two surfaces + - RgNLine : sewn edge of CN-continuity on one surface + - Sharp : sharp edge (of C0-continuity) + If Visible is True (default), only visible edges are returned. + If Visible is False, only invisible edges are returned. + """ + ... + + def makeShapeFromMesh(self, mesh: Tuple[List[Vector], List], tolerance: float) -> TopoShape: + """ + Make a compound shape out of mesh data. + makeShapeFromMesh((vertex,facets),tolerance) -> Shape + -- + Note: This should be used for rather small meshes only. + """ + ... + + @constmethod + def toNurbs(self) -> TopoShape: + """ + Conversion of the complete geometry of a shape into NURBS geometry. + toNurbs() -> Shape + -- + For example, all curves supporting edges of the basis shape are converted + into B-spline curves, and all surfaces supporting its faces are converted + into B-spline surfaces. + """ + ... + + @constmethod + def copy(self, copyGeom: bool = True, copyMesh: bool = False) -> TopoShape: + """ + Create a copy of this shape + copy(copyGeom=True, copyMesh=False) -> Shape + -- + If copyMesh is True, triangulation contained in original shape will be + copied along with geometry. + If copyGeom is False, only topological objects will be copied, while + geometry and triangulation will be shared with original shape. + """ + ... + + @constmethod + def cleaned(self) -> TopoShape: + """ + This creates a cleaned copy of the shape with the triangulation removed. + clean() + -- + This can be useful to reduce file size when exporting as a BREP file. + Warning: Use the cleaned shape with care because certain algorithms may work incorrectly + if the shape has no internal triangulation any more. + """ + ... + + @constmethod + def replaceShape(self, tupleList: List[Tuple[TopoShape, TopoShape]]) -> TopoShape: + """ + Replace a sub-shape with a new shape and return a new shape. + replaceShape(tupleList) -> Shape + -- + The parameter is in the form list of tuples with the two shapes. + """ + ... + + @constmethod + def removeShape(self, shapeList: List[TopoShape]) -> TopoShape: + """ + Remove a sub-shape and return a new shape. + removeShape(shapeList) -> Shape + -- + The parameter is a list of shapes. + """ + ... + + @constmethod + def defeaturing(self, shapeList: List[TopoShape]) -> TopoShape: + """ + Remove a feature defined by supplied faces and return a new shape. + defeaturing(shapeList) -> Shape + -- + The parameter is a list of faces. + """ + ... + + @constmethod + def isInside(self, point: Vector, tolerance: float, checkFace: bool) -> bool: + """ + Checks whether a point is inside or outside the shape. + isInside(point, tolerance, checkFace) => Boolean + -- + checkFace indicates if the point lying directly on a face is considered to be inside or not + """ + ... + + @constmethod + def removeSplitter(self) -> TopoShape: + """ + Removes redundant edges from the B-REP model + removeSplitter() -> Shape + """ + ... + + @constmethod + def proximity(self, shape: TopoShape, tolerance: float = None) -> Tuple[List[int], List[int]]: + """ + Returns two lists of Face indexes for the Faces involved in the intersection. + proximity(shape,[tolerance]) -> (selfFaces, shapeFaces) + """ + ... + + @constmethod + def distToShape(self, shape: TopoShape, tol: float = 1e-7) -> Tuple[float, List[Tuple[Vector, Vector]], List[Tuple]]: + """ + Find the minimum distance to another shape. + distToShape(shape, tol=1e-7) -> (dist, vectors, infos) + -- + dist is the minimum distance, in mm (float value). + + vectors is a list of pairs of App.Vector. Each pair corresponds to solution. + Example: [(App.Vector(2.0, -1.0, 2.0), App.Vector(2.0, 0.0, 2.0)), + (App.Vector(2.0, -1.0, 2.0), App.Vector(2.0, -1.0, 3.0))] + First vector is a point on self, second vector is a point on s. + + infos contains additional info on the solutions. It is a list of tuples: + (topo1, index1, params1, topo2, index2, params2) + + topo1, topo2 are strings identifying type of BREP element: 'Vertex', + 'Edge', or 'Face'. + + index1, index2 are indexes of the elements (zero-based). + + params1, params2 are parameters of internal space of the elements. For + vertices, params is None. For edges, params is one float, u. For faces, + params is a tuple (u,v). + """ + ... + + @constmethod + def getElement(self, elementName: str, silent: bool = False) -> TopoShape: + """ + Returns a SubElement + getElement(elementName, [silent = False]) -> Face | Edge | Vertex + elementName: SubElement name - i.e. 'Edge1', 'Face3' etc. + Accepts TNP mitigation mapped names as well + silent: True to suppress the exception throw if the shape isn't found. + """ + ... + + @constmethod + def countElement(self, type: str) -> int: + """ + Returns the count of a type of element + countElement(type) -> int + """ + ... + + def mapSubElement(self, shape: Union[TopoShape, Tuple[TopoShape, ...]], op: str = "") -> None: + """ + mapSubElement(shape|[shape...], op='') - maps the sub element of other shape + + shape: other shape or sequence of shapes to map the sub-elements + op: optional string prefix to append before the mapped sub element names + """ + ... + + def mapShapes(self, generated: List[Tuple[TopoShape, TopoShape]], modified: List[Tuple[TopoShape, TopoShape]], op: str = "") -> None: + """ + mapShapes(generated, modified, op='') + + generate element names with user defined mapping + + generated: a list of tuple(src, dst) that indicating src shape or shapes + generates dst shape or shapes. Note that the dst shape or shapes + must be sub-shapes of this shape. + modified: a list of tuple(src, dst) that indicating src shape or shapes + modifies into dst shape or shapes. Note that the dst shape or + shapes must be sub-shapes of this shape. + op: optional string prefix to append before the mapped sub element names + """ + ... + + @constmethod + def getElementHistory(self, name: str) -> Union[Tuple[str, str, List[str]], None]: + """ + getElementHistory(name) - returns the element mapped name history + + name: mapped element name belonging to this shape + + Returns tuple(sourceShapeTag, sourceName, [intermediateNames...]), + or None if no history. + """ + ... + + @constmethod + def getTolerance(self, mode: int, ShapeType: str = "Shape") -> float: + """ + Determines a tolerance from the ones stored in a shape + getTolerance(mode, ShapeType=Shape) -> float + -- + mode = 0 : returns the average value between sub-shapes, + mode > 0 : returns the maximal found, + mode < 0 : returns the minimal found. + ShapeType defines what kinds of sub-shapes to consider: + Shape (default) : all : Vertex, Edge, Face, + Vertex : only vertices, + Edge : only edges, + Face : only faces, + Shell : combined Shell + Face, for each face (and containing + shell), also checks edge and Vertex + """ + ... + + @constmethod + def overTolerance(self, value: float, ShapeType: str = "Shape") -> List[TopoShape]: + """ + Determines which shapes have a tolerance over the given value + overTolerance(value, [ShapeType=Shape]) -> ShapeList + -- + ShapeType is interpreted as in the method getTolerance + """ + ... + + @constmethod + def inTolerance(self, valmin: float, valmax: float, ShapeType: str = "Shape") -> List[TopoShape]: + """ + Determines which shapes have a tolerance within a given interval + inTolerance(valmin, valmax, [ShapeType=Shape]) -> ShapeList + -- + ShapeType is interpreted as in the method getTolerance + """ + ... + + @constmethod + def globalTolerance(self, mode: int) -> float: + """ + Returns the computed tolerance according to the mode + globalTolerance(mode) -> float + -- + mode = 0 : average + mode > 0 : maximal + mode < 0 : minimal + """ + ... + + @constmethod + def fixTolerance(self, value: float, ShapeType: str = "Shape") -> None: + """ + Sets (enforces) tolerances in a shape to the given value + fixTolerance(value, [ShapeType=Shape]) + -- + ShapeType = Vertex : only vertices are set + ShapeType = Edge : only edges are set + ShapeType = Face : only faces are set + ShapeType = Wire : to have edges and their vertices set + ShapeType = other value : all (vertices,edges,faces) are set + """ + ... + + @constmethod + def limitTolerance(self, tmin: float, tmax: float = 0, ShapeType: str = "Shape") -> bool: + """ + Limits tolerances in a shape + limitTolerance(tmin, [tmax=0, ShapeType=Shape]) -> bool + -- + tmin = tmax -> as fixTolerance (forces) + tmin = 0 -> maximum tolerance will be tmax + tmax = 0 or not given (more generally, tmax < tmin) -> + tmax ignored, minimum will be tmin + else, maximum will be max and minimum will be min + ShapeType = Vertex : only vertices are set + ShapeType = Edge : only edges are set + ShapeType = Face : only faces are set + ShapeType = Wire : to have edges and their vertices set + ShapeType = other value : all (vertices,edges,faces) are set + Returns True if at least one tolerance of the sub-shape has been modified + """ + ... + + @constmethod + def optimalBoundingBox(self, useTriangulation: bool = True, useShapeTolerance: bool = False) -> BoundBox: + """ + Get the optimal bounding box + optimalBoundingBox([useTriangulation = True, useShapeTolerance = False]) -> bound box + """ + ... + + @constmethod + def clearCache(self) -> None: + """ + Clear internal sub-shape cache + """ + ... + + @constmethod + def findSubShape(self, shape: TopoShape) -> Tuple[Union[str, None], int]: + """ + findSubShape(shape) -> (type_name, index) + + Find sub shape and return the shape type name and index. If not found, + then return (None, 0) + """ + ... + + @constmethod + def findSubShapesWithSharedVertex(self, shape: TopoShape, *, needName: bool = False, checkGeometry: bool = True, tol: float = 1e-7, atol: float = 1e-12) -> Union[List[Tuple[str, TopoShape]], List[TopoShape]]: + """ + findSubShapesWithSharedVertex(shape, needName=False, checkGeometry=True, tol=1e-7, atol=1e-12) -> Shape + + shape: input elementary shape, currently only support Face, Edge, or Vertex + + needName: if True, return a list of tuple(name, shape), or else return a list + of shapes. + + checkGeometry: whether to compare geometry + + tol: distance tolerance + + atol: angular tolerance + + Search sub shape by checking vertex coordinates and comparing the underlying + geometries, This can find shapes that are copied. It currently only works with + elementary shapes, Face, Edge, Vertex. + """ + ... + + @constmethod + def getChildShapes(self, shapetype: str, avoidtype: str = "") -> List[TopoShape]: + """ + getChildShapes(shapetype, avoidtype='') -> list(Shape) + + Return a list of child sub-shapes of given type. + + shapetype: the type of requesting sub shapes + avoidtype: optional shape type to skip when exploring + """ + ... diff --git a/src/Mod/Part/App/TopoShapeCompSolid.pyi b/src/Mod/Part/App/TopoShapeCompSolid.pyi new file mode 100644 index 0000000000..76f791389e --- /dev/null +++ b/src/Mod/Part/App/TopoShapeCompSolid.pyi @@ -0,0 +1,26 @@ +from Base.Metadata import export +from TopoShape import TopoShape + + +@export( + Father="TopoShapePy", + Name="TopoShapeCompSolidPy", + Twin="TopoShape", + TwinPointer="TopoShape", + Include="Mod/Part/App/TopoShape.h", + Namespace="Part", + FatherInclude="Mod/Part/App/TopoShapePy.h", + FatherNamespace="Part", + Constructor=True, +) +class TopoShapeCompSolid(TopoShape): + """ + TopoShapeCompSolid is the OpenCasCade topological compound solid wrapper + """ + + def add(self, solid: TopoShape) -> None: + """ + Add a solid to the compound. + add(solid) + """ + ... diff --git a/src/Mod/Part/App/TopoShapeCompSolidPy.xml b/src/Mod/Part/App/TopoShapeCompSolidPy.xml deleted file mode 100644 index 569bfe2d9a..0000000000 --- a/src/Mod/Part/App/TopoShapeCompSolidPy.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - TopoShapeCompSolid is the OpenCasCade topological compound solid wrapper - - - - Add a solid to the compound. -add(solid) - - - - - diff --git a/src/Mod/Part/App/TopoShapeCompound.pyi b/src/Mod/Part/App/TopoShapeCompound.pyi new file mode 100644 index 0000000000..316bcd3058 --- /dev/null +++ b/src/Mod/Part/App/TopoShapeCompound.pyi @@ -0,0 +1,42 @@ +from Base.Metadata import export, constmethod +from TopoShape import TopoShape + + +@export( + Twin="TopoShape", + TwinPointer="TopoShape", + Include="Mod/Part/App/TopoShape.h", + FatherInclude="Mod/Part/App/TopoShapePy.h", + Constructor=True, +) +class TopoShapeCompound(TopoShape): + """ + Create a compound out of a list of shapes + + Author: Juergen Riegel (Juergen.Riegel@web.de) + Licence: LGPL + """ + + def add(self, shape: TopoShape) -> None: + """ + Add a shape to the compound. + add(shape) + """ + ... + + @constmethod + def connectEdgesToWires(self, Shared: bool = True, Tolerance: float = 1e-7) -> "TopoShapeCompound": + """ + Build a compound of wires out of the edges of this compound. + connectEdgesToWires([Shared = True, Tolerance = 1e-7]) -> Compound + -- + If Shared is True connection is performed only when adjacent edges share the same vertex. + If Shared is False connection is performed only when ends of adjacent edges are at distance less than Tolerance. + """ + ... + + def setFaces(self) -> None: + """ + A shape is created from points and triangles and set to this object + """ + ... diff --git a/src/Mod/Part/App/TopoShapeCompoundPy.xml b/src/Mod/Part/App/TopoShapeCompoundPy.xml deleted file mode 100644 index 19b61cbcd8..0000000000 --- a/src/Mod/Part/App/TopoShapeCompoundPy.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - Create a compound out of a list of shapes - - - - Add a shape to the compound. -add(shape) - - - - - - Build a compound of wires out of the edges of this compound. -connectEdgesToWires([Shared = True, Tolerance = 1e-7]) -> Compound --- -If Shared is True connection is performed only when adjacent edges share the same vertex. -If Shared is False connection is performed only when ends of adjacent edges are at distance less than Tolerance. - - - - - A shape is created from points and triangles and set to this object - - - - diff --git a/src/Mod/Part/App/TopoShapeEdge.pyi b/src/Mod/Part/App/TopoShapeEdge.pyi new file mode 100644 index 0000000000..5c1ec95199 --- /dev/null +++ b/src/Mod/Part/App/TopoShapeEdge.pyi @@ -0,0 +1,573 @@ +from Base.Metadata import export, constmethod +from Base.Vector import Vector +from Wire import Wire +from Vertex import Vertex +from TopoShape import TopoShape +from typing import Final, Tuple, Dict, List, overload + + +@export( + Twin="TopoShape", + TwinPointer="TopoShape", + Include="Mod/Part/App/TopoShape.h", + FatherInclude="Mod/Part/App/TopoShapePy.h", + Constructor=True, +) +class TopoShapeEdge(TopoShape): + """ + TopoShapeEdge is the OpenCasCade topological edge wrapper + + Author: Juergen Riegel (Juergen.Riegel@web.de) + Licence: LGPL + """ + + Tolerance: float = 0.0 + """Set or get the tolerance of the vertex""" + + Length: Final[float] = 0.0 + """Returns the cartesian length of the curve""" + + ParameterRange: Final[Tuple[float, float]] = (0.0, 0.0) + """ + Returns a 2 tuple with the range of the primary parameter + defining the curve. This is the same as would be returned by + the FirstParameter and LastParameter properties, i.e. + + (LastParameter,FirstParameter) + + What the parameter is depends on what type of edge it is. For a + Line the parameter is simply its cartesian length. Some other + examples are shown below: + + Type Parameter + --------------------------------------------------------------- + Circle Angle swept by circle (or arc) in radians + BezierCurve Unitless number in the range 0.0 to 1.0 + Helix Angle swept by helical turns in radians + """ + + FirstParameter: Final[float] = 0.0 + """ + Returns the start value of the range of the primary parameter + defining the curve. + + What the parameter is depends on what type of edge it is. For a + Line the parameter is simply its cartesian length. Some other + examples are shown below: + + Type Parameter + ----------------------------------------------------------- + Circle Angle swept by circle (or arc) in radians + BezierCurve Unitless number in the range 0.0 to 1.0 + Helix Angle swept by helical turns in radians + """ + + LastParameter: Final[float] = 0.0 + """ + Returns the end value of the range of the primary parameter + defining the curve. + + What the parameter is depends on what type of edge it is. For a + Line the parameter is simply its cartesian length. Some other + examples are shown below: + + Type Parameter + ----------------------------------------------------------- + Circle Angle swept by circle (or arc) in radians + BezierCurve Unitless number in the range 0.0 to 1.0 + Helix Angle swept by helical turns in radians + """ + + Curve: Final[object] = object() + """Returns the 3D curve of the edge""" + + Closed: Final[bool] = False + """Returns true if the edge is closed""" + + Degenerated: Final[bool] = False + """Returns true if the edge is degenerated""" + + Mass: Final[object] = object() + """Returns the mass of the current system.""" + + CenterOfMass: Final[object] = object() + """ + Returns the center of mass of the current system. + If the gravitational field is uniform, it is the center of gravity. + The coordinates returned for the center of mass are expressed in the + absolute Cartesian coordinate system. + """ + + MatrixOfInertia: Final[object] = object() + """ + Returns the matrix of inertia. It is a symmetrical matrix. + The coefficients of the matrix are the quadratic moments of + inertia. + + | Ixx Ixy Ixz 0 | + | Ixy Iyy Iyz 0 | + | Ixz Iyz Izz 0 | + | 0 0 0 1 | + + The moments of inertia are denoted by Ixx, Iyy, Izz. + The products of inertia are denoted by Ixy, Ixz, Iyz. + The matrix of inertia is returned in the central coordinate + system (G, Gx, Gy, Gz) where G is the centre of mass of the + system and Gx, Gy, Gz the directions parallel to the X(1,0,0) + Y(0,1,0) Z(0,0,1) directions of the absolute cartesian + coordinate system. + """ + + StaticMoments: Final[object] = object() + """ + Returns Ix, Iy, Iz, the static moments of inertia of the + current system; i.e. the moments of inertia about the + three axes of the Cartesian coordinate system. + """ + + PrincipalProperties: Final[Dict] = {} + """ + Computes the principal properties of inertia of the current system. + There is always a set of axes for which the products + of inertia of a geometric system are equal to 0; i.e. the + matrix of inertia of the system is diagonal. These axes + are the principal axes of inertia. Their origin is + coincident with the center of mass of the system. The + associated moments are called the principal moments of inertia. + This function computes the eigen values and the + eigen vectors of the matrix of inertia of the system. + """ + + Continuity: Final[str] = "" + """Returns the continuity""" + + @constmethod + def getParameterByLength(self, pos: float, tolerance: float = 1e-7) -> float: + """ + Get the value of the primary parameter at the given distance along the cartesian length of the edge. + getParameterByLength(pos, [tolerance = 1e-7]) -> Float + -- + Args: + pos (float or int): The distance along the length of the edge at which to + determine the primary parameter value. See help for the FirstParameter or + LastParameter properties for more information on the primary parameter. + If the given value is positive, the distance from edge start is used. + If the given value is negative, the distance from edge end is used. + tol (float): Computing tolerance. Optional, defaults to 1e-7. + + Returns: + paramval (float): the value of the primary parameter defining the edge at the + given position along its cartesian length. + """ + ... + + @constmethod + def tangentAt(self, paramval: float) -> Vector: + """ + Get the tangent direction at the given primary parameter value along the Edge if it is defined + tangentAt(paramval) -> Vector + -- + Args: + paramval (float or int): The parameter value along the Edge at which to + determine the tangent direction e.g: + + x = Part.makeCircle(1, FreeCAD.Vector(0,0,0), FreeCAD.Vector(0,0,1), 0, 90) + y = x.tangentAt(x.FirstParameter + 0.5 * (x.LastParameter - x.FirstParameter)) + + y is the Vector (-0.7071067811865475, 0.7071067811865476, 0.0) + + Values with magnitude greater than the Edge length return + values of the tangent on the curve extrapolated beyond its + length. This may not be valid for all Edges. Negative values + similarly return a tangent on the curve extrapolated backwards + (before the start point of the Edge). For example, using the + same shape as above: + + >>> x.tangentAt(x.FirstParameter + 3.5*(x.LastParameter - x.FirstParameter)) + Vector (0.7071067811865477, 0.7071067811865474, 0.0) + + Which gives the same result as + + >>> x.tangentAt(x.FirstParameter -0.5*(x.LastParameter - x.FirstParameter)) + Vector (0.7071067811865475, 0.7071067811865476, 0.0) + + Since it is a circle + + Returns: + Vector: representing the tangent to the Edge at the given + location along its length (or extrapolated length) + """ + ... + + @constmethod + def valueAt(self, paramval: float) -> Vector: + """ + Get the value of the cartesian parameter value at the given parameter value along the Edge + valueAt(paramval) -> Vector + -- + Args: + paramval (float or int): The parameter value along the Edge at which to + determine the value in terms of the main parameter defining + the edge, what the parameter value is depends on the type of + edge. See e.g: + + For a circle value + + x = Part.makeCircle(1, FreeCAD.Vector(0,0,0), FreeCAD.Vector(0,0,1), 0, 90) + y = x.valueAt(x.FirstParameter + 0.5 * (x.LastParameter - x.FirstParameter)) + + y is theVector (0.7071067811865476, 0.7071067811865475, 0.0) + + Values with magnitude greater than the Edge length return + values on the curve extrapolated beyond its length. This may + not be valid for all Edges. Negative values similarly return + a parameter value on the curve extrapolated backwards (before the + start point of the Edge). For example, using the same shape + as above: + + >>> x.valueAt(x.FirstParameter + 3.5*(x.LastParameter - x.FirstParameter)) + Vector (0.7071067811865474, -0.7071067811865477, 0.0) + + Which gives the same result as + + >>> x.valueAt(x.FirstParameter -0.5*(x.LastParameter - x.FirstParameter)) + Vector (0.7071067811865476, -0.7071067811865475, 0.0) + + Since it is a circle + + Returns: + Vector: representing the cartesian location on the Edge at the given + distance along its length (or extrapolated length) + """ + ... + + @constmethod + def parameters(self, face: object = ...) -> List[float]: + """ + Get the list of parameters of the tessellation of an edge. + parameters([face]) -> list + -- + If the edge is part of a face then this face is required as argument. + An exception is raised if the edge has no polygon. + """ + ... + + @constmethod + def parameterAt(self, vertex: object) -> float: + """ + Get the parameter at the given vertex if lying on the edge + parameterAt(Vertex) -> Float + """ + ... + + @constmethod + def normalAt(self, paramval: float) -> Vector: + """ + Get the normal direction at the given parameter value along the Edge if it is defined + normalAt(paramval) -> Vector + -- + Args: + paramval (float or int): The parameter value along the Edge at which to + determine the normal direction e.g: + + x = Part.makeCircle(1, FreeCAD.Vector(0,0,0), FreeCAD.Vector(0,0,1), 0, 90) + y = x.normalAt(x.FirstParameter + 0.5 * (x.LastParameter - x.FirstParameter)) + + y is the Vector (-0.7071067811865476, -0.7071067811865475, 0.0) + + Values with magnitude greater than the Edge length return + values of the normal on the curve extrapolated beyond its + length. This may not be valid for all Edges. Negative values + similarly return a normal on the curve extrapolated backwards + (before the start point of the Edge). For example, using the + same shape as above: + + >>> x.normalAt(x.FirstParameter + 3.5*(x.LastParameter - x.FirstParameter)) + Vector (-0.7071067811865474, 0.7071067811865477, 0.0) + + Which gives the same result as + + >>> x.normalAt(x.FirstParameter -0.5*(x.LastParameter - x.FirstParameter)) + Vector (-0.7071067811865476, 0.7071067811865475, 0.0) + + Since it is a circle + + Returns: + Vector: representing the normal to the Edge at the given + location along its length (or extrapolated length) + """ + ... + + @constmethod + def derivative1At(self, paramval: float) -> Vector: + """ + Get the first derivative at the given parameter value along the Edge if it is defined + derivative1At(paramval) -> Vector + -- + Args: + paramval (float or int): The parameter value along the Edge at which to + determine the first derivative e.g: + + x = Part.makeCircle(1, FreeCAD.Vector(0,0,0), FreeCAD.Vector(0,0,1), 0, 90) + y = x.derivative1At(x.FirstParameter + 0.5 * (x.LastParameter - x.FirstParameter)) + + y is the Vector (-0.7071067811865475, 0.7071067811865476, 0.0) + + Values with magnitude greater than the Edge length return + values of the first derivative on the curve extrapolated + beyond its length. This may not be valid for all Edges. + Negative values similarly return a first derivative on the + curve extrapolated backwards (before the start point of the + Edge). For example, using the same shape as above: + + >>> x.derivative1At(x.FirstParameter + 3.5*(x.LastParameter - x.FirstParameter)) + Vector (0.7071067811865477, 0.7071067811865474, 0.0) + + Which gives the same result as + + >>> x.derivative1At(x.FirstParameter -0.5*(x.LastParameter - x.FirstParameter)) + Vector (0.7071067811865475, 0.7071067811865476, 0.0) + + Since it is a circle + + Returns: + Vector: representing the first derivative to the Edge at the + given location along its length (or extrapolated length) + """ + ... + + @constmethod + def derivative2At(self, paramval: float) -> Vector: + """ + Get the second derivative at the given parameter value along the Edge if it is defined + derivative2At(paramval) -> Vector + -- + Args: + paramval (float or int): The parameter value along the Edge at which to + determine the second derivative e.g: + + x = Part.makeCircle(1, FreeCAD.Vector(0,0,0), FreeCAD.Vector(0,0,1), 0, 90) + y = x.derivative2At(x.FirstParameter + 0.5 * (x.LastParameter - x.FirstParameter)) + + y is the Vector (-0.7071067811865476, -0.7071067811865475, 0.0) + + Values with magnitude greater than the Edge length return + values of the second derivative on the curve extrapolated + beyond its length. This may not be valid for all Edges. + Negative values similarly return a second derivative on the + curve extrapolated backwards (before the start point of the + Edge). For example, using the same shape as above: + + >>> x.derivative2At(x.FirstParameter + 3.5*(x.LastParameter - x.FirstParameter)) + Vector (-0.7071067811865474, 0.7071067811865477, 0.0) + + Which gives the same result as + + >>> x.derivative2At(x.FirstParameter -0.5*(x.LastParameter - x.FirstParameter)) + Vector (-0.7071067811865476, 0.7071067811865475, 0.0) + + Since it is a circle + + Returns: + Vector: representing the second derivative to the Edge at the + given location along its length (or extrapolated length) + """ + ... + + @constmethod + def derivative3At(self, paramval: float) -> Vector: + """ + Get the third derivative at the given parameter value along the Edge if it is defined + derivative3At(paramval) -> Vector + -- + Args: + paramval (float or int): The parameter value along the Edge at which to + determine the third derivative e.g: + + x = Part.makeCircle(1, FreeCAD.Vector(0,0,0), FreeCAD.Vector(0,0,1), 0, 90) + y = x.derivative3At(x.FirstParameter + 0.5 * (x.LastParameter - x.FirstParameter)) + + y is the Vector (0.7071067811865475, -0.7071067811865476, -0.0) + + Values with magnitude greater than the Edge length return + values of the third derivative on the curve extrapolated + beyond its length. This may not be valid for all Edges. + Negative values similarly return a third derivative on the + curve extrapolated backwards (before the start point of the + Edge). For example, using the same shape as above: + + >>> x.derivative3At(x.FirstParameter + 3.5*(x.LastParameter - x.FirstParameter)) + Vector (-0.7071067811865477, -0.7071067811865474, 0.0) + + Which gives the same result as + + >>> x.derivative3At(x.FirstParameter -0.5*(x.LastParameter - x.FirstParameter)) + Vector (-0.7071067811865475, -0.7071067811865476, 0.0) + + Since it is a circle + + Returns: + Vector: representing the third derivative to the Edge at the + given location along its length (or extrapolated length) + """ + ... + + @constmethod + def curvatureAt(self, paramval: float) -> float: + """ + Get the curvature at the given parameter [First|Last] if defined + curvatureAt(paramval) -> Float + """ + ... + + @constmethod + def centerOfCurvatureAt(self, paramval: float) -> Vector: + """ + Get the center of curvature at the given parameter [First|Last] if defined + centerOfCurvatureAt(paramval) -> Vector + """ + ... + + @constmethod + def firstVertex(self, Orientation: bool = False) -> Vertex: + """ + Returns the Vertex of orientation FORWARD in this edge. + firstVertex([Orientation=False]) -> Vertex + -- + If there is none a Null shape is returned. + Orientation = True : taking into account the edge orientation + """ + ... + + @constmethod + def lastVertex(self, Orientation: bool = False) -> Vertex: + """ + Returns the Vertex of orientation REVERSED in this edge. + lastVertex([Orientation=False]) -> Vertex + -- + If there is none a Null shape is returned. + Orientation = True : taking into account the edge orientation + """ + ... + + @constmethod + @overload + def discretize( + self, Number: int, First: float = ..., Last: float = ... + ) -> List[Vector]: ... + + @constmethod + @overload + def discretize( + self, QuasiNumber: int, First: float = ..., Last: float = ... + ) -> List[Vector]: ... + + @constmethod + @overload + def discretize( + self, Distance: float, First: float = ..., Last: float = ... + ) -> List[Vector]: ... + + @constmethod + @overload + def discretize( + self, Deflection: float, First: float = ..., Last: float = ... + ) -> List[Vector]: ... + + @constmethod + @overload + def discretize( + self, QuasiDeflection: float, First: float = ..., Last: float = ... + ) -> List[Vector]: ... + + @constmethod + @overload + def discretize( + self, + Angular: float, + Curvature: float, + Minimum: int = ..., + First: float = ..., + Last: float = ..., + ) -> List[Vector]: ... + + @constmethod + def discretize(self, **kwargs) -> List[Vector]: + """ + Discretizes the edge and returns a list of points. + discretize(kwargs) -> list + -- + The function accepts keywords as argument: + discretize(Number=n) => gives a list of 'n' equidistant points + discretize(QuasiNumber=n) => gives a list of 'n' quasi equidistant points (is faster than the method above) + discretize(Distance=d) => gives a list of equidistant points with distance 'd' + discretize(Deflection=d) => gives a list of points with a maximum deflection 'd' to the edge + discretize(QuasiDeflection=d) => gives a list of points with a maximum deflection 'd' to the edge (faster) + discretize(Angular=a,Curvature=c,[Minimum=m]) => gives a list of points with an angular deflection of 'a' + and a curvature deflection of 'c'. Optionally a minimum number of points + can be set which by default is set to 2. + + Optionally you can set the keywords 'First' and 'Last' to define a sub-range of the parameter range + of the edge. + + If no keyword is given then it depends on whether the argument is an int or float. + If it's an int then the behaviour is as if using the keyword 'Number', if it's float + then the behaviour is as if using the keyword 'Distance'. + + Example: + + import Part + e=Part.makeCircle(5) + p=e.discretize(Number=50,First=3.14) + s=Part.Compound([Part.Vertex(i) for i in p]) + Part.show(s) + + p=e.discretize(Angular=0.09,Curvature=0.01,Last=3.14,Minimum=100) + s=Part.Compound([Part.Vertex(i) for i in p]) + Part.show(s) + """ + ... + + @constmethod + def countNodes(self) -> int: + """ + Returns the number of nodes of the 3D polygon of the edge. + """ + ... + + @constmethod + def split(self, paramval: float) -> Wire: + """ + Splits the edge at the given parameter values and builds a wire out of it + split(paramval) -> Wire + -- + Args: + paramval (float or list_of_floats): The parameter values along the Edge at which to + split it e.g: + + edge = Part.makeCircle(1, FreeCAD.Vector(0,0,0), FreeCAD.Vector(0,0,1), 0, 90) + wire = edge.split([0.5, 1.0]) + + Returns: + Wire: wire made up of two Edges + """ + ... + + @constmethod + def isSeam(self, Face: object) -> bool: + """ + Checks whether the edge is a seam edge. + isSeam(Face) + """ + ... + + @constmethod + def curveOnSurface(self, idx: int) -> Tuple[object, object, object, float, float]: + """ + Returns the 2D curve, the surface, the placement and the parameter range of index idx. + curveOnSurface(idx) -> None or tuple + -- + Returns None if index idx is out of range. + Returns a 5-items tuple of a curve, a surface, a placement, first parameter and last parameter. + """ + ... diff --git a/src/Mod/Part/App/TopoShapeEdgePy.xml b/src/Mod/Part/App/TopoShapeEdgePy.xml deleted file mode 100644 index 3d46d83d76..0000000000 --- a/src/Mod/Part/App/TopoShapeEdgePy.xml +++ /dev/null @@ -1,543 +0,0 @@ - - - - - - TopoShapeEdge is the OpenCasCade topological edge wrapper - - - - Get the value of the primary parameter at the given distance along the cartesian length of the edge. -getParameterByLength(pos, [tolerance = 1e-7]) -> Float --- -Args: - pos (float or int): The distance along the length of the edge at which to - determine the primary parameter value. See help for the FirstParameter or - LastParameter properties for more information on the primary parameter. - If the given value is positive, the distance from edge start is used. - If the given value is negative, the distance from edge end is used. - tol (float): Computing tolerance. Optional, defaults to 1e-7. - -Returns: - paramval (float): the value of the primary parameter defining the edge at the - given position along its cartesian length. - - - - - - Get the tangent direction at the given primary parameter value along the Edge if it is defined -tangentAt(paramval) -> Vector --- -Args: - paramval (float or int): The parameter value along the Edge at which to - determine the tangent direction e.g: - - x = Part.makeCircle(1, FreeCAD.Vector(0,0,0), FreeCAD.Vector(0,0,1), 0, 90) - y = x.tangentAt(x.FirstParameter + 0.5 * (x.LastParameter - x.FirstParameter)) - - y is the Vector (-0.7071067811865475, 0.7071067811865476, 0.0) - - Values with magnitude greater than the Edge length return - values of the tangent on the curve extrapolated beyond its - length. This may not be valid for all Edges. Negative values - similarly return a tangent on the curve extrapolated backwards - (before the start point of the Edge). For example, using the - same shape as above: - - >>> x.tangentAt(x.FirstParameter + 3.5*(x.LastParameter - x.FirstParameter)) - Vector (0.7071067811865477, 0.7071067811865474, 0.0) - - Which gives the same result as - - >>> x.tangentAt(x.FirstParameter -0.5*(x.LastParameter - x.FirstParameter)) - Vector (0.7071067811865475, 0.7071067811865476, 0.0) - - Since it is a circle - -Returns: - Vector: representing the tangent to the Edge at the given - location along its length (or extrapolated length) - - - - - - Get the value of the cartesian parameter value at the given parameter value along the Edge -valueAt(paramval) -> Vector --- -Args: - paramval (float or int): The parameter value along the Edge at which to - determine the value in terms of the main parameter defining - the edge, what the parameter value is depends on the type of - edge. See e.g: - - For a circle value - - x = Part.makeCircle(1, FreeCAD.Vector(0,0,0), FreeCAD.Vector(0,0,1), 0, 90) - y = x.valueAt(x.FirstParameter + 0.5 * (x.LastParameter - x.FirstParameter)) - - y is theVector (0.7071067811865476, 0.7071067811865475, 0.0) - - Values with magnitude greater than the Edge length return - values on the curve extrapolated beyond its length. This may - not be valid for all Edges. Negative values similarly return - a parameter value on the curve extrapolated backwards (before the - start point of the Edge). For example, using the same shape - as above: - - >>> x.valueAt(x.FirstParameter + 3.5*(x.LastParameter - x.FirstParameter)) - Vector (0.7071067811865474, -0.7071067811865477, 0.0) - - Which gives the same result as - - >>> x.valueAt(x.FirstParameter -0.5*(x.LastParameter - x.FirstParameter)) - Vector (0.7071067811865476, -0.7071067811865475, 0.0) - - Since it is a circle - -Returns: - Vector: representing the cartesian location on the Edge at the given - distance along its length (or extrapolated length) - - - - - - Get the list of parameters of the tessellation of an edge. -parameters([face]) -> list --- -If the edge is part of a face then this face is required as argument. -An exception is raised if the edge has no polygon. - - - - - - Get the parameter at the given vertex if lying on the edge -parameterAt(Vertex) -> Float - - - - - - Get the normal direction at the given parameter value along the Edge if it is defined -normalAt(paramval) -> Vector --- -Args: - paramval (float or int): The parameter value along the Edge at which to - determine the normal direction e.g: - - x = Part.makeCircle(1, FreeCAD.Vector(0,0,0), FreeCAD.Vector(0,0,1), 0, 90) - y = x.normalAt(x.FirstParameter + 0.5 * (x.LastParameter - x.FirstParameter)) - - y is the Vector (-0.7071067811865476, -0.7071067811865475, 0.0) - - Values with magnitude greater than the Edge length return - values of the normal on the curve extrapolated beyond its - length. This may not be valid for all Edges. Negative values - similarly return a normal on the curve extrapolated backwards - (before the start point of the Edge). For example, using the - same shape as above: - - >>> x.normalAt(x.FirstParameter + 3.5*(x.LastParameter - x.FirstParameter)) - Vector (-0.7071067811865474, 0.7071067811865477, 0.0) - - Which gives the same result as - - >>> x.normalAt(x.FirstParameter -0.5*(x.LastParameter - x.FirstParameter)) - Vector (-0.7071067811865476, 0.7071067811865475, 0.0) - - Since it is a circle - -Returns: - Vector: representing the normal to the Edge at the given - location along its length (or extrapolated length) - - - - - - Get the first derivative at the given parameter value along the Edge if it is defined -derivative1At(paramval) -> Vector --- -Args: - paramval (float or int): The parameter value along the Edge at which to - determine the first derivative e.g: - - x = Part.makeCircle(1, FreeCAD.Vector(0,0,0), FreeCAD.Vector(0,0,1), 0, 90) - y = x.derivative1At(x.FirstParameter + 0.5 * (x.LastParameter - x.FirstParameter)) - - y is the Vector (-0.7071067811865475, 0.7071067811865476, 0.0) - - Values with magnitude greater than the Edge length return - values of the first derivative on the curve extrapolated - beyond its length. This may not be valid for all Edges. - Negative values similarly return a first derivative on the - curve extrapolated backwards (before the start point of the - Edge). For example, using the same shape as above: - - >>> x.derivative1At(x.FirstParameter + 3.5*(x.LastParameter - x.FirstParameter)) - Vector (0.7071067811865477, 0.7071067811865474, 0.0) - - Which gives the same result as - - >>> x.derivative1At(x.FirstParameter -0.5*(x.LastParameter - x.FirstParameter)) - Vector (0.7071067811865475, 0.7071067811865476, 0.0) - - Since it is a circle - -Returns: - Vector: representing the first derivative to the Edge at the - given location along its length (or extrapolated length) - - - - - - Get the second derivative at the given parameter value along the Edge if it is defined -derivative2At(paramval) -> Vector --- -Args: - paramval (float or int): The parameter value along the Edge at which to - determine the second derivative e.g: - - x = Part.makeCircle(1, FreeCAD.Vector(0,0,0), FreeCAD.Vector(0,0,1), 0, 90) - y = x.derivative2At(x.FirstParameter + 0.5 * (x.LastParameter - x.FirstParameter)) - - y is the Vector (-0.7071067811865476, -0.7071067811865475, 0.0) - - Values with magnitude greater than the Edge length return - values of the second derivative on the curve extrapolated - beyond its length. This may not be valid for all Edges. - Negative values similarly return a second derivative on the - curve extrapolated backwards (before the start point of the - Edge). For example, using the same shape as above: - - >>> x.derivative2At(x.FirstParameter + 3.5*(x.LastParameter - x.FirstParameter)) - Vector (-0.7071067811865474, 0.7071067811865477, 0.0) - - Which gives the same result as - - >>> x.derivative2At(x.FirstParameter -0.5*(x.LastParameter - x.FirstParameter)) - Vector (-0.7071067811865476, 0.7071067811865475, 0.0) - - Since it is a circle - -Returns: - Vector: representing the second derivative to the Edge at the - given location along its length (or extrapolated length) - - - - - - Get the third derivative at the given parameter value along the Edge if it is defined -derivative3At(paramval) -> Vector --- -Args: - paramval (float or int): The parameter value along the Edge at which to - determine the third derivative e.g: - - x = Part.makeCircle(1, FreeCAD.Vector(0,0,0), FreeCAD.Vector(0,0,1), 0, 90) - y = x.derivative3At(x.FirstParameter + 0.5 * (x.LastParameter - x.FirstParameter)) - - y is the Vector (0.7071067811865475, -0.7071067811865476, -0.0) - - Values with magnitude greater than the Edge length return - values of the third derivative on the curve extrapolated - beyond its length. This may not be valid for all Edges. - Negative values similarly return a third derivative on the - curve extrapolated backwards (before the start point of the - Edge). For example, using the same shape as above: - - >>> x.derivative3At(x.FirstParameter + 3.5*(x.LastParameter - x.FirstParameter)) - Vector (-0.7071067811865477, -0.7071067811865474, 0.0) - - Which gives the same result as - - >>> x.derivative3At(x.FirstParameter -0.5*(x.LastParameter - x.FirstParameter)) - Vector (-0.7071067811865475, -0.7071067811865476, 0.0) - - Since it is a circle - -Returns: - Vector: representing the third derivative to the Edge at the - given location along its length (or extrapolated length) - - - - - - Get the curvature at the given parameter [First|Last] if defined -curvatureAt(paramval) -> Float - - - - - - Get the center of curvature at the given parameter [First|Last] if defined -centerOfCurvatureAt(paramval) -> Vector - - - - - - Returns the Vertex of orientation FORWARD in this edge. -firstVertex([Orientation=False]) -> Vertex --- -If there is none a Null shape is returned. -Orientation = True : taking into account the edge orientation - - - - - - Returns the Vertex of orientation REVERSED in this edge. -lastVertex([Orientation=False]) -> Vertex --- -If there is none a Null shape is returned. -Orientation = True : taking into account the edge orientation - - - - - - Discretizes the edge and returns a list of points. -discretize(kwargs) -> list --- -The function accepts keywords as argument: -discretize(Number=n) => gives a list of 'n' equidistant points -discretize(QuasiNumber=n) => gives a list of 'n' quasi equidistant points (is faster than the method above) -discretize(Distance=d) => gives a list of equidistant points with distance 'd' -discretize(Deflection=d) => gives a list of points with a maximum deflection 'd' to the edge -discretize(QuasiDeflection=d) => gives a list of points with a maximum deflection 'd' to the edge (faster) -discretize(Angular=a,Curvature=c,[Minimum=m]) => gives a list of points with an angular deflection of 'a' - and a curvature deflection of 'c'. Optionally a minimum number of points - can be set which by default is set to 2. - -Optionally you can set the keywords 'First' and 'Last' to define a sub-range of the parameter range -of the edge. - -If no keyword is given then it depends on whether the argument is an int or float. -If it's an int then the behaviour is as if using the keyword 'Number', if it's float -then the behaviour is as if using the keyword 'Distance'. - -Example: - -import Part -e=Part.makeCircle(5) -p=e.discretize(Number=50,First=3.14) -s=Part.Compound([Part.Vertex(i) for i in p]) -Part.show(s) - -p=e.discretize(Angular=0.09,Curvature=0.01,Last=3.14,Minimum=100) -s=Part.Compound([Part.Vertex(i) for i in p]) -Part.show(s) - - - - - - Returns the number of nodes of the 3D polygon of the edge. - - - - - Splits the edge at the given parameter values and builds a wire out of it -split(paramval) -> Wire --- -Args: - paramval (float or list_of_floats): The parameter values along the Edge at which to - split it e.g: - - edge = Part.makeCircle(1, FreeCAD.Vector(0,0,0), FreeCAD.Vector(0,0,1), 0, 90) - wire = edge.split([0.5, 1.0]) - -Returns: - Wire: wire made up of two Edges - - - - - - Checks whether the edge is a seam edge. -isSeam(Face) - - - - - - Returns the 2D curve, the surface, the placement and the parameter range of index idx. -curveOnSurface(idx) -> None or tuple --- -Returns None if index idx is out of range. -Returns a 5-items tuple of a curve, a surface, a placement, first parameter and last parameter. - - - - - - Set or get the tolerance of the vertex - - - - - - Returns the cartesian length of the curve - - - - - - -Returns a 2 tuple with the range of the primary parameter -defining the curve. This is the same as would be returned by -the FirstParameter and LastParameter properties, i.e. - -(LastParameter,FirstParameter) - -What the parameter is depends on what type of edge it is. For a -Line the parameter is simply its cartesian length. Some other -examples are shown below: - -Type Parameter ---------------------------------------------------------------- -Circle Angle swept by circle (or arc) in radians -BezierCurve Unitless number in the range 0.0 to 1.0 -Helix Angle swept by helical turns in radians - - - - - - - -Returns the start value of the range of the primary parameter -defining the curve. - -What the parameter is depends on what type of edge it is. For a -Line the parameter is simply its cartesian length. Some other -examples are shown below: - -Type Parameter ------------------------------------------------------------ -Circle Angle swept by circle (or arc) in radians -BezierCurve Unitless number in the range 0.0 to 1.0 -Helix Angle swept by helical turns in radians - - - - - - - -Returns the end value of the range of the primary parameter -defining the curve. - -What the parameter is depends on what type of edge it is. For a -Line the parameter is simply its cartesian length. Some other -examples are shown below: - -Type Parameter ------------------------------------------------------------ -Circle Angle swept by circle (or arc) in radians -BezierCurve Unitless number in the range 0.0 to 1.0 -Helix Angle swept by helical turns in radians - - - - - - - Returns the 3D curve of the edge - - - - - - Returns true if the edge is closed - - - - - - Returns true if the edge is degenerated - - - - - - Returns the mass of the current system. - - - - - - Returns the center of mass of the current system. -If the gravitational field is uniform, it is the center of gravity. -The coordinates returned for the center of mass are expressed in the -absolute Cartesian coordinate system. - - - - - - Returns the matrix of inertia. It is a symmetrical matrix. -The coefficients of the matrix are the quadratic moments of -inertia. - - | Ixx Ixy Ixz 0 | - | Ixy Iyy Iyz 0 | - | Ixz Iyz Izz 0 | - | 0 0 0 1 | - -The moments of inertia are denoted by Ixx, Iyy, Izz. -The products of inertia are denoted by Ixy, Ixz, Iyz. -The matrix of inertia is returned in the central coordinate -system (G, Gx, Gy, Gz) where G is the centre of mass of the -system and Gx, Gy, Gz the directions parallel to the X(1,0,0) -Y(0,1,0) Z(0,0,1) directions of the absolute cartesian -coordinate system. - - - - - - Returns Ix, Iy, Iz, the static moments of inertia of the -current system; i.e. the moments of inertia about the -three axes of the Cartesian coordinate system. - - - - - - Computes the principal properties of inertia of the current system. -There is always a set of axes for which the products -of inertia of a geometric system are equal to 0; i.e. the -matrix of inertia of the system is diagonal. These axes -are the principal axes of inertia. Their origin is -coincident with the center of mass of the system. The -associated moments are called the principal moments of inertia. -This function computes the eigen values and the -eigen vectors of the matrix of inertia of the system. - - - - - - Returns the continuity - - - - - diff --git a/src/Mod/Part/App/TopoShapeEdgePyImp.cpp b/src/Mod/Part/App/TopoShapeEdgePyImp.cpp index 6b39d05fd8..b3257c90fd 100644 --- a/src/Mod/Part/App/TopoShapeEdgePyImp.cpp +++ b/src/Mod/Part/App/TopoShapeEdgePyImp.cpp @@ -126,7 +126,8 @@ int TopoShapeEdgePy::PyInit(PyObject* args, PyObject* /*kwd*/) PyErr_Clear(); PyObject *pcObj, *pcObj2; - double first=DBL_MAX, last=DBL_MAX; + double first = std::numeric_limits::max(); + double last = std::numeric_limits::max(); if (PyArg_ParseTuple(args, "O!|dd", &(Part::GeometryPy::Type), &pcObj, &first, &last)) { Geometry* geom = static_cast(pcObj)->getGeometryPtr(); Handle(Geom_Curve) curve = Handle(Geom_Curve)::DownCast(geom->handle()); @@ -135,9 +136,9 @@ int TopoShapeEdgePy::PyInit(PyObject* args, PyObject* /*kwd*/) return -1; } - if (first==DBL_MAX) + if (first == std::numeric_limits::max()) first = curve->FirstParameter(); - if (last==DBL_MAX) + if (last == std::numeric_limits::max()) last = curve->LastParameter(); try { diff --git a/src/Mod/Part/App/TopoShapeExpansion.cpp b/src/Mod/Part/App/TopoShapeExpansion.cpp index 5118bcf94b..39fd232357 100644 --- a/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -1611,6 +1611,7 @@ TopoShape& TopoShape::makeShapeWithElementMap(const TopoDS_Shape& shape, bool delayed = false; while (true) { + constexpr int intMin = std::numeric_limits::min(); // Construct the names for modification/generation info collected in // the previous step @@ -1632,7 +1633,7 @@ TopoShape& TopoShape::makeShapeWithElementMap(const TopoDS_Shape& shape, const auto& first_key = names.begin()->first; auto& first_info = names.begin()->second; - if (!delayed && first_key.shapetype >= 3 && first_info.index > INT_MIN + 1) { + if (!delayed && first_key.shapetype >= 3 && first_info.index > intMin + 1) { // This name is mapped from high level (shell, solid, etc.) // Delay till next round. // @@ -1680,10 +1681,10 @@ TopoShape& TopoShape::makeShapeWithElementMap(const TopoDS_Shape& shape, // 'K' marks the additional source shape of this // generate (or modified) shape. ss2 << elementMapPrefix() << 'K'; - if (other_info.index == INT_MIN) { + if (other_info.index == intMin) { ss2 << '0'; } - else if (other_info.index == INT_MIN + 1) { + else if (other_info.index == intMin + 1) { ss2 << "00"; } else { @@ -1740,10 +1741,10 @@ TopoShape& TopoShape::makeShapeWithElementMap(const TopoDS_Shape& shape, else { ss << modgenPostfix(); } - if (first_info.index == INT_MIN) { + if (first_info.index == intMin) { ss << '0'; } - else if (first_info.index == INT_MIN + 1) { + else if (first_info.index == intMin + 1) { ss << "00"; } else if (abs(first_info.index) > 1) { diff --git a/src/Mod/Part/App/TopoShapeFace.pyi b/src/Mod/Part/App/TopoShapeFace.pyi new file mode 100644 index 0000000000..a97c75a531 --- /dev/null +++ b/src/Mod/Part/App/TopoShapeFace.pyi @@ -0,0 +1,227 @@ +from Base.Metadata import export, constmethod +from Base.Vector import Vector +from TopoShape import TopoShape +from typing import Final, Tuple, Dict, Optional, List + + +@export( + Twin="TopoShape", + TwinPointer="TopoShape", + Include="Mod/Part/App/TopoShape.h", + FatherInclude="Mod/Part/App/TopoShapePy.h", + Constructor=True, +) +class TopoShapeFace(TopoShape): + """ + TopoShapeFace is the OpenCasCade topological face wrapper + + Author: Juergen Riegel (Juergen.Riegel@web.de) + Licence: LGPL + """ + + Tolerance: float = ... + """Set or get the tolerance of the vertex""" + + ParameterRange: Final[Tuple] = ... + """Returns a 4 tuple with the parameter range""" + + Surface: Final[object] = ... + """Returns the geometric surface of the face""" + + Wire: Final[object] = ... + """ + The outer wire of this face + deprecated -- please use OuterWire + """ + + OuterWire: Final[object] = ... + """The outer wire of this face""" + + Mass: Final[object] = ... + """Returns the mass of the current system.""" + + CenterOfMass: Final[object] = ... + """ + Returns the center of mass of the current system. + If the gravitational field is uniform, it is the center of gravity. + The coordinates returned for the center of mass are expressed in the + absolute Cartesian coordinate system. + """ + + MatrixOfInertia: Final[object] = ... + """ + Returns the matrix of inertia. It is a symmetrical matrix. + The coefficients of the matrix are the quadratic moments of + inertia. + + | Ixx Ixy Ixz 0 | + | Ixy Iyy Iyz 0 | + | Ixz Iyz Izz 0 | + | 0 0 0 1 | + + The moments of inertia are denoted by Ixx, Iyy, Izz. + The products of inertia are denoted by Ixy, Ixz, Iyz. + The matrix of inertia is returned in the central coordinate + system (G, Gx, Gy, Gz) where G is the centre of mass of the + system and Gx, Gy, Gz the directions parallel to the X(1,0,0) + Y(0,1,0) Z(0,0,1) directions of the absolute cartesian + coordinate system. + """ + + StaticMoments: Final[object] = ... + """ + Returns Ix, Iy, Iz, the static moments of inertia of the + current system; i.e. the moments of inertia about the + three axes of the Cartesian coordinate system. + """ + + PrincipalProperties: Final[Dict] = ... + """ + Computes the principal properties of inertia of the current system. + There is always a set of axes for which the products + of inertia of a geometric system are equal to 0; i.e. the + matrix of inertia of the system is diagonal. These axes + are the principal axes of inertia. Their origin is + coincident with the center of mass of the system. The + associated moments are called the principal moments of inertia. + This function computes the eigen values and the + eigen vectors of the matrix of inertia of the system. + """ + + def addWire(self, wire: object) -> None: + """ + Adds a wire to the face. + addWire(wire) + """ + ... + + @constmethod + def makeOffset(self, dist: float) -> object: + """ + Offset the face by a given amount. + makeOffset(dist) -> Face + -- + Returns Compound of Wires. Deprecated - use makeOffset2D instead. + """ + ... + + @constmethod + def makeEvolved(self, *, Profile: TopoShape, Join: int, AxeProf: bool, Solid: bool, + ProfOnSpine: bool, Tolerance: float) -> TopoShape: + """ + Profile along the spine + """ + ... + + @constmethod + def getUVNodes(self) -> List[Tuple[float, float]]: + """ + Get the list of (u,v) nodes of the tessellation + getUVNodes() -> list + -- + An exception is raised if the face is not triangulated. + """ + ... + + @constmethod + def tangentAt(self, u: float, v: float) -> Vector: + """ + Get the tangent in u and v isoparametric at the given point if defined + tangentAt(u,v) -> Vector + """ + ... + + @constmethod + def valueAt(self, u: float, v: float) -> Vector: + """ + Get the point at the given parameter [0|Length] if defined + valueAt(u,v) -> Vector + """ + ... + + @constmethod + def normalAt(self, pos: float) -> Vector: + """ + Get the normal vector at the given parameter [0|Length] if defined + normalAt(pos) -> Vector + """ + ... + + @constmethod + def derivative1At(self, u: float, v: float) -> Tuple[Vector, Vector]: + """ + Get the first derivative at the given parameter [0|Length] if defined + derivative1At(u,v) -> (vectorU,vectorV) + """ + ... + + @constmethod + def derivative2At(self, u: float, v: float) -> Tuple[Vector, Vector]: + """ + Vector = d2At(pos) - Get the second derivative at the given parameter [0|Length] if defined + derivative2At(u,v) -> (vectorU,vectorV) + """ + ... + + @constmethod + def curvatureAt(self, u: float, v: float) -> float: + """ + Get the curvature at the given parameter [0|Length] if defined + curvatureAt(u,v) -> Float + """ + ... + + @constmethod + def isPartOfDomain(self, u: float, v: float) -> bool: + """ + Check if a given (u,v) pair is inside the domain of a face + isPartOfDomain(u,v) -> bool + """ + ... + + @constmethod + def makeHalfSpace(self, pos: object) -> object: + """ + Make a half-space solid by this face and a reference point. + makeHalfSpace(pos) -> Shape + """ + ... + + def validate(self) -> None: + """ + Validate the face. + validate() + """ + ... + + @constmethod + def countNodes(self) -> int: + """ + Returns the number of nodes of the triangulation. + """ + ... + + @constmethod + def countTriangles(self) -> int: + """ + Returns the number of triangles of the triangulation. + """ + ... + + @constmethod + def curveOnSurface(self, Edge: object) -> Optional[Tuple[object, float, float]]: + """ + Returns the curve associated to the edge in the parametric space of the face. + curveOnSurface(Edge) -> (curve, min, max) or None + -- + If this curve exists then a tuple of curve and parameter range is returned. + Returns None if this curve does not exist. + """ + ... + + def cutHoles(self, list_of_wires: List[object]) -> None: + """ + Cut holes in the face. + cutHoles(list_of_wires) + """ + ... diff --git a/src/Mod/Part/App/TopoShapeFacePy.xml b/src/Mod/Part/App/TopoShapeFacePy.xml deleted file mode 100644 index 146ddec275..0000000000 --- a/src/Mod/Part/App/TopoShapeFacePy.xml +++ /dev/null @@ -1,227 +0,0 @@ - - - - - - TopoShapeFace is the OpenCasCade topological face wrapper - - - - Adds a wire to the face. -addWire(wire) - - - - - - Offset the face by a given amount. -makeOffset(dist) -> Face --- -Returns Compound of Wires. Deprecated - use makeOffset2D instead. - - - - - - Profile along the spine - - - - - Get the list of (u,v) nodes of the tessellation -getUVNodes() -> list --- -An exception is raised if the face is not triangulated. - - - - - - Get the tangent in u and v isoparametric at the given point if defined -tangentAt(u,v) -> Vector - - - - - - Get the point at the given parameter [0|Length] if defined -valueAt(u,v) -> Vector - - - - - - Get the normal vector at the given parameter [0|Length] if defined -normalAt(pos) -> Vector - - - - - - Get the first derivative at the given parameter [0|Length] if defined -derivative1At(u,v) -> (vectorU,vectorV) - - - - - - Vector = d2At(pos) - Get the second derivative at the given parameter [0|Length] if defined -derivative2At(u,v) -> (vectorU,vectorV) - - - - - - Get the curvature at the given parameter [0|Length] if defined -curvatureAt(u,v) -> Float - - - - - - Check if a given (u,v) pair is inside the domain of a face -isPartOfDomain(u,v) -> bool - - - - - - Make a half-space solid by this face and a reference point. -makeHalfSpace(pos) -> Shape - - - - - - Validate the face. -validate() - - - - - - Returns the number of nodes of the triangulation. - - - - - Returns the number of triangles of the triangulation. - - - - - Returns the curve associated to the edge in the parametric space of the face. -curveOnSurface(Edge) -> (curve, min, max) or None --- -If this curve exists then a tuple of curve and parameter range is returned. -Returns None if this curve does not exist. - - - - - - Cut holes in the face. -cutHoles(list_of_wires) - - - - - - Set or get the tolerance of the vertex - - - - - - Returns a 4 tuple with the parameter range - - - - - - Returns the geometric surface of the face - - - - - - The outer wire of this face -deprecated -- please use OuterWire - - - - - - The outer wire of this face - - - - - - Returns the mass of the current system. - - - - - - Returns the center of mass of the current system. -If the gravitational field is uniform, it is the center of gravity. -The coordinates returned for the center of mass are expressed in the -absolute Cartesian coordinate system. - - - - - - Returns the matrix of inertia. It is a symmetrical matrix. -The coefficients of the matrix are the quadratic moments of -inertia. - - | Ixx Ixy Ixz 0 | - | Ixy Iyy Iyz 0 | - | Ixz Iyz Izz 0 | - | 0 0 0 1 | - -The moments of inertia are denoted by Ixx, Iyy, Izz. -The products of inertia are denoted by Ixy, Ixz, Iyz. -The matrix of inertia is returned in the central coordinate -system (G, Gx, Gy, Gz) where G is the centre of mass of the -system and Gx, Gy, Gz the directions parallel to the X(1,0,0) -Y(0,1,0) Z(0,0,1) directions of the absolute cartesian -coordinate system. - - - - - - Returns Ix, Iy, Iz, the static moments of inertia of the -current system; i.e. the moments of inertia about the -three axes of the Cartesian coordinate system. - - - - - - Computes the principal properties of inertia of the current system. -There is always a set of axes for which the products -of inertia of a geometric system are equal to 0; i.e. the -matrix of inertia of the system is diagonal. These axes -are the principal axes of inertia. Their origin is -coincident with the center of mass of the system. The -associated moments are called the principal moments of inertia. -This function computes the eigen values and the -eigen vectors of the matrix of inertia of the system. - - - - - diff --git a/src/Mod/Part/App/TopoShapeMapper.h b/src/Mod/Part/App/TopoShapeMapper.h index 5ec165efab..0febe93d47 100644 --- a/src/Mod/Part/App/TopoShapeMapper.h +++ b/src/Mod/Part/App/TopoShapeMapper.h @@ -46,7 +46,7 @@ struct ShapeHasher #if OCC_VERSION_HEX >= 0x070800 return std::hash {}(s.getShape()); #else - return s.getShape().HashCode(INT_MAX); + return s.getShape().HashCode(std::numeric_limits::max()); #endif } inline size_t operator()(const TopoDS_Shape& s) const @@ -54,7 +54,7 @@ struct ShapeHasher #if OCC_VERSION_HEX >= 0x070800 return std::hash {}(s); #else - return s.HashCode(INT_MAX); + return s.HashCode(std::numeric_limits::max()); #endif } inline bool operator()(const TopoShape& a, const TopoShape& b) const @@ -78,8 +78,8 @@ struct ShapeHasher size_t res = std::hash {}(s.first.getShape()); hash_combine(res, std::hash {}(s.second.getShape())); #else - size_t res = s.first.getShape().HashCode(INT_MAX); - hash_combine(res, s.second.getShape().HashCode(INT_MAX)); + size_t res = s.first.getShape().HashCode(std::numeric_limits::max()); + hash_combine(res, s.second.getShape().HashCode(std::numeric_limits::max())); #endif return res; } @@ -89,8 +89,8 @@ struct ShapeHasher size_t res = std::hash {}(s.first); hash_combine(res, std::hash {}(s.second)); #else - size_t res = s.first.HashCode(INT_MAX); - hash_combine(res, s.second.HashCode(INT_MAX)); + size_t res = s.first.HashCode(std::numeric_limits::max()); + hash_combine(res, s.second.HashCode(std::numeric_limits::max())); #endif return res; } diff --git a/src/Mod/Part/App/TopoShapePy.xml b/src/Mod/Part/App/TopoShapePy.xml deleted file mode 100644 index ef104074b5..0000000000 --- a/src/Mod/Part/App/TopoShapePy.xml +++ /dev/null @@ -1,1094 +0,0 @@ - - - - - - TopoShape is the OpenCasCade topological shape wrapper. -Sub-elements such as vertices, edges or faces are accessible as: -* Vertex#, where # is in range(1, number of vertices) -* Edge#, where # is in range(1, number of edges) -* Face#, where # is in range(1, number of faces) - - - - Serialize the content of this shape to a string in BREP format. - - - - - Deserialize the content of this shape from a string in BREP format. - - - - - Read in an IGES, STEP or BREP file. -read(filename) - - - - - - Write the mesh in OpenInventor format to a string. -writeInventor() -> string - - - - - - Export the content of this shape to an IGES file. -exportIges(filename) - - - - - - Export the content of this shape to an STEP file. -exportStep(filename) - - - - - - Export the content of this shape to an BREP file. -exportBrep(filename) --- -BREP is an OpenCasCade native format. - - - - - - Export the content of this shape in binary format to a file. -exportBinary(filename) - - - - - - Export the content of this shape to a string in BREP format. -exportBrepToString() -> string --- -BREP is an OpenCasCade native format. - - - - - Dump information about the shape to a string. -dumpToString() -> string - - - - - Export the content of this shape to an STL mesh file. -exportStl(filename) - - - - - Load the shape from a file in BREP format. -importBrep(filename) - - - - - Import the content to this shape of a string in BREP format. -importBinary(filename) - - - - - Load the shape from a string that keeps the content in BREP format. -importBrepFromString(string, [displayProgressBar=True]) --- -importBrepFromString(str, False) to not display a progress bar. - - - - - - Extrude the shape along a vector. -extrude(vector) -> Shape --- -Shp2 = Shp1.extrude(App.Vector(0,0,10)) - extrude the shape 10 mm in the +Z direction. - - - - - Revolve the shape around an Axis to a given degree. -revolve(base, direction, angle) --- -Part.revolve(App.Vector(0,0,0),App.Vector(0,0,1),360) - revolves the shape around the Z Axis 360 degree. - -Hints: Sometimes you want to create a rotation body out of a closed edge or wire. -Example: -from FreeCAD import Base -import Part -V=Base.Vector - -e=Part.Ellipse() -s=e.toShape() -r=s.revolve(V(0,0,0),V(0,1,0), 360) -Part.show(r) - -However, you may possibly realize some rendering artifacts or that the mesh -creation seems to hang. This is because this way the surface is created twice. -Since the curve is a full ellipse it is sufficient to do a rotation of 180 degree -only, i.e. r=s.revolve(V(0,0,0),V(0,1,0), 180) - -Now when rendering this object you may still see some artifacts at the poles. Now the -problem seems to be that the meshing algorithm doesn't like to rotate around a point -where there is no vertex. - -The idea to fix this issue is that you create only half of the ellipse so that its shape -representation has vertexes at its start and end point. - -from FreeCAD import Base -import Part -V=Base.Vector - -e=Part.Ellipse() -s=e.toShape(e.LastParameter/4,3*e.LastParameter/4) -r=s.revolve(V(0,0,0),V(0,1,0), 360) -Part.show(r) - - - - - - Checks the shape and report errors in the shape structure. -check([runBopCheck = False]) --- -This is a more detailed check as done in isValid(). -if runBopCheck is True, a BOPCheck analysis is also performed. - - - - - Union of this and a given (list of) topo shape. -fuse(tool) -> Shape - or -fuse((tool1,tool2,...),[tolerance=0.0]) -> Shape --- -Union of this and a given list of topo shapes. - -Supports (OCCT 6.9.0 and above): -- Fuzzy Boolean operations (global tolerance for a Boolean operation) -- Support of multiple arguments for a single Boolean operation -- Parallelization of Boolean Operations algorithm - -Beginning from OCCT 6.8.1 a tolerance value can be specified. - - - - - Union of this and a given list of topo shapes. -multiFuse((tool1,tool2,...),[tolerance=0.0]) -> Shape --- -Supports (OCCT 6.9.0 and above): -- Fuzzy Boolean operations (global tolerance for a Boolean operation) -- Support of multiple arguments for a single Boolean operation -- Parallelization of Boolean Operations algorithm - -Beginning from OCCT 6.8.1 a tolerance value can be specified. -Deprecated: use fuse() instead. - - - - - Union of this and a given topo shape (old algorithm). -oldFuse(tool) -> Shape - - - - - - Intersection of this and a given (list of) topo shape. -common(tool) -> Shape - or -common((tool1,tool2,...),[tolerance=0.0]) -> Shape --- -Supports: -- Fuzzy Boolean operations (global tolerance for a Boolean operation) -- Support of multiple arguments for a single Boolean operation (s1 AND (s2 OR s3)) -- Parallelization of Boolean Operations algorithm - -OCC 6.9.0 or later is required. - - - - - Section of this with a given (list of) topo shape. -section(tool,[approximation=False]) -> Shape - or -section((tool1,tool2,...),[tolerance=0.0, approximation=False]) -> Shape --- -If approximation is True, section edges are approximated to a C1-continuous BSpline curve. - -Supports: -- Fuzzy Boolean operations (global tolerance for a Boolean operation) -- Support of multiple arguments for a single Boolean operation (s1 AND (s2 OR s3)) -- Parallelization of Boolean Operations algorithm - -OCC 6.9.0 or later is required. - - - - - Make slices of this shape. -slices(direction, distancesList) --> Wires - - - - - - Make single slice of this shape. -slice(direction, distance) --> Wires - - - - - Difference of this and a given (list of) topo shape -cut(tool) -> Shape - or -cut((tool1,tool2,...),[tolerance=0.0]) -> Shape --- -Supports: -- Fuzzy Boolean operations (global tolerance for a Boolean operation) -- Support of multiple arguments for a single Boolean operation -- Parallelization of Boolean Operations algorithm - -OCC 6.9.0 or later is required. - - - - - Run general fuse algorithm (GFA) between this and given shapes. -generalFuse(list_of_other_shapes, [fuzzy_value = 0.0]) -> (result, map) --- -list_of_other_shapes: shapes to run the algorithm against (the list is -effectively prepended by 'self'). - -fuzzy_value: extra tolerance to apply when searching for interferences, in -addition to tolerances of the input shapes. - -Returns a tuple of 2: (result, map). - -result is a compound containing all the pieces generated by the algorithm -(e.g., for two spheres, the pieces are three touching solids). Pieces that -touch share elements. - -map is a list of lists of shapes, providing the info on which children of -result came from which argument. The length of list is equal to length of -list_of_other_shapes + 1. First element is a list of pieces that came from -shape of this, and the rest are those that come from corresponding shapes in -list_of_other_shapes. -hint: use isSame method to test shape equality - -Parallelization of Boolean Operations algorithm - -OCC 6.9.0 or later is required. - - - - - - Sew the shape if there is a gap. -sewShape() - - - - - - Return a list of sub-shapes that are direct children of this shape. -childShapes([cumOri=True, cumLoc=True]) -> list --- -* If cumOri is true, the function composes all - sub-shapes with the orientation of this shape. -* If cumLoc is true, the function multiplies all - sub-shapes by the location of this shape, i.e. it applies to - each sub-shape the transformation that is associated with this shape. - - - - - - For a sub-shape of this shape get its ancestors of a type. -ancestorsOfType(shape, shape type) -> list - - - - - - Removes internal wires (also holes) from the shape. -removeInternalWires(minimalArea) -> bool - - - - - - Mirror this shape on a given plane. -mirror(base, norm) -> Shape --- -The plane is given with its base point and its normal direction. - - - - - Apply geometric transformation on this or a copy the shape. -transformGeometry(matrix) -> Shape --- -This method returns a new shape. -The transformation to be applied is defined as a 4x4 matrix. -The underlying geometry of the following shapes may change: -- a curve which supports an edge of the shape, or -- a surface which supports a face of the shape; - -For example, a circle may be transformed into an ellipse when -applying an affinity transformation. It may also happen that -the circle then is represented as a B-spline curve. - -The transformation is applied to: -- all the curves which support edges of the shape, and -- all the surfaces which support faces of the shape. - -Note: If you want to transform a shape without changing the -underlying geometry then use the methods translate or rotate. - - - - - - Apply transformation on a shape without changing the underlying geometry. -transformShape(Matrix, [boolean copy=False, checkScale=False]) -> None --- -If checkScale is True, it will use transformGeometry if non-uniform -scaling is detected. - - - - - Create a new transformed shape -transformed(Matrix,copy=False,checkScale=False,op=None) -> shape - - - - - - Apply the translation to the current location of this shape. -translate(vector) - - - - - - Create a new shape with translation -translated(vector) -> shape - - - - - - Apply the rotation (base, dir, degree) to the current location of this shape -rotate(base, dir, degree) --- -Shp.rotate(App.Vector(0,0,0), App.Vector(0,0,1), 180) - rotate the shape around the Z Axis 180 degrees. - - - - - - Create a new shape with rotation. -rotated(base, dir, degree) -> shape - - - - - - Apply scaling with point and factor to this shape. -scale(factor, [base=App.Vector(0,0,0)]) - - - - - - Create a new shape with scale. -scaled(factor, [base=App.Vector(0,0,0)]) -> shape - - - - - - Make fillet. -makeFillet(radius, edgeList) -> Shape -or -makeFillet(radius1, radius2, edgeList) -> Shape - - - - - - Make chamfer. -makeChamfer(radius, edgeList) -> Shape -or -makeChamfer(radius1, radius2, edgeList) -> Shape - - - - - Hollow a solid according to given thickness and faces. -makeThickness(List of faces, Offset (Float), Tolerance (Float)) -> Shape --- -A hollowed solid is built from an initial solid and a set of faces on this solid, -which are to be removed. The remaining faces of the solid become the walls of -the hollowed solid, their thickness defined at the time of construction. - - - - - Makes an offset shape (3d offsetting). -makeOffsetShape(offset, tolerance, [inter=False, self_inter=False, offsetMode=0, join=0, fill=False]) -> Shape --- -The function supports keyword arguments. - -* offset: distance to expand the shape by. Negative value will shrink the shape. - -* tolerance: precision of approximation. - -* inter: (parameter to OCC routine; not implemented) - -* self_inter: (parameter to OCC routine; not implemented) - -* offsetMode: 0 = skin; 1 = pipe; 2 = recto-verso - -* join: method of offsetting non-tangent joints. 0 = arcs, 1 = tangent, 2 = -intersection - -* fill: if true, offsetting a shell is to yield a solid - -Returns: result of offsetting. - - - - - Makes an offset shape (2d offsetting). -makeOffset2D(offset, [join=0, fill=False, openResult=False, intersection=False]) -> Shape --- -The function supports keyword arguments. -Input shape (self) can be edge, wire, face, or a compound of those. - -* offset: distance to expand the shape by. Negative value will shrink the shape. - -* join: method of offsetting non-tangent joints. 0 = arcs, 1 = tangent, 2 = intersection - -* fill: if true, the output is a face filling the space covered by offset. If -false, the output is a wire. - -* openResult: affects the way open wires are processed. If False, an open wire -is made. If True, a closed wire is made from a double-sided offset, with rounds -around open vertices. - -* intersection: affects the way compounds are processed. If False, all children -are offset independently. If True, and children are edges/wires, the children -are offset in a collective manner. If compounding is nested, collectiveness -does not spread across compounds (only direct children of a compound are taken -collectively). - -Returns: result of offsetting (wire or face or compound of those). Compounding -structure follows that of source shape. - - - - - Profile along the spine - - - - - Make wire(s) using the edges of this shape -makeWires([op=None]) --- -The function will sort any edges inside the current shape, and connect them -into wire. If more than one wire is found, then it will make a compound out of -all found wires. - -This function is element mapping aware. If the input shape has non-zero Tag, -it will map any edge and vertex element name inside the input shape into the -itself. - -op: an optional string to be appended when auto generates element mapping. - - - - - - Reverses the orientation of this shape. -reverse() - - - - - - Reverses the orientation of a copy of this shape. -reversed() -> Shape - - - - - - Computes the complement of the orientation of this shape, -i.e. reverses the interior/exterior status of boundaries of this shape. -complement() - - - - - - Destroys the reference to the underlying shape stored in this shape. -As a result, this shape becomes null. -nullify() - - - - - - Checks if the shape is closed. -isClosed() -> bool --- -If the shape is a shell it returns True if it has no free boundaries (edges). -If the shape is a wire it returns True if it has no free ends (vertices). -(Internal and External sub-shapes are ignored in these checks) -If the shape is an edge it returns True if its vertices are the same. - - - - - - Checks if both shapes share the same geometry. -Placement and orientation may differ. -isPartner(shape) -> bool - - - - - - Checks if both shapes share the same geometry -and placement. Orientation may differ. -isSame(shape) -> bool - - - - - - Checks if both shapes are equal. -This means geometry, placement and orientation are equal. -isEqual(shape) -> bool - - - - - - Checks if the shape is null. -isNull() -> bool - - - - - Checks if the shape is valid, i.e. neither null, nor empty nor corrupted. -isValid() -> bool - - - - - - Checks if this shape is coplanar with the given shape. -isCoplanar(shape,tol=None) -> bool - - - - - - Checks if this shape has an infinite expansion. -isInfinite() -> bool - - - - - - Returns a plane if the shape is planar -findPlane(tol=None) -> Shape - - - - - - Tries to fix a broken shape. -fix(working precision, minimum precision, maximum precision) -> bool --- -True is returned if the operation succeeded, False otherwise. - - - - - - This value is computed from the value of the underlying shape reference and the location. -hashCode() -> int --- -Orientation is not taken into account. - - - - - Tessellate the shape and return a list of vertices and face indices -tessellate() -> (vertex,facets) - - - - - - Project a list of shapes on this shape -project(shapeList) -> Shape - - - - - - Parallel projection of an edge or wire on this shape -makeParallelProjection(shape, dir) -> Shape - - - - - - Perspective projection of an edge or wire on this shape -makePerspectiveProjection(shape, pnt) -> Shape - - - - - - Build projection or reflect lines of a shape according to a view direction. -reflectLines(ViewDir, [ViewPos, UpDir, EdgeType, Visible, OnShape]) -> Shape (Compound of edges) --- -This algorithm computes the projection of the shape in the ViewDir direction. -If OnShape is False(default), the returned edges are flat on the XY plane defined by -ViewPos(origin) and UpDir(up direction). -If OnShape is True, the returned edges are the corresponding 3D reflect lines located on the shape. -EdgeType is a string defining the type of result edges : -- IsoLine : isoparametric line -- OutLine : outline (silhouette) edge -- Rg1Line : smooth edge of G1-continuity between two surfaces -- RgNLine : sewn edge of CN-continuity on one surface -- Sharp : sharp edge (of C0-continuity) -If Visible is True (default), only visible edges are returned. -If Visible is False, only invisible edges are returned. - - - - - - Make a compound shape out of mesh data. -makeShapeFromMesh((vertex,facets),tolerance) -> Shape --- -Note: This should be used for rather small meshes only. - - - - - Conversion of the complete geometry of a shape into NURBS geometry. -toNurbs() -> Shape --- -For example, all curves supporting edges of the basis shape are converted -into B-spline curves, and all surfaces supporting its faces are converted -into B-spline surfaces. - - - - - Create a copy of this shape -copy(copyGeom=True, copyMesh=False) -> Shape --- -If copyMesh is True, triangulation contained in original shape will be -copied along with geometry. -If copyGeom is False, only topological objects will be copied, while -geometry and triangulation will be shared with original shape. - - - - - - This creates a cleaned copy of the shape with the triangulation removed. -clean() --- -This can be useful to reduce file size when exporting as a BREP file. -Warning: Use the cleaned shape with care because certain algorithms may work incorrectly -if the shape has no internal triangulation any more. - - - - - - Replace a sub-shape with a new shape and return a new shape. -replaceShape(tupleList) -> Shape --- -The parameter is in the form list of tuples with the two shapes. - - - - - Remove a sub-shape and return a new shape. -removeShape(shapeList) -> Shape --- -The parameter is a list of shapes. - - - - - Remove a feature defined by supplied faces and return a new shape. -defeaturing(shapeList) -> Shape --- -The parameter is a list of faces. - - - - - Checks whether a point is inside or outside the shape. -isInside(point, tolerance, checkFace) => Boolean --- -checkFace indicates if the point lying directly on a face is considered to be inside or not - - - - - - Removes redundant edges from the B-REP model -removeSplitter() -> Shape - - - - - - Returns two lists of Face indexes for the Faces involved in the intersection. -proximity(shape,[tolerance]) -> (selfFaces, shapeFaces) - - - - - - Find the minimum distance to another shape. -distToShape(shape, tol=1e-7) -> (dist, vectors, infos) --- -dist is the minimum distance, in mm (float value). - -vectors is a list of pairs of App.Vector. Each pair corresponds to solution. -Example: [(App.Vector(2.0, -1.0, 2.0), App.Vector(2.0, 0.0, 2.0)), -(App.Vector(2.0, -1.0, 2.0), App.Vector(2.0, -1.0, 3.0))] -First vector is a point on self, second vector is a point on s. - -infos contains additional info on the solutions. It is a list of tuples: -(topo1, index1, params1, topo2, index2, params2) - - topo1, topo2 are strings identifying type of BREP element: 'Vertex', - 'Edge', or 'Face'. - - index1, index2 are indexes of the elements (zero-based). - - params1, params2 are parameters of internal space of the elements. For - vertices, params is None. For edges, params is one float, u. For faces, - params is a tuple (u,v). - - - - - -Returns a SubElement -getElement(elementName, [silent = False]) -> Face | Edge | Vertex -elementName: SubElement name - i.e. 'Edge1', 'Face3' etc. - Accepts TNP mitigation mapped names as well -silent: True to suppress the exception throw if the shape isn't found. - - - - - - Returns the count of a type of element -countElement(type) -> int - - - - - - -mapSubElement(shape|[shape...], op='') - maps the sub element of other shape - -shape: other shape or sequence of shapes to map the sub-elements -op: optional string prefix to append before the mapped sub element names - - - - - - -mapShapes(generated, modified, op='') - -generate element names with user defined mapping - -generated: a list of tuple(src, dst) that indicating src shape or shapes -generates dst shape or shapes. Note that the dst shape or shapes -must be sub-shapes of this shape. -modified: a list of tuple(src, dst) that indicating src shape or shapes -modifies into dst shape or shapes. Note that the dst shape or -shapes must be sub-shapes of this shape. -op: optional string prefix to append before the mapped sub element names - - - - - - -getElementHistory(name) - returns the element mapped name history - -name: mapped element name belonging to this shape - -Returns tuple(sourceShapeTag, sourceName, [intermediateNames...]), -or None if no history. - - - - - - Determines a tolerance from the ones stored in a shape -getTolerance(mode, ShapeType=Shape) -> float --- -mode = 0 : returns the average value between sub-shapes, -mode > 0 : returns the maximal found, -mode < 0 : returns the minimal found. -ShapeType defines what kinds of sub-shapes to consider: -Shape (default) : all : Vertex, Edge, Face, -Vertex : only vertices, -Edge : only edges, -Face : only faces, -Shell : combined Shell + Face, for each face (and containing - shell), also checks edge and Vertex - - - - - - Determines which shapes have a tolerance over the given value -overTolerance(value, [ShapeType=Shape]) -> ShapeList --- -ShapeType is interpreted as in the method getTolerance - - - - - - Determines which shapes have a tolerance within a given interval -inTolerance(valmin, valmax, [ShapeType=Shape]) -> ShapeList --- -ShapeType is interpreted as in the method getTolerance - - - - - - Returns the computed tolerance according to the mode -globalTolerance(mode) -> float --- -mode = 0 : average -mode > 0 : maximal -mode < 0 : minimal - - - - - - Sets (enforces) tolerances in a shape to the given value -fixTolerance(value, [ShapeType=Shape]) --- -ShapeType = Vertex : only vertices are set -ShapeType = Edge : only edges are set -ShapeType = Face : only faces are set -ShapeType = Wire : to have edges and their vertices set -ShapeType = other value : all (vertices,edges,faces) are set - - - - - - Limits tolerances in a shape -limitTolerance(tmin, [tmax=0, ShapeType=Shape]) -> bool --- -tmin = tmax -> as fixTolerance (forces) -tmin = 0 -> maximum tolerance will be tmax -tmax = 0 or not given (more generally, tmax < tmin) -> -tmax ignored, minimum will be tmin -else, maximum will be max and minimum will be min -ShapeType = Vertex : only vertices are set -ShapeType = Edge : only edges are set -ShapeType = Face : only faces are set -ShapeType = Wire : to have edges and their vertices set -ShapeType = other value : all (vertices,edges,faces) are set -Returns True if at least one tolerance of the sub-shape has been modified - - - - - - Get the optimal bounding box -optimalBoundingBox([useTriangulation = True, useShapeTolerance = False]) -> bound box - - - - - - Clear internal sub-shape cache - - - - - -findSubShape(shape) -> (type_name, index) - -Find sub shape and return the shape type name and index. If not found, -then return (None, 0) - - - - - - -findSubShapesWithSharedVertex(shape, needName=False, checkGeometry=True, tol=1e-7, atol=1e-12) -> Shape - -shape: input elementary shape, currently only support Face, Edge, or Vertex - -needName: if True, return a list of tuple(name, shape), or else return a list -of shapes. - -checkGeometry: whether to compare geometry - -tol: distance tolerance - -atol: angular tolerance - -Search sub shape by checking vertex coordinates and comparing the underlying -geometries, This can find shapes that are copied. It currently only works with -elementary shapes, Face, Edge, Vertex. - - - - - - -getChildShapes(shapetype, avoidtype='') -> list(Shape) - -Return a list of child sub-shapes of given type. - -shapetype: the type of requesting sub shapes -avoidtype: optional shape type to skip when exploring - - - - - - - Returns the type of the shape. - - - - - - Returns the orientation of the shape. - - - - - - List of faces in this shape. - - - - - - List of vertexes in this shape. - - - - - - List of subsequent shapes in this shape. - - - - - - List of subsequent shapes in this shape. - - - - - - List of subsequent shapes in this shape. - - - - - - List of Edges in this shape. - - - - - - List of wires in this shape. - - - - - - List of compounds in this shape. - - - - - - List of sub-shapes in this shape. - - - - - - Total length of the edges of the shape. - - - - - - Total area of the faces of the shape. - - - - - - Total volume of the solids of the shape. - - - - - diff --git a/src/Mod/Part/App/TopoShapePyImp.cpp b/src/Mod/Part/App/TopoShapePyImp.cpp index 246c0c7e1a..c0b2ca1d2e 100644 --- a/src/Mod/Part/App/TopoShapePyImp.cpp +++ b/src/Mod/Part/App/TopoShapePyImp.cpp @@ -117,7 +117,8 @@ static Py_hash_t _TopoShapeHash(PyObject* self) #if OCC_VERSION_HEX >= 0x070800 return std::hash {}(static_cast(self)->getTopoShapePtr()->getShape()); #else - return static_cast(self)->getTopoShapePtr()->getShape().HashCode(INT_MAX); + return static_cast(self)->getTopoShapePtr()->getShape().HashCode( + std::numeric_limits::max()); #endif } diff --git a/src/Mod/Part/App/TopoShapeShell.pyi b/src/Mod/Part/App/TopoShapeShell.pyi new file mode 100644 index 0000000000..770caeafe6 --- /dev/null +++ b/src/Mod/Part/App/TopoShapeShell.pyi @@ -0,0 +1,101 @@ +from Base.Metadata import export, constmethod +from TopoShape import TopoShape +from typing import Final, Dict + + +@export( + Twin="TopoShape", + TwinPointer="TopoShape", + Include="Mod/Part/App/TopoShape.h", + FatherInclude="Mod/Part/App/TopoShapePy.h", + Constructor=True, +) +class TopoShapeShell(TopoShape): + """ + Create a shell out of a list of faces + + Author: Juergen Riegel (Juergen.Riegel@web.de) + Licence: LGPL + """ + + Mass: Final[object] = ... + """Returns the mass of the current system.""" + + CenterOfMass: Final[object] = ... + """ + Returns the center of mass of the current system. + If the gravitational field is uniform, it is the center of gravity. + The coordinates returned for the center of mass are expressed in the + absolute Cartesian coordinate system. + """ + + MatrixOfInertia: Final[object] = ... + """ + Returns the matrix of inertia. It is a symmetrical matrix. + The coefficients of the matrix are the quadratic moments of + inertia. + + | Ixx Ixy Ixz 0 | + | Ixy Iyy Iyz 0 | + | Ixz Iyz Izz 0 | + | 0 0 0 1 | + + The moments of inertia are denoted by Ixx, Iyy, Izz. + The products of inertia are denoted by Ixy, Ixz, Iyz. + The matrix of inertia is returned in the central coordinate + system (G, Gx, Gy, Gz) where G is the centre of mass of the + system and Gx, Gy, Gz the directions parallel to the X(1,0,0) + Y(0,1,0) Z(0,0,1) directions of the absolute cartesian + coordinate system. + """ + + StaticMoments: Final[object] = ... + """ + Returns Ix, Iy, Iz, the static moments of inertia of the + current system; i.e. the moments of inertia about the + three axes of the Cartesian coordinate system. + """ + + PrincipalProperties: Final[Dict] = ... + """ + Computes the principal properties of inertia of the current system. + There is always a set of axes for which the products + of inertia of a geometric system are equal to 0; i.e. the + matrix of inertia of the system is diagonal. These axes + are the principal axes of inertia. Their origin is + coincident with the center of mass of the system. The + associated moments are called the principal moments of inertia. + This function computes the eigen values and the + eigen vectors of the matrix of inertia of the system. + """ + + def add(self, face: object) -> None: + """ + Add a face to the shell. + add(face) + """ + ... + + @constmethod + def getFreeEdges(self) -> object: + """ + Get free edges as compound. + getFreeEdges() -> compound + """ + ... + + @constmethod + def getBadEdges(self) -> object: + """ + Get bad edges as compound. + getBadEdges() -> compound + """ + ... + + @constmethod + def makeHalfSpace(self, point: object) -> object: + """ + Make a half-space solid by this shell and a reference point. + makeHalfSpace(point) -> Solid + """ + ... diff --git a/src/Mod/Part/App/TopoShapeShellPy.xml b/src/Mod/Part/App/TopoShapeShellPy.xml deleted file mode 100644 index a0b98fb134..0000000000 --- a/src/Mod/Part/App/TopoShapeShellPy.xml +++ /dev/null @@ -1,104 +0,0 @@ - - - - - - Create a shell out of a list of faces - - - - Add a face to the shell. -add(face) - - - - - - Get free edges as compound. -getFreeEdges() -> compound - - - - - - Get bad edges as compound. -getBadEdges() -> compound - - - - - - Make a half-space solid by this shell and a reference point. -makeHalfSpace(point) -> Solid - - - - - - Returns the mass of the current system. - - - - - - Returns the center of mass of the current system. -If the gravitational field is uniform, it is the center of gravity. -The coordinates returned for the center of mass are expressed in the -absolute Cartesian coordinate system. - - - - - - Returns the matrix of inertia. It is a symmetrical matrix. -The coefficients of the matrix are the quadratic moments of -inertia. - - | Ixx Ixy Ixz 0 | - | Ixy Iyy Iyz 0 | - | Ixz Iyz Izz 0 | - | 0 0 0 1 | - -The moments of inertia are denoted by Ixx, Iyy, Izz. -The products of inertia are denoted by Ixy, Ixz, Iyz. -The matrix of inertia is returned in the central coordinate -system (G, Gx, Gy, Gz) where G is the centre of mass of the -system and Gx, Gy, Gz the directions parallel to the X(1,0,0) -Y(0,1,0) Z(0,0,1) directions of the absolute cartesian -coordinate system. - - - - - - Returns Ix, Iy, Iz, the static moments of inertia of the -current system; i.e. the moments of inertia about the -three axes of the Cartesian coordinate system. - - - - - - Computes the principal properties of inertia of the current system. -There is always a set of axes for which the products -of inertia of a geometric system are equal to 0; i.e. the -matrix of inertia of the system is diagonal. These axes -are the principal axes of inertia. Their origin is -coincident with the center of mass of the system. The -associated moments are called the principal moments of inertia. -This function computes the eigen values and the -eigen vectors of the matrix of inertia of the system. - - - - - diff --git a/src/Mod/Part/App/TopoShapeSolid.pyi b/src/Mod/Part/App/TopoShapeSolid.pyi new file mode 100644 index 0000000000..bb6f2dc558 --- /dev/null +++ b/src/Mod/Part/App/TopoShapeSolid.pyi @@ -0,0 +1,119 @@ +from Base.Metadata import export, constmethod +from TopoShape import TopoShape +from Base.Vector import Vector +from Base.Matrix import Matrix +from typing import Final, Dict, Tuple, overload + + +@export( + Twin="TopoShape", + TwinPointer="TopoShape", + Include="Mod/Part/App/TopoShape.h", + FatherInclude="Mod/Part/App/TopoShapePy.h", + Constructor=True, +) +class TopoShapeSolid(TopoShape): + """ + Part.Solid(shape): Create a solid out of shells of shape. If shape is a compsolid, the overall volume solid is created. + + Author: Juergen Riegel (Juergen.Riegel@web.de) + Licence: LGPL + """ + + Mass: Final[float] = 0.0 + """Returns the mass of the current system.""" + + CenterOfMass: Final[Vector] = Vector() + """ + Returns the center of mass of the current system. + If the gravitational field is uniform, it is the center of gravity. + The coordinates returned for the center of mass are expressed in the + absolute Cartesian coordinate system. + """ + + MatrixOfInertia: Final[Matrix] = Matrix() + """ + Returns the matrix of inertia. It is a symmetrical matrix. + The coefficients of the matrix are the quadratic moments of + inertia. + + | Ixx Ixy Ixz 0 | + | Ixy Iyy Iyz 0 | + | Ixz Iyz Izz 0 | + | 0 0 0 1 | + + The moments of inertia are denoted by Ixx, Iyy, Izz. + The products of inertia are denoted by Ixy, Ixz, Iyz. + The matrix of inertia is returned in the central coordinate + system (G, Gx, Gy, Gz) where G is the centre of mass of the + system and Gx, Gy, Gz the directions parallel to the X(1,0,0) + Y(0,1,0) Z(0,0,1) directions of the absolute cartesian + coordinate system. + """ + + StaticMoments: Final[object] = object() + """ + Returns Ix, Iy, Iz, the static moments of inertia of the + current system; i.e. the moments of inertia about the + three axes of the Cartesian coordinate system. + """ + + PrincipalProperties: Final[Dict[str, float]] = {} + """ + Computes the principal properties of inertia of the current system. + There is always a set of axes for which the products + of inertia of a geometric system are equal to 0; i.e. the + matrix of inertia of the system is diagonal. These axes + are the principal axes of inertia. Their origin is + coincident with the center of mass of the system. The + associated moments are called the principal moments of inertia. + This function computes the eigen values and the + eigen vectors of the matrix of inertia of the system. + """ + + OuterShell: Final[TopoShape] = TopoShape() + """ + Returns the outer most shell of this solid or an null + shape if the solid has no shells + """ + + @constmethod + def getMomentOfInertia(self, point: Vector, direction: Vector) -> float: + """ + computes the moment of inertia of the material system about the axis A. + getMomentOfInertia(point,direction) -> Float + """ + ... + + @constmethod + def getRadiusOfGyration(self, point: Vector, direction: Vector) -> float: + """ + Returns the radius of gyration of the current system about the axis A. + getRadiusOfGyration(point,direction) -> Float + """ + ... + + @overload + @constmethod + def offsetFaces( + self, facesTuple: Tuple[TopoShape, ...], offset: float + ) -> TopoShape: ... + + @overload + @constmethod + def offsetFaces(self, facesDict: Dict[TopoShape, float]) -> TopoShape: ... + + @constmethod + def offsetFaces(self, *args, **kwargs) -> TopoShape: + """ + Extrude single faces of the solid. + offsetFaces(facesTuple, offset) -> Solid + or + offsetFaces(dict) -> Solid + -- + Example: + solid.offsetFaces((solid.Faces[0],solid.Faces[1]), 1.5) + + solid.offsetFaces({solid.Faces[0]:1.0,solid.Faces[1]:2.0}) + """ + ... diff --git a/src/Mod/Part/App/TopoShapeSolidPy.xml b/src/Mod/Part/App/TopoShapeSolidPy.xml deleted file mode 100644 index 1f50ab51cf..0000000000 --- a/src/Mod/Part/App/TopoShapeSolidPy.xml +++ /dev/null @@ -1,112 +0,0 @@ - - - - - - Part.Solid(shape): Create a solid out of shells of shape. If shape is a compsolid, the overall volume solid is created. - - - - Returns the mass of the current system. - - - - - - Returns the center of mass of the current system. -If the gravitational field is uniform, it is the center of gravity. -The coordinates returned for the center of mass are expressed in the -absolute Cartesian coordinate system. - - - - - - Returns the matrix of inertia. It is a symmetrical matrix. -The coefficients of the matrix are the quadratic moments of -inertia. - - | Ixx Ixy Ixz 0 | - | Ixy Iyy Iyz 0 | - | Ixz Iyz Izz 0 | - | 0 0 0 1 | - -The moments of inertia are denoted by Ixx, Iyy, Izz. -The products of inertia are denoted by Ixy, Ixz, Iyz. -The matrix of inertia is returned in the central coordinate -system (G, Gx, Gy, Gz) where G is the centre of mass of the -system and Gx, Gy, Gz the directions parallel to the X(1,0,0) -Y(0,1,0) Z(0,0,1) directions of the absolute cartesian -coordinate system. - - - - - - Returns Ix, Iy, Iz, the static moments of inertia of the -current system; i.e. the moments of inertia about the -three axes of the Cartesian coordinate system. - - - - - - Computes the principal properties of inertia of the current system. -There is always a set of axes for which the products -of inertia of a geometric system are equal to 0; i.e. the -matrix of inertia of the system is diagonal. These axes -are the principal axes of inertia. Their origin is -coincident with the center of mass of the system. The -associated moments are called the principal moments of inertia. -This function computes the eigen values and the -eigen vectors of the matrix of inertia of the system. - - - - - - -Returns the outer most shell of this solid or an null -shape if the solid has no shells - - - - - - computes the moment of inertia of the material system about the axis A. -getMomentOfInertia(point,direction) -> Float - - - - - - Returns the radius of gyration of the current system about the axis A. -getRadiusOfGyration(point,direction) -> Float - - - - - - Extrude single faces of the solid. -offsetFaces(facesTuple, offset) -> Solid -or -offsetFaces(dict) -> Solid --- -Example: -solid.offsetFaces((solid.Faces[0],solid.Faces[1]), 1.5) - -solid.offsetFaces({solid.Faces[0]:1.0,solid.Faces[1]:2.0}) - - - - - diff --git a/src/Mod/Part/App/TopoShapeVertex.pyi b/src/Mod/Part/App/TopoShapeVertex.pyi new file mode 100644 index 0000000000..eda89b1c3a --- /dev/null +++ b/src/Mod/Part/App/TopoShapeVertex.pyi @@ -0,0 +1,34 @@ +from Base.Metadata import export +from Base.Vector import Vector +from TopoShape import TopoShape +from typing import Final + + +@export( + Twin="TopoShape", + TwinPointer="TopoShape", + FatherInclude="Mod/Part/App/TopoShapePy.h", + Include="Mod/Part/App/TopoShape.h", + Constructor=True, +) +class TopoShapeVertex(TopoShape): + """ + TopoShapeVertex is the OpenCasCade topological vertex wrapper + + Author: Juergen Riegel (Juergen.Riegel@web.de) + """ + + X: Final[float] = ... + """X component of this Vertex.""" + + Y: Final[float] = ... + """Y component of this Vertex.""" + + Z: Final[float] = ... + """Z component of this Vertex.""" + + Point: Final[Vector] = ... + """Position of this Vertex as a Vector""" + + Tolerance: float = ... + """Set or get the tolerance of the vertex""" diff --git a/src/Mod/Part/App/TopoShapeVertexPy.xml b/src/Mod/Part/App/TopoShapeVertexPy.xml deleted file mode 100644 index 4dad515505..0000000000 --- a/src/Mod/Part/App/TopoShapeVertexPy.xml +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - TopoShapeVertex is the OpenCasCade topological vertex wrapper - - - - X component of this Vertex. - - - - - - Y component of this Vertex. - - - - - - Z component of this Vertex. - - - - - - Position of this Vertex as a Vector - - - - - - Set or get the tolerance of the vertex - - - - - diff --git a/src/Mod/Part/App/TopoShapeWire.pyi b/src/Mod/Part/App/TopoShapeWire.pyi new file mode 100644 index 0000000000..1f6b5fc349 --- /dev/null +++ b/src/Mod/Part/App/TopoShapeWire.pyi @@ -0,0 +1,256 @@ +from Base.Metadata import export, constmethod +from TopoShape import TopoShape +from typing import Dict, List, Final, overload, Optional + + +@export( + Twin="TopoShape", + TwinPointer="TopoShape", + FatherInclude="Mod/Part/App/TopoShapePy.h", + Include="Mod/Part/App/TopoShape.h", + Constructor=True, +) +class TopoShapeWire(TopoShape): + """ + TopoShapeWire is the OpenCasCade topological wire wrapper + + Author: Juergen Riegel (Juergen.Riegel@web.de) + Licence: LGPL + DeveloperDocu: TopoShapeWire is the OpenCasCade topological wire wrapper + """ + + Mass: Final[object] = ... + """Returns the mass of the current system.""" + + CenterOfMass: Final[object] = ... + """ + Returns the center of mass of the current system. + If the gravitational field is uniform, it is the center of gravity. + The coordinates returned for the center of mass are expressed in the + absolute Cartesian coordinate system. + """ + + MatrixOfInertia: Final[object] = ... + """ + Returns the matrix of inertia. It is a symmetrical matrix. + The coefficients of the matrix are the quadratic moments of + inertia. + + | Ixx Ixy Ixz 0 | + | Ixy Iyy Iyz 0 | + | Ixz Iyz Izz 0 | + | 0 0 0 1 | + + The moments of inertia are denoted by Ixx, Iyy, Izz. + The products of inertia are denoted by Ixy, Ixz, Iyz. + The matrix of inertia is returned in the central coordinate + system (G, Gx, Gy, Gz) where G is the centre of mass of the + system and Gx, Gy, Gz the directions parallel to the X(1,0,0) + Y(0,1,0) Z(0,0,1) directions of the absolute cartesian + coordinate system. + """ + + StaticMoments: Final[object] = ... + """ + Returns Ix, Iy, Iz, the static moments of inertia of the + current system; i.e. the moments of inertia about the + three axes of the Cartesian coordinate system. + """ + + PrincipalProperties: Final[Dict] = ... + """ + Computes the principal properties of inertia of the current system. + There is always a set of axes for which the products + of inertia of a geometric system are equal to 0; i.e. the + matrix of inertia of the system is diagonal. These axes + are the principal axes of inertia. Their origin is + coincident with the center of mass of the system. The + associated moments are called the principal moments of inertia. + This function computes the eigen values and the + eigen vectors of the matrix of inertia of the system. + """ + + OrderedEdges: Final[List] = ... + """List of ordered edges in this shape.""" + + Continuity: Final[str] = ... + """Returns the continuity""" + + OrderedVertexes: Final[List] = ... + """List of ordered vertexes in this shape.""" + + @constmethod + def makeOffset(self) -> object: + """ + Offset the shape by a given amount. DEPRECATED - use makeOffset2D instead. + """ + ... + + def add(self, edge: object) -> None: + """ + Add an edge to the wire + add(edge) + """ + ... + + def fixWire( + self, face: Optional[object] = None, tolerance: Optional[float] = None + ) -> None: + """ + Fix wire + fixWire([face, tolerance]) + -- + A face and a tolerance can optionally be supplied to the algorithm: + """ + ... + + @constmethod + def makeHomogenousWires(self, wire: object) -> object: + """ + Make this and the given wire homogeneous to have the same number of edges + makeHomogenousWires(wire) -> Wire + """ + ... + + @constmethod + def makePipe(self, profile: object) -> object: + """ + Make a pipe by sweeping along a wire. + makePipe(profile) -> Shape + """ + ... + + @constmethod + def makePipeShell( + self, + shapeList: List[object], + isSolid: bool = False, + isFrenet: bool = False, + transition: int = 0, + ) -> object: + """ + Make a loft defined by a list of profiles along a wire. + makePipeShell(shapeList,[isSolid=False,isFrenet=False,transition=0]) -> Shape + -- + Transition can be 0 (default), 1 (right corners) or 2 (rounded corners). + """ + ... + + @constmethod + def makeEvolved(self, *, Profile: TopoShape, Join: int, AxeProf: bool, Solid: bool, + ProfOnSpine: bool, Tolerance: float) -> TopoShape: + """ + Profile along the spine + """ + ... + + @constmethod + def approximate( + self, + *, + Tol2d: float = None, + Tol3d: float = 0.0001, + MaxSegments: int = 10, + MaxDegree: int = 3, + ) -> object: + """ + Approximate B-Spline-curve from this wire + approximate([Tol2d,Tol3d=1e-4,MaxSegments=10,MaxDegree=3]) -> BSpline + """ + ... + + @overload + @constmethod + def discretize(self, Number: int) -> List[object]: + """ + discretize(Number=n) -> list + """ + ... + + @overload + @constmethod + def discretize(self, QuasiNumber: int) -> List[object]: + """ + discretize(QuasiNumber=n) -> list + """ + ... + + @overload + @constmethod + def discretize(self, Distance: float) -> List[object]: + """ + discretize(Distance=d) -> list + """ + ... + + @overload + @constmethod + def discretize(self, Deflection: float) -> List[object]: + """ + discretize(Deflection=d) -> list + """ + ... + + @overload + @constmethod + def discretize(self, QuasiDeflection: float) -> List[object]: + """ + discretize(QuasiDeflection=d) -> list + """ + ... + + @overload + @constmethod + def discretize( + self, Angular: float, Curvature: float, Minimum: int = 2 + ) -> List[object]: + """ + discretize(Angular=a,Curvature=c,[Minimum=m]) -> list + """ + ... + + @constmethod + def discretize( + self, + **kwargs + ) -> List[object]: + """ + Discretizes the wire and returns a list of points. + discretize(kwargs) -> list + -- + The function accepts keywords as argument: + discretize(Number=n) => gives a list of 'n' equidistant points + discretize(QuasiNumber=n) => gives a list of 'n' quasi equidistant points (is faster than the method above) + discretize(Distance=d) => gives a list of equidistant points with distance 'd' + discretize(Deflection=d) => gives a list of points with a maximum deflection 'd' to the wire + discretize(QuasiDeflection=d) => gives a list of points with a maximum deflection 'd' to the wire (faster) + discretize(Angular=a,Curvature=c,[Minimum=m]) => gives a list of points with an angular deflection of 'a' + and a curvature deflection of 'c'. Optionally a minimum number of points + can be set which by default is set to 2. + + Optionally you can set the keywords 'First' and 'Last' to define a sub-range of the parameter range + of the wire. + + If no keyword is given then it depends on whether the argument is an int or float. + If it's an int then the behaviour is as if using the keyword 'Number', if it's float + then the behaviour is as if using the keyword 'Distance'. + + Example: + + import Part + V=App.Vector + + e1=Part.makeCircle(5,V(0,0,0),V(0,0,1),0,180) + e2=Part.makeCircle(5,V(10,0,0),V(0,0,1),180,360) + w=Part.Wire([e1,e2]) + + p=w.discretize(Number=50) + s=Part.Compound([Part.Vertex(i) for i in p]) + Part.show(s) + + + p=w.discretize(Angular=0.09,Curvature=0.01,Minimum=100) + s=Part.Compound([Part.Vertex(i) for i in p]) + Part.show(s) + """ + ... diff --git a/src/Mod/Part/App/TopoShapeWirePy.xml b/src/Mod/Part/App/TopoShapeWirePy.xml deleted file mode 100644 index f3de0a58c2..0000000000 --- a/src/Mod/Part/App/TopoShapeWirePy.xml +++ /dev/null @@ -1,192 +0,0 @@ - - - - - - TopoShapeWire is the OpenCasCade topological wire wrapper - - - - Offset the shape by a given amount. DEPRECATED - use makeOffset2D instead. - - - - - Add an edge to the wire -add(edge) - - - - - - Fix wire -fixWire([face, tolerance]) --- -A face and a tolerance can optionally be supplied to the algorithm: - - - - - - Make this and the given wire homogeneous to have the same number of edges -makeHomogenousWires(wire) -> Wire - - - - - - Make a pipe by sweeping along a wire. -makePipe(profile) -> Shape - - - - - - Make a loft defined by a list of profiles along a wire. -makePipeShell(shapeList,[isSolid=False,isFrenet=False,transition=0]) -> Shape --- -Transition can be 0 (default), 1 (right corners) or 2 (rounded corners). - - - - - - Profile along the spine - - - - - Approximate B-Spline-curve from this wire -approximate([Tol2d,Tol3d=1e-4,MaxSegments=10,MaxDegree=3]) -> BSpline - - - - - - Discretizes the wire and returns a list of points. -discretize(kwargs) -> list --- -The function accepts keywords as argument: -discretize(Number=n) => gives a list of 'n' equidistant points -discretize(QuasiNumber=n) => gives a list of 'n' quasi equidistant points (is faster than the method above) -discretize(Distance=d) => gives a list of equidistant points with distance 'd' -discretize(Deflection=d) => gives a list of points with a maximum deflection 'd' to the wire -discretize(QuasiDeflection=d) => gives a list of points with a maximum deflection 'd' to the wire (faster) -discretize(Angular=a,Curvature=c,[Minimum=m]) => gives a list of points with an angular deflection of 'a' - and a curvature deflection of 'c'. Optionally a minimum number of points - can be set which by default is set to 2. - -Optionally you can set the keywords 'First' and 'Last' to define a sub-range of the parameter range -of the wire. - -If no keyword is given then it depends on whether the argument is an int or float. -If it's an int then the behaviour is as if using the keyword 'Number', if it's float -then the behaviour is as if using the keyword 'Distance'. - -Example: - -import Part -V=App.Vector - -e1=Part.makeCircle(5,V(0,0,0),V(0,0,1),0,180) -e2=Part.makeCircle(5,V(10,0,0),V(0,0,1),180,360) -w=Part.Wire([e1,e2]) - -p=w.discretize(Number=50) -s=Part.Compound([Part.Vertex(i) for i in p]) -Part.show(s) - - -p=w.discretize(Angular=0.09,Curvature=0.01,Minimum=100) -s=Part.Compound([Part.Vertex(i) for i in p]) -Part.show(s) - - - - - - Returns the mass of the current system. - - - - - - Returns the center of mass of the current system. -If the gravitational field is uniform, it is the center of gravity. -The coordinates returned for the center of mass are expressed in the -absolute Cartesian coordinate system. - - - - - - Returns the matrix of inertia. It is a symmetrical matrix. -The coefficients of the matrix are the quadratic moments of -inertia. - - | Ixx Ixy Ixz 0 | - | Ixy Iyy Iyz 0 | - | Ixz Iyz Izz 0 | - | 0 0 0 1 | - -The moments of inertia are denoted by Ixx, Iyy, Izz. -The products of inertia are denoted by Ixy, Ixz, Iyz. -The matrix of inertia is returned in the central coordinate -system (G, Gx, Gy, Gz) where G is the centre of mass of the -system and Gx, Gy, Gz the directions parallel to the X(1,0,0) -Y(0,1,0) Z(0,0,1) directions of the absolute cartesian -coordinate system. - - - - - - Returns Ix, Iy, Iz, the static moments of inertia of the -current system; i.e. the moments of inertia about the -three axes of the Cartesian coordinate system. - - - - - - Computes the principal properties of inertia of the current system. -There is always a set of axes for which the products -of inertia of a geometric system are equal to 0; i.e. the -matrix of inertia of the system is diagonal. These axes -are the principal axes of inertia. Their origin is -coincident with the center of mass of the system. The -associated moments are called the principal moments of inertia. -This function computes the eigen values and the -eigen vectors of the matrix of inertia of the system. - - - - - - List of ordered edges in this shape. - - - - - - Returns the continuity - - - - - - List of ordered vertexes in this shape. - - - - - diff --git a/src/Mod/Part/App/Toroid.pyi b/src/Mod/Part/App/Toroid.pyi new file mode 100644 index 0000000000..698576389f --- /dev/null +++ b/src/Mod/Part/App/Toroid.pyi @@ -0,0 +1,40 @@ +from Base.Metadata import export, constmethod +from Part.GeometrySurface import GeometrySurface +from Base.Vector import Vector +from typing import Final + + +@export( + Name="ToroidPy", + Namespace="Part", + Twin="GeomToroid", + TwinPointer="GeomToroid", + PythonName="Part.Toroid", + FatherInclude="Mod/Part/App/GeometrySurfacePy.h", + Include="Mod/Part/App/Geometry.h", + Father="GeometrySurfacePy", + FatherNamespace="Part", + Constructor=True, +) +class Toroid(GeometrySurface): + """ + Describes a toroid in 3D space + """ + + MajorRadius: float = ... + """The major radius of the toroid.""" + + MinorRadius: float = ... + """The minor radius of the toroid.""" + + Center: Vector = ... + """Center of the toroid.""" + + Axis: Vector = ... + """The axis direction of the toroid""" + + Area: Final[float] = 0.0 + """Compute the area of the toroid.""" + + Volume: Final[float] = 0.0 + """Compute the volume of the toroid.""" diff --git a/src/Mod/Part/App/ToroidPy.xml b/src/Mod/Part/App/ToroidPy.xml deleted file mode 100644 index dd0aeb7e65..0000000000 --- a/src/Mod/Part/App/ToroidPy.xml +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - Describes a toroid in 3D space - - - - The major radius of the toroid. - - - - - - The minor radius of the toroid. - - - - - - Center of the toroid. - - - - - - The axis direction of the toroid - - - - - - Compute the area of the toroid. - - - - - - Compute the volume of the toroid. - - - - - diff --git a/src/Mod/Part/App/TrimmedCurve.pyi b/src/Mod/Part/App/TrimmedCurve.pyi new file mode 100644 index 0000000000..83e974d30a --- /dev/null +++ b/src/Mod/Part/App/TrimmedCurve.pyi @@ -0,0 +1,25 @@ +from Base.Metadata import export +from BoundedCurve import BoundedCurve + + +@export( + Twin="GeomTrimmedCurve", + TwinPointer="GeomTrimmedCurve", + PythonName="Part.TrimmedCurve", + FatherInclude="Mod/Part/App/BoundedCurvePy.h", + Include="Mod/Part/App/Geometry.h", + Constructor=True, +) +class TrimmedCurve(BoundedCurve): + """ + The abstract class TrimmedCurve is the root class of all trimmed curve objects. + + Author: Abdullah Tahiri (abdullah.tahiri.yo@gmail.com) + Licence: LGPL + """ + + def setParameterRange(self, first: float, last: float) -> None: + """ + Re-trims this curve to the provided parameter range ([Float=First, Float=Last]) + """ + ... diff --git a/src/Mod/Part/App/TrimmedCurvePy.xml b/src/Mod/Part/App/TrimmedCurvePy.xml deleted file mode 100644 index c2032d91e6..0000000000 --- a/src/Mod/Part/App/TrimmedCurvePy.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - The abstract class TrimmedCurve is the root class of all trimmed curve objects. - - - - Re-trims this curve to the provided parameter range ([Float=First, Float=Last]) - - - - diff --git a/src/Mod/Part/App/WireJoiner.cpp b/src/Mod/Part/App/WireJoiner.cpp index 0987454766..abe3cf1326 100644 --- a/src/Mod/Part/App/WireJoiner.cpp +++ b/src/Mod/Part/App/WireJoiner.cpp @@ -784,11 +784,12 @@ public: const bool isLinear) { std::unique_ptr geo; - for (auto vit = vmap.qbegin(bgi::nearest(p1, INT_MAX)); vit != vmap.qend(); ++vit) { + constexpr int max = std::numeric_limits::max(); + for (auto vit = vmap.qbegin(bgi::nearest(p1, max)); vit != vmap.qend(); ++vit) { auto& vinfo = *vit; if (canShowShape()) { #if OCC_VERSION_HEX < 0x070800 - FC_MSG("addcheck " << vinfo.edge().HashCode(INT_MAX)); + FC_MSG("addcheck " << vinfo.edge().HashCode(max)); #else FC_MSG("addcheck " << std::hash {}(vinfo.edge())); #endif @@ -1568,7 +1569,8 @@ public: } info.iEnd[ic] = info.iStart[ic] = (int)adjacentList.size(); - for (auto vit = vmap.qbegin(bgi::nearest(pt[ic], INT_MAX)); vit != vmap.qend(); + constexpr int max = std::numeric_limits::max(); + for (auto vit = vmap.qbegin(bgi::nearest(pt[ic], max)); vit != vmap.qend(); ++vit) { auto& vinfo = *vit; if (vinfo.pt().SquareDistance(pt[ic]) > myTol2) { @@ -2717,7 +2719,8 @@ public: FC_MSG("init:"); for (const auto& shape : sourceEdges) { #if OCC_VERSION_HEX < 0x070800 - FC_MSG(shape.getShape().TShape().get() << ", " << shape.getShape().HashCode(INT_MAX)); + constexpr int max = std::numeric_limits::max(); + FC_MSG(shape.getShape().TShape().get() << ", " << shape.getShape().HashCode(max)); #else FC_MSG(shape.getShape().TShape().get() << ", " << std::hash {}(shape.getShape())); @@ -2736,7 +2739,8 @@ public: for (int i = 1; i <= wireData->NbEdges(); ++i) { auto shape = wireData->Edge(i); #if OCC_VERSION_HEX < 0x070800 - FC_MSG(shape.TShape().get() << ", " << shape.HashCode(INT_MAX)); + constexpr int max = std::numeric_limits::max(); + FC_MSG(shape.TShape().get() << ", " << shape.HashCode(max)); #else FC_MSG(shape.TShape().get() << ", " << std::hash {}(shape)); #endif @@ -2800,9 +2804,10 @@ public: for (TopTools_ListIteratorOfListOfShape it(hist->Modified(shape.getShape())); it.More(); it.Next()) { #if OCC_VERSION_HEX < 0x070800 - FC_MSG(shape.getShape().TShape().get() - << ", " << shape.getShape().HashCode(INT_MAX) << " -> " - << it.Value().TShape().get() << ", " << it.Value().HashCode(INT_MAX)); + constexpr int max = std::numeric_limits::max(); + FC_MSG(shape.getShape().TShape().get() + << ", " << shape.getShape().HashCode(max) << " -> " + << it.Value().TShape().get() << ", " << it.Value().HashCode(max)); #else FC_MSG(shape.getShape().TShape().get() << ", " << std::hash {}(shape.getShape()) << " -> " diff --git a/src/Mod/Part/App/modelRefine.cpp b/src/Mod/Part/App/modelRefine.cpp index ebf58c6c27..0c6338f979 100644 --- a/src/Mod/Part/App/modelRefine.cpp +++ b/src/Mod/Part/App/modelRefine.cpp @@ -24,6 +24,7 @@ #ifndef _PreComp_ # include +# include # include # include # include @@ -613,8 +614,8 @@ bool wireEncirclesAxis(const TopoDS_Wire& wire, const Handle(Geom_CylindricalSur // For an exact calculation, only two results would be possible: // totalArc = 0.0: The wire does not encircle the axis - // totalArc = 2 * M_PI * radius: The wire encircles the axis - return (fabs(totalArc) > M_PI * radius); + // totalArc = 2 * std::numbers::pi * radius: The wire encircles the axis + return (fabs(totalArc) > std::numbers::pi * radius); } TopoDS_Face FaceTypedCylinder::buildFace(const FaceVectorType &faces) const diff --git a/src/Mod/Part/Gui/CrossSections.cpp b/src/Mod/Part/Gui/CrossSections.cpp index ae425a6776..ed2f1f8dad 100644 --- a/src/Mod/Part/Gui/CrossSections.cpp +++ b/src/Mod/Part/Gui/CrossSections.cpp @@ -23,7 +23,6 @@ #include "PreCompiled.h" #ifndef _PreComp_ -# include # include # include @@ -126,9 +125,10 @@ CrossSections::CrossSections(const Base::BoundBox3d& bb, QWidget* parent, Qt::Wi ui->setupUi(this); setupConnections(); - ui->position->setRange(-DBL_MAX, DBL_MAX); + constexpr double max = std::numeric_limits::max(); + ui->position->setRange(-max, max); ui->position->setUnit(Base::Unit::Length); - ui->distance->setRange(0, DBL_MAX); + ui->distance->setRange(0, max); ui->distance->setUnit(Base::Unit::Length); vp = new ViewProviderCrossSections(); diff --git a/src/Mod/Part/Gui/DlgFilletEdges.cpp b/src/Mod/Part/Gui/DlgFilletEdges.cpp index 7a1ece5eec..ea0356bd02 100644 --- a/src/Mod/Part/Gui/DlgFilletEdges.cpp +++ b/src/Mod/Part/Gui/DlgFilletEdges.cpp @@ -89,7 +89,7 @@ QWidget *FilletRadiusDelegate::createEditor(QWidget *parent, const QStyleOptionV Gui::QuantitySpinBox *editor = new Gui::QuantitySpinBox(parent); editor->setUnit(Base::Unit::Length); editor->setMinimum(0.0); - editor->setMaximum(INT_MAX); + editor->setMaximum(std::numeric_limits::max()); editor->setSingleStep(0.1); return editor; @@ -234,11 +234,11 @@ DlgFilletEdges::DlgFilletEdges(FilletType type, Part::FilletBase* fillet, QWidge ui->setupUi(this); setupConnections(); - ui->filletStartRadius->setMaximum(INT_MAX); + ui->filletStartRadius->setMaximum(std::numeric_limits::max()); ui->filletStartRadius->setMinimum(0); ui->filletStartRadius->setUnit(Base::Unit::Length); - ui->filletEndRadius->setMaximum(INT_MAX); + ui->filletEndRadius->setMaximum(std::numeric_limits::max()); ui->filletEndRadius->setMinimum(0); ui->filletEndRadius->setUnit(Base::Unit::Length); diff --git a/src/Mod/Part/Gui/DlgPrimitives.cpp b/src/Mod/Part/Gui/DlgPrimitives.cpp index bde63661a4..ef411ecb96 100644 --- a/src/Mod/Part/Gui/DlgPrimitives.cpp +++ b/src/Mod/Part/Gui/DlgPrimitives.cpp @@ -239,8 +239,8 @@ PlanePrimitive::PlanePrimitive(std::shared_ptr ui, Part::Plane : AbstractPrimitive(feature) , ui(ui) { - ui->planeLength->setRange(0, INT_MAX); - ui->planeWidth->setRange(0, INT_MAX); + ui->planeLength->setRange(0, std::numeric_limits::max()); + ui->planeWidth->setRange(0, std::numeric_limits::max()); if (feature) { ui->planeLength->setValue(feature->Length.getQuantityValue()); @@ -308,9 +308,9 @@ BoxPrimitive::BoxPrimitive(std::shared_ptr ui, Part::Box* feat : AbstractPrimitive(feature) , ui(ui) { - ui->boxLength->setRange(0, INT_MAX); - ui->boxWidth->setRange(0, INT_MAX); - ui->boxHeight->setRange(0, INT_MAX); + ui->boxLength->setRange(0, std::numeric_limits::max()); + ui->boxWidth->setRange(0, std::numeric_limits::max()); + ui->boxHeight->setRange(0, std::numeric_limits::max()); if (feature) { ui->boxLength->setValue(feature->Length.getQuantityValue()); @@ -388,8 +388,8 @@ CylinderPrimitive::CylinderPrimitive(std::shared_ptr ui, Part: : AbstractPrimitive(feature) , ui(ui) { - ui->cylinderRadius->setRange(0, INT_MAX); - ui->cylinderHeight->setRange(0, INT_MAX); + ui->cylinderRadius->setRange(0, std::numeric_limits::max()); + ui->cylinderHeight->setRange(0, std::numeric_limits::max()); ui->cylinderAngle->setRange(0, 360); if (feature) { @@ -488,9 +488,9 @@ ConePrimitive::ConePrimitive(std::shared_ptr ui, Part::Cone* f : AbstractPrimitive(feature) , ui(ui) { - ui->coneRadius1->setRange(0, INT_MAX); - ui->coneRadius2->setRange(0, INT_MAX); - ui->coneHeight->setRange(0, INT_MAX); + ui->coneRadius1->setRange(0, std::numeric_limits::max()); + ui->coneRadius2->setRange(0, std::numeric_limits::max()); + ui->coneHeight->setRange(0, std::numeric_limits::max()); ui->coneAngle->setRange(0, 360); if (feature) { @@ -579,7 +579,7 @@ SpherePrimitive::SpherePrimitive(std::shared_ptr ui, Part::Sph : AbstractPrimitive(feature) , ui(ui) { - ui->sphereRadius->setRange(0, INT_MAX); + ui->sphereRadius->setRange(0, std::numeric_limits::max()); ui->sphereAngle1->setRange(-90, 90); ui->sphereAngle2->setRange(-90, 90); ui->sphereAngle3->setRange(0, 360); @@ -670,9 +670,9 @@ EllipsoidPrimitive::EllipsoidPrimitive(std::shared_ptr ui, Par : AbstractPrimitive(feature) , ui(ui) { - ui->ellipsoidRadius1->setRange(0, INT_MAX); - ui->ellipsoidRadius2->setRange(0, INT_MAX); - ui->ellipsoidRadius3->setRange(0, INT_MAX); + ui->ellipsoidRadius1->setRange(0, std::numeric_limits::max()); + ui->ellipsoidRadius2->setRange(0, std::numeric_limits::max()); + ui->ellipsoidRadius3->setRange(0, std::numeric_limits::max()); ui->ellipsoidAngle1->setRange(-90, 90); ui->ellipsoidAngle2->setRange(-90, 90); ui->ellipsoidAngle3->setRange(0, 360); @@ -784,8 +784,8 @@ TorusPrimitive::TorusPrimitive(std::shared_ptr ui, Part::Torus : AbstractPrimitive(feature) , ui(ui) { - ui->torusRadius1->setRange(0, INT_MAX); - ui->torusRadius2->setRange(0, INT_MAX); + ui->torusRadius1->setRange(0, std::numeric_limits::max()); + ui->torusRadius2->setRange(0, std::numeric_limits::max()); ui->torusAngle1->setRange(-180, 180); ui->torusAngle2->setRange(-180, 180); ui->torusAngle3->setRange(0, 360); @@ -886,8 +886,8 @@ PrismPrimitive::PrismPrimitive(std::shared_ptr ui, Part::Prism : AbstractPrimitive(feature) , ui(ui) { - ui->prismCircumradius->setRange(0, INT_MAX); - ui->prismHeight->setRange(0, INT_MAX); + ui->prismCircumradius->setRange(0, std::numeric_limits::max()); + ui->prismHeight->setRange(0, std::numeric_limits::max()); if (feature) { ui->prismPolygon->setValue(feature->Polygon.getValue()); @@ -984,26 +984,28 @@ WedgePrimitive::WedgePrimitive(std::shared_ptr ui, Part::Wedge : AbstractPrimitive(feature) , ui(ui) { - ui->wedgeXmin->setMinimum(INT_MIN); - ui->wedgeXmin->setMaximum(INT_MAX); - ui->wedgeYmin->setMinimum(INT_MIN); - ui->wedgeYmin->setMaximum(INT_MAX); - ui->wedgeZmin->setMinimum(INT_MIN); - ui->wedgeZmin->setMaximum(INT_MAX); - ui->wedgeX2min->setMinimum(INT_MIN); - ui->wedgeX2min->setMaximum(INT_MAX); - ui->wedgeZ2min->setMinimum(INT_MIN); - ui->wedgeZ2min->setMaximum(INT_MAX); - ui->wedgeXmax->setMinimum(INT_MIN); - ui->wedgeXmax->setMaximum(INT_MAX); - ui->wedgeYmax->setMinimum(INT_MIN); - ui->wedgeYmax->setMaximum(INT_MAX); - ui->wedgeZmax->setMinimum(INT_MIN); - ui->wedgeZmax->setMaximum(INT_MAX); - ui->wedgeX2max->setMinimum(INT_MIN); - ui->wedgeX2max->setMaximum(INT_MAX); - ui->wedgeZ2max->setMinimum(INT_MIN); - ui->wedgeZ2max->setMaximum(INT_MAX); + constexpr int min = std::numeric_limits::min(); + constexpr int max = std::numeric_limits::max(); + ui->wedgeXmin->setMinimum(min); + ui->wedgeXmin->setMaximum(max); + ui->wedgeYmin->setMinimum(min); + ui->wedgeYmin->setMaximum(max); + ui->wedgeZmin->setMinimum(min); + ui->wedgeZmin->setMaximum(max); + ui->wedgeX2min->setMinimum(min); + ui->wedgeX2min->setMaximum(max); + ui->wedgeZ2min->setMinimum(min); + ui->wedgeZ2min->setMaximum(max); + ui->wedgeXmax->setMinimum(min); + ui->wedgeXmax->setMaximum(max); + ui->wedgeYmax->setMinimum(min); + ui->wedgeYmax->setMaximum(max); + ui->wedgeZmax->setMinimum(min); + ui->wedgeZmax->setMaximum(max); + ui->wedgeX2max->setMinimum(min); + ui->wedgeX2max->setMaximum(max); + ui->wedgeZ2max->setMinimum(min); + ui->wedgeZ2max->setMaximum(max); if (feature) { ui->wedgeXmin->setValue(feature->Xmin.getQuantityValue()); @@ -1151,9 +1153,9 @@ HelixPrimitive::HelixPrimitive(std::shared_ptr ui, Part::Helix : AbstractPrimitive(feature) , ui(ui) { - ui->helixPitch->setRange(0, INT_MAX); - ui->helixHeight->setRange(0, INT_MAX); - ui->helixRadius->setRange(0, INT_MAX); + ui->helixPitch->setRange(0, std::numeric_limits::max()); + ui->helixHeight->setRange(0, std::numeric_limits::max()); + ui->helixRadius->setRange(0, std::numeric_limits::max()); ui->helixAngle->setRange(-89.9, 89.9); if (feature) { @@ -1252,9 +1254,9 @@ SpiralPrimitive::SpiralPrimitive(std::shared_ptr ui, Part::Spi : AbstractPrimitive(feature) , ui(ui) { - ui->spiralGrowth->setRange(0, INT_MAX); - ui->spiralRotation->setRange(0, INT_MAX); - ui->spiralRadius->setRange(0, INT_MAX); + ui->spiralGrowth->setRange(0, std::numeric_limits::max()); + ui->spiralRotation->setRange(0, std::numeric_limits::max()); + ui->spiralRadius->setRange(0, std::numeric_limits::max()); if (feature) { ui->spiralGrowth->setValue(feature->Growth.getQuantityValue()); @@ -1331,7 +1333,7 @@ CirclePrimitive::CirclePrimitive(std::shared_ptr ui, Part::Cir : AbstractPrimitive(feature) , ui(ui) { - ui->circleRadius->setRange(0, INT_MAX); + ui->circleRadius->setRange(0, std::numeric_limits::max()); ui->circleAngle1->setRange(0, 360); ui->circleAngle2->setRange(0, 360); @@ -1411,8 +1413,8 @@ EllipsePrimitive::EllipsePrimitive(std::shared_ptr ui, Part::E : AbstractPrimitive(feature) , ui(ui) { - ui->ellipseMajorRadius->setRange(0, INT_MAX); - ui->ellipseMinorRadius->setRange(0, INT_MAX); + ui->ellipseMajorRadius->setRange(0, std::numeric_limits::max()); + ui->ellipseMinorRadius->setRange(0, std::numeric_limits::max()); ui->ellipseAngle1->setRange(0, 360); ui->ellipseAngle2->setRange(0, 360); @@ -1502,7 +1504,7 @@ PolygonPrimitive::PolygonPrimitive(std::shared_ptr ui, Part::R : AbstractPrimitive(feature) , ui(ui) { - ui->regularPolygonCircumradius->setRange(0, INT_MAX); + ui->regularPolygonCircumradius->setRange(0, std::numeric_limits::max()); if (feature) { ui->regularPolygonPolygon->setValue(feature->Polygon.getValue()); @@ -1569,18 +1571,20 @@ LinePrimitive::LinePrimitive(std::shared_ptr ui, Part::Line* f : AbstractPrimitive(feature) , ui(ui) { - ui->edgeX1->setMaximum(INT_MAX); - ui->edgeX1->setMinimum(INT_MIN); - ui->edgeY1->setMaximum(INT_MAX); - ui->edgeY1->setMinimum(INT_MIN); - ui->edgeZ1->setMaximum(INT_MAX); - ui->edgeZ1->setMinimum(INT_MIN); - ui->edgeX2->setMaximum(INT_MAX); - ui->edgeX2->setMinimum(INT_MIN); - ui->edgeY2->setMaximum(INT_MAX); - ui->edgeY2->setMinimum(INT_MIN); - ui->edgeZ2->setMaximum(INT_MAX); - ui->edgeZ2->setMinimum(INT_MIN); + constexpr int min = std::numeric_limits::min(); + constexpr int max = std::numeric_limits::max(); + ui->edgeX1->setMaximum(max); + ui->edgeX1->setMinimum(min); + ui->edgeY1->setMaximum(max); + ui->edgeY1->setMinimum(min); + ui->edgeZ1->setMaximum(max); + ui->edgeZ1->setMinimum(min); + ui->edgeX2->setMaximum(max); + ui->edgeX2->setMinimum(min); + ui->edgeY2->setMaximum(max); + ui->edgeY2->setMinimum(min); + ui->edgeZ2->setMaximum(max); + ui->edgeZ2->setMinimum(min); if (feature) { ui->edgeX1->setValue(feature->X1.getQuantityValue()); @@ -1688,12 +1692,14 @@ VertexPrimitive::VertexPrimitive(std::shared_ptr ui, Part::Ver : AbstractPrimitive(feature) , ui(ui) { - ui->vertexX->setMaximum(INT_MAX); - ui->vertexY->setMaximum(INT_MAX); - ui->vertexZ->setMaximum(INT_MAX); - ui->vertexX->setMinimum(INT_MIN); - ui->vertexY->setMinimum(INT_MIN); - ui->vertexZ->setMinimum(INT_MIN); + constexpr int min = std::numeric_limits::min(); + constexpr int max = std::numeric_limits::max(); + ui->vertexX->setMaximum(max); + ui->vertexY->setMaximum(max); + ui->vertexZ->setMaximum(max); + ui->vertexX->setMinimum(min); + ui->vertexY->setMinimum(min); + ui->vertexZ->setMinimum(min); if (feature) { ui->vertexX->setValue(feature->X.getQuantityValue()); diff --git a/src/Mod/Part/Gui/DlgRevolution.cpp b/src/Mod/Part/Gui/DlgRevolution.cpp index 950552184b..04672dd218 100644 --- a/src/Mod/Part/Gui/DlgRevolution.cpp +++ b/src/Mod/Part/Gui/DlgRevolution.cpp @@ -103,16 +103,17 @@ DlgRevolution::DlgRevolution(QWidget* parent, Qt::WindowFlags fl) ui->setupUi(this); setupConnections(); - ui->xPos->setRange(-DBL_MAX,DBL_MAX); - ui->yPos->setRange(-DBL_MAX,DBL_MAX); - ui->zPos->setRange(-DBL_MAX,DBL_MAX); + constexpr double max = std::numeric_limits::max(); + ui->xPos->setRange(-max, max); + ui->yPos->setRange(-max, max); + ui->zPos->setRange(-max, max); ui->xPos->setUnit(Base::Unit::Length); ui->yPos->setUnit(Base::Unit::Length); ui->zPos->setUnit(Base::Unit::Length); - ui->xDir->setRange(-DBL_MAX,DBL_MAX); - ui->yDir->setRange(-DBL_MAX,DBL_MAX); - ui->zDir->setRange(-DBL_MAX,DBL_MAX); + ui->xDir->setRange(-max, max); + ui->yDir->setRange(-max, max); + ui->zDir->setRange(-max, max); ui->xDir->setUnit(Base::Unit()); ui->yDir->setUnit(Base::Unit()); ui->zDir->setUnit(Base::Unit()); @@ -307,7 +308,7 @@ bool DlgRevolution::validate() //check angle if (!axisLinkHasAngle){ - if (fabs(this->getAngle() / 180.0 * M_PI) < Precision::Angular()) { + if (fabs(this->getAngle() / 180.0 * std::numbers::pi) < Precision::Angular()) { QMessageBox::critical(this, windowTitle(), tr("Revolution angle span is zero. It must be non-zero.")); ui->angle->setFocus(); diff --git a/src/Mod/Part/Gui/Mirroring.cpp b/src/Mod/Part/Gui/Mirroring.cpp index ad456338cb..09881413da 100644 --- a/src/Mod/Part/Gui/Mirroring.cpp +++ b/src/Mod/Part/Gui/Mirroring.cpp @@ -24,9 +24,6 @@ #ifndef _PreComp_ -// to avoid compiler warnings of redefining contents of basic.h -// later by #include -# define _USE_MATH_DEFINES # include # include @@ -40,7 +37,6 @@ # include # include -# include # include # include # include @@ -182,9 +178,10 @@ Mirroring::Mirroring(QWidget* parent) : QWidget(parent), ui(new Ui_Mirroring) { ui->setupUi(this); - ui->baseX->setRange(-DBL_MAX, DBL_MAX); - ui->baseY->setRange(-DBL_MAX, DBL_MAX); - ui->baseZ->setRange(-DBL_MAX, DBL_MAX); + constexpr double max = std::numeric_limits::max(); + ui->baseX->setRange(-max, max); + ui->baseY->setRange(-max, max); + ui->baseZ->setRange(-max, max); ui->baseX->setUnit(Base::Unit::Length); ui->baseY->setUnit(Base::Unit::Length); ui->baseZ->setUnit(Base::Unit::Length); diff --git a/src/Mod/Part/Gui/PreCompiled.h b/src/Mod/Part/Gui/PreCompiled.h index 2f667b09ab..2eabeb2acf 100644 --- a/src/Mod/Part/Gui/PreCompiled.h +++ b/src/Mod/Part/Gui/PreCompiled.h @@ -45,7 +45,6 @@ #ifdef _PreComp_ // standard -#include #include // STL diff --git a/src/Mod/Part/Gui/PropertyEnumAttacherItem.cpp b/src/Mod/Part/Gui/PropertyEnumAttacherItem.cpp index 4907910d5d..15956d3bd9 100644 --- a/src/Mod/Part/Gui/PropertyEnumAttacherItem.cpp +++ b/src/Mod/Part/Gui/PropertyEnumAttacherItem.cpp @@ -24,10 +24,7 @@ #include "PreCompiled.h" #ifndef _PreComp_ -# ifdef _MSC_VER -# define _USE_MATH_DEFINES -# include -# endif //_MSC_VER +# include #endif // _PreComp_ #include diff --git a/src/Mod/Part/Gui/SectionCutting.cpp b/src/Mod/Part/Gui/SectionCutting.cpp index 55043207c6..0885ffa9ed 100644 --- a/src/Mod/Part/Gui/SectionCutting.cpp +++ b/src/Mod/Part/Gui/SectionCutting.cpp @@ -23,10 +23,6 @@ #include "PreCompiled.h" #ifndef _PreComp_ - -// to avoid compiler warnings of redefining contents of basic.h -// later by #include -# define _USE_MATH_DEFINES // NOLINT # include # include @@ -136,9 +132,10 @@ SectionCut::SectionCut(QWidget* parent) void SectionCut::initSpinBoxes() { - ui->cutX->setRange(-INT_MAX, INT_MAX); - ui->cutY->setRange(-INT_MAX, INT_MAX); - ui->cutZ->setRange(-INT_MAX, INT_MAX); + constexpr int max = std::numeric_limits::max(); + ui->cutX->setRange(-max, max); + ui->cutY->setRange(-max, max); + ui->cutZ->setRange(-max, max); } void SectionCut::initControls(const Base::BoundBox3d& BoundCompound) diff --git a/src/Mod/Part/Gui/SoBrepEdgeSet.cpp b/src/Mod/Part/Gui/SoBrepEdgeSet.cpp index dbb959bea4..6d8c5d2bba 100644 --- a/src/Mod/Part/Gui/SoBrepEdgeSet.cpp +++ b/src/Mod/Part/Gui/SoBrepEdgeSet.cpp @@ -32,7 +32,6 @@ # include # endif # include -# include # include # include # include @@ -85,7 +84,7 @@ void SoBrepEdgeSet::GLRender(SoGLRenderAction *action) selContext2->sl.push_back(-1); }else if(ctx) selContext2->sl = ctx->sl; - if(selContext2->highlightIndex==INT_MAX) { + if(selContext2->highlightIndex == std::numeric_limits::max()) { selContext2->hl.clear(); selContext2->hl.push_back(-1); }else if(ctx) @@ -93,7 +92,7 @@ void SoBrepEdgeSet::GLRender(SoGLRenderAction *action) ctx = selContext2; } - if(ctx && ctx->highlightIndex==INT_MAX) { + if(ctx && ctx->highlightIndex == std::numeric_limits::max()) { if(ctx->selectionIndex.empty() || ctx->isSelectAll()) { if(ctx2) { ctx2->selectionColor = ctx->highlightColor; @@ -329,7 +328,7 @@ void SoBrepEdgeSet::doAction(SoAction* action) if (!detail) { SelContextPtr ctx = Gui::SoFCSelectionRoot::getActionContext(action,this,selContext); ctx->highlightColor = hlaction->getColor(); - ctx->highlightIndex = INT_MAX; + ctx->highlightIndex = std::numeric_limits::max(); ctx->hl.clear(); ctx->hl.push_back(-1); touch(); diff --git a/src/Mod/Part/Gui/SoBrepFaceSet.cpp b/src/Mod/Part/Gui/SoBrepFaceSet.cpp index 14e716f20c..1f0c9be301 100644 --- a/src/Mod/Part/Gui/SoBrepFaceSet.cpp +++ b/src/Mod/Part/Gui/SoBrepFaceSet.cpp @@ -30,7 +30,6 @@ #ifndef _PreComp_ # include -# include # include # include # include @@ -194,7 +193,7 @@ void SoBrepFaceSet::doAction(SoAction* action) const SoDetail* detail = hlaction->getElement(); if (!detail) { SelContextPtr ctx = Gui::SoFCSelectionRoot::getActionContext(action,this,selContext); - ctx->highlightIndex = INT_MAX; + ctx->highlightIndex = std::numeric_limits::max(); ctx->highlightColor = hlaction->getColor(); touch(); }else { @@ -549,7 +548,7 @@ void SoBrepFaceSet::GLRender(SoGLRenderAction *action) // Transparency complicates stuff even more, but not here. It will be handled inside // overrideMaterialBinding() // - if(ctx && ctx->highlightIndex==INT_MAX) { + if(ctx && ctx->highlightIndex == std::numeric_limits::max()) { if(ctx->selectionIndex.empty() || ctx->isSelectAll()) { if(ctx2) { ctx2->selectionColor = ctx->highlightColor; @@ -1222,7 +1221,7 @@ void SoBrepFaceSet::renderHighlight(SoGLRenderAction *action, SelContextPtr ctx) mb.sendFirst(); // make sure we have the correct material int id = ctx->highlightIndex; - if (id!=INT_MAX && id >= this->partIndex.getNum()) { + if (id != std::numeric_limits::max() && id >= this->partIndex.getNum()) { SoDebugError::postWarning("SoBrepFaceSet::renderHighlight", "highlightIndex out of range"); } else { @@ -1234,7 +1233,7 @@ void SoBrepFaceSet::renderHighlight(SoGLRenderAction *action, SelContextPtr ctx) // coords int start=0; int length; - if(id==INT_MAX) { + if(id == std::numeric_limits::max()) { length = numindices; id = 0; } else { diff --git a/src/Mod/Part/Gui/SoBrepPointSet.cpp b/src/Mod/Part/Gui/SoBrepPointSet.cpp index def234fcd6..e8cef49446 100644 --- a/src/Mod/Part/Gui/SoBrepPointSet.cpp +++ b/src/Mod/Part/Gui/SoBrepPointSet.cpp @@ -32,7 +32,6 @@ # include # endif # include -# include # include # include # include @@ -82,7 +81,7 @@ void SoBrepPointSet::GLRender(SoGLRenderAction *action) if(selContext2->checkGlobal(ctx)) ctx = selContext2; - if(ctx && ctx->highlightIndex==INT_MAX) { + if(ctx && ctx->highlightIndex == std::numeric_limits::max()) { if(ctx->selectionIndex.empty() || ctx->isSelectAll()) { if(ctx2) { ctx2->selectionColor = ctx->highlightColor; @@ -192,7 +191,7 @@ void SoBrepPointSet::renderHighlight(SoGLRenderAction *action, SelContextPtr ctx int id = ctx->highlightIndex; const SbVec3f * coords3d = coords->getArrayPtr3(); if(coords3d) { - if(id == INT_MAX) { + if(id == std::numeric_limits::max()) { glBegin(GL_POINTS); for(int idx=startIndex.getValue();idxgetNum();++idx) glVertex3fv((const GLfloat*) (coords3d + idx)); @@ -269,7 +268,7 @@ void SoBrepPointSet::doAction(SoAction* action) SelContextPtr ctx = Gui::SoFCSelectionRoot::getActionContext(action,this,selContext); const SoDetail* detail = hlaction->getElement(); if (!detail) { - ctx->highlightIndex = INT_MAX; + ctx->highlightIndex = std::numeric_limits::max(); ctx->highlightColor = hlaction->getColor(); touch(); return; diff --git a/src/Mod/Part/Gui/SoFCShapeObject.cpp b/src/Mod/Part/Gui/SoFCShapeObject.cpp index 50691d970b..1ee21830ce 100644 --- a/src/Mod/Part/Gui/SoFCShapeObject.cpp +++ b/src/Mod/Part/Gui/SoFCShapeObject.cpp @@ -32,7 +32,6 @@ # include # endif # include -# include # include # include # include @@ -160,9 +159,10 @@ void SoFCControlPoints::computeBBox(SoAction *action, SbBox3f &box, SbVec3f &cen const SbVec3f * points = coords->getArrayPtr3(); if (!points) return; - float maxX=-FLT_MAX, minX=FLT_MAX, - maxY=-FLT_MAX, minY=FLT_MAX, - maxZ=-FLT_MAX, minZ=FLT_MAX; + constexpr float floatMax = std::numeric_limits::max(); + float maxX=-floatMax, minX=floatMax, + maxY=-floatMax, minY=floatMax, + maxZ=-floatMax, minZ=floatMax; int32_t len = coords->getNum(); if (len > 0) { for (int32_t i=0; iui.spinOffset->setUnit(Base::Unit::Length); - d->ui.spinOffset->setRange(-INT_MAX, INT_MAX); + d->ui.spinOffset->setRange(-std::numeric_limits::max(), + std::numeric_limits::max()); d->ui.spinOffset->setSingleStep(0.1); d->ui.facesButton->hide(); diff --git a/src/Mod/Part/Gui/TaskThickness.cpp b/src/Mod/Part/Gui/TaskThickness.cpp index 09ed63c457..cc5e27dafc 100644 --- a/src/Mod/Part/Gui/TaskThickness.cpp +++ b/src/Mod/Part/Gui/TaskThickness.cpp @@ -91,7 +91,8 @@ ThicknessWidget::ThicknessWidget(Part::Thickness* thickness, QWidget* parent) d->ui.fillOffset->hide(); QSignalBlocker blockOffset(d->ui.spinOffset); - d->ui.spinOffset->setRange(-INT_MAX, INT_MAX); + d->ui.spinOffset->setRange(-std::numeric_limits::max(), + std::numeric_limits::max()); d->ui.spinOffset->setSingleStep(0.1); d->ui.spinOffset->setValue(d->thickness->Value.getValue()); diff --git a/src/Mod/Part/Gui/ViewProvider2DObject.cpp b/src/Mod/Part/Gui/ViewProvider2DObject.cpp index 50f8a20502..b54dab267f 100644 --- a/src/Mod/Part/Gui/ViewProvider2DObject.cpp +++ b/src/Mod/Part/Gui/ViewProvider2DObject.cpp @@ -23,7 +23,6 @@ #include "PreCompiled.h" #ifndef _PreComp_ -# include # include @@ -57,7 +56,8 @@ using namespace std; // Construction/Destruction const char* ViewProvider2DObjectGrid::GridStyleEnums[]= {"Dashed","Light",nullptr}; -App::PropertyQuantityConstraint::Constraints ViewProvider2DObjectGrid::GridSizeRange = {0.001,DBL_MAX,1.0}; +App::PropertyQuantityConstraint::Constraints ViewProvider2DObjectGrid::GridSizeRange = { + 0.001, std::numeric_limits::max(), 1.0}; PROPERTY_SOURCE(PartGui::ViewProvider2DObjectGrid, PartGui::ViewProvider2DObject) @@ -106,10 +106,11 @@ SoSeparator* ViewProvider2DObjectGrid::createGrid() else { // make sure that nine of the numbers are exactly zero because log(0) // is not defined - float xMin = std::abs(MinX) < FLT_EPSILON ? 0.01f : MinX; - float xMax = std::abs(MaxX) < FLT_EPSILON ? 0.01f : MaxX; - float yMin = std::abs(MinY) < FLT_EPSILON ? 0.01f : MinY; - float yMax = std::abs(MaxY) < FLT_EPSILON ? 0.01f : MaxY; + constexpr float floatEpsilon = std::numeric_limits::epsilon(); + float xMin = std::abs(MinX) < floatEpsilon ? 0.01f : MinX; + float xMax = std::abs(MaxX) < floatEpsilon ? 0.01f : MaxX; + float yMin = std::abs(MinY) < floatEpsilon ? 0.01f : MinY; + float yMax = std::abs(MaxY) < floatEpsilon ? 0.01f : MaxY; MiX = -exp(ceil(log(std::abs(xMin)))); MiX = std::min(MiX,(float)-exp(ceil(log(std::abs(0.1f*xMax))))); MaX = exp(ceil(log(std::abs(xMax)))); diff --git a/src/Mod/Part/Gui/ViewProviderAttachExtension.cpp b/src/Mod/Part/Gui/ViewProviderAttachExtension.cpp index 848fa93336..0a99471950 100644 --- a/src/Mod/Part/Gui/ViewProviderAttachExtension.cpp +++ b/src/Mod/Part/Gui/ViewProviderAttachExtension.cpp @@ -23,10 +23,6 @@ #include "PreCompiled.h" #ifndef _PreComp_ -# ifdef _MSC_VER -# define _USE_MATH_DEFINES -# include -# endif # include # include #endif diff --git a/src/Mod/Part/Gui/ViewProviderExt.cpp b/src/Mod/Part/Gui/ViewProviderExt.cpp index 0c23016cd6..6c771fa64e 100644 --- a/src/Mod/Part/Gui/ViewProviderExt.cpp +++ b/src/Mod/Part/Gui/ViewProviderExt.cpp @@ -976,7 +976,7 @@ void ViewProviderPartExt::updateVisual() //deflection = std::min(deflection, 20.0); // create or use the mesh on the data structure - Standard_Real AngDeflectionRads = AngularDeflection.getValue() / 180.0 * M_PI; + Standard_Real AngDeflectionRads = AngularDeflection.getValue() / 180.0 * std::numbers::pi; IMeshTools_Parameters meshParams; meshParams.Deflection = deflection; diff --git a/src/Mod/Part/Gui/ViewProviderGridExtension.cpp b/src/Mod/Part/Gui/ViewProviderGridExtension.cpp index d0d0a84eee..f624823a22 100644 --- a/src/Mod/Part/Gui/ViewProviderGridExtension.cpp +++ b/src/Mod/Part/Gui/ViewProviderGridExtension.cpp @@ -23,7 +23,6 @@ #include "PreCompiled.h" #ifndef _PreComp_ -# include # include # include @@ -60,7 +59,8 @@ using namespace std; EXTENSION_PROPERTY_SOURCE(PartGui::ViewProviderGridExtension, Gui::ViewProviderExtension) -App::PropertyQuantityConstraint::Constraints ViewProviderGridExtension::GridSizeRange = { 0.001,DBL_MAX,1.0 }; +App::PropertyQuantityConstraint::Constraints ViewProviderGridExtension::GridSizeRange = { + 0.001, std::numeric_limits::max(), 1.0 }; namespace PartGui { diff --git a/src/Mod/Part/Gui/ViewProviderReference.cpp b/src/Mod/Part/Gui/ViewProviderReference.cpp index d6f272d076..b679a7d60a 100644 --- a/src/Mod/Part/Gui/ViewProviderReference.cpp +++ b/src/Mod/Part/Gui/ViewProviderReference.cpp @@ -23,9 +23,6 @@ #include "PreCompiled.h" #ifndef _PreComp_ -// to avoid compiler warnings of redefining contents of basic.h -// later by #include "ViewProvider.h" -# define _USE_MATH_DEFINES # include # include diff --git a/src/Mod/PartDesign/App/BodyPy.xml b/src/Mod/PartDesign/App/BodyPy.xml deleted file mode 100644 index d2dd7cab87..0000000000 --- a/src/Mod/PartDesign/App/BodyPy.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - PartDesign body class - - - - insertObject(feature, target, after=False) -Insert the feature into the body after the given feature. - -@param feature The feature to insert into the body -@param target The feature relative which one should be inserted the given. - If target is NULL than insert into the end if where is InsertBefore - and into the begin if where is InsertAfter. -@param after if true insert the feature after the target. Default is false. - -@note the method doesn't modify the Tip unlike addObject() - - - - - - Return the visible feature of this body - - - - - diff --git a/src/Mod/PartDesign/App/CMakeLists.txt b/src/Mod/PartDesign/App/CMakeLists.txt index a07159f787..27481c47c7 100644 --- a/src/Mod/PartDesign/App/CMakeLists.txt +++ b/src/Mod/PartDesign/App/CMakeLists.txt @@ -11,9 +11,7 @@ include_directories( ) link_directories(${OCC_LIBRARY_DIR}) -generate_from_xml(BodyPy) generate_from_py(Body) -generate_from_xml(FeaturePy) generate_from_py(Feature) @@ -125,9 +123,9 @@ SOURCE_GROUP("Module" FILES ${Module_SRCS}) SET(Python_SRCS - BodyPy.xml + Body.pyi BodyPyImp.cpp - FeaturePy.xml + Feature.pyi FeaturePyImp.cpp ) SOURCE_GROUP("Python" FILES ${Python_SRCS}) diff --git a/src/Mod/PartDesign/App/FeatureChamfer.cpp b/src/Mod/PartDesign/App/FeatureChamfer.cpp index 9a0bfab347..c09aa683d2 100644 --- a/src/Mod/PartDesign/App/FeatureChamfer.cpp +++ b/src/Mod/PartDesign/App/FeatureChamfer.cpp @@ -35,6 +35,8 @@ # include #endif +#include + #include #include #include @@ -49,7 +51,7 @@ using namespace PartDesign; PROPERTY_SOURCE(PartDesign::Chamfer, PartDesign::DressUp) const char* ChamferTypeEnums[] = {"Equal distance", "Two distances", "Distance and Angle", nullptr}; -const App::PropertyQuantityConstraint::Constraints Chamfer::floatSize = {0.0, FLT_MAX, 0.1}; +const App::PropertyQuantityConstraint::Constraints Chamfer::floatSize = {0.0, std::numeric_limits::max(), 0.1}; const App::PropertyAngle::Constraints Chamfer::floatAngle = {0.0, 180.0, 1.0}; static App::DocumentObjectExecReturn *validateParameters(int chamferType, double size, double size2, double angle); diff --git a/src/Mod/PartDesign/App/FeatureDraft.cpp b/src/Mod/PartDesign/App/FeatureDraft.cpp index 7b74b486fc..d317b77863 100644 --- a/src/Mod/PartDesign/App/FeatureDraft.cpp +++ b/src/Mod/PartDesign/App/FeatureDraft.cpp @@ -244,7 +244,7 @@ App::DocumentObjectExecReturn *Draft::execute() if (c.GetType() != GeomAbs_Line) throw Base::TypeError("Neutral plane reference edge must be linear"); double a = c.Line().Angle(gp_Lin(c.Value(c.FirstParameter()), pullDirection)); - if (std::fabs(a - M_PI_2) > Precision::Confusion()) + if (std::fabs(a - std::numbers::pi/2) > Precision::Confusion()) throw Base::ValueError("Neutral plane reference edge must be normal to pull direction"); neutralPlane = gp_Pln(c.Value(c.FirstParameter()), pullDirection); } else { diff --git a/src/Mod/PartDesign/App/FeatureExtrude.cpp b/src/Mod/PartDesign/App/FeatureExtrude.cpp index 73659cc6bd..fbe4560184 100644 --- a/src/Mod/PartDesign/App/FeatureExtrude.cpp +++ b/src/Mod/PartDesign/App/FeatureExtrude.cpp @@ -50,7 +50,8 @@ using namespace PartDesign; PROPERTY_SOURCE(PartDesign::FeatureExtrude, PartDesign::ProfileBased) -App::PropertyQuantityConstraint::Constraints FeatureExtrude::signedLengthConstraint = { -DBL_MAX, DBL_MAX, 1.0 }; +App::PropertyQuantityConstraint::Constraints FeatureExtrude::signedLengthConstraint = { + -std::numeric_limits::max(), std::numeric_limits::max(), 1.0 }; double FeatureExtrude::maxAngle = 90 - Base::toDegrees(Precision::Angular()); App::PropertyAngle::Constraints FeatureExtrude::floatAngle = { -maxAngle, maxAngle, 1.0 }; @@ -714,11 +715,13 @@ App::DocumentObjectExecReturn* FeatureExtrude::buildExtrusion(ExtrudeOptions opt } } else { + using std::numbers::pi; + Part::ExtrusionParameters params; params.dir = dir; params.solid = makeface; - params.taperAngleFwd = this->TaperAngle.getValue() * M_PI / 180.0; - params.taperAngleRev = this->TaperAngle2.getValue() * M_PI / 180.0; + params.taperAngleFwd = this->TaperAngle.getValue() * pi / 180.0; + params.taperAngleRev = this->TaperAngle2.getValue() * pi / 180.0; if (L2 == 0.0 && Midplane.getValue()) { params.lengthFwd = L / 2; params.lengthRev = L / 2; @@ -732,8 +735,8 @@ App::DocumentObjectExecReturn* FeatureExtrude::buildExtrusion(ExtrudeOptions opt } if (std::fabs(params.taperAngleFwd) >= Precision::Angular() || std::fabs(params.taperAngleRev) >= Precision::Angular()) { - if (fabs(params.taperAngleFwd) > M_PI * 0.5 - Precision::Angular() - || fabs(params.taperAngleRev) > M_PI * 0.5 - Precision::Angular()) { + if (fabs(params.taperAngleFwd) > pi * 0.5 - Precision::Angular() + || fabs(params.taperAngleRev) > pi * 0.5 - Precision::Angular()) { return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP( "Exception", "Magnitude of taper angle matches or exceeds 90 degrees")); diff --git a/src/Mod/PartDesign/App/FeatureFillet.cpp b/src/Mod/PartDesign/App/FeatureFillet.cpp index 25d60c2778..c44653a76f 100644 --- a/src/Mod/PartDesign/App/FeatureFillet.cpp +++ b/src/Mod/PartDesign/App/FeatureFillet.cpp @@ -44,7 +44,7 @@ using namespace PartDesign; PROPERTY_SOURCE(PartDesign::Fillet, PartDesign::DressUp) -const App::PropertyQuantityConstraint::Constraints floatRadius = {0.0,FLT_MAX,0.1}; +const App::PropertyQuantityConstraint::Constraints floatRadius = {0.0, std::numeric_limits::max(), 0.1}; Fillet::Fillet() { diff --git a/src/Mod/PartDesign/App/FeatureGroove.cpp b/src/Mod/PartDesign/App/FeatureGroove.cpp index 66f27fdcd3..3df6e019eb 100644 --- a/src/Mod/PartDesign/App/FeatureGroove.cpp +++ b/src/Mod/PartDesign/App/FeatureGroove.cpp @@ -270,7 +270,7 @@ void Groove::generateRevolution(TopoDS_Shape& revol, angleOffset = angle2 * -1.0; } else if (method == RevolMethod::ThroughAll) { - angleTotal = 2 * M_PI; + angleTotal = 2 * std::numbers::pi; } else if (midplane) { // Rotate the face by half the angle to get Groove symmetric to sketch plane diff --git a/src/Mod/PartDesign/App/FeatureHelix.cpp b/src/Mod/PartDesign/App/FeatureHelix.cpp index 94cfab1c86..7acc3f267b 100644 --- a/src/Mod/PartDesign/App/FeatureHelix.cpp +++ b/src/Mod/PartDesign/App/FeatureHelix.cpp @@ -61,8 +61,8 @@ const char* Helix::ModeEnums[] = { "pitch-height-angle", "pitch-turns-angle", "h PROPERTY_SOURCE(PartDesign::Helix, PartDesign::ProfileBased) // we purposely use not FLT_MAX because this would not be computable -const App::PropertyFloatConstraint::Constraints Helix::floatTurns = { Precision::Confusion(), INT_MAX, 1.0 }; -const App::PropertyFloatConstraint::Constraints Helix::floatTolerance = { 0.1, INT_MAX, 1.0 }; +const App::PropertyFloatConstraint::Constraints Helix::floatTurns = { Precision::Confusion(), std::numeric_limits::max(), 1.0 }; +const App::PropertyFloatConstraint::Constraints Helix::floatTolerance = { 0.1, std::numeric_limits::max(), 1.0 }; const App::PropertyAngle::Constraints Helix::floatAngle = { -89.0, 89.0, 1.0 }; Helix::Helix() @@ -442,13 +442,13 @@ TopoDS_Shape Helix::generateHelixPath(double breakAtTurn) // because of the radius factor we used above, we must reverse after the // startOffset movement (that brings the path back to the desired position) if (reversed) { - mov.SetRotation(gp_Ax1(origo, dir_axis2), M_PI); + mov.SetRotation(gp_Ax1(origo, dir_axis2), std::numbers::pi); TopLoc_Location loc(mov); path.Move(loc); } if (turned) { // turn the helix so that the starting point aligns with the profile - mov.SetRotation(gp_Ax1(origo, dir_axis1), M_PI); + mov.SetRotation(gp_Ax1(origo, dir_axis1), std::numbers::pi); TopLoc_Location loc(mov); path.Move(loc); } @@ -495,7 +495,7 @@ double Helix::safePitch() } } - double angle = Angle.getValue() / 180.0 * M_PI; + double angle = Angle.getValue() / 180.0 * std::numbers::pi; gp_Dir direction(axisVec.x, axisVec.y, axisVec.z); gp_Dir directionStart(startVec.x, startVec.y, startVec.z); TopoDS_Shape sketchshape = getVerifiedFace(); diff --git a/src/Mod/PartDesign/App/FeatureHole.cpp b/src/Mod/PartDesign/App/FeatureHole.cpp index 18dbb723ec..d3fc78e2f9 100644 --- a/src/Mod/PartDesign/App/FeatureHole.cpp +++ b/src/Mod/PartDesign/App/FeatureHole.cpp @@ -732,7 +732,7 @@ PROPERTY_SOURCE(PartDesign::Hole, PartDesign::ProfileBased) const App::PropertyAngle::Constraints Hole::floatAngle = { Base::toDegrees(Precision::Angular()), 180.0 - Base::toDegrees(Precision::Angular()), 1.0 }; // OCC can only create holes with a min diameter of 10 times the Precision::Confusion() -const App::PropertyQuantityConstraint::Constraints diameterRange = { 10 * Precision::Confusion(), FLT_MAX, 1.0 }; +const App::PropertyQuantityConstraint::Constraints diameterRange = { 10 * Precision::Confusion(), std::numeric_limits::max(), 1.0 }; Hole::Hole() { @@ -2244,7 +2244,7 @@ TopoDS_Shape Hole::makeThread(const gp_Vec& xDir, const gp_Vec& zDir, double len // | base-sharpV Rmaj H // the little adjustment of p1 and p4 is here to prevent coincidencies - double marginX = std::tan(62.5 * M_PI / 180.0) * marginZ; + double marginX = std::tan(62.5 * std::numbers::pi / 180.0) * marginZ; gp_Pnt p1 = toPnt( (RmajC - 5 * H / 6 + marginX) * xDir @@ -2281,7 +2281,7 @@ TopoDS_Shape Hole::makeThread(const gp_Vec& xDir, const gp_Vec& zDir, double len // | base-sharpV Rmaj // the little adjustment of p1 and p4 is here to prevent coincidencies - double marginX = std::tan(60.0 * M_PI / 180.0) * marginZ; + double marginX = std::tan(60.0 * std::numbers::pi / 180.0) * marginZ; gp_Pnt p1 = toPnt( (RmajC - h + marginX) * xDir + marginZ * zDir @@ -2343,7 +2343,7 @@ TopoDS_Shape Hole::makeThread(const gp_Vec& xDir, const gp_Vec& zDir, double len // Reverse the direction of the helix. So that it goes into the material gp_Trsf mov; - mov.SetRotation(gp_Ax1(origo, dir_axis2), M_PI); + mov.SetRotation(gp_Ax1(origo, dir_axis2), std::numbers::pi); TopLoc_Location loc1(mov); helix.Move(loc1); diff --git a/src/Mod/PartDesign/App/FeatureLinearPattern.cpp b/src/Mod/PartDesign/App/FeatureLinearPattern.cpp index 58455b4624..9d01fc33ab 100644 --- a/src/Mod/PartDesign/App/FeatureLinearPattern.cpp +++ b/src/Mod/PartDesign/App/FeatureLinearPattern.cpp @@ -49,7 +49,8 @@ namespace PartDesign { PROPERTY_SOURCE(PartDesign::LinearPattern, PartDesign::Transformed) -const App::PropertyIntegerConstraint::Constraints LinearPattern::intOccurrences = { 1, INT_MAX, 1 }; +const App::PropertyIntegerConstraint::Constraints LinearPattern::intOccurrences = { + 1, std::numeric_limits::max(), 1 }; const char* LinearPattern::ModeEnums[] = { "length", "offset", nullptr }; diff --git a/src/Mod/PartDesign/App/FeaturePolarPattern.cpp b/src/Mod/PartDesign/App/FeaturePolarPattern.cpp index 0647a3ee17..7272147711 100644 --- a/src/Mod/PartDesign/App/FeaturePolarPattern.cpp +++ b/src/Mod/PartDesign/App/FeaturePolarPattern.cpp @@ -48,7 +48,8 @@ namespace PartDesign { PROPERTY_SOURCE(PartDesign::PolarPattern, PartDesign::Transformed) -const App::PropertyIntegerConstraint::Constraints PolarPattern::intOccurrences = { 1, INT_MAX, 1 }; +const App::PropertyIntegerConstraint::Constraints PolarPattern::intOccurrences = { + 1, std::numeric_limits::max(), 1 }; const App::PropertyAngle::Constraints PolarPattern::floatAngle = { Base::toDegrees(Precision::Angular()), 360.0, 1.0 }; const char* PolarPattern::ModeEnums[] = {"angle", "offset", nullptr}; diff --git a/src/Mod/PartDesign/App/FeaturePrimitive.cpp b/src/Mod/PartDesign/App/FeaturePrimitive.cpp index 21cc034d8d..72e772765a 100644 --- a/src/Mod/PartDesign/App/FeaturePrimitive.cpp +++ b/src/Mod/PartDesign/App/FeaturePrimitive.cpp @@ -56,8 +56,8 @@ const App::PropertyQuantityConstraint::Constraints angleRangeU = { 0.0, 360.0, 1 const App::PropertyQuantityConstraint::Constraints angleRangeV = { -90.0, 90.0, 1.0 }; // it turned out that OCC cannot e.g. create a box with a width of Precision::Confusion() // with two times Precision::Confusion() all geometric primitives can be created -const App::PropertyQuantityConstraint::Constraints quantityRange = { 2 * Precision::Confusion(), FLT_MAX, 0.1 }; -const App::PropertyQuantityConstraint::Constraints quantityRangeZero = { 0.0, FLT_MAX, 0.1 }; +const App::PropertyQuantityConstraint::Constraints quantityRange = { 2 * Precision::Confusion(), std::numeric_limits::max(), 0.1 }; +const App::PropertyQuantityConstraint::Constraints quantityRangeZero = { 0.0, std::numeric_limits::max(), 0.1 }; PROPERTY_SOURCE_WITH_EXTENSIONS(PartDesign::FeaturePrimitive, PartDesign::FeatureAddSub) diff --git a/src/Mod/PartDesign/App/FeaturePy.xml b/src/Mod/PartDesign/App/FeaturePy.xml deleted file mode 100644 index f8d37e0a80..0000000000 --- a/src/Mod/PartDesign/App/FeaturePy.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - This is the father of all PartDesign object classes - - - - getBaseObject: returns feature this one fuses itself to, or None. Normally, this should be the same as BaseFeature property, except for legacy workflow. In legacy workflow, it will look up the support of referenced sketch. - - - - diff --git a/src/Mod/PartDesign/Gui/CMakeLists.txt b/src/Mod/PartDesign/Gui/CMakeLists.txt index 7fdf0b18a2..8a980b71e1 100644 --- a/src/Mod/PartDesign/Gui/CMakeLists.txt +++ b/src/Mod/PartDesign/Gui/CMakeLists.txt @@ -9,7 +9,6 @@ include_directories( ${CMAKE_SOURCE_DIR}/src/3rdParty/json/single_include/nlohmann/ ) -generate_from_xml(ViewProviderPy) generate_from_py(ViewProvider) @@ -230,7 +229,7 @@ SOURCE_GROUP("Module" FILES ${PartDesignGuiModule_SRCS}) SET(Python_SRCS - ViewProviderPy.xml + ViewProvider.pyi ViewProviderPyImp.cpp ) SOURCE_GROUP("Python" FILES ${Python_SRCS}) diff --git a/src/Mod/PartDesign/Gui/TaskHoleParameters.cpp b/src/Mod/PartDesign/Gui/TaskHoleParameters.cpp index d690da3512..658b3314f9 100644 --- a/src/Mod/PartDesign/Gui/TaskHoleParameters.cpp +++ b/src/Mod/PartDesign/Gui/TaskHoleParameters.cpp @@ -128,7 +128,7 @@ TaskHoleParameters::TaskHoleParameters(ViewProviderHole* HoleView, QWidget* pare || pcHole->HoleCutCustomValues.isReadOnly() ); // HoleCutDiameter must not be smaller or equal than the Diameter - ui->HoleCutDiameter->setMinimum(pcHole->Diameter.getValue() + 0.1); + updateHoleCutLimits(pcHole); ui->HoleCutDiameter->setValue(pcHole->HoleCutDiameter.getValue()); ui->HoleCutDiameter->setDisabled(pcHole->HoleCutDiameter.isReadOnly()); ui->HoleCutDepth->setValue(pcHole->HoleCutDepth.getValue()); @@ -731,8 +731,7 @@ void TaskHoleParameters::threadDiameterChanged(double value) if (auto hole = getObject()) { hole->Diameter.setValue(value); - // HoleCutDiameter must not be smaller or equal than the Diameter - ui->HoleCutDiameter->setMinimum(value + 0.1); + updateHoleCutLimits(hole); recomputeFeature(); } @@ -838,6 +837,7 @@ void TaskHoleParameters::changedObject(const App::Document&, const App::Property else if (&Prop == &hole->Diameter) { ui->Diameter->setEnabled(true); updateSpinBox(ui->Diameter, hole->Diameter.getValue()); + updateHoleCutLimits(hole); } else if (&Prop == &hole->ThreadDirection) { ui->directionRightHand->setEnabled(true); @@ -1130,6 +1130,14 @@ void TaskHoleParameters::apply() isApplying = false; } +void TaskHoleParameters::updateHoleCutLimits(PartDesign::Hole* hole) +{ + constexpr double minHoleCutDifference = 0.1; + // HoleCutDiameter must not be smaller or equal than the Diameter + ui->HoleCutDiameter->setMinimum(hole->Diameter.getValue() + minHoleCutDifference); +} + + //************************************************************************** //************************************************************************** // TaskDialog diff --git a/src/Mod/PartDesign/Gui/TaskHoleParameters.h b/src/Mod/PartDesign/Gui/TaskHoleParameters.h index a3f2e30c62..1af45a2d41 100644 --- a/src/Mod/PartDesign/Gui/TaskHoleParameters.h +++ b/src/Mod/PartDesign/Gui/TaskHoleParameters.h @@ -126,6 +126,7 @@ protected: private: void onSelectionChanged(const Gui::SelectionChanges &msg) override; + void updateHoleCutLimits(PartDesign::Hole* hole); private: diff --git a/src/Mod/PartDesign/Gui/TaskPrimitiveParameters.cpp b/src/Mod/PartDesign/Gui/TaskPrimitiveParameters.cpp index 539ed47128..a6d37a9362 100644 --- a/src/Mod/PartDesign/Gui/TaskPrimitiveParameters.cpp +++ b/src/Mod/PartDesign/Gui/TaskPrimitiveParameters.cpp @@ -222,26 +222,26 @@ TaskBoxPrimitives::TaskBoxPrimitives(ViewProviderPrimitive* vp, QWidget* parent) ui->wedgeZ2max->bind(getObject()->Z2max); ui->wedgeZ2min->setValue(getObject()->Z2min.getValue()); ui->wedgeZ2min->bind(getObject()->Z2min); - ui->wedgeXmin->setMinimum(INT_MIN); + ui->wedgeXmin->setMinimum(std::numeric_limits::min()); ui->wedgeXmin->setMaximum(ui->wedgeXmax->rawValue()); // must be < than wedgeXmax - ui->wedgeYmin->setMinimum(INT_MIN); + ui->wedgeYmin->setMinimum(std::numeric_limits::min()); ui->wedgeYmin->setMaximum(ui->wedgeYmax->rawValue()); // must be < than wedgeYmax - ui->wedgeZmin->setMinimum(INT_MIN); + ui->wedgeZmin->setMinimum(std::numeric_limits::min()); ui->wedgeZmin->setMaximum(ui->wedgeZmax->rawValue()); // must be < than wedgeZmax - ui->wedgeX2min->setMinimum(INT_MIN); + ui->wedgeX2min->setMinimum(std::numeric_limits::min());; ui->wedgeX2min->setMaximum(ui->wedgeX2max->rawValue()); // must be <= than wedgeXmax - ui->wedgeZ2min->setMinimum(INT_MIN); + ui->wedgeZ2min->setMinimum(std::numeric_limits::min());; ui->wedgeZ2min->setMaximum(ui->wedgeZ2max->rawValue()); // must be <= than wedgeXmax ui->wedgeXmax->setMinimum(ui->wedgeXmin->rawValue()); - ui->wedgeXmax->setMaximum(INT_MAX); + ui->wedgeXmax->setMaximum(std::numeric_limits::max()); ui->wedgeYmax->setMinimum(ui->wedgeYmin->rawValue()); - ui->wedgeYmax->setMaximum(INT_MAX); + ui->wedgeYmax->setMaximum(std::numeric_limits::max()); ui->wedgeZmax->setMinimum(ui->wedgeZmin->rawValue()); - ui->wedgeZmax->setMaximum(INT_MAX); + ui->wedgeZmax->setMaximum(std::numeric_limits::max()); ui->wedgeX2max->setMinimum(ui->wedgeX2min->rawValue()); - ui->wedgeX2max->setMaximum(INT_MAX); + ui->wedgeX2max->setMaximum(std::numeric_limits::max()); ui->wedgeZ2max->setMinimum(ui->wedgeZ2min->rawValue()); - ui->wedgeZ2max->setMaximum(INT_MAX); + ui->wedgeZ2max->setMaximum(std::numeric_limits::max()); break; } diff --git a/src/Mod/PartDesign/Gui/TaskScaledParameters.cpp b/src/Mod/PartDesign/Gui/TaskScaledParameters.cpp index c6b30c4b75..ffb399a6ed 100644 --- a/src/Mod/PartDesign/Gui/TaskScaledParameters.cpp +++ b/src/Mod/PartDesign/Gui/TaskScaledParameters.cpp @@ -78,7 +78,7 @@ void TaskScaledParameters::setupParameterUI(QWidget* widget) auto pcScaled = getObject(); ui->spinFactor->bind(pcScaled->Factor); - ui->spinOccurrences->setMaximum(INT_MAX); + ui->spinOccurrences->setMaximum(std::numeric_limits::max()); ui->spinOccurrences->bind(pcScaled->Occurrences); ui->spinFactor->setEnabled(true); ui->spinOccurrences->setEnabled(true); diff --git a/src/Mod/PartDesign/Gui/Utils.cpp b/src/Mod/PartDesign/Gui/Utils.cpp index 3976f848cf..09346de5d7 100644 --- a/src/Mod/PartDesign/Gui/Utils.cpp +++ b/src/Mod/PartDesign/Gui/Utils.cpp @@ -327,7 +327,7 @@ void fixSketchSupport (Sketcher::SketchObject* sketch) // Offset to base plane // Find out which direction we need to offset double a = sketchVector.GetAngle(pnt); - if ((a < -M_PI_2) || (a > M_PI_2)) + if ((a < -std::numbers::pi/2) || (a > std::numbers::pi/2)) offset *= -1.0; std::string Datum = doc->getUniqueObjectName("DatumPlane"); diff --git a/src/Mod/PartDesign/Gui/ViewProviderAddSub.cpp b/src/Mod/PartDesign/Gui/ViewProviderAddSub.cpp index 8f44733ad8..66ad7b123d 100644 --- a/src/Mod/PartDesign/Gui/ViewProviderAddSub.cpp +++ b/src/Mod/PartDesign/Gui/ViewProviderAddSub.cpp @@ -121,7 +121,7 @@ void ViewProviderAddSub::updateAddSubShapeIndicator() { Standard_Real deflection = ((xMax-xMin)+(yMax-yMin)+(zMax-zMin))/300.0 * Deviation.getValue(); // create or use the mesh on the data structure - Standard_Real AngDeflectionRads = AngularDeflection.getValue() / 180.0 * M_PI; + Standard_Real AngDeflectionRads = AngularDeflection.getValue() / 180.0 * std::numbers::pi; BRepMesh_IncrementalMesh(cShape, deflection, Standard_False, AngDeflectionRads, Standard_True); // We must reset the location here because the transformation data diff --git a/src/Mod/PartDesign/Gui/ViewProviderDatumCS.cpp b/src/Mod/PartDesign/Gui/ViewProviderDatumCS.cpp index 2961cc4a4e..6c7f7f77c3 100644 --- a/src/Mod/PartDesign/Gui/ViewProviderDatumCS.cpp +++ b/src/Mod/PartDesign/Gui/ViewProviderDatumCS.cpp @@ -45,8 +45,10 @@ using namespace PartDesignGui; PROPERTY_SOURCE(PartDesignGui::ViewProviderDatumCoordinateSystem,PartDesignGui::ViewProviderDatum) -const App::PropertyFloatConstraint::Constraints ZoomConstraint = {0.0,DBL_MAX,0.2}; -const App::PropertyIntegerConstraint::Constraints FontConstraint = {1,INT_MAX,1}; +const App::PropertyFloatConstraint::Constraints ZoomConstraint = { + 0.0, std::numeric_limits::max(), 0.2}; +const App::PropertyIntegerConstraint::Constraints FontConstraint = { + 1,std::numeric_limits::max(), 1}; ViewProviderDatumCoordinateSystem::ViewProviderDatumCoordinateSystem() { diff --git a/src/Mod/PartDesign/Gui/ViewProviderPy.xml b/src/Mod/PartDesign/Gui/ViewProviderPy.xml deleted file mode 100644 index 0cdb9893ba..0000000000 --- a/src/Mod/PartDesign/Gui/ViewProviderPy.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - This is the father of all PartDesign ViewProvider classes - - - -setBodyMode(bool): body mode means that the object is part of a body -and that the body is used to set the visual properties, not the features. Hence -setting body mode to true will hide most viewprovider properties. - - - - -makeTemporaryVisible(bool): makes this viewprovider visible in the -scene graph without changing any properties, not the visibility one and also not -the display mode. This can be used to show the shape of this viewprovider from -other viewproviders without doing anything to the document and properties. - - - - - diff --git a/src/Mod/PartDesign/Gui/ViewProviderTransformed.cpp b/src/Mod/PartDesign/Gui/ViewProviderTransformed.cpp index cca6ab97d7..b3edfa0efe 100644 --- a/src/Mod/PartDesign/Gui/ViewProviderTransformed.cpp +++ b/src/Mod/PartDesign/Gui/ViewProviderTransformed.cpp @@ -228,7 +228,7 @@ void ViewProviderTransformed::showRejectedShape(TopoDS_Shape shape) // create or use the mesh on the data structure // Note: This DOES have an effect on shape - Standard_Real AngDeflectionRads = AngularDeflection.getValue() / 180.0 * M_PI; + Standard_Real AngDeflectionRads = AngularDeflection.getValue() / 180.0 * std::numbers::pi; BRepMesh_IncrementalMesh(shape, deflection, Standard_False, AngDeflectionRads, Standard_True); // We must reset the location here because the transformation data diff --git a/src/Mod/Points/App/PointsGrid.cpp b/src/Mod/Points/App/PointsGrid.cpp index 162b7cbc21..6b43641d5a 100644 --- a/src/Mod/Points/App/PointsGrid.cpp +++ b/src/Mod/Points/App/PointsGrid.cpp @@ -808,7 +808,7 @@ bool PointsGridIterator::InitOnRay(const Base::Vector3d& rclPt, // needed in NextOnRay() to avoid an infinite loop _cSearchPositions.clear(); - _fMaxSearchArea = FLOAT_MAX; + _fMaxSearchArea = std::numeric_limits::max(); raulElements.clear(); diff --git a/src/Mod/Points/App/PointsGrid.h b/src/Mod/Points/App/PointsGrid.h index 6965751178..52a6fc248a 100644 --- a/src/Mod/Points/App/PointsGrid.h +++ b/src/Mod/Points/App/PointsGrid.h @@ -293,7 +293,7 @@ private: Base::Vector3d _clPt; /**< Base point of search ray. */ Base::Vector3d _clDir; /**< Direction of search ray. */ bool _bValidRay {false}; /**< Search ray ok? */ - float _fMaxSearchArea {FLOAT_MAX}; + float _fMaxSearchArea {std::numeric_limits::max()}; /** Checks if a grid position is already visited by NextOnRay(). */ struct GridElement { diff --git a/src/Mod/ReverseEngineering/App/RegionGrowing.cpp b/src/Mod/ReverseEngineering/App/RegionGrowing.cpp index 1bdcd2cd83..68ce1df37f 100644 --- a/src/Mod/ReverseEngineering/App/RegionGrowing.cpp +++ b/src/Mod/ReverseEngineering/App/RegionGrowing.cpp @@ -88,7 +88,7 @@ void RegionGrowing::perform(int ksearch) reg.setInputCloud(cloud); // reg.setIndices (indices); reg.setInputNormals(normals); - reg.setSmoothnessThreshold(3.0 / 180.0 * M_PI); + reg.setSmoothnessThreshold(3.0 / 180.0 * std::numbers::pi); reg.setCurvatureThreshold(1.0); std::vector clusters; @@ -142,7 +142,7 @@ void RegionGrowing::perform(const std::vector& myNormals) reg.setInputCloud(cloud); // reg.setIndices (indices); reg.setInputNormals(normals); - reg.setSmoothnessThreshold(3.0 / 180.0 * M_PI); + reg.setSmoothnessThreshold(3.0 / 180.0 * std::numbers::pi); reg.setCurvatureThreshold(1.0); std::vector clusters; diff --git a/src/Mod/ReverseEngineering/App/SurfaceTriangulation.cpp b/src/Mod/ReverseEngineering/App/SurfaceTriangulation.cpp index 7da03c93a7..a718e20d6d 100644 --- a/src/Mod/ReverseEngineering/App/SurfaceTriangulation.cpp +++ b/src/Mod/ReverseEngineering/App/SurfaceTriangulation.cpp @@ -114,9 +114,9 @@ void SurfaceTriangulation::perform(int ksearch) gp3.setSearchRadius(searchRadius); gp3.setMu(mu); gp3.setMaximumNearestNeighbors(100); - gp3.setMaximumSurfaceAngle(M_PI / 4); // 45 degrees - gp3.setMinimumAngle(M_PI / 18); // 10 degrees - gp3.setMaximumAngle(2 * M_PI / 3); // 120 degrees + gp3.setMaximumSurfaceAngle(std::numbers::pi / 4); // 45 degrees + gp3.setMinimumAngle(std::numbers::pi / 18); // 10 degrees + gp3.setMaximumAngle(2 * std::numbers::pi / 3); // 120 degrees gp3.setNormalConsistency(false); gp3.setConsistentVertexOrdering(true); @@ -171,9 +171,9 @@ void SurfaceTriangulation::perform(const std::vector& normals) gp3.setSearchRadius(searchRadius); gp3.setMu(mu); gp3.setMaximumNearestNeighbors(100); - gp3.setMaximumSurfaceAngle(M_PI / 4); // 45 degrees - gp3.setMinimumAngle(M_PI / 18); // 10 degrees - gp3.setMaximumAngle(2 * M_PI / 3); // 120 degrees + gp3.setMaximumSurfaceAngle(std::numbers::pi / 4); // 45 degrees + gp3.setMinimumAngle(std::numbers::pi / 18); // 10 degrees + gp3.setMaximumAngle(2 * std::numbers::pi / 3); // 120 degrees gp3.setNormalConsistency(true); gp3.setConsistentVertexOrdering(true); diff --git a/src/Mod/ReverseEngineering/Gui/Command.cpp b/src/Mod/ReverseEngineering/Gui/Command.cpp index aac035434c..2b184c39fa 100644 --- a/src/Mod/ReverseEngineering/Gui/Command.cpp +++ b/src/Mod/ReverseEngineering/Gui/Command.cpp @@ -272,7 +272,7 @@ void CmdApproxCylinder::activated(int) fit.SetInitialValues(base, axis); } - if (fit.Fit() < FLOAT_MAX) { + if (fit.Fit() < std::numeric_limits::max()) { Base::Vector3f base, top; fit.GetBounding(base, top); float height = Base::Distance(base, top); @@ -329,7 +329,7 @@ void CmdApproxSphere::activated(int) const MeshCore::MeshKernel& kernel = mesh.getKernel(); MeshCore::SphereFit fit; fit.AddPoints(kernel.GetPoints()); - if (fit.Fit() < FLOAT_MAX) { + if (fit.Fit() < std::numeric_limits::max()) { Base::Vector3f base = fit.GetCenter(); std::stringstream str; @@ -378,7 +378,7 @@ void CmdApproxPolynomial::activated(int) const MeshCore::MeshKernel& kernel = mesh.getKernel(); MeshCore::SurfaceFit fit; fit.AddPoints(kernel.GetPoints()); - if (fit.Fit() < FLOAT_MAX) { + if (fit.Fit() < std::numeric_limits::max()) { Base::BoundBox3f bbox = fit.GetBoundings(); std::vector poles = fit.toBezier(bbox.MinX, bbox.MaxX, bbox.MinY, bbox.MaxY); diff --git a/src/Mod/ReverseEngineering/Gui/FitBSplineSurface.cpp b/src/Mod/ReverseEngineering/Gui/FitBSplineSurface.cpp index d109879661..de9da74cec 100644 --- a/src/Mod/ReverseEngineering/Gui/FitBSplineSurface.cpp +++ b/src/Mod/ReverseEngineering/Gui/FitBSplineSurface.cpp @@ -122,7 +122,7 @@ void FitBSplineSurfaceWidget::onMakePlacementClicked() }); MeshCore::PlaneFit fit; fit.AddPoints(data); - if (fit.Fit() < FLOAT_MAX) { + if (fit.Fit() < std::numeric_limits::max()) { Base::Vector3f base = fit.GetBase(); Base::Vector3f dirU = fit.GetDirU(); Base::Vector3f norm = fit.GetNormal(); diff --git a/src/Mod/ReverseEngineering/Gui/Segmentation.cpp b/src/Mod/ReverseEngineering/Gui/Segmentation.cpp index 54ab2b9d49..6a47a77e86 100644 --- a/src/Mod/ReverseEngineering/Gui/Segmentation.cpp +++ b/src/Mod/ReverseEngineering/Gui/Segmentation.cpp @@ -59,7 +59,7 @@ Segmentation::Segmentation(Mesh::Feature* mesh, QWidget* parent, Qt::WindowFlags , myMesh(mesh) { ui->setupUi(this); - ui->numPln->setRange(1, INT_MAX); + ui->numPln->setRange(1, std::numeric_limits::max()); ui->numPln->setValue(100); ui->checkBoxSmooth->setChecked(false); @@ -115,7 +115,7 @@ void Segmentation::accept() std::vector indexes = kernel.GetFacetPoints(jt); MeshCore::PlaneFit fit; fit.AddPoints(kernel.GetPoints(indexes)); - if (fit.Fit() < FLOAT_MAX) { + if (fit.Fit() < std::numeric_limits::max()) { Base::Vector3f base = fit.GetBase(); Base::Vector3f axis = fit.GetNormal(); MeshCore::AbstractSurfaceFit* fitter = diff --git a/src/Mod/ReverseEngineering/Gui/SegmentationManual.cpp b/src/Mod/ReverseEngineering/Gui/SegmentationManual.cpp index 55b7d376cb..0b0bac5e83 100644 --- a/src/Mod/ReverseEngineering/Gui/SegmentationManual.cpp +++ b/src/Mod/ReverseEngineering/Gui/SegmentationManual.cpp @@ -47,7 +47,7 @@ SegmentationManual::SegmentationManual(QWidget* parent, Qt::WindowFlags fl) { ui->setupUi(this); setupConnections(); - ui->spSelectComp->setRange(1, INT_MAX); + ui->spSelectComp->setRange(1, std::numeric_limits::max()); ui->spSelectComp->setValue(10); Gui::Selection().clearSelection(); @@ -215,7 +215,7 @@ void SegmentationManual::onPlaneDetectClicked() MeshCore::PlaneFit fit; fit.AddPoints(points); - if (fit.Fit() < FLOAT_MAX) { + if (fit.Fit() < std::numeric_limits::max()) { Base::Vector3f base = fit.GetBase(); Base::Vector3f axis = fit.GetNormal(); return new MeshCore::PlaneSurfaceFit(base, axis); @@ -239,7 +239,7 @@ void SegmentationManual::onCylinderDetectClicked() Base::Vector3f axis = fit.GetInitialAxisFromNormals(normal); fit.SetInitialValues(base, axis); } - if (fit.Fit() < FLOAT_MAX) { + if (fit.Fit() < std::numeric_limits::max()) { Base::Vector3f base = fit.GetBase(); Base::Vector3f axis = fit.GetAxis(); float radius = fit.GetRadius(); @@ -259,7 +259,7 @@ void SegmentationManual::onSphereDetectClicked() MeshCore::SphereFit fit; fit.AddPoints(points); - if (fit.Fit() < FLOAT_MAX) { + if (fit.Fit() < std::numeric_limits::max()) { Base::Vector3f base = fit.GetCenter(); float radius = fit.GetRadius(); return new MeshCore::SphereSurfaceFit(base, radius); diff --git a/src/Mod/Robot/App/kdl_cp/chainiksolverpos_nr_jl.cpp b/src/Mod/Robot/App/kdl_cp/chainiksolverpos_nr_jl.cpp index 1ed3c4d72a..3814a77b79 100644 --- a/src/Mod/Robot/App/kdl_cp/chainiksolverpos_nr_jl.cpp +++ b/src/Mod/Robot/App/kdl_cp/chainiksolverpos_nr_jl.cpp @@ -21,17 +21,10 @@ // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +#include + #include "chainiksolverpos_nr_jl.hpp" -// FreeCAD change -#ifndef M_PI - #define M_PI 3.14159265358979323846 /* pi */ -#endif - -#ifndef M_PI_2 - #define M_PI_2 1.57079632679489661923 /* pi/2 */ -#endif - namespace KDL { ChainIkSolverPos_NR_JL::ChainIkSolverPos_NR_JL(const Chain& _chain, const JntArray& _q_min, const JntArray& _q_max, ChainFkSolverPos& _fksolver,ChainIkSolverVel& _iksolver, @@ -60,14 +53,14 @@ namespace KDL for(unsigned int j=0; j q_max(j)) //q_out(j) = q_max(j); // FreeCAD change - q_out(j) = q_out(j) - M_PI *2; + q_out(j) = q_out(j) - std::numbers::pi *2; } } diff --git a/src/Mod/Robot/App/kdl_cp/frames.cpp b/src/Mod/Robot/App/kdl_cp/frames.cpp index 67baff97a5..ff24c29f6e 100644 --- a/src/Mod/Robot/App/kdl_cp/frames.cpp +++ b/src/Mod/Robot/App/kdl_cp/frames.cpp @@ -26,9 +26,7 @@ ***************************************************************************/ #include "frames.hpp" - -#define _USE_MATH_DEFINES // For MSVC -#include +#include namespace KDL { @@ -244,7 +242,7 @@ void Rotation::GetRPY(double& roll,double& pitch,double& yaw) const { double epsilon=1E-12; pitch = atan2(-data[6], sqrt( sqr(data[0]) +sqr(data[3]) ) ); - if ( fabs(pitch) > (M_PI/2.0-epsilon) ) { + if ( fabs(pitch) > (std::numbers::pi/2.0-epsilon) ) { yaw = atan2( -data[1], data[4]); roll = 0.0 ; } else { @@ -358,7 +356,7 @@ double Rotation::GetRotAngle(Vector& axis,double eps) const { return 0; } if (ca < -1+t) { - // The case of angles consisting of multiples of M_PI: + // The case of angles consisting of multiples of std::numbers::pi: // two solutions, choose a positive Z-component of the axis double x = sqrt( (data[0]+1.0)/2); double y = sqrt( (data[4]+1.0)/2); diff --git a/src/Mod/Robot/App/kdl_cp/kdl.hpp b/src/Mod/Robot/App/kdl_cp/kdl.hpp index b02e776aac..56bd5669e8 100644 --- a/src/Mod/Robot/App/kdl_cp/kdl.hpp +++ b/src/Mod/Robot/App/kdl_cp/kdl.hpp @@ -30,7 +30,7 @@ * href="http://www.orocos.org">Orocos, but that can be used * independently of Orocos. KDL offers different kinds of * functionality, grouped in the following Modules: - * - \subpage geomprim + * - \ref geomprim * - \ref KinematicFamily : functionality to build kinematic chains and access their kinematic and dynamic properties, such as e.g. Forward and Inverse kinematics and dynamics. * - \ref Motion : functionality to specify motion trajectories of frames and kinematic chains, such as e.g. Trapezoidal Velocity profiles. * @@ -38,7 +38,7 @@ **/ /** * \page geomprim Geometric Primitives - * \section Introduction + * \section kdl_geometric_prims Introduction * Geometric primitives are represented by the following classes. * - KDL::Vector * - KDL::Rotation diff --git a/src/Mod/Robot/App/kdl_cp/path_roundedcomposite.hpp b/src/Mod/Robot/App/kdl_cp/path_roundedcomposite.hpp index 79c2657f57..ddb4bf7cc2 100644 --- a/src/Mod/Robot/App/kdl_cp/path_roundedcomposite.hpp +++ b/src/Mod/Robot/App/kdl_cp/path_roundedcomposite.hpp @@ -96,7 +96,7 @@ class Path_RoundedComposite : public Path * - 3101 if the eq. radius <= 0 * - 3102 if the first segment in a rounding has zero length. * - 3103 if the second segment in a rounding has zero length. - * - 3104 if the angle between the first and the second segment is close to M_PI. + * - 3104 if the angle between the first and the second segment is close to std::numbers::pi. * (meaning that the segments are on top of each other) * - 3105 if the distance needed for the rounding is larger then the first segment. * - 3106 if the distance needed for the rounding is larger then the second segment. diff --git a/src/Mod/Robot/App/kdl_cp/utilities/rall2d.h b/src/Mod/Robot/App/kdl_cp/utilities/rall2d.h index ef90674391..b80c27d13a 100644 --- a/src/Mod/Robot/App/kdl_cp/utilities/rall2d.h +++ b/src/Mod/Robot/App/kdl_cp/utilities/rall2d.h @@ -26,7 +26,7 @@ #ifndef Rall2D_H #define Rall2D_H -#include +#include #include #include "utility.h" diff --git a/src/Mod/Robot/Gui/ViewProviderRobotObject.cpp b/src/Mod/Robot/Gui/ViewProviderRobotObject.cpp index ec3ecd8feb..5b1852c2ba 100644 --- a/src/Mod/Robot/Gui/ViewProviderRobotObject.cpp +++ b/src/Mod/Robot/Gui/ViewProviderRobotObject.cpp @@ -173,6 +173,8 @@ void ViewProviderRobotObject::onChanged(const App::Property* prop) void ViewProviderRobotObject::updateData(const App::Property* prop) { + using std::numbers::pi; + Robot::RobotObject* robObj = static_cast(pcObject); if (prop == &robObj->RobotVrmlFile) { // read also from file @@ -269,33 +271,33 @@ void ViewProviderRobotObject::updateData(const App::Property* prop) } if (Axis1Node) { Axis1Node->rotation.setValue(SbVec3f(0.0, 1.0, 0.0), - robObj->Axis1.getValue() * (M_PI / 180)); + robObj->Axis1.getValue() * (pi / 180)); } if (Axis2Node) { Axis2Node->rotation.setValue(SbVec3f(0.0, 1.0, 0.0), - robObj->Axis2.getValue() * (M_PI / 180)); + robObj->Axis2.getValue() * (pi / 180)); } if (Axis3Node) { Axis3Node->rotation.setValue(SbVec3f(0.0, 1.0, 0.0), - robObj->Axis3.getValue() * (M_PI / 180)); + robObj->Axis3.getValue() * (pi / 180)); } if (Axis4Node) { Axis4Node->rotation.setValue(SbVec3f(0.0, 1.0, 0.0), - robObj->Axis4.getValue() * (M_PI / 180)); + robObj->Axis4.getValue() * (pi / 180)); } if (Axis5Node) { Axis5Node->rotation.setValue(SbVec3f(0.0, 1.0, 0.0), - robObj->Axis5.getValue() * (M_PI / 180)); + robObj->Axis5.getValue() * (pi / 180)); } if (Axis6Node) { Axis6Node->rotation.setValue(SbVec3f(0.0, 1.0, 0.0), - robObj->Axis6.getValue() * (M_PI / 180)); + robObj->Axis6.getValue() * (pi / 180)); } } else if (prop == &robObj->Axis1) { if (Axis1Node) { Axis1Node->rotation.setValue(SbVec3f(0.0, 1.0, 0.0), - robObj->Axis1.getValue() * (M_PI / 180)); + robObj->Axis1.getValue() * (pi / 180)); if (toolShape) { toolShape->setTransformation( (robObj->Tcp.getValue() * (robObj->ToolBase.getValue().inverse())).toMatrix()); @@ -305,7 +307,7 @@ void ViewProviderRobotObject::updateData(const App::Property* prop) else if (prop == &robObj->Axis2) { if (Axis2Node) { Axis2Node->rotation.setValue(SbVec3f(0.0, 1.0, 0.0), - robObj->Axis2.getValue() * (M_PI / 180)); + robObj->Axis2.getValue() * (pi / 180)); if (toolShape) { toolShape->setTransformation( (robObj->Tcp.getValue() * (robObj->ToolBase.getValue().inverse())).toMatrix()); @@ -315,7 +317,7 @@ void ViewProviderRobotObject::updateData(const App::Property* prop) else if (prop == &robObj->Axis3) { if (Axis3Node) { Axis3Node->rotation.setValue(SbVec3f(0.0, 1.0, 0.0), - robObj->Axis3.getValue() * (M_PI / 180)); + robObj->Axis3.getValue() * (pi / 180)); if (toolShape) { toolShape->setTransformation( (robObj->Tcp.getValue() * (robObj->ToolBase.getValue().inverse())).toMatrix()); @@ -325,7 +327,7 @@ void ViewProviderRobotObject::updateData(const App::Property* prop) else if (prop == &robObj->Axis4) { if (Axis4Node) { Axis4Node->rotation.setValue(SbVec3f(0.0, 1.0, 0.0), - robObj->Axis4.getValue() * (M_PI / 180)); + robObj->Axis4.getValue() * (pi / 180)); if (toolShape) { toolShape->setTransformation( (robObj->Tcp.getValue() * (robObj->ToolBase.getValue().inverse())).toMatrix()); @@ -335,7 +337,7 @@ void ViewProviderRobotObject::updateData(const App::Property* prop) else if (prop == &robObj->Axis5) { if (Axis5Node) { Axis5Node->rotation.setValue(SbVec3f(0.0, 1.0, 0.0), - robObj->Axis5.getValue() * (M_PI / 180)); + robObj->Axis5.getValue() * (pi / 180)); if (toolShape) { toolShape->setTransformation( (robObj->Tcp.getValue() * (robObj->ToolBase.getValue().inverse())).toMatrix()); @@ -345,7 +347,7 @@ void ViewProviderRobotObject::updateData(const App::Property* prop) else if (prop == &robObj->Axis6) { if (Axis6Node) { Axis6Node->rotation.setValue(SbVec3f(0.0, 1.0, 0.0), - robObj->Axis6.getValue() * (M_PI / 180)); + robObj->Axis6.getValue() * (pi / 180)); if (toolShape) { toolShape->setTransformation( (robObj->Tcp.getValue() * (robObj->ToolBase.getValue().inverse())).toMatrix()); @@ -395,27 +397,29 @@ void ViewProviderRobotObject::setAxisTo(float A1, float A6, const Base::Placement& Tcp) { + using std::numbers::pi; + Robot::RobotObject* robObj = static_cast(pcObject); if (Axis1Node) { // FIXME Ugly hack for the wrong transformation of the Kuka 500 robot VRML the minus sign on // Axis 1 - Axis1Node->rotation.setValue(SbVec3f(0.0, 1.0, 0.0), A1 * (M_PI / 180)); + Axis1Node->rotation.setValue(SbVec3f(0.0, 1.0, 0.0), A1 * (pi / 180)); } if (Axis2Node) { - Axis2Node->rotation.setValue(SbVec3f(0.0, 1.0, 0.0), A2 * (M_PI / 180)); + Axis2Node->rotation.setValue(SbVec3f(0.0, 1.0, 0.0), A2 * (pi / 180)); } if (Axis3Node) { - Axis3Node->rotation.setValue(SbVec3f(0.0, 1.0, 0.0), A3 * (M_PI / 180)); + Axis3Node->rotation.setValue(SbVec3f(0.0, 1.0, 0.0), A3 * (pi / 180)); } if (Axis4Node) { - Axis4Node->rotation.setValue(SbVec3f(0.0, 1.0, 0.0), A4 * (M_PI / 180)); + Axis4Node->rotation.setValue(SbVec3f(0.0, 1.0, 0.0), A4 * (pi / 180)); } if (Axis5Node) { - Axis5Node->rotation.setValue(SbVec3f(0.0, 1.0, 0.0), A5 * (M_PI / 180)); + Axis5Node->rotation.setValue(SbVec3f(0.0, 1.0, 0.0), A5 * (pi / 180)); } if (Axis6Node) { - Axis6Node->rotation.setValue(SbVec3f(0.0, 1.0, 0.0), A6 * (M_PI / 180)); + Axis6Node->rotation.setValue(SbVec3f(0.0, 1.0, 0.0), A6 * (pi / 180)); } // update tool position if (toolShape) { diff --git a/src/Mod/Sandbox/Gui/GLGraphicsView.cpp b/src/Mod/Sandbox/Gui/GLGraphicsView.cpp index 544762e22b..316dc31aa0 100644 --- a/src/Mod/Sandbox/Gui/GLGraphicsView.cpp +++ b/src/Mod/Sandbox/Gui/GLGraphicsView.cpp @@ -454,11 +454,7 @@ GraphicsScene::setNavigationModeFile(const QUrl & url) QFile file(QString::fromLatin1(filenametmp)); if (file.open(QIODevice::ReadOnly)) { QByteArray fileContents = file.readAll(); -#if COIN_MAJOR_VERSION >= 4 stateMachine = ScXML::readBuffer(SbByteBuffer(fileContents.size(), fileContents.constData())); -#else - stateMachine = ScXML::readBuffer(fileContents.constData()); -#endif file.close(); } } diff --git a/src/Mod/Sketcher/App/ExternalGeometryExtension.cpp b/src/Mod/Sketcher/App/ExternalGeometryExtension.cpp index ced70dde49..7053525dc6 100644 --- a/src/Mod/Sketcher/App/ExternalGeometryExtension.cpp +++ b/src/Mod/Sketcher/App/ExternalGeometryExtension.cpp @@ -82,11 +82,7 @@ std::unique_ptr ExternalGeometryExtension::copy() const copyAttributes(cpy.get()); -#if defined(__GNUC__) && (__GNUC__ <= 4) - return std::move(cpy); -#else return cpy; -#endif } PyObject* ExternalGeometryExtension::getPyObject() diff --git a/src/Mod/Sketcher/App/Sketch.cpp b/src/Mod/Sketcher/App/Sketch.cpp index fef264d601..24d350825d 100644 --- a/src/Mod/Sketcher/App/Sketch.cpp +++ b/src/Mod/Sketcher/App/Sketch.cpp @@ -3060,6 +3060,7 @@ int Sketch::addAngleAtPointConstraint(int geoId1, ConstraintType cTyp, bool driving) { + using std::numbers::pi; if (!(cTyp == Angle || cTyp == Tangent || cTyp == Perpendicular)) { // assert(0);//none of the three types. Why are we here?? return -1; @@ -3132,28 +3133,28 @@ int Sketch::addAngleAtPointConstraint(int geoId1, // the desired angle value (and we are to decide if 180* should be added to it) double angleDesire = 0.0; if (cTyp == Tangent) { - angleOffset = -M_PI / 2; + angleOffset = -pi / 2; angleDesire = 0.0; } if (cTyp == Perpendicular) { angleOffset = 0; - angleDesire = M_PI / 2; + angleDesire = pi / 2; } if (*value == 0.0) { // autodetect tangency internal/external (and same for perpendicularity) double angleErr = GCSsys.calculateAngleViaPoint(*crv1, *crv2, p) - angleDesire; // bring angleErr to -pi..pi - if (angleErr > M_PI) { - angleErr -= M_PI * 2; + if (angleErr > pi) { + angleErr -= pi * 2; } - if (angleErr < -M_PI) { - angleErr += M_PI * 2; + if (angleErr < -pi) { + angleErr += pi * 2; } // the autodetector - if (fabs(angleErr) > M_PI / 2) { - angleDesire += M_PI; + if (fabs(angleErr) > pi / 2) { + angleDesire += pi; } *angle = angleDesire; @@ -4542,7 +4543,7 @@ bool Sketch::updateNonDrivingConstraints() } else if ((*it).constr->Type == Angle) { - (*it).constr->setValue(std::fmod(*((*it).value), 2.0 * M_PI)); + (*it).constr->setValue(std::fmod(*((*it).value), 2.0 * std::numbers::pi)); } else if ((*it).constr->Type == Diameter && (*it).constr->First >= 0) { diff --git a/src/Mod/Sketcher/App/SketchAnalysis.cpp b/src/Mod/Sketcher/App/SketchAnalysis.cpp index e2e4751771..81d20781ef 100644 --- a/src/Mod/Sketcher/App/SketchAnalysis.cpp +++ b/src/Mod/Sketcher/App/SketchAnalysis.cpp @@ -591,7 +591,7 @@ void SketchAnalysis::analyseMissingPointOnPointCoincident(double angleprecision) if (fabs(tgv1 * tgv2) > fabs(cos(angleprecision))) { vc.Type = Sketcher::Tangent; } - else if (fabs(tgv1 * tgv2) < fabs(cos(M_PI / 2 - angleprecision))) { + else if (fabs(tgv1 * tgv2) < fabs(cos(std::numbers::pi / 2 - angleprecision))) { vc.Type = Sketcher::Perpendicular; } } @@ -726,7 +726,8 @@ void SketchAnalysis::makeMissingVerticalHorizontalOneByOne() bool SketchAnalysis::checkVertical(Base::Vector3d dir, double angleprecision) { - return (dir.x == 0. && dir.y != 0.) || (fabs(dir.y / dir.x) > tan(M_PI / 2 - angleprecision)); + return (dir.x == 0. && dir.y != 0.) + || (fabs(dir.y / dir.x) > tan(std::numbers::pi / 2 - angleprecision)); } bool SketchAnalysis::checkHorizontal(Base::Vector3d dir, double angleprecision) diff --git a/src/Mod/Sketcher/App/SketchAnalysis.h b/src/Mod/Sketcher/App/SketchAnalysis.h index 4695254d29..dc354b6d80 100644 --- a/src/Mod/Sketcher/App/SketchAnalysis.h +++ b/src/Mod/Sketcher/App/SketchAnalysis.h @@ -94,7 +94,7 @@ public: int detectMissingPointOnPointConstraints(double precision = Precision::Confusion() * 1000, bool includeconstruction = true); /// Point on Point constraint simple routine Analyse step (see constructor) - void analyseMissingPointOnPointCoincident(double angleprecision = M_PI / 8); + void analyseMissingPointOnPointCoincident(double angleprecision = std::numbers::pi / 8); /// Point on Point constraint simple routine Get step (see constructor) std::vector& getMissingPointOnPointConstraints() { @@ -113,7 +113,7 @@ public: void makeMissingPointOnPointCoincidentOneByOne(); /// Vertical/Horizontal constraints simple routine Detect step (see constructor) - int detectMissingVerticalHorizontalConstraints(double angleprecision = M_PI / 8); + int detectMissingVerticalHorizontalConstraints(double angleprecision = std::numbers::pi / 8); /// Vertical/Horizontal constraints simple routine Get step (see constructor) std::vector& getMissingVerticalHorizontalConstraints() { @@ -168,7 +168,7 @@ public: /// /// It applies coincidents - vertical/horizontal constraints and equality constraints. int autoconstraint(double precision = Precision::Confusion() * 1000, - double angleprecision = M_PI / 8, + double angleprecision = std::numbers::pi / 8, bool includeconstruction = true); // helper functions, which may be used by more complex methods, and/or called directly by user diff --git a/src/Mod/Sketcher/App/SketchGeometryExtension.cpp b/src/Mod/Sketcher/App/SketchGeometryExtension.cpp index 00929143b6..4759004f4e 100644 --- a/src/Mod/Sketcher/App/SketchGeometryExtension.cpp +++ b/src/Mod/Sketcher/App/SketchGeometryExtension.cpp @@ -103,12 +103,7 @@ std::unique_ptr SketchGeometryExtension::copy() const auto cpy = std::make_unique(); copyAttributes(cpy.get()); - -#if defined(__GNUC__) && (__GNUC__ <= 4) - return std::move(cpy); -#else return cpy; -#endif } PyObject* SketchGeometryExtension::getPyObject() diff --git a/src/Mod/Sketcher/App/SketchObject.cpp b/src/Mod/Sketcher/App/SketchObject.cpp index 64b717d48d..c456d45f16 100644 --- a/src/Mod/Sketcher/App/SketchObject.cpp +++ b/src/Mod/Sketcher/App/SketchObject.cpp @@ -600,8 +600,9 @@ int SketchObject::solve(bool updateGeoAfterSolving /*=true*/) Part::PropertyGeometryList tmp; tmp.setValues(std::move(geomlist)); // Only set values if there is actual changes - if (!Geometry.isSame(tmp)) + if (Constraints.isTouched() || !Geometry.isSame(tmp)) { Geometry.moveValues(std::move(tmp)); + } } } else if (err < 0) { @@ -1143,7 +1144,7 @@ void SketchObject::reverseAngleConstraintToSupplementary(Constraint* constr, int } else { double actAngle = constr->getValue(); - constr->setValue(M_PI - actAngle); + constr->setValue(std::numbers::pi - actAngle); } } @@ -3248,12 +3249,12 @@ int SketchObject::fillet(int GeoId1, int GeoId2, const Base::Vector3d& refPnt1, std::swap(startAngle, endAngle); } - if (endAngle > 2 * M_PI) { - endAngle -= 2 * M_PI; + if (endAngle > 2 * std::numbers::pi) { + endAngle -= 2 * std::numbers::pi; } if (startAngle < 0) { - endAngle += 2 * M_PI; + endAngle += 2 * std::numbers::pi; } // Create Arc Segment @@ -4905,6 +4906,8 @@ std::vector SketchObject::getSymmetric(const std::vector& int refGeoId, Sketcher::PointPos refPosId) { + using std::numbers::pi; + std::vector symmetricVals; bool refIsLine = refPosId == Sketcher::PointPos::none; int cgeoid = getHighestCurveIndex() + 1; @@ -4980,8 +4983,8 @@ std::vector SketchObject::getSymmetric(const std::vector& Base::Vector3d scp = cp + 2.0 * (cp.Perpendicular(refGeoLine->getStartPoint(), vectline) - cp); - double theta1 = Base::fmod(atan2(sep.y - scp.y, sep.x - scp.x), 2.f * M_PI); - double theta2 = Base::fmod(atan2(ssp.y - scp.y, ssp.x - scp.x), 2.f * M_PI); + double theta1 = Base::fmod(atan2(sep.y - scp.y, sep.x - scp.x), 2.f * std::numbers::pi); + double theta2 = Base::fmod(atan2(ssp.y - scp.y, ssp.x - scp.x), 2.f * std::numbers::pi); geoaoc->setCenter(scp); geoaoc->setRange(theta1, theta2, true); @@ -5028,12 +5031,12 @@ std::vector SketchObject::getSymmetric(const std::vector& double theta1, theta2; geosymaoe->getRange(theta1, theta2, true); - theta1 = 2.0 * M_PI - theta1; - theta2 = 2.0 * M_PI - theta2; + theta1 = 2.0 * pi - theta1; + theta2 = 2.0 * pi - theta2; std::swap(theta1, theta2); if (theta1 < 0) { - theta1 += 2.0 * M_PI; - theta2 += 2.0 * M_PI; + theta1 += 2.0 * pi; + theta2 += 2.0 * pi; } geosymaoe->setRange(theta1, theta2, true); @@ -5178,8 +5181,8 @@ std::vector SketchObject::getSymmetric(const std::vector& Base::Vector3d sep = ep + 2.0 * (refpoint - ep); Base::Vector3d scp = cp + 2.0 * (refpoint - cp); - double theta1 = Base::fmod(atan2(ssp.y - scp.y, ssp.x - scp.x), 2.f * M_PI); - double theta2 = Base::fmod(atan2(sep.y - scp.y, sep.x - scp.x), 2.f * M_PI); + double theta1 = Base::fmod(atan2(ssp.y - scp.y, ssp.x - scp.x), 2.f * pi); + double theta2 = Base::fmod(atan2(sep.y - scp.y, sep.x - scp.x), 2.f * pi); geoaoc->setCenter(scp); geoaoc->setRange(theta1, theta2, true); @@ -8568,6 +8571,8 @@ void processEdge(const TopoDS_Edge& edge, gp_Ax3& sketchAx3, TopoDS_Shape& aProjFace) { + using std::numbers::pi; + BRepAdaptor_Curve curve(edge); if (curve.GetType() == GeomAbs_Line) { geos.emplace_back(projectLine(curve, gPlane, invPlm)); @@ -8641,11 +8646,11 @@ void processEdge(const TopoDS_Edge& edge, int tours = 0; double startAngle = baseAngle + alpha; // bring startAngle back in [-pi/2 , 3pi/2[ - while (startAngle < -M_PI / 2.0 && tours < 10) { - startAngle = baseAngle + ++tours * 2.0 * M_PI + alpha; + while (startAngle < -pi / 2.0 && tours < 10) { + startAngle = baseAngle + ++tours * 2.0 * pi + alpha; } - while (startAngle >= 3.0 * M_PI / 2.0 && tours > -10) { - startAngle = baseAngle + --tours * 2.0 * M_PI + alpha; + while (startAngle >= 3.0 * pi / 2.0 && tours > -10) { + startAngle = baseAngle + --tours * 2.0 * pi + alpha; } // apply same offset to end angle @@ -8661,7 +8666,7 @@ void processEdge(const TopoDS_Edge& edge, // P2 = P2 already defined P1 = ProjPointOnPlane_XYZ(beg, sketchPlane); } - else if (endAngle < M_PI) { + else if (endAngle < pi) { // P2 = P2, already defined P1 = ProjPointOnPlane_XYZ(end, sketchPlane); } @@ -8671,16 +8676,16 @@ void processEdge(const TopoDS_Edge& edge, } } } - else if (startAngle < M_PI) { - if (endAngle < M_PI) { + else if (startAngle < pi) { + if (endAngle < pi) { P1 = ProjPointOnPlane_XYZ(beg, sketchPlane); P2 = ProjPointOnPlane_XYZ(end, sketchPlane); } - else if (endAngle < 2.0 * M_PI - startAngle) { + else if (endAngle < 2.0 * pi - startAngle) { P2 = ProjPointOnPlane_XYZ(beg, sketchPlane); // P1 = P1, already defined } - else if (endAngle < 2.0 * M_PI) { + else if (endAngle < 2.0 * pi) { P2 = ProjPointOnPlane_XYZ(end, sketchPlane); // P1 = P1, already defined } @@ -8690,15 +8695,15 @@ void processEdge(const TopoDS_Edge& edge, } } else { - if (endAngle < 2 * M_PI) { + if (endAngle < 2 * pi) { P1 = ProjPointOnPlane_XYZ(beg, sketchPlane); P2 = ProjPointOnPlane_XYZ(end, sketchPlane); } - else if (endAngle < 4 * M_PI - startAngle) { + else if (endAngle < 4 * pi - startAngle) { P1 = ProjPointOnPlane_XYZ(beg, sketchPlane); // P2 = P2, already defined } - else if (endAngle < 3 * M_PI) { + else if (endAngle < 3 * pi) { // P1 = P1, already defined P2 = ProjPointOnPlane_XYZ(end, sketchPlane); } @@ -8818,7 +8823,7 @@ void processEdge(const TopoDS_Edge& edge, gp_Vec2d PB = ProjVecOnPlane_UV(origAxisMinor, sketchPlane); double t_max = 2.0 * PA.Dot(PB) / (PA.SquareMagnitude() - PB.SquareMagnitude()); t_max = 0.5 * atan(t_max);// gives new major axis is most cases, but not all - double t_min = t_max + 0.5 * M_PI; + double t_min = t_max + 0.5 * pi; // ON_max = OM(t_max) gives the point, which projected on the sketch plane, // becomes the apoapse of the projected ellipse. @@ -9313,7 +9318,7 @@ void SketchObject::rebuildExternalGeometry(std::optional extToAdd processEdge(edge, geos, gPlane, invPlm, mov, sketchPlane, invRot, sketchAx3, aProjFace); } - if (fabs(dnormal.Angle(snormal) - M_PI_2) < Precision::Confusion()) { + if (fabs(dnormal.Angle(snormal) - std::numbers::pi/2) < Precision::Confusion()) { // The face is normal to the sketch plane // We don't want to keep the projection of all the edges of the face. // We need a single line that goes from min to max of all the projections. @@ -11310,6 +11315,8 @@ int SketchObject::port_reversedExternalArcs(bool justAnalyze) /// false - fail (this indicates an error, or that a constraint locking isn't supported). bool SketchObject::AutoLockTangencyAndPerpty(Constraint* cstr, bool bForce, bool bLock) { + using std::numbers::pi; + try { // assert ( cstr->Type == Tangent || cstr->Type == Perpendicular); /*tangency type already set. If not bForce - don't touch.*/ @@ -11359,25 +11366,25 @@ bool SketchObject::AutoLockTangencyAndPerpty(Constraint* cstr, bool bForce, bool // the desired angle value (and we are to decide if 180* should be added to it) double angleDesire = 0.0; if (cstr->Type == Tangent) { - angleOffset = -M_PI / 2; + angleOffset = -pi / 2; angleDesire = 0.0; } if (cstr->Type == Perpendicular) { angleOffset = 0; - angleDesire = M_PI / 2; + angleDesire = pi / 2; } double angleErr = calculateAngleViaPoint(geoId1, geoId2, p.x, p.y) - angleDesire; // bring angleErr to -pi..pi - if (angleErr > M_PI) - angleErr -= M_PI * 2; - if (angleErr < -M_PI) - angleErr += M_PI * 2; + if (angleErr > pi) + angleErr -= pi * 2; + if (angleErr < -pi) + angleErr += pi * 2; // the autodetector - if (fabs(angleErr) > M_PI / 2) - angleDesire += M_PI; + if (fabs(angleErr) > pi / 2) + angleDesire += pi; // external tangency. The angle stored is offset by Pi/2 so that a value of 0.0 is // invalid and treated as "undecided". diff --git a/src/Mod/Sketcher/App/SketchObject.h b/src/Mod/Sketcher/App/SketchObject.h index 05a484c7be..4d17218403 100644 --- a/src/Mod/Sketcher/App/SketchObject.h +++ b/src/Mod/Sketcher/App/SketchObject.h @@ -841,13 +841,13 @@ public: public: // Analyser functions int autoConstraint(double precision = Precision::Confusion() * 1000, - double angleprecision = M_PI / 20, + double angleprecision = std::numbers::pi / 20, bool includeconstruction = true); int detectMissingPointOnPointConstraints(double precision = Precision::Confusion() * 1000, bool includeconstruction = true); - void analyseMissingPointOnPointCoincident(double angleprecision = M_PI / 8); - int detectMissingVerticalHorizontalConstraints(double angleprecision = M_PI / 8); + void analyseMissingPointOnPointCoincident(double angleprecision = std::numbers::pi / 8); + int detectMissingVerticalHorizontalConstraints(double angleprecision = std::numbers::pi / 8); int detectMissingEqualityConstraints(double precision); std::vector& getMissingPointOnPointConstraints(); diff --git a/src/Mod/Sketcher/App/SketchObjectPyImp.cpp b/src/Mod/Sketcher/App/SketchObjectPyImp.cpp index eaf4156d1a..28cc485a3f 100644 --- a/src/Mod/Sketcher/App/SketchObjectPyImp.cpp +++ b/src/Mod/Sketcher/App/SketchObjectPyImp.cpp @@ -1918,7 +1918,7 @@ PyObject* SketchObjectPy::insertBSplineKnot(PyObject* args) PyObject* SketchObjectPy::autoconstraint(PyObject* args) { double precision = Precision::Confusion() * 1000; - double angleprecision = M_PI / 8; + double angleprecision = std::numbers::pi / 8; PyObject* includeconstruction = Py_True; @@ -1960,7 +1960,7 @@ PyObject* SketchObjectPy::detectMissingPointOnPointConstraints(PyObject* args) PyObject* SketchObjectPy::detectMissingVerticalHorizontalConstraints(PyObject* args) { - double angleprecision = M_PI / 8; + double angleprecision = std::numbers::pi / 8; if (!PyArg_ParseTuple(args, "|d", &angleprecision)) { return nullptr; @@ -1984,7 +1984,7 @@ PyObject* SketchObjectPy::detectMissingEqualityConstraints(PyObject* args) PyObject* SketchObjectPy::analyseMissingPointOnPointCoincident(PyObject* args) { - double angleprecision = M_PI / 8; + double angleprecision = std::numbers::pi / 8; if (!PyArg_ParseTuple(args, "|d", &angleprecision)) { return nullptr; diff --git a/src/Mod/Sketcher/App/SolverGeometryExtension.cpp b/src/Mod/Sketcher/App/SolverGeometryExtension.cpp index 89c94a2dfe..f0e83cf6bd 100644 --- a/src/Mod/Sketcher/App/SolverGeometryExtension.cpp +++ b/src/Mod/Sketcher/App/SolverGeometryExtension.cpp @@ -53,12 +53,7 @@ std::unique_ptr SolverGeometryExtension::copy() const auto cpy = std::make_unique(); copyAttributes(cpy.get()); - -#if defined(__GNUC__) && (__GNUC__ <= 4) - return std::move(cpy); -#else return cpy; -#endif } PyObject* SolverGeometryExtension::getPyObject() diff --git a/src/Mod/Sketcher/App/planegcs/Constraints.cpp b/src/Mod/Sketcher/App/planegcs/Constraints.cpp index 539dfd9f95..336d20e3d2 100644 --- a/src/Mod/Sketcher/App/planegcs/Constraints.cpp +++ b/src/Mod/Sketcher/App/planegcs/Constraints.cpp @@ -24,8 +24,8 @@ #pragma warning(disable : 4251) #endif -#define _USE_MATH_DEFINES #include +#include #include #define DEBUG_DERIVS 0 @@ -864,6 +864,8 @@ double ConstraintP2PAngle::grad(double* param) double ConstraintP2PAngle::maxStep(MAP_pD_D& dir, double lim) { + constexpr double pi_18 = std::numbers::pi / 18; + MAP_pD_D::iterator it = dir.find(angle()); if (it != dir.end()) { double step = std::abs(it->second); @@ -1444,6 +1446,8 @@ double ConstraintL2LAngle::grad(double* param) double ConstraintL2LAngle::maxStep(MAP_pD_D& dir, double lim) { + constexpr double pi_18 = std::numbers::pi / 18; + MAP_pD_D::iterator it = dir.find(angle()); if (it != dir.end()) { double step = std::abs(it->second); @@ -3536,10 +3540,10 @@ void ConstraintArcLength::errorgrad(double* err, double* grad, double* param) double startA = *arc.startAngle; // Assume positive angles and CCW arc while (startA < 0.) { - startA += 2. * M_PI; + startA += 2. * std::numbers::pi; } while (endA < startA) { - endA += 2. * M_PI; + endA += 2. * std::numbers::pi; } if (err) { *err = rad * (endA - startA) - *distance(); diff --git a/src/Mod/Sketcher/App/planegcs/GCS.cpp b/src/Mod/Sketcher/App/planegcs/GCS.cpp index f397db6a10..bc63d84608 100644 --- a/src/Mod/Sketcher/App/planegcs/GCS.cpp +++ b/src/Mod/Sketcher/App/planegcs/GCS.cpp @@ -47,10 +47,10 @@ #endif #include -#include #include #include #include +#include #include "GCS.h" #include "qp_eq.h" @@ -1029,6 +1029,8 @@ int System::addConstraintPerpendicularLine2Arc(Point& p1, int tagId, bool driving) { + using std::numbers::pi; + addConstraintP2PCoincident(p2, a.start, tagId, driving); double dx = *(p2.x) - *(p1.x); double dy = *(p2.y) - *(p1.y); @@ -1046,6 +1048,8 @@ int System::addConstraintPerpendicularArc2Line(Arc& a, int tagId, bool driving) { + using std::numbers::pi; + addConstraintP2PCoincident(p1, a.end, tagId, driving); double dx = *(p2.x) - *(p1.x); double dy = *(p2.y) - *(p1.y); @@ -1063,8 +1067,10 @@ int System::addConstraintPerpendicularCircle2Arc(Point& center, int tagId, bool driving) { + using std::numbers::pi; + addConstraintP2PDistance(a.start, center, radius, tagId, driving); - double incrAngle = *(a.startAngle) < *(a.endAngle) ? pi_2 : -pi_2; + double incrAngle = *(a.startAngle) < *(a.endAngle) ? pi / 2 : -pi / 2; double tangAngle = *a.startAngle + incrAngle; double dx = *(a.start.x) - *(center.x); double dy = *(a.start.y) - *(center.y); @@ -1082,8 +1088,10 @@ int System::addConstraintPerpendicularArc2Circle(Arc& a, int tagId, bool driving) { + using std::numbers::pi; + addConstraintP2PDistance(a.end, center, radius, tagId, driving); - double incrAngle = *(a.startAngle) < *(a.endAngle) ? -pi_2 : pi_2; + double incrAngle = *(a.startAngle) < *(a.endAngle) ? -pi / 2 : pi / 2; double tangAngle = *a.endAngle + incrAngle; double dx = *(a.end.x) - *(center.x); double dy = *(a.end.y) - *(center.y); @@ -2270,12 +2278,13 @@ int System::solve_LM(SubSystem* subsys, bool isRedundantsolving) x_new = x + h; h_norm = h.squaredNorm(); + constexpr double epsilon = std::numeric_limits::epsilon(); if (h_norm <= eps1 * eps1 * x.norm()) { // relative change in p is small, stop stop = 3; break; } - else if (h_norm >= (x.norm() + eps1) / (DBL_EPSILON * DBL_EPSILON)) { + else if (h_norm >= (x.norm() + eps1) / (epsilon * epsilon)) { // almost singular stop = 4; break; diff --git a/src/Mod/Sketcher/App/planegcs/Geo.h b/src/Mod/Sketcher/App/planegcs/Geo.h index 4604c55b26..bbfdf39736 100644 --- a/src/Mod/Sketcher/App/planegcs/Geo.h +++ b/src/Mod/Sketcher/App/planegcs/Geo.h @@ -53,9 +53,6 @@ public: }; using VEC_P = std::vector; -static constexpr double pi = boost::math::constants::pi(); -static constexpr double pi_2 = pi / 2.0; -static constexpr double pi_18 = pi / 18.0; /// Class DeriVector2 holds a vector value and its derivative on the /// parameter that the derivatives are being calculated for now. x,y is the diff --git a/src/Mod/Sketcher/Gui/CommandConstraints.cpp b/src/Mod/Sketcher/Gui/CommandConstraints.cpp index d46109ce7f..8ed676bb93 100644 --- a/src/Mod/Sketcher/Gui/CommandConstraints.cpp +++ b/src/Mod/Sketcher/Gui/CommandConstraints.cpp @@ -24,7 +24,6 @@ #ifndef _PreComp_ #include #include -#include #endif #include @@ -114,10 +113,10 @@ void finishDatumConstraint(Gui::Command* cmd, if (lastConstraintType == Radius || lastConstraintType == Diameter) { labelPosition = hGrp->GetFloat("RadiusDiameterConstraintDisplayBaseAngle", 15.0) - * (M_PI / 180);// Get radius/diameter constraint display angle + * (std::numbers::pi / 180);// Get radius/diameter constraint display angle labelPositionRandomness = hGrp->GetFloat("RadiusDiameterConstraintDisplayAngleRandomness", 0.0) - * (M_PI / 180);// Get randomness + * (std::numbers::pi / 180);// Get randomness // Adds a random value around the base angle, so that possibly overlapping labels get likely // a different position. @@ -320,7 +319,7 @@ bool SketcherGui::calculateAngle(Sketcher::SketchObject* Obj, int& GeoId1, int& } else { // if all points are collinear - double length = DBL_MAX; + double length = std::numeric_limits::max(); for (int i = 0; i <= 1; i++) { for (int j = 0; j <= 1; j++) { double tmp = Base::DistanceP2(p2[j], p1[i]); diff --git a/src/Mod/Sketcher/Gui/CommandSketcherBSpline.cpp b/src/Mod/Sketcher/Gui/CommandSketcherBSpline.cpp index 3b6e6819d0..ee3f9b5381 100644 --- a/src/Mod/Sketcher/Gui/CommandSketcherBSpline.cpp +++ b/src/Mod/Sketcher/Gui/CommandSketcherBSpline.cpp @@ -24,7 +24,6 @@ #ifndef _PreComp_ #include #include -#include #endif #include diff --git a/src/Mod/Sketcher/Gui/CommandSketcherOverlay.cpp b/src/Mod/Sketcher/Gui/CommandSketcherOverlay.cpp index 4dc6673d96..0e0158f73e 100644 --- a/src/Mod/Sketcher/Gui/CommandSketcherOverlay.cpp +++ b/src/Mod/Sketcher/Gui/CommandSketcherOverlay.cpp @@ -24,7 +24,6 @@ #ifndef _PreComp_ #include #include -#include #endif #include diff --git a/src/Mod/Sketcher/Gui/CommandSketcherTools.cpp b/src/Mod/Sketcher/Gui/CommandSketcherTools.cpp index 1fbfc714cb..4058d4e827 100644 --- a/src/Mod/Sketcher/Gui/CommandSketcherTools.cpp +++ b/src/Mod/Sketcher/Gui/CommandSketcherTools.cpp @@ -22,7 +22,6 @@ #include "PreCompiled.h" #ifndef _PreComp_ -#include #include #include @@ -1205,6 +1204,8 @@ public: void mouseMove(Base::Vector2d onSketchPos) override { + using std::numbers::pi; + if (Mode == STATUS_SEEK_First) { if (QApplication::keyboardModifiers() == Qt::ControlModifier) @@ -1218,14 +1219,14 @@ public: Base::Vector2d endpoint = onSketchPos; if (snapMode == SnapMode::Snap5Degree) { - angle = round(angle / (M_PI / 36)) * M_PI / 36; + angle = round(angle / (pi / 36)) * pi / 36; endpoint = EditCurve[0] + length * Base::Vector2d(cos(angle), sin(angle)); } if (showCursorCoords()) { SbString text; std::string lengthString = lengthToDisplayFormat(length, 1); - std::string angleString = angleToDisplayFormat(angle * 180.0 / M_PI, 1); + std::string angleString = angleToDisplayFormat(angle * 180.0 / pi, 1); text.sprintf(" (%s, %s)", lengthString.c_str(), angleString.c_str()); setPositionText(endpoint, text); } @@ -1781,6 +1782,8 @@ public: void mouseMove(Base::Vector2d onSketchPos) override { + using std::numbers::pi; + if (Mode == STATUS_SEEK_First) { if (QApplication::keyboardModifiers() == Qt::ControlModifier) @@ -1794,14 +1797,14 @@ public: Base::Vector2d endpoint = onSketchPos; if (snapMode == SnapMode::Snap5Degree) { - angle = round(angle / (M_PI / 36)) * M_PI / 36; + angle = round(angle / (pi / 36)) * pi / 36; endpoint = EditCurve[0] + length * Base::Vector2d(cos(angle), sin(angle)); } if (showCursorCoords()) { SbString text; std::string lengthString = lengthToDisplayFormat(length, 1); - std::string angleString = angleToDisplayFormat(angle * 180.0 / M_PI, 1); + std::string angleString = angleToDisplayFormat(angle * 180.0 / pi, 1); text.sprintf(" (%s, %s)", lengthString.c_str(), angleString.c_str()); setPositionText(endpoint, text); } diff --git a/src/Mod/Sketcher/Gui/CommandSketcherVirtualSpace.cpp b/src/Mod/Sketcher/Gui/CommandSketcherVirtualSpace.cpp index 6fcfdbfdf9..4af8107eb2 100644 --- a/src/Mod/Sketcher/Gui/CommandSketcherVirtualSpace.cpp +++ b/src/Mod/Sketcher/Gui/CommandSketcherVirtualSpace.cpp @@ -22,7 +22,6 @@ #include "PreCompiled.h" #ifndef _PreComp_ -#include #endif #include diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandler.cpp b/src/Mod/Sketcher/Gui/DrawSketchHandler.cpp index e378625ac1..b6ff76dfd8 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandler.cpp +++ b/src/Mod/Sketcher/Gui/DrawSketchHandler.cpp @@ -425,6 +425,8 @@ int DrawSketchHandler::seekAutoConstraint(std::vector& suggested const Base::Vector2d& Dir, AutoConstraint::TargetType type) { + using std::numbers::pi; + suggestedConstraints.clear(); SketchObject* obj = sketchgui->getSketchObject(); @@ -550,18 +552,18 @@ int DrawSketchHandler::seekAutoConstraint(std::vector& suggested // Number of Degree of deviation from horizontal or vertical lines const double angleDev = 2; - const double angleDevRad = angleDev * M_PI / 180.; + const double angleDevRad = angleDev * pi / 180.; AutoConstraint constr; constr.Type = Sketcher::None; constr.GeoId = GeoEnum::GeoUndef; constr.PosId = PointPos::none; double angle = std::abs(atan2(Dir.y, Dir.x)); - if (angle < angleDevRad || (M_PI - angle) < angleDevRad) { + if (angle < angleDevRad || (pi - angle) < angleDevRad) { // Suggest horizontal constraint constr.Type = Sketcher::Horizontal; } - else if (std::abs(angle - M_PI_2) < angleDevRad) { + else if (std::abs(angle - pi / 2) < angleDevRad) { // Suggest vertical constraint constr.Type = Sketcher::Vertical; } @@ -665,7 +667,7 @@ int DrawSketchHandler::seekAutoConstraint(std::vector& suggested double angle = atan2(projPnt.y, projPnt.x); while (angle < startAngle) { - angle += 2 * D_PI; // Bring it to range of arc + angle += 2 * pi; // Bring it to range of arc } // if the point is on correct side of arc @@ -714,10 +716,10 @@ int DrawSketchHandler::seekAutoConstraint(std::vector& suggested aoe->getMinorRadius() * ((tmpPos.x - center.x) * majdir.x + (tmpPos.y - center.y) * majdir.y)) - startAngle, - 2.f * M_PI); + 2.f * pi); while (angle < startAngle) { - angle += 2 * D_PI; // Bring it to range of arc + angle += 2 * pi; // Bring it to range of arc } // if the point is on correct side of arc @@ -978,7 +980,7 @@ void DrawSketchHandler::drawDirectionAtCursor(const Base::Vector2d& position, SbString text; std::string lengthString = lengthToDisplayFormat(length, 1); - std::string angleString = angleToDisplayFormat(angle * 180.0 / M_PI, 1); + std::string angleString = angleToDisplayFormat(angle * 180.0 / std::numbers::pi, 1); text.sprintf(" (%s, %s)", lengthString.c_str(), angleString.c_str()); setPositionText(position, text); } @@ -1009,7 +1011,7 @@ void DrawSketchHandler::drawDoubleAtCursor(const Base::Vector2d& position, SbString text; std::string doubleString = unit == Base::Unit::Length ? lengthToDisplayFormat(val, 1) - : angleToDisplayFormat(val * 180.0 / M_PI, 1); + : angleToDisplayFormat(val * 180.0 / std::numbers::pi, 1); text.sprintf(" (%s)", doubleString.c_str()); setPositionText(position, text); } diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandlerArc.h b/src/Mod/Sketcher/Gui/DrawSketchHandlerArc.h index af25729b50..54123fb115 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandlerArc.h +++ b/src/Mod/Sketcher/Gui/DrawSketchHandlerArc.h @@ -132,7 +132,7 @@ private: if (constructionMethod() == ConstructionMethod::Center) { secondPoint = onSketchPos; double angle1 = (onSketchPos - centerPoint).Angle() - startAngle; - double angle2 = angle1 + (angle1 < 0. ? 2 : -2) * M_PI; + double angle2 = angle1 + (angle1 < 0. ? 2 : -2) * std::numbers::pi; arcAngle = abs(angle1 - arcAngle) < abs(angle2 - arcAngle) ? angle1 : angle2; if (arcAngle > 0) { @@ -183,7 +183,7 @@ private: } startAngle = std::max(angle1, angle2); endAngle = std::min(angle1, angle2); - arcAngle = 2 * M_PI - (startAngle - endAngle); + arcAngle = 2 * std::numbers::pi - (startAngle - endAngle); } } @@ -562,7 +562,7 @@ void DSHArcControllerBase::doEnforceControlParameters(Base::Vector2d& onSketchPo if (onViewParameters[OnViewParameter::Fifth]->isSet) { double arcAngle = Base::toRadians(onViewParameters[OnViewParameter::Fifth]->getValue()); - if (fmod(fabs(arcAngle), 2 * M_PI) < Precision::Confusion()) { + if (fmod(fabs(arcAngle), 2 * std::numbers::pi) < Precision::Confusion()) { unsetOnViewParameter(onViewParameters[OnViewParameter::Fifth].get()); return; } diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandlerArcOfEllipse.h b/src/Mod/Sketcher/Gui/DrawSketchHandlerArcOfEllipse.h index 5185010b1c..a4ebaad0f1 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandlerArcOfEllipse.h +++ b/src/Mod/Sketcher/Gui/DrawSketchHandlerArcOfEllipse.h @@ -68,6 +68,8 @@ public: void mouseMove(Base::Vector2d onSketchPos) override { + using std::numbers::pi; + if (Mode == STATUS_SEEK_First) { setPositionText(onSketchPos); seekAndRenderAutoConstraint(sugConstr1, @@ -78,7 +80,7 @@ public: double rx0 = onSketchPos.x - EditCurve[0].x; double ry0 = onSketchPos.y - EditCurve[0].y; for (int i = 0; i < 16; i++) { - double angle = i * M_PI / 16.0; + double angle = i * pi / 16.0; double rx1 = rx0 * cos(angle) + ry0 * sin(angle); double ry1 = -rx0 * sin(angle) + ry0 * cos(angle); EditCurve[1 + i] = Base::Vector2d(EditCurve[0].x + rx1, EditCurve[0].y + ry1); @@ -115,7 +117,7 @@ public: / (sin(angleatpoint) * cos(phi)); for (int i = 1; i < 16; i++) { - double angle = i * M_PI / 16.0; + double angle = i * pi / 16.0; double rx1 = a * cos(angle) * cos(phi) - b * sin(angle) * sin(phi); double ry1 = a * cos(angle) * sin(phi) + b * sin(angle) * cos(phi); EditCurve[1 + i] = Base::Vector2d(EditCurve[0].x + rx1, EditCurve[0].y + ry1); @@ -161,7 +163,7 @@ public: + (onSketchPos.y - centerPoint.y) * sin(phi))) - startAngle; - double angle2 = angle1 + (angle1 < 0. ? 2 : -2) * M_PI; + double angle2 = angle1 + (angle1 < 0. ? 2 : -2) * pi; arcAngle = abs(angle1 - arcAngle) < abs(angle2 - arcAngle) ? angle1 : angle2; for (int i = 0; i < 34; i++) { @@ -178,7 +180,7 @@ public: SbString text; std::string aString = lengthToDisplayFormat(a, 1); std::string bString = lengthToDisplayFormat(b, 1); - std::string angleString = angleToDisplayFormat(arcAngle * 180.0 / M_PI, 1); + std::string angleString = angleToDisplayFormat(arcAngle * 180.0 / pi, 1); text.sprintf(" (R%s, R%s, %s)", aString.c_str(), bString.c_str(), @@ -222,6 +224,9 @@ public: bool releaseButton(Base::Vector2d onSketchPos) override { Q_UNUSED(onSketchPos); + + using std::numbers::pi; + if (Mode == STATUS_Close) { unsetCursor(); resetPositionText(); @@ -245,7 +250,7 @@ public: + (endPoint.y - centerPoint.y) * sin(phi))) - startAngle; - double angle2 = angle1 + (angle1 < 0. ? 2 : -2) * M_PI; + double angle2 = angle1 + (angle1 < 0. ? 2 : -2) * pi; arcAngle = abs(angle1 - arcAngle) < abs(angle2 - arcAngle) ? angle1 : angle2; bool isOriginalArcCCW = true; @@ -281,8 +286,8 @@ public: perp.Scale(abs(b)); majAxisPoint = centerPoint + perp; minAxisPoint = centerPoint + minAxisDir; - endAngle += M_PI / 2; - startAngle += M_PI / 2; + endAngle += pi / 2; + startAngle += pi / 2; } int currentgeoid = getHighestCurveIndex(); diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandlerArcOfHyperbola.h b/src/Mod/Sketcher/Gui/DrawSketchHandlerArcOfHyperbola.h index 620f1bc0a1..9f96e3f169 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandlerArcOfHyperbola.h +++ b/src/Mod/Sketcher/Gui/DrawSketchHandlerArcOfHyperbola.h @@ -103,7 +103,7 @@ public: if (!boost::math::isnan(b)) { for (int i = 15; i >= -15; i--) { // P(U) = O + MajRad*Cosh(U)*XDir + MinRad*Sinh(U)*YDir - // double angle = i*M_PI/16.0; + // double angle = i*std::numbers::pi/16.0; double angle = i * angleatpoint / 15; double rx = a * cosh(angle) * cos(phi) - b * sinh(angle) * sin(phi); double ry = a * cosh(angle) * sin(phi) + b * sinh(angle) * cos(phi); @@ -150,7 +150,7 @@ public: /*double angle1 = angleatpoint - startAngle; - double angle2 = angle1 + (angle1 < 0. ? 2 : -2) * M_PI ; + double angle2 = angle1 + (angle1 < 0. ? 2 : -2) * std::numbers::pi ; arcAngle = abs(angle1-arcAngle) < abs(angle2-arcAngle) ? angle1 : angle2;*/ arcAngle = angleatpoint - startAngle; @@ -290,8 +290,8 @@ public: perp.Scale(abs(b)); majAxisPoint = centerPoint + perp; minAxisPoint = centerPoint + minAxisDir; - endAngle += M_PI / 2; - startAngle += M_PI / 2; + endAngle += std::numbers::pi / 2; + startAngle += std::numbers::pi / 2; } int currentgeoid = getHighestCurveIndex(); diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandlerArcSlot.h b/src/Mod/Sketcher/Gui/DrawSketchHandlerArcSlot.h index 354930156a..cbd996489a 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandlerArcSlot.h +++ b/src/Mod/Sketcher/Gui/DrawSketchHandlerArcSlot.h @@ -133,7 +133,7 @@ private: startAngle = startAngleBackup; double angle1 = (onSketchPos - centerPoint).Angle() - startAngle; - double angle2 = angle1 + (angle1 < 0. ? 2 : -2) * M_PI; + double angle2 = angle1 + (angle1 < 0. ? 2 : -2) * std::numbers::pi; arcAngle = abs(angle1 - arcAngle) < abs(angle2 - arcAngle) ? angle1 : angle2; reverseIfNecessary(); @@ -305,6 +305,8 @@ private: void createShape(bool onlyeditoutline) override { + using std::numbers::pi; + ShapeGeometry.clear(); if (radius < Precision::Confusion()) { @@ -331,14 +333,14 @@ private: isConstructionMode()); addArcToShapeGeometry(toVector3d(startPoint), - angleReversed ? endAngle : startAngle + M_PI, - angleReversed ? endAngle + M_PI : startAngle + 2 * M_PI, + angleReversed ? endAngle : startAngle + pi, + angleReversed ? endAngle + pi : startAngle + 2 * pi, r, isConstructionMode()); addArcToShapeGeometry(toVector3d(endPoint), - angleReversed ? startAngle + M_PI : endAngle, - angleReversed ? startAngle + 2 * M_PI : M_PI + endAngle, + angleReversed ? startAngle + pi : endAngle, + angleReversed ? startAngle + 2 * pi : pi + endAngle, r, isConstructionMode()); @@ -635,7 +637,7 @@ void DSHArcSlotControllerBase::doEnforceControlParameters(Base::Vector2d& onSket if (onViewParameters[OnViewParameter::Fifth]->isSet) { double arcAngle = Base::toRadians(onViewParameters[OnViewParameter::Fifth]->getValue()); - if (fmod(fabs(arcAngle), 2 * M_PI) < Precision::Confusion()) { + if (fmod(fabs(arcAngle), 2 * std::numbers::pi) < Precision::Confusion()) { unsetOnViewParameter(onViewParameters[OnViewParameter::Fifth].get()); } else { diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandlerExtend.h b/src/Mod/Sketcher/Gui/DrawSketchHandlerExtend.h index f6561f017d..587db8d527 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandlerExtend.h +++ b/src/Mod/Sketcher/Gui/DrawSketchHandlerExtend.h @@ -110,6 +110,9 @@ public: void mouseMove(Base::Vector2d onSketchPos) override { Q_UNUSED(onSketchPos); + + using std::numbers::pi; + if (Mode == STATUS_SEEK_Second) { const Part::Geometry* geom = sketchgui->getSketchObject()->getGeometry(BaseGeoId); if (geom->is()) { @@ -142,7 +145,7 @@ public: */ bool inCurve = (projection.Length() < recenteredLine.Length() && projection.GetAngle(recenteredLine) - < 0.1); // Two possible values here, M_PI and 0, but 0.1 is to + < 0.1); // Two possible values here, pi and 0, but 0.1 is to // avoid floating point problems. if (inCurve) { Increment = SavedExtendFromStart @@ -185,8 +188,8 @@ public: bool isCCWFromStart = crossProduct(angle, startAngle) < 0; if (outOfArc) { if (isCCWFromStart) { - modStartAngle -= 2 * M_PI - angleToStartAngle; - modArcAngle += 2 * M_PI - angleToStartAngle; + modStartAngle -= 2 * pi - angleToStartAngle; + modArcAngle += 2 * pi - angleToStartAngle; } else { modStartAngle -= angleToStartAngle; @@ -199,8 +202,8 @@ public: modArcAngle -= angleToStartAngle; } else { - modStartAngle += 2 * M_PI - angleToStartAngle; - modArcAngle -= 2 * M_PI - angleToStartAngle; + modStartAngle += 2 * pi - angleToStartAngle; + modArcAngle -= 2 * pi - angleToStartAngle; } } } @@ -208,7 +211,7 @@ public: bool isCWFromEnd = crossProduct(angle, endAngle) >= 0; if (outOfArc) { if (isCWFromEnd) { - modArcAngle += 2 * M_PI - angleToEndAngle; + modArcAngle += 2 * pi - angleToEndAngle; } else { modArcAngle += angleToEndAngle; @@ -219,7 +222,7 @@ public: modArcAngle -= angleToEndAngle; } else { - modArcAngle -= 2 * M_PI - angleToEndAngle; + modArcAngle -= 2 * pi - angleToEndAngle; } } } diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandlerLine.h b/src/Mod/Sketcher/Gui/DrawSketchHandlerLine.h index efbe278b5e..50f7ea7377 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandlerLine.h +++ b/src/Mod/Sketcher/Gui/DrawSketchHandlerLine.h @@ -646,16 +646,17 @@ void DSHLineController::addConstraints() }; auto constraintp4angle = [&]() { + using std::numbers::pi; + double angle = Base::toRadians(p4); - if (fabs(angle - M_PI) < Precision::Confusion() - || fabs(angle + M_PI) < Precision::Confusion() + if (fabs(angle - pi) < Precision::Confusion() || fabs(angle + pi) < Precision::Confusion() || fabs(angle) < Precision::Confusion()) { Gui::cmdAppObjectArgs(obj, "addConstraint(Sketcher.Constraint('Horizontal',%d)) ", firstCurve); } - else if (fabs(angle - M_PI / 2) < Precision::Confusion() - || fabs(angle + M_PI / 2) < Precision::Confusion()) { + else if (fabs(angle - pi / 2) < Precision::Confusion() + || fabs(angle + pi / 2) < Precision::Confusion()) { Gui::cmdAppObjectArgs(obj, "addConstraint(Sketcher.Constraint('Vertical',%d)) ", firstCurve); diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandlerLineSet.h b/src/Mod/Sketcher/Gui/DrawSketchHandlerLineSet.h index 798f9f674b..37b0f6834e 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandlerLineSet.h +++ b/src/Mod/Sketcher/Gui/DrawSketchHandlerLineSet.h @@ -188,6 +188,8 @@ public: void mouseMove(Base::Vector2d onSketchPos) override { + using std::numbers::pi; + suppressTransition = false; if (Mode == STATUS_SEEK_First) { setPositionText(onSketchPos); @@ -222,7 +224,7 @@ public: if (showCursorCoords()) { SbString text; std::string lengthString = lengthToDisplayFormat(length, 1); - std::string angleString = angleToDisplayFormat(angle * 180.0 / M_PI, 1); + std::string angleString = angleToDisplayFormat(angle * 180.0 / pi, 1); text.sprintf(" (%s, %s)", lengthString.c_str(), angleString.c_str()); setPositionText(EditCurve[1], text); } @@ -289,14 +291,14 @@ public: arcAngle = 0.f; } if (arcRadius >= 0 && arcAngle > 0) { - arcAngle -= 2 * M_PI; + arcAngle -= 2 * pi; } if (arcRadius < 0 && arcAngle < 0) { - arcAngle += 2 * M_PI; + arcAngle += 2 * pi; } if (SnapMode == SNAP_MODE_45Degree) { - arcAngle = round(arcAngle / (M_PI / 4)) * M_PI / 4; + arcAngle = round(arcAngle / (pi / 4)) * pi / 4; } endAngle = startAngle + arcAngle; @@ -316,7 +318,7 @@ public: if (showCursorCoords()) { SbString text; std::string radiusString = lengthToDisplayFormat(std::abs(arcRadius), 1); - std::string angleString = angleToDisplayFormat(arcAngle * 180.0 / M_PI, 1); + std::string angleString = angleToDisplayFormat(arcAngle * 180.0 / pi, 1); text.sprintf(" (R%s, %s)", radiusString.c_str(), angleString.c_str()); setPositionText(onSketchPos, text); } @@ -536,8 +538,8 @@ public: // #3974: if in radians, the printf %f defaults to six decimals, which leads to // loss of precision - double arcAngle = - abs(round((endAngle - startAngle) / (M_PI / 4)) * 45); // in degrees + double arcAngle = abs(round((endAngle - startAngle) / (std::numbers::pi / 4)) + * 45); // in degrees Gui::cmdAppObjectArgs(sketchgui->getObject(), "addConstraint(Sketcher.Constraint('Angle',%i,App.Units." diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandlerOffset.h b/src/Mod/Sketcher/Gui/DrawSketchHandlerOffset.h index 0bf9697039..509d347027 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandlerOffset.h +++ b/src/Mod/Sketcher/Gui/DrawSketchHandlerOffset.h @@ -932,7 +932,7 @@ private: void findOffsetLength() { - double newOffsetLength = DBL_MAX; + double newOffsetLength = std::numeric_limits::max(); BRepBuilderAPI_MakeVertex mkVertex({endpoint.x, endpoint.y, 0.0}); TopoDS_Vertex vertex = mkVertex.Vertex(); @@ -960,7 +960,7 @@ private: } } - if (newOffsetLength != DBL_MAX) { + if (newOffsetLength != std::numeric_limits::max()) { offsetLength = newOffsetLength; } } diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandlerPolygon.h b/src/Mod/Sketcher/Gui/DrawSketchHandlerPolygon.h index 0a6a242da7..54ce11d364 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandlerPolygon.h +++ b/src/Mod/Sketcher/Gui/DrawSketchHandlerPolygon.h @@ -242,7 +242,8 @@ private: return; } - double angleOfSeparation = 2.0 * M_PI / static_cast(numberOfCorners); // NOLINT + double angleOfSeparation = + 2.0 * std::numbers::pi / static_cast(numberOfCorners); // NOLINT double cos_v = cos(angleOfSeparation); double sin_v = sin(angleOfSeparation); diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandlerRectangle.h b/src/Mod/Sketcher/Gui/DrawSketchHandlerRectangle.h index 8070c64436..f3ba68f73f 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandlerRectangle.h +++ b/src/Mod/Sketcher/Gui/DrawSketchHandlerRectangle.h @@ -111,6 +111,8 @@ public: private: void updateDataAndDrawToPosition(Base::Vector2d onSketchPos) override { + using std::numbers::pi; + switch (state()) { case SelectMode::SeekFirst: { toolWidgetManager.drawPositionAtCursor(onSketchPos); @@ -144,8 +146,8 @@ private: corner2 = Base::Vector2d(corner1.x, onSketchPos.y); cornersReversed = true; } - angle123 = M_PI / 2; - angle412 = M_PI / 2; + angle123 = pi / 2; + angle412 = pi / 2; } else if (constructionMethod() == ConstructionMethod::CenterAndCorner) { toolWidgetManager.drawDirectionAtCursor(onSketchPos, center); @@ -162,8 +164,8 @@ private: corner2 = Base::Vector2d(corner1.x, onSketchPos.y); cornersReversed = true; } - angle123 = M_PI / 2; - angle412 = M_PI / 2; + angle123 = pi / 2; + angle412 = pi / 2; } else if (constructionMethod() == ConstructionMethod::ThreePoints) { toolWidgetManager.drawDirectionAtCursor(onSketchPos, corner1); @@ -174,8 +176,8 @@ private: perpendicular.y = (corner2 - corner1).x; corner3 = corner2 + perpendicular; corner4 = corner1 + perpendicular; - angle123 = M_PI / 2; - angle412 = M_PI / 2; + angle123 = pi / 2; + angle412 = pi / 2; corner2Initial = corner2; side = getPointSideOfVector(corner3, corner2 - corner1, corner1); } @@ -189,8 +191,8 @@ private: perpendicular.y = (onSketchPos - center).x; corner2 = center + perpendicular; corner4 = center - perpendicular; - angle123 = M_PI / 2; - angle412 = M_PI / 2; + angle123 = pi / 2; + angle412 = pi / 2; side = getPointSideOfVector(corner2, corner3 - corner1, corner1); } @@ -247,7 +249,7 @@ private: acos((a.x * b.x + a.y * b.y) / (sqrt(a.x * a.x + a.y * a.y) * sqrt(b.x * b.x + b.y * b.y))); } - angle412 = M_PI - angle123; + angle412 = pi - angle123; if (roundCorners) { radius = std::min(length, width) / 6 // NOLINT * std::min(sqrt(1 - cos(angle412) * cos(angle412)), @@ -276,7 +278,7 @@ private: acos((a.x * b.x + a.y * b.y) / (sqrt(a.x * a.x + a.y * a.y) * sqrt(b.x * b.x + b.y * b.y))); } - angle123 = M_PI - angle412; + angle123 = pi - angle412; if (roundCorners) { radius = std::min(length, width) / 6 // NOLINT * std::min(sqrt(1 - cos(angle412) * cos(angle412)), @@ -677,7 +679,7 @@ private: width = vecW.Length(); angle = vecL.Angle(); if (length < Precision::Confusion() || width < Precision::Confusion() - || fmod(fabs(angle123), M_PI) < Precision::Confusion()) { + || fmod(fabs(angle123), std::numbers::pi) < Precision::Confusion()) { return; } @@ -738,9 +740,11 @@ private: void createFirstRectangleFillets(Base::Vector2d vecL, Base::Vector2d vecW, double L1, double L2) { + using std::numbers::pi; + // center points required later for special case of round corner frame with // radiusFrame = 0. - double end = angle - M_PI / 2; + double end = angle - pi / 2; Base::Vector2d b1 = (vecL + vecW) / (vecL + vecW).Length(); Base::Vector2d b2 = (vecL - vecW) / (vecL - vecW).Length(); @@ -749,16 +753,18 @@ private: center3 = toVector3d(corner3 - b1 * L2); center4 = toVector3d(corner4 + b2 * L1); - addArcToShapeGeometry(center1, end - M_PI + angle412, end, radius, isConstructionMode()); - addArcToShapeGeometry(center2, end, end - M_PI - angle123, radius, isConstructionMode()); - addArcToShapeGeometry(center3, end + angle412, end - M_PI, radius, isConstructionMode()); - addArcToShapeGeometry(center4, end - M_PI, end - angle123, radius, isConstructionMode()); + addArcToShapeGeometry(center1, end - pi + angle412, end, radius, isConstructionMode()); + addArcToShapeGeometry(center2, end, end - pi - angle123, radius, isConstructionMode()); + addArcToShapeGeometry(center3, end + angle412, end - pi, radius, isConstructionMode()); + addArcToShapeGeometry(center4, end - pi, end - angle123, radius, isConstructionMode()); } void createSecondRectangleGeometries(Base::Vector2d vecL, Base::Vector2d vecW, double L1, double L2) { - double end = angle - M_PI / 2; + using std::numbers::pi; + + double end = angle - pi / 2; if (radius < Precision::Confusion()) { radiusFrame = 0.; @@ -800,22 +806,22 @@ private: Base::Vector2d b2 = (vecL - vecW) / (vecL - vecW).Length(); addArcToShapeGeometry(toVector3d(frameCorner1 + b1 * L2F), - end - M_PI + angle412, + end - pi + angle412, end, radiusFrame, isConstructionMode()); addArcToShapeGeometry(toVector3d(frameCorner2 - b2 * L1F), end, - end - M_PI - angle123, + end - pi - angle123, radiusFrame, isConstructionMode()); addArcToShapeGeometry(toVector3d(frameCorner3 - b1 * L2F), end + angle412, - end - M_PI, + end - pi, radiusFrame, isConstructionMode()); addArcToShapeGeometry(toVector3d(frameCorner4 + b2 * L1F), - end - M_PI, + end - pi, end - angle123, radiusFrame, isConstructionMode()); @@ -1313,7 +1319,7 @@ private: firstCurve + 1, Sketcher::PointPos::none, firstCurve + 3); - if (fabs(angle123 - M_PI / 2) < Precision::Confusion()) { + if (fabs(angle123 - std::numbers::pi / 2) < Precision::Confusion()) { addToShapeConstraints(Sketcher::Perpendicular, firstCurve, Sketcher::PointPos::none, @@ -1932,7 +1938,7 @@ void DSHRectangleControllerBase::doEnforceControlParameters(Base::Vector2d& onSk if (onViewParameters[OnViewParameter::Sixth]->isSet) { double angle = Base::toRadians(onViewParameters[OnViewParameter::Sixth]->getValue()); - if (fmod(angle, M_PI) < Precision::Confusion()) { + if (fmod(angle, std::numbers::pi) < Precision::Confusion()) { unsetOnViewParameter(onViewParameters[OnViewParameter::Sixth].get()); return; } @@ -1944,8 +1950,8 @@ void DSHRectangleControllerBase::doEnforceControlParameters(Base::Vector2d& onSk int sign = handler->side != sign1 ? 1 : -1; - double angle123 = - (handler->corner2Initial - handler->corner1).Angle() + M_PI + sign * angle; + double angle123 = (handler->corner2Initial - handler->corner1).Angle() + + std::numbers::pi + sign * angle; onSketchPos.x = handler->corner2Initial.x + cos(angle123) * width; onSketchPos.y = handler->corner2Initial.y + sin(angle123) * width; @@ -1969,12 +1975,12 @@ void DSHRectangleControllerBase::doEnforceControlParameters(Base::Vector2d& onSk if (onViewParameters[OnViewParameter::Sixth]->isSet) { double c = Base::toRadians(onViewParameters[OnViewParameter::Sixth]->getValue()); - if (fmod(c, M_PI) < Precision::Confusion()) { + if (fmod(c, std::numbers::pi) < Precision::Confusion()) { unsetOnViewParameter(onViewParameters[OnViewParameter::Sixth].get()); return; } - double a = asin(width * sin(M_PI - c) + double a = asin(width * sin(std::numbers::pi - c) / (handler->corner3 - handler->corner1).Length()); int sign1 = handler->getPointSideOfVector(onSketchPos, @@ -2399,6 +2405,8 @@ void DSHRectangleController::doChangeDrawSketchHandlerMode() template<> void DSHRectangleController::addConstraints() { + using std::numbers::pi; + App::DocumentObject* obj = handler->sketchgui->getObject(); int firstCurve = handler->firstCurve; @@ -2569,15 +2577,15 @@ void DSHRectangleController::addConstraints() if (handler->constructionMethod() == ConstructionMethod::ThreePoints) { if (angleSet) { - if (fabs(angle - M_PI) < Precision::Confusion() - || fabs(angle + M_PI) < Precision::Confusion() + if (fabs(angle - pi) < Precision::Confusion() + || fabs(angle + pi) < Precision::Confusion() || fabs(angle) < Precision::Confusion()) { Gui::cmdAppObjectArgs(obj, "addConstraint(Sketcher.Constraint('Horizontal',%d)) ", firstCurve); } - else if (fabs(angle - M_PI / 2) < Precision::Confusion() - || fabs(angle + M_PI / 2) < Precision::Confusion()) { + else if (fabs(angle - pi / 2) < Precision::Confusion() + || fabs(angle + pi / 2) < Precision::Confusion()) { Gui::cmdAppObjectArgs(obj, "addConstraint(Sketcher.Constraint('Vertical',%d)) ", firstCurve); @@ -2591,7 +2599,7 @@ void DSHRectangleController::addConstraints() } } if (innerAngleSet) { - if (fabs(innerAngle - M_PI / 2) > Precision::Confusion()) { + if (fabs(innerAngle - pi / 2) > Precision::Confusion()) { // if 90? then perpendicular already created. Gui::cmdAppObjectArgs(obj, "addConstraint(Sketcher.Constraint('Angle',%d,%d,%d,%d,%f)) ", @@ -2617,7 +2625,7 @@ void DSHRectangleController::addConstraints() obj); } if (innerAngleSet) { - if (fabs(innerAngle - M_PI / 2) > Precision::Confusion()) { + if (fabs(innerAngle - pi / 2) > Precision::Confusion()) { // if 90? then perpendicular already created. Gui::cmdAppObjectArgs(obj, "addConstraint(Sketcher.Constraint('Angle',%d,%d,%d,%d,%f)) ", diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandlerRotate.h b/src/Mod/Sketcher/Gui/DrawSketchHandlerRotate.h index 32266e9d38..03be54ee61 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandlerRotate.h +++ b/src/Mod/Sketcher/Gui/DrawSketchHandlerRotate.h @@ -103,7 +103,7 @@ private: endpoint = centerPoint + length * Base::Vector2d(cos(endAngle), sin(endAngle)); double angle1 = endAngle - startAngle; - double angle2 = angle1 + (angle1 < 0. ? 2 : -2) * M_PI; + double angle2 = angle1 + (angle1 < 0. ? 2 : -2) * std::numbers::pi; totalAngle = abs(angle1 - totalAngle) < abs(angle2 - totalAngle) ? angle1 : angle2; CreateAndDrawShapeGeometry(); @@ -408,10 +408,11 @@ private: // the new geometry. /*if (cstr->Type == DistanceX || cstr->Type == DistanceY) { //DistanceX/Y can be applied only if the rotation if 90 or 180. - if (fabs(fmod(individualAngle, M_PI)) < Precision::Confusion()) { + if (fabs(fmod(individualAngle, std::numbers::pi)) < + Precision::Confusion()) { // ok and nothing to do actually } - else if (fabs(fmod(individualAngle, M_PI * 0.5)) < + else if (fabs(fmod(individualAngle, std::numbers::pi * 0.5)) < Precision::Confusion()) { cstr->Type = cstr->Type == DistanceX ? DistanceY : DistanceX; } @@ -567,7 +568,7 @@ void DSHRotateControllerBase::doEnforceControlParameters(Base::Vector2d& onSketc double arcAngle = Base::toRadians(onViewParameters[OnViewParameter::Third]->getValue()); - if (fmod(fabs(arcAngle), 2 * M_PI) < Precision::Confusion()) { + if (fmod(fabs(arcAngle), 2 * std::numbers::pi) < Precision::Confusion()) { unsetOnViewParameter(onViewParameters[OnViewParameter::Third].get()); return; } @@ -580,7 +581,7 @@ void DSHRotateControllerBase::doEnforceControlParameters(Base::Vector2d& onSketc double arcAngle = Base::toRadians(onViewParameters[OnViewParameter::Fourth]->getValue()); - if (fmod(fabs(arcAngle), 2 * M_PI) < Precision::Confusion()) { + if (fmod(fabs(arcAngle), 2 * std::numbers::pi) < Precision::Confusion()) { unsetOnViewParameter(onViewParameters[OnViewParameter::Fourth].get()); return; } diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandlerSlot.h b/src/Mod/Sketcher/Gui/DrawSketchHandlerSlot.h index 1108aa244e..46b27d58b3 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandlerSlot.h +++ b/src/Mod/Sketcher/Gui/DrawSketchHandlerSlot.h @@ -241,6 +241,8 @@ private: void createShape(bool onlyeditoutline) override { + using std::numbers::pi; + ShapeGeometry.clear(); if (length < Precision::Confusion() || radius < Precision::Confusion()) { @@ -248,14 +250,14 @@ private: } Part::GeomArcOfCircle* arc1 = addArcToShapeGeometry(toVector3d(startPoint), - M_PI / 2 + angle, - 1.5 * M_PI + angle, + pi / 2 + angle, + 1.5 * pi + angle, radius, isConstructionMode()); Part::GeomArcOfCircle* arc2 = addArcToShapeGeometry(toVector3d(secondPoint), - 1.5 * M_PI + angle, - M_PI / 2 + angle, + 1.5 * pi + angle, + pi / 2 + angle, radius, isConstructionMode()); @@ -323,13 +325,15 @@ private: void checkHorizontalVertical() { + using std::numbers::pi; + isHorizontal = false; isVertical = false; - if (fmod(fabs(angle), M_PI) < Precision::Confusion()) { + if (fmod(fabs(angle), pi) < Precision::Confusion()) { isHorizontal = true; } - else if (fmod(fabs(angle + M_PI / 2), M_PI) < Precision::Confusion()) { + else if (fmod(fabs(angle + pi / 2), pi) < Precision::Confusion()) { isVertical = true; } } diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandlerTranslate.h b/src/Mod/Sketcher/Gui/DrawSketchHandlerTranslate.h index bac2d51208..f1412f8d7c 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandlerTranslate.h +++ b/src/Mod/Sketcher/Gui/DrawSketchHandlerTranslate.h @@ -584,7 +584,8 @@ void DSHTranslateControllerBase::doEnforceControlParameters(Base::Vector2d& onSk } if (onViewParameters[OnViewParameter::Fourth]->isSet) { - double angle = onViewParameters[OnViewParameter::Fourth]->getValue() * M_PI / 180; + double angle = + onViewParameters[OnViewParameter::Fourth]->getValue() * std::numbers::pi / 180; onSketchPos.x = handler->referencePoint.x + cos(angle) * length; onSketchPos.y = handler->referencePoint.y + sin(angle) * length; } @@ -607,7 +608,8 @@ void DSHTranslateControllerBase::doEnforceControlParameters(Base::Vector2d& onSk } if (onViewParameters[OnViewParameter::Sixth]->isSet) { - double angle = onViewParameters[OnViewParameter::Sixth]->getValue() * M_PI / 180; + double angle = + onViewParameters[OnViewParameter::Sixth]->getValue() * std::numbers::pi / 180; onSketchPos.x = handler->referencePoint.x + cos(angle) * length; onSketchPos.y = handler->referencePoint.y + sin(angle) * length; } @@ -647,7 +649,7 @@ void DSHTranslateController::adaptParameters(Base::Vector2d onSketchPos) Base::Vector2d vec2d = Base::Vector2d(handler->firstTranslationVector.x, handler->firstTranslationVector.y); double angle = vec2d.Angle(); - double range = angle * 180 / M_PI; + double range = angle * 180 / std::numbers::pi; if (!onViewParameters[OnViewParameter::Fourth]->isSet) { setOnViewParameterValue(OnViewParameter::Fourth, range, Base::Unit::Angle); @@ -669,7 +671,7 @@ void DSHTranslateController::adaptParameters(Base::Vector2d onSketchPos) Base::Vector2d vec2d = Base::Vector2d(handler->secondTranslationVector.x, handler->secondTranslationVector.y); double angle = vec2d.Angle(); - double range = angle * 180 / M_PI; + double range = angle * 180 / std::numbers::pi; if (!onViewParameters[OnViewParameter::Sixth]->isSet) { setOnViewParameterValue(OnViewParameter::Sixth, range, Base::Unit::Angle); diff --git a/src/Mod/Sketcher/Gui/EditModeConstraintCoinManager.cpp b/src/Mod/Sketcher/Gui/EditModeConstraintCoinManager.cpp index e368a8d7de..f69d71d71e 100644 --- a/src/Mod/Sketcher/Gui/EditModeConstraintCoinManager.cpp +++ b/src/Mod/Sketcher/Gui/EditModeConstraintCoinManager.cpp @@ -114,6 +114,8 @@ void EditModeConstraintCoinManager::updateVirtualSpace() void EditModeConstraintCoinManager::processConstraints(const GeoListFacade& geolistfacade) { + using std::numbers::pi; + const auto& constrlist = ViewProviderSketchCoinAttorney::getConstraints(viewProvider); auto zConstrH = ViewProviderSketchCoinAttorney::getViewOrientationFactor(viewProvider) @@ -262,7 +264,7 @@ Restart: const Part::GeomCircle* circle = static_cast(geo); ra = circle->getRadius(); - angle = M_PI / 4; + angle = pi / 4; midpos = circle->getCenter(); } else if (geo->is()) { @@ -281,7 +283,7 @@ Restart: rb = ellipse->getMinorRadius(); Base::Vector3d majdir = ellipse->getMajorAxisDir(); angle = atan2(majdir.y, majdir.x); - angleplus = M_PI / 4; + angleplus = pi / 4; midpos = ellipse->getCenter(); } else if (geo->is()) { @@ -460,7 +462,7 @@ Restart: norm1.Normalize(); dir1 = norm1; - dir1.RotateZ(-M_PI / 2.0); + dir1.RotateZ(-pi / 2.0); } else if (Constr->FirstPos == Sketcher::PointPos::none) { @@ -485,7 +487,7 @@ Restart: else if (geo1->is()) { const Part::GeomCircle* circle = static_cast(geo1); - norm1 = Base::Vector3d(cos(M_PI / 4), sin(M_PI / 4), 0); + norm1 = Base::Vector3d(cos(pi / 4), sin(pi / 4), 0); dir1 = Base::Vector3d(-norm1.y, norm1.x, 0); midpos1 = circle->getCenter() + circle->getRadius() * norm1; } @@ -514,7 +516,7 @@ Restart: else if (geo2->is()) { const Part::GeomCircle* circle = static_cast(geo2); - norm2 = Base::Vector3d(cos(M_PI / 4), sin(M_PI / 4), 0); + norm2 = Base::Vector3d(cos(pi / 4), sin(pi / 4), 0); dir2 = Base::Vector3d(-norm2.y, norm2.x, 0); midpos2 = circle->getCenter() + circle->getRadius() * norm2; } @@ -576,7 +578,7 @@ Restart: const Part::GeomCircle* circle = static_cast(geo1); r1a = circle->getRadius(); - angle1 = M_PI / 4; + angle1 = pi / 4; midpos1 = circle->getCenter(); } else if (geo1->is()) { @@ -595,7 +597,7 @@ Restart: r1b = ellipse->getMinorRadius(); Base::Vector3d majdir = ellipse->getMajorAxisDir(); angle1 = atan2(majdir.y, majdir.x); - angle1plus = M_PI / 4; + angle1plus = pi / 4; midpos1 = ellipse->getCenter(); } else if (geo1->is()) { @@ -641,7 +643,7 @@ Restart: const Part::GeomCircle* circle = static_cast(geo2); r2a = circle->getRadius(); - angle2 = M_PI / 4; + angle2 = pi / 4; midpos2 = circle->getCenter(); } else if (geo2->is()) { @@ -660,7 +662,7 @@ Restart: r2b = ellipse->getMinorRadius(); Base::Vector3d majdir = ellipse->getMajorAxisDir(); angle2 = atan2(majdir.y, majdir.x); - angle2plus = M_PI / 4; + angle2plus = pi / 4; midpos2 = ellipse->getCenter(); } else if (geo2->is()) { @@ -968,7 +970,7 @@ Restart: // otherwise We still use findHelperAngles before to find if helper // is needed. helperStartAngle1 = endAngle; - helperRange1 = 2 * M_PI - (endAngle - startAngle); + helperRange1 = 2 * pi - (endAngle - startAngle); numPoints++; } @@ -991,7 +993,7 @@ Restart: if (helperRange2 != 0.) { helperStartAngle2 = endAngle; - helperRange2 = 2 * M_PI - (endAngle - startAngle); + helperRange2 = 2 * pi - (endAngle - startAngle); numPoints++; } @@ -1089,7 +1091,7 @@ Restart: // getSolvedSketch().calculateNormalAtPoint(Constr->Second, pos.x, pos.y); norm.Normalize(); Base::Vector3d dir = norm; - dir.RotateZ(-M_PI / 2.0); + dir.RotateZ(-pi / 2.0); relPos = seekConstraintPosition( pos, @@ -1340,7 +1342,7 @@ Restart: p1[1] = line1->getEndPoint(); p2[0] = line2->getStartPoint(); p2[1] = line2->getEndPoint(); - double length = DBL_MAX; + double length = std::numeric_limits::max(); for (int i = 0; i <= 1; i++) { for (int j = 0; j <= 1; j++) { double tmp = (p2[j] - p1[i]).Length(); @@ -1385,12 +1387,12 @@ Restart: // TODO: Check // dir1 = getSolvedSketch().calculateNormalAtPoint(Constr->First, // p.x, p.y); - dir1.RotateZ(-M_PI / 2); // convert to vector of tangency by rotating + dir1.RotateZ(-pi / 2); // convert to vector of tangency by rotating dir2 = getNormal(geolistfacade, Constr->Second, p); // TODO: Check // dir2 = getSolvedSketch().calculateNormalAtPoint(Constr->Second, // p.x, p.y); - dir2.RotateZ(-M_PI / 2); + dir2.RotateZ(-pi / 2); startangle = atan2(dir1.y, dir1.x); range = atan2(dir1.x * dir2.y - dir1.y * dir2.x, @@ -1632,26 +1634,28 @@ void EditModeConstraintCoinManager::findHelperAngles(double& helperStartAngle, double startAngle, double endAngle) { + using std::numbers::pi; + double margin = 0.2; // about 10deg if (angle < 0) { - angle = angle + 2 * M_PI; + angle = angle + 2 * pi; } // endAngle can be more than 2*pi as its startAngle + arcAngle - if (endAngle > 2 * M_PI && angle < endAngle - 2 * M_PI) { - angle = angle + 2 * M_PI; + if (endAngle > 2 * pi && angle < endAngle - 2 * pi) { + angle = angle + 2 * pi; } if (!(angle > startAngle && angle < endAngle)) { - if ((angle < startAngle && startAngle - angle < angle + 2 * M_PI - endAngle) - || (angle > endAngle && startAngle + 2 * M_PI - angle < angle - endAngle)) { + if ((angle < startAngle && startAngle - angle < angle + 2 * pi - endAngle) + || (angle > endAngle && startAngle + 2 * pi - angle < angle - endAngle)) { if (angle > startAngle) { - angle -= 2 * M_PI; + angle -= 2 * pi; } helperStartAngle = angle - margin; helperRange = startAngle - angle + margin; } else { if (angle < endAngle) { - angle += 2 * M_PI; + angle += 2 * pi; } helperStartAngle = endAngle; helperRange = angle - endAngle + margin; diff --git a/src/Mod/Sketcher/Gui/PreCompiled.h b/src/Mod/Sketcher/Gui/PreCompiled.h index 8d2c1892ce..d4a2ac5e7c 100644 --- a/src/Mod/Sketcher/Gui/PreCompiled.h +++ b/src/Mod/Sketcher/Gui/PreCompiled.h @@ -32,7 +32,6 @@ #ifdef _PreComp_ // standard -#include #include #include diff --git a/src/Mod/Sketcher/Gui/SnapManager.cpp b/src/Mod/Sketcher/Gui/SnapManager.cpp index 399688add4..fd34a6df0e 100644 --- a/src/Mod/Sketcher/Gui/SnapManager.cpp +++ b/src/Mod/Sketcher/Gui/SnapManager.cpp @@ -122,7 +122,8 @@ void SnapManager::ParameterObserver::updateSnapAngleParameter(const std::string& { ParameterGrp::handle hGrp = getParameterGrpHandle(); - client.snapAngle = fmod(hGrp->GetFloat(parametername.c_str(), 5.) * M_PI / 180, 2 * M_PI); + client.snapAngle = fmod(hGrp->GetFloat(parametername.c_str(), 5.) * std::numbers::pi / 180, + 2 * std::numbers::pi); } void SnapManager::ParameterObserver::subscribeToParameters() @@ -222,7 +223,7 @@ bool SnapManager::snapAtAngle(double& x, double& y) double length = (pointToOverride - referencePoint).Length(); double angle1 = (pointToOverride - referencePoint).Angle(); - double angle2 = angle1 + (angle1 < 0. ? 2 : -2) * M_PI; + double angle2 = angle1 + (angle1 < 0. ? 2 : -2) * std::numbers::pi; lastMouseAngle = abs(angle1 - lastMouseAngle) < abs(angle2 - lastMouseAngle) ? angle1 : angle2; double angle = round(lastMouseAngle / snapAngle) * snapAngle; @@ -368,10 +369,10 @@ bool SnapManager::snapToArcMiddle(Base::Vector3d& pointToOverride, const Part::G double u, v; arc->getRange(u, v, true); if (v < u) { - v += 2 * M_PI; + v += 2 * std::numbers::pi; } double angle = v - u; - int revert = angle < M_PI ? 1 : -1; + int revert = angle < std::numbers::pi ? 1 : -1; /*To know if we are close to the middle of the arc, we are going to compare the angle of the * (mouse cursor - center) to the angle of the middle of the arc. If it's less than 10% of the diff --git a/src/Mod/Sketcher/Gui/SoZoomTranslation.cpp b/src/Mod/Sketcher/Gui/SoZoomTranslation.cpp index c1981f7a68..e42a545aff 100644 --- a/src/Mod/Sketcher/Gui/SoZoomTranslation.cpp +++ b/src/Mod/Sketcher/Gui/SoZoomTranslation.cpp @@ -22,7 +22,6 @@ #include "PreCompiled.h" #ifndef _PreComp_ -#include #include #include diff --git a/src/Mod/Sketcher/Gui/TaskSketcherConstraints.cpp b/src/Mod/Sketcher/Gui/TaskSketcherConstraints.cpp index 5a6a4237e7..397d501703 100644 --- a/src/Mod/Sketcher/Gui/TaskSketcherConstraints.cpp +++ b/src/Mod/Sketcher/Gui/TaskSketcherConstraints.cpp @@ -720,8 +720,9 @@ ConstraintFilterList::ConstraintFilterList(QWidget* parent) { ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath( "User parameter:BaseApp/Preferences/Mod/Sketcher/General"); - int filterState = hGrp->GetInt("ConstraintFilterState", - INT_MAX);// INT_MAX = 1111111111111111111111111111111 in binary. + int filterState = hGrp->GetInt( + "ConstraintFilterState", + std::numeric_limits::max()); // INT_MAX = 01111111111111111111111111111111 in binary. normalFilterCount = filterItems.size() - 2;// All filter but selected and associated selectedFilterIndex = normalFilterCount; diff --git a/src/Mod/Sketcher/Gui/TaskSketcherElements.cpp b/src/Mod/Sketcher/Gui/TaskSketcherElements.cpp index e5df986f5d..551b9fa90f 100644 --- a/src/Mod/Sketcher/Gui/TaskSketcherElements.cpp +++ b/src/Mod/Sketcher/Gui/TaskSketcherElements.cpp @@ -1124,8 +1124,9 @@ ElementFilterList::ElementFilterList(QWidget* parent) { ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath( "User parameter:BaseApp/Preferences/Mod/Sketcher/General"); - int filterState = hGrp->GetInt("ElementFilterState", - INT_MAX);// INT_MAX = 1111111111111111111111111111111 in binary. + int filterState = hGrp->GetInt( + "ElementFilterState", + std::numeric_limits::max());// INT_MAX = 01111111111111111111111111111111 in binary. for (auto const& filterItem : filterItems) { Q_UNUSED(filterItem); @@ -1320,7 +1321,7 @@ void TaskSketcherElements::onListMultiFilterItemChanged(QListWidgetItem* item) } // Save the state of the filter. - int filterState = INT_MIN;// INT_MIN = 000000000000000000000000000000 in binary. + int filterState = 0; // All bits are cleared. for (int i = filterList->count() - 1; i >= 0; i--) { bool isChecked = filterList->item(i)->checkState() == Qt::Checked; filterState = filterState << 1;// we shift left first, else the list is shifted at the end. diff --git a/src/Mod/Sketcher/Gui/TaskSketcherGeneral.ui.autosave b/src/Mod/Sketcher/Gui/TaskSketcherGeneral.ui.autosave new file mode 100644 index 0000000000..3b73e8be00 --- /dev/null +++ b/src/Mod/Sketcher/Gui/TaskSketcherGeneral.ui.autosave @@ -0,0 +1,77 @@ + + + TaskSketcherGeneral + + + + 0 + 0 + 253 + 61 + + + + + .AppleSystemUIFont + + + + Form + + + + + + + 0 + 0 + + + + Link + + + + + + + + 0 + 0 + + + + DOF + + + + + + + + 1 + 0 + + + + Auto Recompute + + + + + + + + Gui::StatefulLabel + QLabel +
    Gui/Widgets.h
    +
    + + Gui::UrlLabel + QLabel +
    Gui/Widgets.h
    +
    +
    + + +
    diff --git a/src/Mod/Sketcher/Gui/Utils.cpp b/src/Mod/Sketcher/Gui/Utils.cpp index 9a6f1ee223..6fc5562cf7 100644 --- a/src/Mod/Sketcher/Gui/Utils.cpp +++ b/src/Mod/Sketcher/Gui/Utils.cpp @@ -22,7 +22,6 @@ #include "PreCompiled.h" #ifndef _PreComp_ -#include #include #include @@ -432,7 +431,7 @@ double SketcherGui::GetPointAngle(const Base::Vector2d& p1, const Base::Vector2d { double dX = p2.x - p1.x; double dY = p2.y - p1.y; - return dY >= 0 ? atan2(dY, dX) : atan2(dY, dX) + 2 * M_PI; + return dY >= 0 ? atan2(dY, dX) : atan2(dY, dX) + 2 * std::numbers::pi; } // Set the two points on circles at minimal distance diff --git a/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp b/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp index 1bde48de8d..3c27a517df 100644 --- a/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp +++ b/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp @@ -861,7 +861,7 @@ void ViewProviderSketch::getCoordsOnSketchPlane(const SbVec3f& point, const SbVe // line Base::Vector3d R1(point[0], point[1], point[2]), RA(normal[0], normal[1], normal[2]); - if (fabs(RN * RA) < FLT_EPSILON) + if (fabs(RN * RA) < std::numeric_limits::epsilon()) throw Base::ZeroDivisionError("View direction is parallel to sketch plane"); // intersection point on plane Base::Vector3d S = R1 + ((RN * (R0 - R1)) / (RN * RA)) * RA; @@ -1930,9 +1930,9 @@ void ViewProviderSketch::moveConstraint(Sketcher::Constraint* Constr, int constN || Constr->Type == Weight) dir = (p2 - p1).Normalize(); else if (Constr->Type == DistanceX) - dir = Base::Vector3d((p2.x - p1.x >= FLT_EPSILON) ? 1 : -1, 0, 0); + dir = Base::Vector3d((p2.x - p1.x >= std::numeric_limits::epsilon()) ? 1 : -1, 0, 0); else if (Constr->Type == DistanceY) - dir = Base::Vector3d(0, (p2.y - p1.y >= FLT_EPSILON) ? 1 : -1, 0); + dir = Base::Vector3d(0, (p2.y - p1.y >= std::numeric_limits::epsilon()) ? 1 : -1, 0); if (Constr->Type == Radius || Constr->Type == Diameter || Constr->Type == Weight) { Constr->LabelDistance = vec.x * dir.x + vec.y * dir.y; @@ -2029,9 +2029,9 @@ void ViewProviderSketch::moveAngleConstraint(Sketcher::Constraint* constr, int c Base::Vector3d p = getSolvedSketch().getPoint(constr->Third, constr->ThirdPos); p0 = Base::Vector3d(p.x, p.y, 0); Base::Vector3d dir1 = getSolvedSketch().calculateNormalAtPoint(constr->First, p.x, p.y); - dir1.RotateZ(-M_PI / 2);// convert to vector of tangency by rotating + dir1.RotateZ(-std::numbers::pi / 2);// convert to vector of tangency by rotating Base::Vector3d dir2 = getSolvedSketch().calculateNormalAtPoint(constr->Second, p.x, p.y); - dir2.RotateZ(-M_PI / 2); + dir2.RotateZ(-std::numbers::pi / 2); Base::Vector3d vec = Base::Vector3d(toPos.x, toPos.y, 0) - p0; factor = factor * Base::sgn((dir1 + dir2) * vec); @@ -3996,7 +3996,7 @@ double ViewProviderSketch::getRotation(SbVec3f pos0, SbVec3f pos1) const getCoordsOnSketchPlane(pos0, vol.getProjectionDirection(), x0, y0); getCoordsOnSketchPlane(pos1, vol.getProjectionDirection(), x1, y1); - return -atan2((y1 - y0), (x1 - x0)) * 180 / M_PI; + return -atan2((y1 - y0), (x1 - x0)) * 180 / std::numbers::pi; } catch (const Base::ZeroDivisionError&) { return 0; diff --git a/src/Mod/Sketcher/Gui/ViewProviderSketchGeometryExtension.cpp b/src/Mod/Sketcher/Gui/ViewProviderSketchGeometryExtension.cpp index 05feb2c345..b2d177b524 100644 --- a/src/Mod/Sketcher/Gui/ViewProviderSketchGeometryExtension.cpp +++ b/src/Mod/Sketcher/Gui/ViewProviderSketchGeometryExtension.cpp @@ -57,11 +57,7 @@ std::unique_ptr ViewProviderSketchGeometryExtension::co copyAttributes(cpy.get()); -#if defined(__GNUC__) && (__GNUC__ <= 4) - return std::move(cpy); -#else return cpy; -#endif } void ViewProviderSketchGeometryExtension::restoreAttributes(Base::XMLReader& reader) diff --git a/src/Mod/Spreadsheet/Gui/SheetTableView.cpp b/src/Mod/Spreadsheet/Gui/SheetTableView.cpp index f8a35e3287..10013b96b5 100644 --- a/src/Mod/Spreadsheet/Gui/SheetTableView.cpp +++ b/src/Mod/Spreadsheet/Gui/SheetTableView.cpp @@ -698,9 +698,9 @@ void SheetTableView::copySelection() void SheetTableView::_copySelection(const std::vector& ranges, bool copy) { - int minRow = INT_MAX; + int minRow = std::numeric_limits::max(); int maxRow = 0; - int minCol = INT_MAX; + int minCol = std::numeric_limits::max(); int maxCol = 0; for (auto& range : ranges) { minRow = std::min(minRow, range.from().row()); diff --git a/src/Mod/Start/Gui/StartView.cpp b/src/Mod/Start/Gui/StartView.cpp index dc45b87849..a8b45b89f0 100644 --- a/src/Mod/Start/Gui/StartView.cpp +++ b/src/Mod/Start/Gui/StartView.cpp @@ -330,6 +330,11 @@ void StartView::newArchFile() const catch (...) { Gui::Application::Instance->activateWorkbench("ArchWorkbench"); } + + // Set the camera zoom level to 10 m, which is more appropriate for architectural projects + Gui::Command::doCommand( + Gui::Command::Gui, + "Gui.activeDocument().activeView().viewDefaultOrientation(None, 10000.0)"); postStart(PostStartBehavior::doNotSwitchWorkbench); } diff --git a/src/Mod/Surface/App/FeatureExtend.cpp b/src/Mod/Surface/App/FeatureExtend.cpp index 83701ba824..518a300d18 100644 --- a/src/Mod/Surface/App/FeatureExtend.cpp +++ b/src/Mod/Surface/App/FeatureExtend.cpp @@ -42,7 +42,9 @@ using namespace Surface; -const App::PropertyIntegerConstraint::Constraints SampleRange = {2, INT_MAX, 1}; +const App::PropertyIntegerConstraint::Constraints SampleRange = {2, + std::numeric_limits::max(), + 1}; const App::PropertyFloatConstraint::Constraints ToleranceRange = {0.0, 10.0, 0.01}; const App::PropertyFloatConstraint::Constraints ExtendRange = {-0.5, 10.0, 0.01}; PROPERTY_SOURCE(Surface::Extend, Part::Spline) diff --git a/src/Mod/TechDraw/App/CenterLine.cpp b/src/Mod/TechDraw/App/CenterLine.cpp index a61d4ad12a..3ca87f7832 100644 --- a/src/Mod/TechDraw/App/CenterLine.cpp +++ b/src/Mod/TechDraw/App/CenterLine.cpp @@ -392,7 +392,7 @@ std::pair CenterLine::rotatePointsAroundMid(cons const double angleDeg) { std::pair result; - double angleRad = angleDeg * M_PI / 180.0; + double angleRad = angleDeg * std::numbers::pi / 180.0; result.first.x = ((p1.x - mid.x) * cos(angleRad)) - ((p1.y - mid.y) * sin(angleRad)) + mid.x; result.first.y = ((p1.x - mid.x) * sin(angleRad)) + ((p1.y - mid.y) * cos(angleRad)) + mid.y; diff --git a/src/Mod/TechDraw/App/CosmeticVertex.cpp b/src/Mod/TechDraw/App/CosmeticVertex.cpp index a33e6c2dd8..35275c0546 100644 --- a/src/Mod/TechDraw/App/CosmeticVertex.cpp +++ b/src/Mod/TechDraw/App/CosmeticVertex.cpp @@ -166,7 +166,7 @@ Base::Vector3d CosmeticVertex::rotatedAndScaled(const double scale, const double // invert the Y coordinate so the rotation math works out // the stored point is inverted scaledPoint = DU::invertY(scaledPoint); - scaledPoint.RotateZ(rotDegrees * M_PI / DegreesHalfCircle); + scaledPoint.RotateZ(rotDegrees * std::numbers::pi / DegreesHalfCircle); scaledPoint = DU::invertY(scaledPoint); } return scaledPoint; @@ -182,7 +182,7 @@ Base::Vector3d CosmeticVertex::makeCanonicalPoint(DrawViewPart* dvp, Base::Vecto Base::Vector3d result = point; if (rotDeg != 0.0) { // unrotate the point - double rotRad = rotDeg * M_PI / DegreesHalfCircle; + double rotRad = rotDeg * std::numbers::pi / DegreesHalfCircle; // we always rotate around the origin. result.RotateZ(-rotRad); } diff --git a/src/Mod/TechDraw/App/DrawBrokenView.cpp b/src/Mod/TechDraw/App/DrawBrokenView.cpp index 4dd3a15a02..af7c91fc54 100644 --- a/src/Mod/TechDraw/App/DrawBrokenView.cpp +++ b/src/Mod/TechDraw/App/DrawBrokenView.cpp @@ -1113,7 +1113,7 @@ Base::Vector3d DrawBrokenView::makePerpendicular(Base::Vector3d inDir) const gp_Pnt origin(0.0, 0.0, 0.0); auto dir = getProjectionCS().Direction(); gp_Ax1 axis(origin, dir); - auto gRotated = gDir.Rotated(axis, M_PI_2); + auto gRotated = gDir.Rotated(axis, std::numbers::pi/2); return Base::convertTo(gRotated); } diff --git a/src/Mod/TechDraw/App/DrawComplexSection.cpp b/src/Mod/TechDraw/App/DrawComplexSection.cpp index eda78c83e3..198d5da1ec 100644 --- a/src/Mod/TechDraw/App/DrawComplexSection.cpp +++ b/src/Mod/TechDraw/App/DrawComplexSection.cpp @@ -100,7 +100,6 @@ #include #endif -#define _USE_MATH_DEFINES #include #include @@ -387,7 +386,7 @@ void DrawComplexSection::makeAlignedPieces(const TopoDS_Shape& rawShape) } //we only want to reverse the segment normal if it is not perpendicular to section normal if (segmentNormal.Dot(gProjectionUnit) != 0.0 - && segmentNormal.Angle(gProjectionUnit) <= M_PI_2) { + && segmentNormal.Angle(gProjectionUnit) <= std::numbers::pi/2) { segmentNormal.Reverse(); } @@ -957,7 +956,7 @@ gp_Vec DrawComplexSection::projectVector(const gp_Vec& vec) const // being slightly wrong. see https://forum.freecad.org/viewtopic.php?t=79017&sid=612a62a60f5db955ee071a7aaa362dbb bool DrawComplexSection::validateOffsetProfile(TopoDS_Wire profile, Base::Vector3d direction, double angleThresholdDeg) const { - double angleThresholdRad = angleThresholdDeg * M_PI / 180.0; // 5 degrees + double angleThresholdRad = angleThresholdDeg * std::numbers::pi / 180.0; // 5 degrees TopExp_Explorer explEdges(profile, TopAbs_EDGE); for (; explEdges.More(); explEdges.Next()) { std::pair segmentEnds = getSegmentEnds(TopoDS::Edge(explEdges.Current())); diff --git a/src/Mod/TechDraw/App/DrawGeomHatch.cpp b/src/Mod/TechDraw/App/DrawGeomHatch.cpp index 66366a000e..72412cf557 100644 --- a/src/Mod/TechDraw/App/DrawGeomHatch.cpp +++ b/src/Mod/TechDraw/App/DrawGeomHatch.cpp @@ -385,6 +385,8 @@ std::vector DrawGeomHatch::getTrimmedLines(DrawViewPart* source, /* static */ std::vector DrawGeomHatch::makeEdgeOverlay(PATLineSpec hatchLine, Bnd_Box bBox, double scale, double rotation) { + using std::numbers::pi; + const size_t MaxNumberOfEdges = Preferences::getPreferenceGroup("PAT")->GetInt("MaxSeg", 10000l); std::vector result; @@ -399,12 +401,12 @@ std::vector DrawGeomHatch::makeEdgeOverlay(PATLineSpec hatchLine, B double interval = hatchLine.getInterval() * scale; double offset = hatchLine.getOffset() * scale; double angle = hatchLine.getAngle() + rotation; - origin.RotateZ(rotation * M_PI / 180.); + origin.RotateZ(rotation * pi / 180.); if (scale == 0. || interval == 0.) return {}; - Base::Vector3d hatchDirection(cos(angle * M_PI / 180.), sin(angle * M_PI / 180.), 0.); + Base::Vector3d hatchDirection(cos(angle * pi / 180.), sin(angle * pi / 180.), 0.); Base::Vector3d hatchPerpendicular(-hatchDirection.y, hatchDirection.x, 0.); Base::Vector3d hatchIntervalAndOffset = offset * hatchDirection + interval * hatchPerpendicular; diff --git a/src/Mod/TechDraw/App/DrawLeaderLine.cpp b/src/Mod/TechDraw/App/DrawLeaderLine.cpp index 124b5164b2..bca5eea111 100644 --- a/src/Mod/TechDraw/App/DrawLeaderLine.cpp +++ b/src/Mod/TechDraw/App/DrawLeaderLine.cpp @@ -375,7 +375,7 @@ std::vector DrawLeaderLine::getScaledAndRotatedPoints(bool doSc double rotationRad{0.0}; if (doRotate) { - rotationRad = dvp->Rotation.getValue() * M_PI / DegreesHalfCircle; + rotationRad = dvp->Rotation.getValue() * std::numbers::pi / DegreesHalfCircle; } std::vector pointsAll = WayPoints.getValues(); @@ -409,7 +409,7 @@ DrawLeaderLine::makeCanonicalPoints(const std::vector& inPoints, double rotationRad{0.0}; if (doRotate) { - rotationRad = - dvp->Rotation.getValue() * M_PI / DegreesHalfCircle; + rotationRad = - dvp->Rotation.getValue() * std::numbers::pi / DegreesHalfCircle; } std::vector result; diff --git a/src/Mod/TechDraw/App/DrawPage.cpp b/src/Mod/TechDraw/App/DrawPage.cpp index 2ad8f2c926..bd5c908e68 100644 --- a/src/Mod/TechDraw/App/DrawPage.cpp +++ b/src/Mod/TechDraw/App/DrawPage.cpp @@ -118,7 +118,7 @@ void DrawPage::onChanged(const App::Property* prop) for (auto* obj : getViews()) { auto* view = dynamic_cast(obj); if (view && view->ScaleType.isValue("Page")) { - if (std::abs(view->Scale.getValue() - Scale.getValue()) > FLT_EPSILON) { + if (std::abs(view->Scale.getValue() - Scale.getValue()) > std::numeric_limits::epsilon()) { view->Scale.setValue(Scale.getValue()); } } diff --git a/src/Mod/TechDraw/App/DrawProjGroup.cpp b/src/Mod/TechDraw/App/DrawProjGroup.cpp index 2b491aff97..2b05d8ab60 100644 --- a/src/Mod/TechDraw/App/DrawProjGroup.cpp +++ b/src/Mod/TechDraw/App/DrawProjGroup.cpp @@ -142,7 +142,7 @@ void DrawProjGroup::onChanged(const App::Property* prop) if (prop == &ScaleType) { if (ScaleType.isValue("Page")) { double newScale = page->Scale.getValue(); - if (std::abs(getScale() - newScale) > FLT_EPSILON) { + if (std::abs(getScale() - newScale) > std::numeric_limits::epsilon()) { Scale.setValue(newScale); updateChildrenScale(); } @@ -1146,9 +1146,9 @@ void DrawProjGroup::spin(const SpinDirection& spindirection) { double angle; if (spindirection == SpinDirection::CW) - angle = M_PI / 2.0;// Top -> Right -> Bottom -> Left -> Top + angle = std::numbers::pi / 2.0;// Top -> Right -> Bottom -> Left -> Top if (spindirection == SpinDirection::CCW) - angle = -M_PI / 2.0;// Top -> Left -> Bottom -> Right -> Top + angle = -std::numbers::pi / 2.0;// Top -> Left -> Bottom -> Right -> Top spin(angle); } diff --git a/src/Mod/TechDraw/App/DrawProjectSplit.cpp b/src/Mod/TechDraw/App/DrawProjectSplit.cpp index dd4b1825a0..7853e729b6 100644 --- a/src/Mod/TechDraw/App/DrawProjectSplit.cpp +++ b/src/Mod/TechDraw/App/DrawProjectSplit.cpp @@ -345,10 +345,12 @@ std::vector DrawProjectSplit::sortEdges(std::vector& //************************* std::string edgeSortItem::dump() { + using std::numbers::pi; + std::string result; std::stringstream builder; builder << "edgeSortItem - s: " << DrawUtil::formatVector(start) << " e: " << DrawUtil::formatVector(end) << - " sa: " << startAngle * 180.0/M_PI << " ea: " << endAngle* 180.0/M_PI << " idx: " << idx; + " sa: " << startAngle * 180.0/pi << " ea: " << endAngle* 180.0/pi << " idx: " << idx; return builder.str(); } diff --git a/src/Mod/TechDraw/App/DrawUtil.cpp b/src/Mod/TechDraw/App/DrawUtil.cpp index bbe2edda84..5e0dfc7a1c 100644 --- a/src/Mod/TechDraw/App/DrawUtil.cpp +++ b/src/Mod/TechDraw/App/DrawUtil.cpp @@ -203,7 +203,7 @@ double DrawUtil::angleWithX(Base::Vector3d inVec) { double result = atan2(inVec.y, inVec.x); if (result < 0) { - result += 2.0 * M_PI; + result += 2.0 * std::numbers::pi; } return result; @@ -225,7 +225,7 @@ double DrawUtil::angleWithX(TopoDS_Edge e, bool reverse) } double result = atan2(u.y, u.x); if (result < 0) { - result += 2.0 * M_PI; + result += 2.0 * std::numbers::pi; } return result; @@ -251,7 +251,7 @@ double DrawUtil::angleWithX(TopoDS_Edge e, TopoDS_Vertex v, double tolerance) c->D1(param, paramPoint, derivative); double angle = atan2(derivative.Y(), derivative.X()); if (angle < 0) {//map from [-PI:PI] to [0:2PI] - angle += 2.0 * M_PI; + angle += 2.0 * std::numbers::pi; } return angle; } @@ -289,7 +289,7 @@ double DrawUtil::incidenceAngleAtVertex(TopoDS_Edge e, TopoDS_Vertex v, double t //map to [0:2PI] if (incidenceAngle < 0.0) { - incidenceAngle = M_2PI + incidenceAngle; + incidenceAngle = 2*std::numbers::pi + incidenceAngle; } return incidenceAngle; @@ -1059,7 +1059,7 @@ Base::Vector3d DrawUtil::toAppSpace(const DrawViewPart& dvp, const Base::Vector // remove the effect of the Rotation property double rotDeg = dvp.Rotation.getValue(); - double rotRad = rotDeg * M_PI / 180.0; + double rotRad = rotDeg * std::numbers::pi / 180.0; if (rotDeg != 0.0) { // we always rotate around the origin. appPoint.RotateZ(-rotRad); @@ -1396,11 +1396,13 @@ double DrawUtil::sqr(double x) void DrawUtil::angleNormalize(double& fi) { - while (fi <= -M_PI) { - fi += M_2PI; + using std::numbers::pi; + + while (fi <= -pi) { + fi += 2*pi; } - while (fi > M_PI) { - fi -= M_2PI; + while (fi > pi) { + fi -= 2*pi; } } @@ -1414,13 +1416,14 @@ double DrawUtil::angleComposition(double fi, double delta) double DrawUtil::angleDifference(double fi1, double fi2, bool reflex) { + using std::numbers::pi; angleNormalize(fi1); angleNormalize(fi2); fi1 -= fi2; - if ((fi1 > +M_PI || fi1 <= -M_PI) != reflex) { - fi1 += fi1 > 0.0 ? -M_2PI : +M_2PI; + if ((fi1 > +pi || fi1 <= -pi) != reflex) { + fi1 += fi1 > 0.0 ? -2*pi : +2*pi; } return fi1; @@ -1559,6 +1562,7 @@ void DrawUtil::intervalMarkLinear(std::vector>& marking, void DrawUtil::intervalMarkCircular(std::vector>& marking, double start, double length, bool value) { + using std::numbers::pi; if (length == 0.0) { return; } @@ -1566,15 +1570,15 @@ void DrawUtil::intervalMarkCircular(std::vector>& markin length = -length; start -= length; } - if (length > M_2PI) { - length = M_2PI; + if (length > 2*pi) { + length = 2*pi; } angleNormalize(start); double end = start + length; - if (end > M_PI) { - end -= M_2PI; + if (end > pi) { + end -= 2*pi; } // Just make sure the point is stored, its index is read last @@ -1785,13 +1789,15 @@ void DrawUtil::findCircularArcRectangleIntersections(const Base::Vector2d& circl const Base::BoundBox2d& rectangle, std::vector& intersections) { + using std::numbers::pi; + findCircleRectangleIntersections(circleCenter, circleRadius, rectangle, intersections); if (arcRotation < 0.0) { arcRotation = -arcRotation; arcBaseAngle -= arcRotation; - if (arcBaseAngle <= -M_PI) { - arcBaseAngle += M_2PI; + if (arcBaseAngle <= -pi) { + arcBaseAngle += 2*pi; } } @@ -1799,7 +1805,7 @@ void DrawUtil::findCircularArcRectangleIntersections(const Base::Vector2d& circl for (unsigned int i = 0; i < intersections.size();) { double pointAngle = (intersections[i] - circleCenter).Angle(); if (pointAngle < arcBaseAngle - Precision::Confusion()) { - pointAngle += M_2PI; + pointAngle += 2*pi; } if (pointAngle > arcBaseAngle + arcRotation + Precision::Confusion()) { diff --git a/src/Mod/TechDraw/App/DrawUtil.h b/src/Mod/TechDraw/App/DrawUtil.h index 5a4e10d9b8..7490e58622 100644 --- a/src/Mod/TechDraw/App/DrawUtil.h +++ b/src/Mod/TechDraw/App/DrawUtil.h @@ -50,10 +50,6 @@ #include -#ifndef M_2PI -#define M_2PI ((M_PI)*2.0) -#endif - constexpr double DegreesHalfCircle{180.0}; #define VERTEXTOLERANCE (2.0 * Precision::Confusion()) @@ -108,7 +104,8 @@ public: static bool isFirstVert(TopoDS_Edge e, TopoDS_Vertex v, double tolerance = VERTEXTOLERANCE); static bool isLastVert(TopoDS_Edge e, TopoDS_Vertex v, double tolerance = VERTEXTOLERANCE); - static bool fpCompare(const double& d1, const double& d2, double tolerance = FLT_EPSILON); + static bool fpCompare(const double& d1, const double& d2, + double tolerance = std::numeric_limits::epsilon()); static std::pair boxIntersect2d(Base::Vector3d point, Base::Vector3d dir, double xRange, double yRange); static bool apparentIntersection(const Handle(Geom_Curve) curve1, @@ -147,7 +144,7 @@ public: static Base::Vector3d toR3(const gp_Ax2& fromSystem, const Base::Vector3d& fromPoint); static bool checkParallel(const Base::Vector3d v1, const Base::Vector3d v2, - double tolerance = FLT_EPSILON); + double tolerance = std::numeric_limits::epsilon()); //! rotate vector by angle radians around axis through org static Base::Vector3d vecRotate(Base::Vector3d vec, double angle, Base::Vector3d axis, Base::Vector3d org = Base::Vector3d(0.0, 0.0, 0.0)); diff --git a/src/Mod/TechDraw/App/DrawView.cpp b/src/Mod/TechDraw/App/DrawView.cpp index d3b52a6e4b..4b01917bf6 100644 --- a/src/Mod/TechDraw/App/DrawView.cpp +++ b/src/Mod/TechDraw/App/DrawView.cpp @@ -130,7 +130,7 @@ void DrawView::checkScale() TechDraw::DrawPage *page = findParentPage(); if(page) { if (ScaleType.isValue("Page")) { - if(std::abs(page->Scale.getValue() - Scale.getValue()) > FLT_EPSILON) { + if(std::abs(page->Scale.getValue() - Scale.getValue()) > std::numeric_limits::epsilon()) { Scale.setValue(page->Scale.getValue()); Scale.purgeTouched(); } @@ -190,7 +190,7 @@ void DrawView::onChanged(const App::Property* prop) } if (ScaleType.isValue("Page")) { Scale.setStatus(App::Property::ReadOnly, true); - if(std::abs(page->Scale.getValue() - getScale()) > FLT_EPSILON) { + if(std::abs(page->Scale.getValue() - getScale()) > std::numeric_limits::epsilon()) { Scale.setValue(page->Scale.getValue()); } } else if ( ScaleType.isValue("Custom") ) { @@ -200,7 +200,7 @@ void DrawView::onChanged(const App::Property* prop) Scale.setStatus(App::Property::ReadOnly, true); if (!checkFit(page)) { double newScale = autoScale(page->getPageWidth(), page->getPageHeight()); - if(std::abs(newScale - getScale()) > FLT_EPSILON) { + if(std::abs(newScale - getScale()) > std::numeric_limits::epsilon()) { Scale.setValue(newScale); } } diff --git a/src/Mod/TechDraw/App/DrawViewDimension.cpp b/src/Mod/TechDraw/App/DrawViewDimension.cpp index 72a7af43a0..a16883c267 100644 --- a/src/Mod/TechDraw/App/DrawViewDimension.cpp +++ b/src/Mod/TechDraw/App/DrawViewDimension.cpp @@ -105,11 +105,11 @@ const char* DrawViewDimension::TypeEnums[] = {"Distance", const char* DrawViewDimension::MeasureTypeEnums[] = {"True", "Projected", nullptr}; // constraint to set the step size to 0.1 -static const App::PropertyQuantityConstraint::Constraints ToleranceConstraint = {-DBL_MAX, - DBL_MAX, - 0.1}; +static const App::PropertyQuantityConstraint::Constraints ToleranceConstraint = { + -std::numeric_limits::max(), std::numeric_limits::max(), 0.1}; // constraint to force positive values -static const App::PropertyQuantityConstraint::Constraints PositiveConstraint = {0.0, DBL_MAX, 0.1}; +static const App::PropertyQuantityConstraint::Constraints PositiveConstraint = { + 0.0, std::numeric_limits::max(), 0.1}; DrawViewDimension::DrawViewDimension() { diff --git a/src/Mod/TechDraw/App/DrawViewPart.cpp b/src/Mod/TechDraw/App/DrawViewPart.cpp index d0359df991..117001a094 100644 --- a/src/Mod/TechDraw/App/DrawViewPart.cpp +++ b/src/Mod/TechDraw/App/DrawViewPart.cpp @@ -986,7 +986,7 @@ double DrawViewPart::getSizeAlongVector(Base::Vector3d alignmentVector) if (getEdgeCompound().IsNull()) { return 1.0; } - TopoDS_Shape rotatedShape = ShapeUtils::rotateShape(getEdgeCompound(), OXYZ, alignmentAngle * 180.0 / M_PI); + TopoDS_Shape rotatedShape = ShapeUtils::rotateShape(getEdgeCompound(), OXYZ, alignmentAngle * 180.0 / std::numbers::pi); Bnd_Box shapeBox; shapeBox.SetGap(0.0); BRepBndLib::AddOptimal(rotatedShape, shapeBox); @@ -1105,7 +1105,7 @@ gp_Ax2 DrawViewPart::getRotatedCS(const Base::Vector3d basePoint) const // Base::Console().Message("DVP::getRotatedCS() - %s - %s\n", getNameInDocument(), Label.getValue()); gp_Ax2 unrotated = getProjectionCS(basePoint); gp_Ax1 rotationAxis(Base::convertTo(basePoint), unrotated.Direction()); - double angleRad = Rotation.getValue() * M_PI / 180.0; + double angleRad = Rotation.getValue() * std::numbers::pi / 180.0; gp_Ax2 rotated = unrotated.Rotated(rotationAxis, -angleRad); return rotated; } @@ -1302,9 +1302,9 @@ void DrawViewPart::spin(const SpinDirection& spindirection) { double angle; if (spindirection == SpinDirection::CW) - angle = M_PI / 2.0;// Top -> Right -> Bottom -> Left -> Top + angle = std::numbers::pi / 2.0;// Top -> Right -> Bottom -> Left -> Top if (spindirection == SpinDirection::CCW) - angle = -M_PI / 2.0;// Top -> Left -> Bottom -> Right -> Top + angle = -std::numbers::pi / 2.0;// Top -> Left -> Bottom -> Right -> Top spin(angle); } @@ -1338,7 +1338,7 @@ std::pair DrawViewPart::getDirsFromFront(ProjDir gp_Dir gNewDir; gp_Dir gNewXDir; - double angle = M_PI / 2.0;//90* + double angle = std::numbers::pi / 2.0;//90* if (viewType == ProjDirection::Right) { newCS = anchorCS.Rotated(gUpAxis, angle); diff --git a/src/Mod/TechDraw/App/EdgeWalker.cpp b/src/Mod/TechDraw/App/EdgeWalker.cpp index ae3d5b0af7..d30f1d9607 100644 --- a/src/Mod/TechDraw/App/EdgeWalker.cpp +++ b/src/Mod/TechDraw/App/EdgeWalker.cpp @@ -307,11 +307,11 @@ std::vector EdgeWalker::makeWalkerEdges(std::vector edg TopoDS_Vertex edgeVertex1 = TopExp::FirstVertex(e); TopoDS_Vertex edgeVertex2 = TopExp::LastVertex(e); std::size_t vertex1Index = findUniqueVert(edgeVertex1, verts); - if (vertex1Index == SIZE_MAX) { + if (vertex1Index == std::numeric_limits::max()) { continue; } std::size_t vertex2Index = findUniqueVert(edgeVertex2, verts); - if (vertex2Index == SIZE_MAX) { + if (vertex2Index == std::numeric_limits::max()) { continue; } @@ -338,7 +338,7 @@ size_t EdgeWalker::findUniqueVert(TopoDS_Vertex vx, std::vector & } idx++; } - return SIZE_MAX; + return std::numeric_limits::max(); } std::vector EdgeWalker::sortStrip(std::vector fw, bool includeBiggest) @@ -556,7 +556,7 @@ std::string embedItem::dump() std::stringstream builder; builder << "embedItem - vertex: " << iVertex << " incidenceList: "; for (auto& ii : incidenceList) { - builder << " e:" << ii.iEdge << "/a:" << (ii.angle * (180.0/M_PI)) << "/ed:" << ii.eDesc; + builder << " e:" << ii.iEdge << "/a:" << (ii.angle * (180.0/std::numbers::pi)) << "/ed:" << ii.eDesc; } return builder.str(); } diff --git a/src/Mod/TechDraw/App/Geometry.cpp b/src/Mod/TechDraw/App/Geometry.cpp index 8519cf623f..6011cbea15 100644 --- a/src/Mod/TechDraw/App/Geometry.cpp +++ b/src/Mod/TechDraw/App/Geometry.cpp @@ -657,7 +657,7 @@ Ellipse::Ellipse(Base::Vector3d c, double mnr, double mjr) Base::Console().Message("G:Ellipse - failed to make Ellipse\n"); } const Handle(Geom_Ellipse) gEllipse = me.Value(); - BRepBuilderAPI_MakeEdge mkEdge(gEllipse, 0.0, 2 * M_PI); + BRepBuilderAPI_MakeEdge mkEdge(gEllipse, 0.0, 2 * std::numbers::pi); if (mkEdge.IsDone()) { occEdge = mkEdge.Edge(); } @@ -686,10 +686,10 @@ AOE::AOE(const TopoDS_Edge &e) : Ellipse(e) e.GetMessageString()); } - startAngle = fmod(f, 2.0*M_PI); - endAngle = fmod(l, 2.0*M_PI); + startAngle = fmod(f, 2.0*std::numbers::pi); + endAngle = fmod(l, 2.0*std::numbers::pi); cw = (a < 0) ? true: false; - largeArc = (l-f > M_PI) ? true : false; + largeArc = (l-f > std::numbers::pi) ? true : false; startPnt = Base::Vector3d(s.X(), s.Y(), s.Z()); endPnt = Base::Vector3d(ePt.X(), ePt.Y(), ePt.Z()); @@ -709,6 +709,8 @@ Circle::Circle() Circle::Circle(Base::Vector3d c, double r) { + using std::numbers::pi; + geomType = GeomType::CIRCLE; radius = r; center = c; @@ -722,7 +724,7 @@ Circle::Circle(Base::Vector3d c, double r) double angle2 = 360.0; Handle(Geom_Circle) hCircle = new Geom_Circle (circle); - BRepBuilderAPI_MakeEdge aMakeEdge(hCircle, angle1*(M_PI/180), angle2*(M_PI/180)); + BRepBuilderAPI_MakeEdge aMakeEdge(hCircle, angle1*(pi/180), angle2*(pi/180)); TopoDS_Edge edge = aMakeEdge.Edge(); occEdge = edge; } @@ -794,12 +796,12 @@ AOC::AOC(const TopoDS_Edge &e) : Circle(e) // this is the wrong determination of cw/ccw. needs to be determined by edge. double a = v3.DotCross(v1, v2); //error if v1 = v2? - startAngle = fmod(f, 2.0*M_PI); - endAngle = fmod(l, 2.0*M_PI); + startAngle = fmod(f, 2.0*std::numbers::pi); + endAngle = fmod(l, 2.0*std::numbers::pi); cw = (a < 0) ? true: false; - largeArc = (fabs(l-f) > M_PI) ? true : false; + largeArc = (fabs(l-f) > std::numbers::pi) ? true : false; startPnt = Base::convertTo(s); endPnt = Base::convertTo(ePt); @@ -823,7 +825,7 @@ AOC::AOC(Base::Vector3d c, double r, double sAng, double eAng) : Circle() circle.SetRadius(r); Handle(Geom_Circle) hCircle = new Geom_Circle (circle); - BRepBuilderAPI_MakeEdge aMakeEdge(hCircle, sAng*(M_PI/180), eAng*(M_PI/180)); + BRepBuilderAPI_MakeEdge aMakeEdge(hCircle, sAng*(std::numbers::pi/180), eAng*(std::numbers::pi/180)); TopoDS_Edge edge = aMakeEdge.Edge(); occEdge = edge; @@ -844,10 +846,10 @@ AOC::AOC(Base::Vector3d c, double r, double sAng, double eAng) : Circle() // this cw flag is a problem. we should just declare that arcs are always ccw and flip the start and end angles. double a = v3.DotCross(v1, v2); //error if v1 = v2? - startAngle = fmod(f, 2.0*M_PI); - endAngle = fmod(l, 2.0*M_PI); + startAngle = fmod(f, 2.0*std::numbers::pi); + endAngle = fmod(l, 2.0*std::numbers::pi); cw = (a < 0) ? true: false; - largeArc = (fabs(l-f) > M_PI) ? true : false; + largeArc = (fabs(l-f) > std::numbers::pi) ? true : false; startPnt = Base::convertTo(s); endPnt = Base::convertTo(ePt); @@ -866,7 +868,7 @@ AOC::AOC() : Circle() endPnt = Base::Vector3d(0.0, 0.0, 0.0); midPnt = Base::Vector3d(0.0, 0.0, 0.0); startAngle = 0.0; - endAngle = 2.0 * M_PI; + endAngle = 2.0 * std::numbers::pi; cw = false; largeArc = false; @@ -1095,7 +1097,7 @@ double Generic::slope() { Base::Vector3d v = asVector(); if (v.x == 0.0) { - return DOUBLE_MAX; + return std::numeric_limits::max(); } else { return v.y/v.x; } @@ -1146,11 +1148,11 @@ BSpline::BSpline(const TopoDS_Edge &e) startAngle = atan2(startPnt.y, startPnt.x); if (startAngle < 0) { - startAngle += 2.0 * M_PI; + startAngle += 2.0 * std::numbers::pi; } endAngle = atan2(endPnt.y, endPnt.x); if (endAngle < 0) { - endAngle += 2.0 * M_PI; + endAngle += 2.0 * std::numbers::pi; } Standard_Real tol3D = 0.001; //1/1000 of a mm? screen can't resolve this @@ -1473,7 +1475,7 @@ TopoDS_Edge GeometryUtils::edgeFromCircle(TechDraw::CirclePtr c) circle.SetAxis(axis); circle.SetRadius(c->radius); Handle(Geom_Circle) hCircle = new Geom_Circle (circle); - BRepBuilderAPI_MakeEdge aMakeEdge(hCircle, 0.0, 2.0 * M_PI); + BRepBuilderAPI_MakeEdge aMakeEdge(hCircle, 0.0, 2.0 * std::numbers::pi); return aMakeEdge.Edge(); } @@ -1493,87 +1495,124 @@ TopoDS_Edge GeometryUtils::edgeFromCircleArc(TechDraw::AOCPtr c) } //used by DVDim for approximate dims -bool GeometryUtils::isCircle(TopoDS_Edge occEdge) +bool GeometryUtils::isCircle(const TopoDS_Edge& occEdge) { - double radius; + double radius{0}; Base::Vector3d center; bool isArc = false; return GeometryUtils::getCircleParms(occEdge, radius, center, isArc); } //! tries to interpret a B-spline edge as a circle. Used by DVDim for approximate dimensions. -//! calculates the curvature of the spline at a number of places and measures the deviation from the average -//! a true circle has constant curvature and would have no deviation from the average. -bool GeometryUtils::getCircleParms(TopoDS_Edge occEdge, double& radius, Base::Vector3d& center, bool& isArc) +//! calculates the radius and center of circles using groups of 4 points on the b-spline. if the +//! groups of 4 points all lie on a circle, we use that circle to get the radius and center. +bool GeometryUtils::getCircleParms(const TopoDS_Edge& occEdge, double& radius, Base::Vector3d& center, bool& isArc) { - int testCount = 5; - double curveLimit = EWTOLERANCE; - BRepAdaptor_Curve c(occEdge); - Handle(Geom_BSplineCurve) spline = c.BSpline(); - double f, l; - f = c.FirstParameter(); - l = c.LastParameter(); - double parmRange = fabs(l - f); - double parmStep = parmRange/testCount; - std::vector curvatures; - std::vector centers; - gp_Pnt curveCenter; - double sumCurvature = 0; - Base::Vector3d sumCenter, valueAt; - try { - GeomLProp_CLProps prop(spline, f, 3, Precision::Confusion()); + constexpr int PointCount{8}; // number of points on the edge to examine (>= 8) + constexpr int TestCount{3}; // number of candidate circles to test - // check only the interior points of the edge - for (int i = 1; i < (testCount - 1); i++) { - prop.SetParameter(parmStep * i); - curvatures.push_back(prop.Curvature()); - sumCurvature += prop.Curvature(); - prop.CentreOfCurvature(curveCenter); - centers.push_back(curveCenter); - sumCenter += Base::convertTo(curveCenter); - } + BRepAdaptor_Curve curveAdapt(occEdge); + double firstParam = curveAdapt.FirstParameter(); + auto firstPoint = Base::convertTo(curveAdapt.Value(firstParam)); + double lastParam = curveAdapt.LastParameter(); + auto lastPoint = Base::convertTo(curveAdapt.Value(lastParam)); + double parmRange = fabs(lastParam - firstParam); + double parmStep = parmRange / PointCount; + + std::vector pointsOnCurve; + for (size_t iPoint = 0; iPoint < PointCount; iPoint++) { + auto iPointMath = static_cast(iPoint); + auto newpoint = curveAdapt.Value(firstParam + iPointMath * parmStep); + pointsOnCurve.push_back(Base::convertTo(newpoint)); } - catch (Standard_Failure&) { + + auto edgeLong = edgeLength(occEdge); + constexpr double LimitFactor{0.001}; // 0.1% not sure about this value + double tolerance = edgeLong * LimitFactor; + + isArc = true; + if (firstPoint.IsEqual(lastPoint, tolerance)) { + isArc = false; + } + + int passCount{0}; + int firstIndex{0}; + for (int iTest = 0; iTest < TestCount; iTest++) { + firstIndex++; + auto A = pointsOnCurve.at(firstIndex); + auto B = pointsOnCurve.at(firstIndex + 1); + auto C = pointsOnCurve.at(firstIndex + 2); + auto D = pointsOnCurve.at(firstIndex + 3); + if (pointsAreOnCircle(A, B, C, D, tolerance)) { + passCount++; + } + } + + if (passCount != TestCount) { + // at least 1 test failed. return false; } - Base::Vector3d avgCenter = sumCenter/ centers.size(); - double avgCurve = sumCurvature/ centers.size(); - double errorCurve = 0; - // sum the errors in curvature - for (auto& cv: curvatures) { - errorCurve += avgCurve - cv; + // each group of 4 points lies on a circle. Since the groups of 4 overlap, all the points lie + // on the same circle. https://en.wikipedia.org/wiki/Ptolemy%27s_theorem and + // https://math.stackexchange.com/questions/3130053/how-to-check-if-a-set-of-points-in-cartesian-space-could-lie-on-the-circumferenc + // so we can use any three points to make our circle. + + auto gPoint0 = Base::convertTo(pointsOnCurve.at(1)); + auto gPoint1 = Base::convertTo(pointsOnCurve.at(3)); + auto gPoint2 = Base::convertTo(pointsOnCurve.at(5)); //NOLINT readability-magic-numbers + try { + GC_MakeCircle mkCircle(gPoint0, gPoint1, gPoint2); + if (!mkCircle.IsDone()) { + return false; + } + + const Handle(Geom_Circle) circleFromParms = mkCircle.Value(); + radius = circleFromParms->Circ().Radius(); + center = Base::convertTo(circleFromParms->Circ().Location()); + return true; + } + catch (Standard_Failure& err) { + Base::Console().Message("Geo::getCircleParms - failed to make a circle\n"); } - double errorCenter{0}; - for (auto& observe : centers) { - auto error = (Base::convertTo(observe)- avgCenter).Length(); - errorCenter += error; + return false; +} + + +//! returns true if the A, B, C and D all lie on the same circle according to Ptolemy's theorem +//! we can skip the test for same plane, since the points are all on the XY plane(?not true +//! for 3d dims?). +bool GeometryUtils::pointsAreOnCircle(Base::Vector3d A, + Base::Vector3d B, + Base::Vector3d C, + Base::Vector3d D, + double tolerance) +{ + auto AB = (B-A).Length(); + auto AC = (C-A).Length(); + auto AD = (D-A).Length(); + auto BC = (C-B).Length(); + auto BD = (D-B).Length(); + auto CD = (D-C).Length(); + + auto pieceLength = AB + BC + CD; + auto wholeLength = AD; + if (DU::fpCompare(pieceLength, wholeLength, tolerance)) { + // these points are colinear + return false; } - // calculate average error in curvature. we are only interested in the magnitude of the error - errorCurve = fabs(errorCurve / curvatures.size()); - // calculate the average error in center of curvature - errorCenter = errorCenter / curvatures.size(); - auto edgeLong = edgeLength(occEdge); - double centerLimit = edgeLong * 0.01; - - isArc = !c.IsClosed(); - bool isCircle(false); - if ( errorCurve <= curveLimit && - errorCenter <= centerLimit) { - isCircle = true; - radius = 1.0/avgCurve; - center = avgCenter; - } - - return isCircle; + bool eq1 = DU::fpCompare(AB*CD + AC*BD, AD*BC, tolerance); + bool eq2 = DU::fpCompare(AB*CD + AD*BC, AC*BD, tolerance); + bool eq3 = DU::fpCompare(AC*BD + AD*BC, AB*CD, tolerance); + return eq1 || eq2 || eq3; } //! make a circle or arc of circle Edge from BSpline Edge // Note that the input edge has been inverted by GeometryObject, so +Y points down. -TopoDS_Edge GeometryUtils::asCircle(TopoDS_Edge splineEdge, bool& arc) +TopoDS_Edge GeometryUtils::asCircle(const TopoDS_Edge& splineEdge, bool& arc) { double radius{0}; Base::Vector3d center; @@ -1582,11 +1621,16 @@ TopoDS_Edge GeometryUtils::asCircle(TopoDS_Edge splineEdge, bool& arc) if (!canMakeCircle) { throw Base::RuntimeError("GU::asCircle received non-circular edge!"); } + arc = isArc; gp_Pnt gCenter = Base::convertTo(center); gp_Dir gNormal{0, 0, 1}; Handle(Geom_Circle) circleFromParms = GC_MakeCircle(gCenter, gNormal, radius); + if (!isArc) { + return BRepBuilderAPI_MakeEdge(circleFromParms); + } + // find the ends of the edge from the underlying curve BRepAdaptor_Curve curveAdapt(splineEdge); double firstParam = curveAdapt.FirstParameter(); @@ -1594,40 +1638,42 @@ TopoDS_Edge GeometryUtils::asCircle(TopoDS_Edge splineEdge, bool& arc) gp_Pnt startPoint = curveAdapt.Value(firstParam); gp_Pnt endPoint = curveAdapt.Value(lastParam); - if (startPoint.IsEqual(endPoint, EWTOLERANCE)) { //more reliable than IsClosed flag - arc = false; - return BRepBuilderAPI_MakeEdge(circleFromParms); - } - - arc = true; double midRange = (lastParam + firstParam) / 2; gp_Pnt midPoint = curveAdapt.Value(midRange); + // this should be using circleFromParms as a base instead of points on the original spline. + // could be problems with very small errors?? other versions of GC_MakeArcOfCircle have + // poorly explained parameters. GC_MakeArcOfCircle mkArc(startPoint, midPoint, endPoint); auto circleArc = mkArc.Value(); - + if (!mkArc.IsDone()) { + throw Base::RuntimeError("GU::asCircle failed to create arc"); + } return BRepBuilderAPI_MakeEdge(circleArc); } - -bool GeometryUtils::isLine(TopoDS_Edge occEdge) +bool GeometryUtils::isLine(const TopoDS_Edge& occEdge) { - BRepAdaptor_Curve c(occEdge); + BRepAdaptor_Curve adapt(occEdge); - Handle(Geom_BSplineCurve) spline = c.BSpline(); - double f = c.FirstParameter(); - double l = c.LastParameter(); - gp_Pnt s = c.Value(f); - gp_Pnt e = c.Value(l); + Handle(Geom_BSplineCurve) spline = adapt.BSpline(); + double firstParm = adapt.FirstParameter(); + double lastParm = adapt.LastParameter(); + auto startPoint = Base::convertTo(adapt.Value(firstParm)); + auto endPoint = Base::convertTo(adapt.Value(lastParm)); + auto edgeLong = edgeLength(occEdge); - bool samePnt = s.IsEqual(e, FLT_EPSILON); - if (samePnt) { + constexpr double LimitFactor{0.001}; // 0.1% not sure about this value + double tolerance = edgeLong * LimitFactor; + if (startPoint.IsEqual(endPoint, tolerance)) { + // either not a line or a zero length line? return false; } - Base::Vector3d vs = Base::convertTo(s); - Base::Vector3d ve = Base::convertTo(e); - double endLength = (vs - ve).Length(); + // in a line the sum of the lengths of the segments should equal the distance + // from start to end + double endPointLength = (endPoint - startPoint).Length(); + int low = 0; int high = spline->NbPoles() - 1; TColgp_Array1OfPnt poles(low, high); @@ -1641,15 +1687,12 @@ bool GeometryUtils::isLine(TopoDS_Edge occEdge) lenTotal += (v2-v1).Length(); } - if (DrawUtil::fpCompare(lenTotal, endLength)) { - return true; - } - return false; + return DrawUtil::fpCompare(lenTotal, endPointLength, tolerance); } //! make a line Edge from B-spline Edge -TopoDS_Edge GeometryUtils::asLine(TopoDS_Edge occEdge) +TopoDS_Edge GeometryUtils::asLine(const TopoDS_Edge& occEdge) { BRepAdaptor_Curve c(occEdge); diff --git a/src/Mod/TechDraw/App/Geometry.h b/src/Mod/TechDraw/App/Geometry.h index 8373881d12..81a316a1e4 100644 --- a/src/Mod/TechDraw/App/Geometry.h +++ b/src/Mod/TechDraw/App/Geometry.h @@ -315,7 +315,6 @@ class TechDrawExport BSpline: public BaseGeom bool isLine(); bool isCircle(); TopoDS_Edge asCircle(bool& isArc); -// void getCircleParms(bool& isCircle, double& radius, Base::Vector3d& center, bool& isArc); bool intersectsArc(Base::Vector3d p1, Base::Vector3d p2); std::vector segments; }; @@ -382,23 +381,23 @@ class TechDrawExport Vertex : public TechDraw::Tag Base::Vector3d point() const { return Base::Vector3d(pnt.x, pnt.y, 0.0); } void point(Base::Vector3d v){ pnt = Base::Vector3d(v.x, v.y); } - double x() {return pnt.x;} - double y() {return pnt.y;} + double x() const {return pnt.x;} + double y() const {return pnt.y;} // attribute setters and getters - bool getHlrVisible() { return hlrVisible; } + bool getHlrVisible() const { return hlrVisible; } void setHlrVisible(bool state) { hlrVisible = state; } - int getRef3d() { return ref3D; } + int getRef3d() const { return ref3D; } void setRef3d(int ref) { ref3D = ref; } TopoDS_Vertex getOCCVertex() { return occVertex; } - void setOCCVertex(TopoDS_Vertex newVertex) { occVertex = newVertex; } - bool getCosmetic() { return cosmetic; } + void setOCCVertex(const TopoDS_Vertex& newVertex) { occVertex = newVertex; } + bool getCosmetic() const { return cosmetic; } void setCosmetic (bool state) { cosmetic = state; } std::string getCosmeticTag() { return cosmeticTag; } - void setCosmeticTag(std::string t) { cosmeticTag = t; } - bool isCenter() {return m_center;} + void setCosmeticTag(const std::string& t) { cosmeticTag = t; } + bool isCenter() const {return m_center;} void isCenter(bool state) { m_center = state; } - bool isReference() { return m_reference; } + bool isReference() const { return m_reference; } void isReference(bool state) { m_reference = state; } Part::TopoShape asTopoShape(double scale = 1.0); @@ -447,17 +446,22 @@ class TechDrawExport GeometryUtils static TopoDS_Edge edgeFromCircle(TechDraw::CirclePtr c); static TopoDS_Edge edgeFromCircleArc(TechDraw::AOCPtr c); - static bool isCircle(TopoDS_Edge occEdge); - static bool getCircleParms(TopoDS_Edge occEdge, double& radius, Base::Vector3d& center, bool& isArc); - static TopoDS_Edge asCircle(TopoDS_Edge splineEdge, bool& arc); - static bool isLine(TopoDS_Edge occEdge); - static TopoDS_Edge asLine(TopoDS_Edge occEdge); + static bool isCircle(const TopoDS_Edge& occEdge); + static bool getCircleParms(const TopoDS_Edge& occEdge, double& radius, Base::Vector3d& center, bool& isArc); + static TopoDS_Edge asCircle(const TopoDS_Edge& splineEdge, bool& arc); + static bool isLine(const TopoDS_Edge& occEdge); + static TopoDS_Edge asLine(const TopoDS_Edge& occEdge); static double edgeLength(TopoDS_Edge occEdge); static TopoDS_Face makePerforatedFace(FacePtr bigCheese, const std::vector& holesAll); static std::vector findHolesInFace(const DrawViewPart* dvp, const std::string& bigCheeseSubRef); + static bool pointsAreOnCircle(Base::Vector3d A, + Base::Vector3d B, + Base::Vector3d C, + Base::Vector3d D, + double tolerance); }; diff --git a/src/Mod/TechDraw/App/GeometryObject.cpp b/src/Mod/TechDraw/App/GeometryObject.cpp index b56e54943f..021cb13534 100644 --- a/src/Mod/TechDraw/App/GeometryObject.cpp +++ b/src/Mod/TechDraw/App/GeometryObject.cpp @@ -759,24 +759,26 @@ TechDraw::DrawViewDetail* GeometryObject::isParentDetail() bool GeometryObject::isWithinArc(double theta, double first, double last, bool cw) const { - if (fabs(last - first) >= 2 * M_PI) { + using std::numbers::pi; + + if (fabs(last - first) >= 2 * pi) { return true; } // Put params within [0, 2*pi) - not totally sure this is necessary - theta = fmod(theta, 2 * M_PI); + theta = fmod(theta, 2 * pi); if (theta < 0) { - theta += 2 * M_PI; + theta += 2 * pi; } - first = fmod(first, 2 * M_PI); + first = fmod(first, 2 * pi); if (first < 0) { - first += 2 * M_PI; + first += 2 * pi; } - last = fmod(last, 2 * M_PI); + last = fmod(last, 2 * pi); if (last < 0) { - last += 2 * M_PI; + last += 2 * pi; } if (cw) { diff --git a/src/Mod/TechDraw/App/HatchLine.cpp b/src/Mod/TechDraw/App/HatchLine.cpp index 2c2814cac1..e37626a2d1 100644 --- a/src/Mod/TechDraw/App/HatchLine.cpp +++ b/src/Mod/TechDraw/App/HatchLine.cpp @@ -396,7 +396,7 @@ double PATLineSpec::getSlope() } else if (angle < -90.0) { angle = (180 + angle); } - return tan(angle * M_PI/180.0); + return tan(angle * std::numbers::pi/180.0); } bool PATLineSpec::isDashed() @@ -413,7 +413,7 @@ double PATLineSpec::getIntervalX() return getInterval(); } else { double perpAngle = fabs(getAngle() - 90.0); - return fabs(getInterval() / cos(perpAngle * M_PI/180.0)); + return fabs(getInterval() / cos(perpAngle * std::numbers::pi/180.0)); } } @@ -426,7 +426,7 @@ double PATLineSpec::getIntervalY() return 0.0; } else { double perpAngle = fabs(getAngle() - 90.0); - return fabs(getInterval() * tan(perpAngle * M_PI/180.0)); + return fabs(getInterval() * tan(perpAngle * std::numbers::pi/180.0)); } } diff --git a/src/Mod/TechDraw/App/ShapeUtils.cpp b/src/Mod/TechDraw/App/ShapeUtils.cpp index 88db95abc8..6e5c070fe5 100644 --- a/src/Mod/TechDraw/App/ShapeUtils.cpp +++ b/src/Mod/TechDraw/App/ShapeUtils.cpp @@ -96,7 +96,7 @@ gp_Ax2 ShapeUtils::getViewAxis(const Base::Vector3d origin, const Base::Vector3d cross = cross.Cross(stdZ); } - if (cross.IsEqual(stdOrg, FLT_EPSILON)) { + if (cross.IsEqual(stdOrg, std::numeric_limits::epsilon())) { viewAxis = gp_Ax2(inputCenter, gp_Dir(direction.x, direction.y, direction.z)); return viewAxis; } @@ -142,7 +142,7 @@ gp_Ax2 ShapeUtils::legacyViewAxis1(const Base::Vector3d origin, const Base::Vect cross = cross.Cross(stdZ); } - if (cross.IsEqual(stdOrg, FLT_EPSILON)) { + if (cross.IsEqual(stdOrg, std::numeric_limits::epsilon())) { return gp_Ax2(inputCenter, gp_Dir(flipDirection.x, flipDirection.y, flipDirection.z)); } @@ -273,7 +273,7 @@ TopoDS_Shape ShapeUtils::rotateShape(const TopoDS_Shape& input, const gp_Ax2& vi } gp_Ax1 rotAxis = viewAxis.Axis(); - double rotation = rotAngle * M_PI / 180.0; + double rotation = rotAngle * std::numbers::pi / 180.0; try { gp_Trsf tempTransform; diff --git a/src/Mod/TechDraw/App/TechDrawExport.cpp b/src/Mod/TechDraw/App/TechDrawExport.cpp index dd7391eac6..eb97f04a0e 100644 --- a/src/Mod/TechDraw/App/TechDrawExport.cpp +++ b/src/Mod/TechDraw/App/TechDrawExport.cpp @@ -220,7 +220,7 @@ void SVGOutput::printCircle(const BRepAdaptor_Curve& c, std::ostream& out) else { // See also https://developer.mozilla.org/en/SVG/Tutorial/Paths char xar = '0'; // x-axis-rotation - char las = (l-f > M_PI) ? '1' : '0'; // large-arc-flag + char las = (l-f > std::numbers::pi) ? '1' : '0'; // large-arc-flag char swp = (a < 0) ? '1' : '0'; // sweep-flag, i.e. clockwise (0) or counter-clockwise (1) out << " M_PI) ? '1' : '0'; // large-arc-flag + char las = (l-f > std::numbers::pi) ? '1' : '0'; // large-arc-flag char swp = (a < 0) ? '1' : '0'; // sweep-flag, i.e. clockwise (0) or counter-clockwise (1) out << " M_PI) ? '1' : '0'; // large-arc-flag + char las = (l-f > std::numbers::pi) ? '1' : '0'; // large-arc-flag char swp = (a < 0) ? '1' : '0'; // sweep-flag, i.e. clockwise (0) or counter-clockwise (1) out << " 0) { double temp = start_angle; @@ -583,7 +583,7 @@ void DXFOutput::printEllipse(const BRepAdaptor_Curve& c, int /*id*/, std::ostrea gp_Dir xaxis = ellp.XAxis().Direction(); Standard_Real angle = xaxis.Angle(gp_Dir(1, 0,0)); angle = Base::toDegrees(angle); - char las = (l-f > D_PI) ? '1' : '0'; // large-arc-flag + char las = (l-f > std::numbers::pi) ? '1' : '0'; // large-arc-flag char swp = (a < 0) ? '1' : '0'; // sweep-flag, i.e. clockwise (0) or counter-clockwise (1) out << "::max(); + double minY = std::numeric_limits::max(); + double maxX = -std::numeric_limits::max(); + double maxY = -std::numeric_limits::max(); for (auto dim : dims) { TechDraw::pointPair pp = dim->getLinearPoints(); Base::Vector3d pnt1 = Rez::guiX(pp.first()); diff --git a/src/Mod/TechDraw/Gui/CommandExtensionDims.cpp b/src/Mod/TechDraw/Gui/CommandExtensionDims.cpp index 64d16ae320..7da9433ff9 100644 --- a/src/Mod/TechDraw/Gui/CommandExtensionDims.cpp +++ b/src/Mod/TechDraw/Gui/CommandExtensionDims.cpp @@ -2050,7 +2050,7 @@ void execCreateHorizChamferDimension(Gui::Command* cmd) { std::vector allVertexes; allVertexes = _getVertexInfo(objFeat, subNames); if (!allVertexes.empty() && allVertexes.size() > 1) { - const auto Pi180 = 180.0 / M_PI; + const auto Pi180 = 180.0 / std::numbers::pi; TechDraw::DrawViewDimension* dim; dim = _createLinDimension(objFeat, allVertexes[0].name, allVertexes[1].name, "DistanceX"); float yMax = std::max(abs(allVertexes[0].point.y), abs(allVertexes[1].point.y)) + 7.0; @@ -2119,7 +2119,7 @@ void execCreateVertChamferDimension(Gui::Command* cmd) { std::vector allVertexes; allVertexes = _getVertexInfo(objFeat, subNames); if (!allVertexes.empty() && allVertexes.size() > 1) { - const auto Pi180 = 180.0 / M_PI; + const auto Pi180 = 180.0 / std::numbers::pi; TechDraw::DrawViewDimension* dim; dim = _createLinDimension(objFeat, allVertexes[0].name, allVertexes[1].name, "DistanceY"); float xMax = std::max(abs(allVertexes[0].point.x), abs(allVertexes[1].point.x)) + 7.0; diff --git a/src/Mod/TechDraw/Gui/CommandExtensionPack.cpp b/src/Mod/TechDraw/Gui/CommandExtensionPack.cpp index e37c3a7795..582bcfdd22 100644 --- a/src/Mod/TechDraw/Gui/CommandExtensionPack.cpp +++ b/src/Mod/TechDraw/Gui/CommandExtensionPack.cpp @@ -2099,7 +2099,7 @@ double _getAngle(Base::Vector3d center, Base::Vector3d point) { constexpr double DegreesHalfCircle{180.0}; Base::Vector3d vecCP = point - center; - double angle = DU::angleWithX(vecCP) * DegreesHalfCircle / M_PI; + double angle = DU::angleWithX(vecCP) * DegreesHalfCircle / std::numbers::pi; return angle; } diff --git a/src/Mod/TechDraw/Gui/DimensionValidators.cpp b/src/Mod/TechDraw/Gui/DimensionValidators.cpp index c277f6b1e6..9f24fdf162 100644 --- a/src/Mod/TechDraw/Gui/DimensionValidators.cpp +++ b/src/Mod/TechDraw/Gui/DimensionValidators.cpp @@ -210,11 +210,11 @@ DimensionGeometry TechDraw::validateDimSelection3d( return DimensionGeometry::isInvalid; } -bool TechDraw::validateSubnameList(StringVector subNames, GeometrySet acceptableGeometrySet) +bool TechDraw::validateSubnameList(const StringVector& subNames, GeometrySet acceptableGeometrySet) { for (auto& sub : subNames) { std::string geometryType = DrawUtil::getGeomTypeFromName(ShapeFinder::getLastTerm(sub)); - if (acceptableGeometrySet.count(geometryType) == 0) { + if (!acceptableGeometrySet.contains(geometryType)) { //this geometry type is not allowed return false; } @@ -223,13 +223,13 @@ bool TechDraw::validateSubnameList(StringVector subNames, GeometrySet acceptable } //count how many of each "Edge", "Vertex, etc and compare totals to required minimum -bool TechDraw::checkGeometryOccurrences(StringVector subNames, GeomCountMap keyedMinimumCounts) +bool TechDraw::checkGeometryOccurrences(const StringVector& subNames, GeomCountMap keyedMinimumCounts) { //how many of each geometry descriptor are input GeomCountMap foundCounts; for (auto& sub : subNames) { std::string geometryType = DrawUtil::getGeomTypeFromName(ShapeFinder::getLastTerm(sub)); - std::map::iterator it0(foundCounts.find(geometryType)); + auto it0(foundCounts.find(geometryType)); if (it0 == foundCounts.end()) { //first occurrence of this geometryType foundCounts[geometryType] = 1; @@ -294,7 +294,7 @@ DimensionGeometry TechDraw::getGeometryConfiguration(ReferenceVector valid2dRefe //return the first valid configuration contained in the already validated references DimensionGeometry TechDraw::getGeometryConfiguration3d(DrawViewPart* dvp, - ReferenceVector valid3dReferences) + const ReferenceVector& valid3dReferences) { //first we check for whole object references ReferenceVector wholeObjectRefs; @@ -360,7 +360,7 @@ GeomCountMap TechDraw::loadRequiredCounts(const StringVector& acceptableGeometry } //! verify that Selection contains a valid Geometry for a single Edge Dimension -DimensionGeometry TechDraw::isValidSingleEdge(ReferenceEntry ref) +DimensionGeometry TechDraw::isValidSingleEdge(const ReferenceEntry& ref) { auto objFeat(dynamic_cast(ref.getObject())); if (!objFeat) { @@ -386,13 +386,13 @@ DimensionGeometry TechDraw::isValidSingleEdge(ReferenceEntry ref) return DimensionGeometry::isInvalid; } Base::Vector3d line = gen1->points.at(1) - gen1->points.at(0); - if (fabs(line.y) < FLT_EPSILON) { - return DimensionGeometry::isHorizontal; - } else if (fabs(line.x) < FLT_EPSILON) { - return DimensionGeometry::isHorizontal; - } else { - return DimensionGeometry::isDiagonal; + if (fabs(line.y) < std::numeric_limits::epsilon()) { + return DimensionGeometry::isVertical; } + if (fabs(line.x) < std::numeric_limits::epsilon()) { + return DimensionGeometry::isHorizontal; + } + return DimensionGeometry::isDiagonal; } else if (geom->getGeomType() == GeomType::CIRCLE || geom->getGeomType() == GeomType::ARCOFCIRCLE) { return DimensionGeometry::isCircle; } else if (geom->getGeomType() == GeomType::ELLIPSE || geom->getGeomType() == GeomType::ARCOFELLIPSE) { @@ -401,15 +401,14 @@ DimensionGeometry TechDraw::isValidSingleEdge(ReferenceEntry ref) TechDraw::BSplinePtr spline = std::static_pointer_cast(geom); if (spline->isCircle()) { return DimensionGeometry::isBSplineCircle; - } else { - return DimensionGeometry::isBSpline; } + return DimensionGeometry::isBSpline; } return DimensionGeometry::isInvalid; } //! verify that Selection contains a valid Geometry for a single Edge Dimension -DimensionGeometry TechDraw::isValidSingleEdge3d(DrawViewPart* dvp, ReferenceEntry ref) +DimensionGeometry TechDraw::isValidSingleEdge3d(DrawViewPart* dvp, const ReferenceEntry& ref) { (void)dvp; //the Name starts with "Edge" @@ -431,12 +430,13 @@ DimensionGeometry TechDraw::isValidSingleEdge3d(DrawViewPart* dvp, ReferenceEntr Base::Vector3d point1 = Base::convertTo(BRep_Tool::Pnt(TopExp::LastVertex(occEdge))); point1 = dvp->projectPoint(point1); Base::Vector3d line = point1 - point0; - if (fabs(line.y) < FLT_EPSILON) { - return DimensionGeometry::isHorizontal; - } else if (fabs(line.x) < FLT_EPSILON) { + if (fabs(line.y) < std::numeric_limits::epsilon()) { + return DimensionGeometry::isVertical; + } + if (fabs(line.x) < std::numeric_limits::epsilon()) { return DimensionGeometry::isHorizontal; } - // else if (fabs(line.z) < FLT_EPSILON) { + // else if (fabs(line.z) < std::numeric_limits::epsilon()) { // return TechDraw::isZLimited; // } else { @@ -449,16 +449,15 @@ DimensionGeometry TechDraw::isValidSingleEdge3d(DrawViewPart* dvp, ReferenceEntr } else if (adapt.GetType() == GeomAbs_BSplineCurve) { if (GeometryUtils::isCircle(occEdge)) { return DimensionGeometry::isBSplineCircle; - } else { - return DimensionGeometry::isBSpline; } + return DimensionGeometry::isBSpline; } return DimensionGeometry::isInvalid; } //! verify that Selection contains a valid Geometry for a single Edge Dimension -DimensionGeometry TechDraw::isValidSingleFace(ReferenceEntry ref) +DimensionGeometry TechDraw::isValidSingleFace(const ReferenceEntry& ref) { auto objFeat(dynamic_cast(ref.getObject())); if (!objFeat) { @@ -480,7 +479,7 @@ DimensionGeometry TechDraw::isValidSingleFace(ReferenceEntry ref) } //! verify that Selection contains a valid Geometry for a single Edge Dimension -DimensionGeometry TechDraw::isValidSingleFace3d(DrawViewPart* dvp, ReferenceEntry ref) +DimensionGeometry TechDraw::isValidSingleFace3d(DrawViewPart* dvp, const ReferenceEntry& ref) { (void)dvp; //the Name starts with "Edge" @@ -500,7 +499,7 @@ DimensionGeometry TechDraw::isValidSingleFace3d(DrawViewPart* dvp, ReferenceEntr //! verify that the edge references can make a dimension. Currently only extent //! dimensions support more than 2 edges -DimensionGeometry TechDraw::isValidMultiEdge(ReferenceVector refs) +DimensionGeometry TechDraw::isValidMultiEdge(const ReferenceVector& refs) { //there has to be at least 2 if (refs.size() < 2) { @@ -551,7 +550,7 @@ DimensionGeometry TechDraw::isValidMultiEdge(ReferenceVector refs) //! verify that the edge references can make a dimension. Currently only extent //! dimensions support more than 2 edges -DimensionGeometry TechDraw::isValidMultiEdge3d(DrawViewPart* dvp, ReferenceVector refs) +DimensionGeometry TechDraw::isValidMultiEdge3d(DrawViewPart* dvp, const ReferenceVector& refs) { (void)dvp; //there has to be at least 2 @@ -615,9 +614,9 @@ DimensionGeometry TechDraw::isValidMultiEdge3d(DrawViewPart* dvp, ReferenceVecto } //! verify that the vertex references can make a dimension -DimensionGeometry TechDraw::isValidVertexes(ReferenceVector refs) +DimensionGeometry TechDraw::isValidVertexes(const ReferenceVector& refs) { - TechDraw::DrawViewPart* dvp(dynamic_cast(refs.front().getObject())); + auto* dvp(dynamic_cast(refs.front().getObject())); if (!dvp) { //probably redundant throw Base::RuntimeError("Logic error in isValidMultiEdge"); @@ -633,9 +632,9 @@ DimensionGeometry TechDraw::isValidVertexes(ReferenceVector refs) TechDraw::VertexPtr v0 = dvp->getVertex(refs.at(0).getSubName()); TechDraw::VertexPtr v1 = dvp->getVertex(refs.at(1).getSubName()); Base::Vector3d line = v1->point() - v0->point(); - if (fabs(line.y) < FLT_EPSILON) { + if (fabs(line.y) < std::numeric_limits::epsilon()) { return DimensionGeometry::isHorizontal; - } else if (fabs(line.x) < FLT_EPSILON) { + } else if (fabs(line.x) < std::numeric_limits::epsilon()) { return DimensionGeometry::isHorizontal; } else { return DimensionGeometry::isDiagonal; @@ -650,7 +649,7 @@ DimensionGeometry TechDraw::isValidVertexes(ReferenceVector refs) } //! verify that the vertex references can make a dimension -DimensionGeometry TechDraw::isValidVertexes3d(DrawViewPart* dvp, ReferenceVector refs) +DimensionGeometry TechDraw::isValidVertexes3d(DrawViewPart* dvp, const ReferenceVector& refs) { (void)dvp; const std::string matchToken{"Vertex"}; @@ -671,11 +670,12 @@ DimensionGeometry TechDraw::isValidVertexes3d(DrawViewPart* dvp, ReferenceVector Base::Vector3d point1 = Base::convertTo(BRep_Tool::Pnt(TopoDS::Vertex(geometry1))); point1 = dvp->projectPoint(point1); Base::Vector3d line = point1 - point0; - if (fabs(line.y) < FLT_EPSILON) { + if (fabs(line.y) < std::numeric_limits::epsilon()) { + return DimensionGeometry::isVertical; + } + if (fabs(line.x) < std::numeric_limits::epsilon()) { return DimensionGeometry::isHorizontal; - } else if (fabs(line.x) < FLT_EPSILON) { - return DimensionGeometry::isHorizontal; - // } else if(fabs(line.z) < FLT_EPSILON) { + // } else if(fabs(line.z) < std::numeric_limits::epsilon()) { // return isZLimited; } else { return DimensionGeometry::isDiagonal; @@ -691,7 +691,7 @@ DimensionGeometry TechDraw::isValidVertexes3d(DrawViewPart* dvp, ReferenceVector } //! verify that the mixed bag (ex Vertex-Edge) of references can make a dimension -DimensionGeometry TechDraw::isValidHybrid(ReferenceVector refs) +DimensionGeometry TechDraw::isValidHybrid(const ReferenceVector& refs) { if (refs.empty()) { return DimensionGeometry::isInvalid; @@ -716,7 +716,7 @@ DimensionGeometry TechDraw::isValidHybrid(ReferenceVector refs) } //! verify that the mixed bag (ex Vertex-Edge) of references can make a dimension -DimensionGeometry TechDraw::isValidHybrid3d(DrawViewPart* dvp, ReferenceVector refs) +DimensionGeometry TechDraw::isValidHybrid3d(DrawViewPart* dvp, const ReferenceVector& refs) { (void)dvp; //we can reuse the 2d check here. @@ -745,9 +745,11 @@ long int TechDraw::mapGeometryTypeToDimType(long int dimType, DimensionGeometry case DimensionGeometry::isAngle3Pt: return DrawViewDimension::Angle3Pt; default: - break; // For all other cases, return dimType + return dimType; } - } else if (geometry2d != DimensionGeometry::isViewReference) { + } + + if (geometry2d != DimensionGeometry::isViewReference) { switch (geometry2d) { case DimensionGeometry::isDiagonal: return DrawViewDimension::Distance; @@ -763,6 +765,7 @@ long int TechDraw::mapGeometryTypeToDimType(long int dimType, DimensionGeometry break; // For all other cases, return dimType } } + return dimType; } diff --git a/src/Mod/TechDraw/Gui/DimensionValidators.h b/src/Mod/TechDraw/Gui/DimensionValidators.h index f70ec0663b..29bfa64107 100644 --- a/src/Mod/TechDraw/Gui/DimensionValidators.h +++ b/src/Mod/TechDraw/Gui/DimensionValidators.h @@ -74,27 +74,27 @@ DimensionGeometry validateDimSelection3d(DrawViewPart* dvp, const std::vector& minimumCounts, //how many of each geometry are needed for a good dimension const std::vector& acceptableDimensionGeometrys);//isVertical, isHorizontal, ... -bool validateSubnameList(StringVector subNames, GeometrySet acceptableGeometrySet); +bool validateSubnameList(const StringVector& subNames, GeometrySet acceptableGeometrySet); DimensionGeometry getGeometryConfiguration(ReferenceVector valid2dReferences); DimensionGeometry getGeometryConfiguration3d(DrawViewPart* dvp, - ReferenceVector valid3dReferences); + const ReferenceVector& valid3dReferences); GeomCountMap loadRequiredCounts(const StringVector& acceptableGeometry, const std::vector& minimumCouts); -bool checkGeometryOccurrences(StringVector subNames, GeomCountMap keyedMinimumCounts); +bool checkGeometryOccurrences(const StringVector& subNames, GeomCountMap keyedMinimumCounts); -DimensionGeometry isValidVertexes(ReferenceVector refs); -DimensionGeometry isValidMultiEdge(ReferenceVector refs); -DimensionGeometry isValidSingleEdge(ReferenceEntry ref); -DimensionGeometry isValidSingleFace(ReferenceEntry ref); -DimensionGeometry isValidHybrid(ReferenceVector refs); +DimensionGeometry isValidVertexes(const ReferenceVector& refs); +DimensionGeometry isValidMultiEdge(const ReferenceVector& refs); +DimensionGeometry isValidSingleEdge(const ReferenceEntry& ref); +DimensionGeometry isValidSingleFace(const ReferenceEntry& ref); +DimensionGeometry isValidHybrid(const ReferenceVector& refs); -DimensionGeometry isValidVertexes3d(DrawViewPart* dvp, ReferenceVector refs); -DimensionGeometry isValidMultiEdge3d(DrawViewPart* dvp, ReferenceVector refs); -DimensionGeometry isValidSingleEdge3d(DrawViewPart* dvp, ReferenceEntry ref); -DimensionGeometry isValidSingleFace3d(DrawViewPart* dvp, ReferenceEntry ref); -DimensionGeometry isValidHybrid3d(DrawViewPart* dvp, ReferenceVector refs); +DimensionGeometry isValidVertexes3d(DrawViewPart* dvp, const ReferenceVector& refs); +DimensionGeometry isValidMultiEdge3d(DrawViewPart* dvp, const ReferenceVector& refs); +DimensionGeometry isValidSingleEdge3d(DrawViewPart* dvp, const ReferenceEntry& ref); +DimensionGeometry isValidSingleFace3d(DrawViewPart* dvp, const ReferenceEntry& ref); +DimensionGeometry isValidHybrid3d(DrawViewPart* dvp, const ReferenceVector& refs); long int mapGeometryTypeToDimType(long int dimType, DimensionGeometry geometry2d, DimensionGeometry geometry3d); diff --git a/src/Mod/TechDraw/Gui/DlgStringListEditor.cpp b/src/Mod/TechDraw/Gui/DlgStringListEditor.cpp deleted file mode 100644 index 5a0efc28d2..0000000000 --- a/src/Mod/TechDraw/Gui/DlgStringListEditor.cpp +++ /dev/null @@ -1,144 +0,0 @@ -/**************************************************************************** - * Copyright (c) 2022 Wanderer Fan * - * * - * 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 * - * * - ****************************************************************************/ - -#include "PreCompiled.h" -#ifndef _PreComp_ -# include -#endif - -#include "DlgStringListEditor.h" -#include "ui_DlgStringListEditor.h" - - -using namespace TechDrawGui; - -/* TRANSLATOR Gui::DlgStringListEditor */ - -DlgStringListEditor::DlgStringListEditor(const std::vector texts, QWidget* parent, - Qt::WindowFlags fl) - : QDialog(parent, fl), - ui(new Ui_DlgStringListEditor) -{ - ui->setupUi(this); - ui->lwTexts->setSortingEnabled(false); - - fillList(texts); - - connect(ui->lwTexts, - &QListWidget::itemActivated, - this, - &DlgStringListEditor::slotItemActivated); - connect(ui->pbAdd, &QPushButton::clicked, this, &DlgStringListEditor::slotAddItem); - connect(ui->pbRemove, &QPushButton::clicked, this, &DlgStringListEditor::slotRemoveItem); - connect(ui->bbButtons, &QDialogButtonBox::accepted, this, &DlgStringListEditor::accept); - connect(ui->bbButtons, &QDialogButtonBox::rejected, this, &DlgStringListEditor::reject); -} - -/** - * Destroys the object and frees any allocated resources - */ -DlgStringListEditor::~DlgStringListEditor() -{ - // no need to delete child widgets, Qt does it all for us - delete ui; -} - -void DlgStringListEditor::fillList(std::vector texts) -{ - QString qText; - int textCount = texts.size(); - int i = 0; - for (; i < textCount; i++) { - qText = QString::fromStdString(texts[i]); - QListWidgetItem* item = new QListWidgetItem(qText); - item->setFlags(item->flags() | Qt::ItemIsEditable); - ui->lwTexts->addItem(item); - } - //add a blank line at the end to allow extending the list - QListWidgetItem* item = new QListWidgetItem(QStringLiteral("")); - item->setFlags(item->flags() | Qt::ItemIsEditable); - ui->lwTexts->addItem(item); -} - -void DlgStringListEditor::slotItemActivated(QListWidgetItem* item) -{ - ui->lwTexts->editItem(item); -} - -void DlgStringListEditor::slotAddItem() -{ - QString newText = ui->leNewItem->text(); - QListWidgetItem* item = new QListWidgetItem(newText); - item->setFlags(item->flags() | Qt::ItemIsEditable); - int row = ui->lwTexts->currentRow(); - if (row < 0) { - //no location set yet, add to end of list - ui->lwTexts->addItem(item); - } - else { - //insert item at current row and push the rest down 1 position - ui->lwTexts->insertItem(row, item); - } - ui->leNewItem->clear(); - //TODO: how to append to end of list? -} - -void DlgStringListEditor::slotRemoveItem() -{ - if (ui->lwTexts->count() < 1) { - return; - } - int row = ui->lwTexts->currentRow(); - if (row >= 0) { - auto item = ui->lwTexts->takeItem(row); - delete item; - } -} - -std::vector DlgStringListEditor::getTexts() const -{ - std::vector outTexts; - if (ui->lwTexts->count() < 1) { - return outTexts; - } - - for (int iRow = 0; iRow < ui->lwTexts->count(); iRow++) { - QString itemText = ui->lwTexts->item(iRow)->text(); - outTexts.push_back(itemText.toStdString()); - } - if (outTexts.back().empty()) { - outTexts.pop_back(); - } - return outTexts; -} - -void DlgStringListEditor::accept() -{ - QDialog::accept(); -} - -void DlgStringListEditor::reject() -{ - QDialog::reject(); -} - -#include "moc_DlgStringListEditor.cpp" diff --git a/src/Mod/TechDraw/Gui/DlgStringListEditor.h b/src/Mod/TechDraw/Gui/DlgStringListEditor.h deleted file mode 100644 index b8a28c2e06..0000000000 --- a/src/Mod/TechDraw/Gui/DlgStringListEditor.h +++ /dev/null @@ -1,64 +0,0 @@ -/**************************************************************************** - * Copyright (c) 2022 Wanderer Fan * - * * - * 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 GUI_DLGEDITABLETEXT_H -#define GUI_DLGEDITABLETEXT_H - -#include - -#include - - -class QListWidgetItem; - -namespace TechDrawGui { - -class Ui_DlgStringListEditor; -class TechDrawGuiExport DlgStringListEditor : public QDialog -{ - Q_OBJECT - -public: - DlgStringListEditor(const std::vector texts, - QWidget* parent = nullptr, Qt::WindowFlags fl = Qt::WindowFlags()); - ~DlgStringListEditor() override; - - std::vector getTexts() const; - void accept() override; - void reject() override; - -public Q_SLOTS: - void slotItemActivated(QListWidgetItem* item); - void slotAddItem(); - void slotRemoveItem(); - -private: - void fillList(std::vector texts); - - Ui_DlgStringListEditor* ui; -}; - -} // namespace Gui - - -#endif // GUI_DLGEDITABLETEXT_H - diff --git a/src/Mod/TechDraw/Gui/DlgStringListEditor.ui b/src/Mod/TechDraw/Gui/DlgStringListEditor.ui deleted file mode 100644 index 43076bbc58..0000000000 --- a/src/Mod/TechDraw/Gui/DlgStringListEditor.ui +++ /dev/null @@ -1,140 +0,0 @@ - - - TechDrawGui::DlgStringListEditor - - - Qt::WindowModal - - - - 0 - 0 - 360 - 331 - - - - String List Editor - - - - - - true - - - - - - - - <html><head/><body><p>Double click to edit a line. New lines are added at the current location in the list.</p></body></html> - - - QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed|QAbstractItemView::SelectedClicked - - - true - - - - - - - - - - :/icons/list-add.svg - - - - - - - - - - - - - - - - - - - :/icons/list-remove.svg:/icons/list-remove.svg - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - false - - - - - - - - - - - - bbButtons - accepted() - TechDrawGui::DlgStringListEditor - accept() - - - 179 - 228 - - - 179 - 139 - - - - - bbButtons - rejected() - TechDrawGui::DlgStringListEditor - reject() - - - 179 - 228 - - - 179 - 139 - - - - - diff --git a/src/Mod/TechDraw/Gui/DrawGuiUtil.cpp b/src/Mod/TechDraw/Gui/DrawGuiUtil.cpp index 3954090e30..a6e9dce171 100644 --- a/src/Mod/TechDraw/Gui/DrawGuiUtil.cpp +++ b/src/Mod/TechDraw/Gui/DrawGuiUtil.cpp @@ -832,7 +832,7 @@ void DrawGuiUtil::rotateToAlign(DrawViewPart* view, const Base::Vector2d& oldDir double toRotate = newDirection.GetAngle(oldDirection); // Radians to degrees - toRotate = toRotate * 180 / M_PI; + toRotate = toRotate * 180 / std::numbers::pi; // Rotate least amount possible if(toRotate > 90) { diff --git a/src/Mod/TechDraw/Gui/PathBuilder.cpp b/src/Mod/TechDraw/Gui/PathBuilder.cpp index 2d5240baab..11e26758b3 100644 --- a/src/Mod/TechDraw/Gui/PathBuilder.cpp +++ b/src/Mod/TechDraw/Gui/PathBuilder.cpp @@ -327,11 +327,11 @@ void PathBuilder::pathArc(QPainterPath& path, double rx, double ry, double x_axi th_arc = th1 - th0; if (th_arc < 0 && sweep_flag) - th_arc += 2 * M_PI; + th_arc += 2 * std::numbers::pi; else if (th_arc > 0 && !sweep_flag) - th_arc -= 2 * M_PI; + th_arc -= 2 * std::numbers::pi; - n_segs = qCeil(qAbs(th_arc / (M_PI * 0.5 + 0.001))); + n_segs = qCeil(qAbs(th_arc / (std::numbers::pi * 0.5 + 0.001))); path.moveTo(curx, cury); diff --git a/src/Mod/TechDraw/Gui/QGIDimLines.cpp b/src/Mod/TechDraw/Gui/QGIDimLines.cpp index d3d8f09e6f..ef4f47a0ed 100644 --- a/src/Mod/TechDraw/Gui/QGIDimLines.cpp +++ b/src/Mod/TechDraw/Gui/QGIDimLines.cpp @@ -41,8 +41,6 @@ QGIDimLines::QGIDimLines() setAcceptHoverEvents(false); setFlag(QGraphicsItem::ItemIsSelectable, false); setFlag(QGraphicsItem::ItemIsMovable, false); - - setFill(QColor(100, 100, 0)); setWidth(0.5); } diff --git a/src/Mod/TechDraw/Gui/QGIHighlight.cpp b/src/Mod/TechDraw/Gui/QGIHighlight.cpp index 165a9156e6..85d58d9ffc 100644 --- a/src/Mod/TechDraw/Gui/QGIHighlight.cpp +++ b/src/Mod/TechDraw/Gui/QGIHighlight.cpp @@ -125,7 +125,7 @@ void QGIHighlight::makeReference() QRectF r(m_start, m_end); double radius = r.width() / 2.0; QPointF center = r.center(); - double angleRad = m_referenceAngle * M_PI / 180.0; + double angleRad = m_referenceAngle * std::numbers::pi / 180.0; double posX = center.x() + cos(angleRad) * radius + horizOffset; double posY = center.y() - sin(angleRad) * radius - vertOffset; m_reference->setPos(posX, posY); diff --git a/src/Mod/TechDraw/Gui/QGILeaderLine.cpp b/src/Mod/TechDraw/Gui/QGILeaderLine.cpp index 40c1e15ecd..b8e23aac6c 100644 --- a/src/Mod/TechDraw/Gui/QGILeaderLine.cpp +++ b/src/Mod/TechDraw/Gui/QGILeaderLine.cpp @@ -589,7 +589,7 @@ Base::Vector3d QGILeaderLine::getAttachPoint() double yPos = Rez::guiX(featLeader->Y.getValue()); Base::Vector3d vAttachPoint{xPos, yPos}; vAttachPoint = vAttachPoint * baseScale; - double rotationRad = parent->Rotation.getValue() * M_PI / DegreesHalfCircle; + double rotationRad = parent->Rotation.getValue() * std::numbers::pi / DegreesHalfCircle; if (rotationRad != 0.0) { vAttachPoint.RotateZ(rotationRad); } diff --git a/src/Mod/TechDraw/Gui/QGISectionLine.cpp b/src/Mod/TechDraw/Gui/QGISectionLine.cpp index 37415d4df5..c5546e85ec 100644 --- a/src/Mod/TechDraw/Gui/QGISectionLine.cpp +++ b/src/Mod/TechDraw/Gui/QGISectionLine.cpp @@ -402,9 +402,9 @@ double QGISectionLine::getArrowRotation(Base::Vector3d arrowDir) arrowDir.Normalize(); double angle = atan2f(arrowDir.y, arrowDir.x); if (angle < 0.0) { - angle = 2 * M_PI + angle; + angle = 2 * std::numbers::pi + angle; } - double arrowRotation = 360.0 - angle * (180.0/M_PI); //convert to Qt rotation (clockwise degrees) + double arrowRotation = 360.0 - angle * (180.0/std::numbers::pi); //convert to Qt rotation (clockwise degrees) return arrowRotation; } diff --git a/src/Mod/TechDraw/Gui/QGIViewAnnotation.cpp b/src/Mod/TechDraw/Gui/QGIViewAnnotation.cpp index 363e4aa1c5..e1df9385e9 100644 --- a/src/Mod/TechDraw/Gui/QGIViewAnnotation.cpp +++ b/src/Mod/TechDraw/Gui/QGIViewAnnotation.cpp @@ -37,6 +37,7 @@ #include #endif +#include "ViewProviderAnnotation.h" #include #include #include @@ -45,7 +46,6 @@ #include #include -#include "DlgStringListEditor.h" #include "QGCustomText.h" #include "QGIViewAnnotation.h" #include "Rez.h" @@ -181,20 +181,18 @@ void QGIViewAnnotation::rotateView() void QGIViewAnnotation::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event) { - Q_UNUSED(event); - + // forwards the double click on the page to the view provider, as of the item in the tree was + // double clicked, just like the QGILeaderLine TechDraw::DrawViewAnnotation* annotation = dynamic_cast(getViewObject()); if (!annotation) { return; } - - const std::vector& values = annotation->Text.getValues(); - DlgStringListEditor dlg(values, Gui::getMainWindow()); - dlg.setWindowTitle(QStringLiteral("Annotation Text Editor")); - if (dlg.exec() == QDialog::Accepted) { - App::GetApplication().setActiveTransaction("Set Annotation Text"); - annotation->Text.setValues(dlg.getTexts()); - App::GetApplication().closeActiveTransaction(); + auto ViewProvider = dynamic_cast(getViewProvider(annotation)); + if (!ViewProvider) { + qWarning() << "QGIViewAnnotation::mouseDoubleClickEvent: No valid view provider"; + return; } + ViewProvider->startDefaultEditMode(); + QGraphicsItem::mouseDoubleClickEvent(event); } diff --git a/src/Mod/TechDraw/Gui/QGIViewBalloon.cpp b/src/Mod/TechDraw/Gui/QGIViewBalloon.cpp index c63e7c5cdc..b1d4258814 100644 --- a/src/Mod/TechDraw/Gui/QGIViewBalloon.cpp +++ b/src/Mod/TechDraw/Gui/QGIViewBalloon.cpp @@ -606,6 +606,8 @@ void QGIViewBalloon::draw() void QGIViewBalloon::drawBalloon(bool originDrag) { + using std::numbers::pi; + if ((!originDrag) && m_dragInProgress) { // TODO there are 2 drag status variables. m_draggingInProgress appears to be the one to use? // dragged shows false while drag is still in progress. @@ -696,14 +698,14 @@ void QGIViewBalloon::drawBalloon(bool originDrag) double radius = sqrt(pow((textHeight / 2.0), 2) + pow((textWidth / 2.0), 2)); radius = radius * scale; radius += Rez::guiX(3.0); - offsetLR = (tan(30 * M_PI / 180) * radius); + offsetLR = (tan(30 * pi / 180) * radius); QPolygonF triangle; - double startAngle = -M_PI / 2; + double startAngle = -pi / 2; double angle = startAngle; for (int i = 0; i < 4; i++) { triangle += QPointF(lblCenter.x + (radius * cos(angle)), lblCenter.y + (radius * sin(angle))); - angle += (2 * M_PI / 3); + angle += (2 * pi / 3); } balloonPath.moveTo(lblCenter.x + (radius * cos(startAngle)), lblCenter.y + (radius * sin(startAngle))); @@ -737,12 +739,12 @@ void QGIViewBalloon::drawBalloon(bool originDrag) radius += Rez::guiX(1.0); offsetLR = radius; QPolygonF triangle; - double startAngle = -2 * M_PI / 3; + double startAngle = -2 * pi / 3; double angle = startAngle; for (int i = 0; i < 7; i++) { triangle += QPointF(lblCenter.x + (radius * cos(angle)), lblCenter.y + (radius * sin(angle))); - angle += (2 * M_PI / 6); + angle += (2 * pi / 6); } balloonPath.moveTo(lblCenter.x + (radius * cos(startAngle)), lblCenter.y + (radius * sin(startAngle))); @@ -805,7 +807,7 @@ void QGIViewBalloon::drawBalloon(bool originDrag) dirballoonLinesLine = (arrowTipPosInParent - dLineStart).Normalize(); } - float arAngle = atan2(dirballoonLinesLine.y, dirballoonLinesLine.x) * 180 / M_PI; + float arAngle = atan2(dirballoonLinesLine.y, dirballoonLinesLine.x) * 180 / pi; if ((endType == ArrowType::FILLED_TRIANGLE) && (prefOrthoPyramid())) { if (arAngle < 0.0) { @@ -824,7 +826,7 @@ void QGIViewBalloon::drawBalloon(bool originDrag) else { arAngle = 0; } - double radAngle = arAngle * M_PI / 180.0; + double radAngle = arAngle * pi / 180.0; double sinAngle = sin(radAngle); double cosAngle = cos(radAngle); xAdj = Rez::guiX(arrowAdj * cosAngle); diff --git a/src/Mod/TechDraw/Gui/QGIViewDimension.cpp b/src/Mod/TechDraw/Gui/QGIViewDimension.cpp index 60040fbbba..27fdc744ed 100644 --- a/src/Mod/TechDraw/Gui/QGIViewDimension.cpp +++ b/src/Mod/TechDraw/Gui/QGIViewDimension.cpp @@ -22,10 +22,6 @@ #include "PreCompiled.h" -#ifdef FC_OS_WIN32 -# define _USE_MATH_DEFINES //resolves Windows & M_PI issues -#endif - #ifndef _PreComp_ # include @@ -440,25 +436,27 @@ void QGIViewDimension::draw() double QGIViewDimension::getAnglePlacementFactor(double testAngle, double endAngle, double startRotation) { + using std::numbers::pi; + if (startRotation > 0.0) { startRotation = -startRotation; endAngle -= startRotation; - if (endAngle > M_PI) { - endAngle -= M_2PI; + if (endAngle > pi) { + endAngle -= 2 * pi; } } if (testAngle > endAngle) { - testAngle -= M_2PI; + testAngle -= 2 * pi; } if (testAngle >= endAngle + startRotation) { return +1.0; } - testAngle += M_PI; + testAngle += pi; if (testAngle > endAngle) { - testAngle -= M_2PI; + testAngle -= 2 * pi; } if (testAngle >= endAngle + startRotation) { @@ -472,7 +470,7 @@ int QGIViewDimension::compareAngleStraightness(double straightAngle, double left double rightAngle, double leftStrikeFactor, double rightStrikeFactor) { - double leftDelta = DrawUtil::angleComposition(M_PI, straightAngle - leftAngle); + double leftDelta = DrawUtil::angleComposition(std::numbers::pi, straightAngle - leftAngle); double rightDelta = DrawUtil::angleComposition(rightAngle, -straightAngle); if (fabs(leftDelta - rightDelta) <= Precision::Confusion()) { @@ -492,7 +490,7 @@ int QGIViewDimension::compareAngleStraightness(double straightAngle, double left double QGIViewDimension::getIsoStandardLinePlacement(double labelAngle) { // According to ISO 129-1 Standard Figure 23, the bordering angle is 1/2 PI, resp. -1/2 PI - return labelAngle < -M_PI / 2.0 || labelAngle > +M_PI / 2.0 ? +1.0 : -1.0; + return labelAngle < -std::numbers::pi / 2.0 || labelAngle > +std::numbers::pi / 2.0 ? +1.0 : -1.0; } Base::Vector2d QGIViewDimension::getIsoRefOutsetPoint(const Base::BoundBox2d& labelRectangle, @@ -599,7 +597,7 @@ double QGIViewDimension::computeLineAndLabelAngles(const Base::Vector2d& rotatio double devAngle = getIsoStandardLinePlacement(rawAngle) * asin(lineLabelDistance / rawDistance); lineAngle = DrawUtil::angleComposition(lineAngle, devAngle); - labelAngle = devAngle < 0.0 ? lineAngle : DrawUtil::angleComposition(lineAngle, M_PI); + labelAngle = devAngle < 0.0 ? lineAngle : DrawUtil::angleComposition(lineAngle, std::numbers::pi); return devAngle; } @@ -669,7 +667,7 @@ QGIViewDimension::computeArcStrikeFactor(const Base::BoundBox2d& labelRectangle, double arcAngle = drawMarking[startIndex].first; double arcRotation = drawMarking[currentIndex].first - arcAngle; if (arcRotation < 0.0) { - arcRotation += M_2PI; + arcRotation += 2 * std::numbers::pi; } DrawUtil::findCircularArcRectangleIntersections(arcCenter, arcRadius, arcAngle, @@ -689,7 +687,7 @@ double QGIViewDimension::normalizeStartPosition(double& startPosition, double& l { if (startPosition > 0.0) { startPosition = -startPosition; - lineAngle += M_PI; + lineAngle += std::numbers::pi; return -1.0; } @@ -872,7 +870,7 @@ bool QGIViewDimension::constructDimensionArc( // Add the arrow tails - these are drawn always double tailDelta = - arcRadius >= Precision::Confusion() ? getDefaultArrowTailLength() / arcRadius : M_PI_4; + arcRadius >= Precision::Confusion() ? getDefaultArrowTailLength() / arcRadius : std::numbers::pi / 4.0; double placementFactor = flipArrows ? +1.0 : -1.0; DrawUtil::intervalMarkCircular(outputMarking, endAngle, @@ -993,7 +991,7 @@ void QGIViewDimension::drawSingleArc(QPainterPath& painterPath, const Base::Vect return; } if (endAngle < startAngle) { - endAngle += M_2PI; + endAngle += 2 * std::numbers::pi; } QRectF qtArcRectangle( @@ -1019,7 +1017,7 @@ void QGIViewDimension::drawMultiArc(QPainterPath& painterPath, const Base::Vecto } if (entryIndex >= drawMarking.size()) { - drawSingleArc(painterPath, arcCenter, arcRadius, 0, M_2PI); + drawSingleArc(painterPath, arcCenter, arcRadius, 0, 2 * std::numbers::pi); return; } @@ -1067,7 +1065,7 @@ void QGIViewDimension::drawDimensionLine(QPainterPath& painterPath, double arrowAngles[2]; arrowAngles[0] = lineAngle; - arrowAngles[1] = lineAngle + M_PI; + arrowAngles[1] = lineAngle + std::numbers::pi; drawArrows(arrowCount, arrowPositions, arrowAngles, flipArrows, forcePointStyle); } @@ -1077,13 +1075,15 @@ void QGIViewDimension::drawDimensionArc(QPainterPath& painterPath, const Base::V double jointAngle, const Base::BoundBox2d& labelRectangle, int arrowCount, int standardStyle, bool flipArrows) const { + using std::numbers::pi; + // Keep the convention start rotation <= 0 double handednessFactor = normalizeStartRotation(startRotation); // Split the rest of 2PI minus the angle and assign joint offset so > 0 is closer to end arc side double jointRotation = handednessFactor * (jointAngle - endAngle); - if (fabs(jointRotation - startRotation * 0.5) > M_PI) { - jointRotation += jointRotation < 0.0 ? +M_2PI : -M_2PI; + if (fabs(jointRotation - startRotation * 0.5) > pi) { + jointRotation += jointRotation < 0.0 ? +2*pi : -2*pi; } std::vector> drawMarks; @@ -1099,8 +1099,8 @@ void QGIViewDimension::drawDimensionArc(QPainterPath& painterPath, const Base::V + Base::Vector2d::FromPolar(arcRadius, endAngle + handednessFactor * startRotation); double arrowAngles[2]; - arrowAngles[0] = endAngle + handednessFactor * M_PI_2; - arrowAngles[1] = endAngle + handednessFactor * (startRotation - M_PI_2); + arrowAngles[0] = endAngle + handednessFactor * std::numbers::pi/2; + arrowAngles[1] = endAngle + handednessFactor * (startRotation - std::numbers::pi/2); drawArrows(arrowCount, arrowPositions, arrowAngles, flipArrows); } @@ -1119,6 +1119,8 @@ void QGIViewDimension::drawDistanceExecutive(const Base::Vector2d& startPoint, int standardStyle, int renderExtent, bool flipArrows) const { + using std::numbers::pi; + QPainterPath distancePath; Base::Vector2d labelCenter(labelRectangle.GetCenter()); @@ -1170,9 +1172,9 @@ void QGIViewDimension::drawDistanceExecutive(const Base::Vector2d& startPoint, // Orient the leader line angle correctly towards the target point double angles[2]; angles[0] = - jointPositions[0] > 0.0 ? DrawUtil::angleComposition(lineAngle, M_PI) : lineAngle; + jointPositions[0] > 0.0 ? DrawUtil::angleComposition(lineAngle, pi) : lineAngle; angles[1] = - jointPositions[1] > 0.0 ? DrawUtil::angleComposition(lineAngle, M_PI) : lineAngle; + jointPositions[1] > 0.0 ? DrawUtil::angleComposition(lineAngle, pi) : lineAngle; // Select the placement, where the label is not obscured by the leader line // or (if both behave the same) the one that bends the reference line less @@ -1226,7 +1228,7 @@ void QGIViewDimension::drawDistanceExecutive(const Base::Vector2d& startPoint, // We may rotate the label so no leader and reference lines are needed double placementFactor = getIsoStandardLinePlacement(lineAngle); labelAngle = - placementFactor > 0.0 ? DrawUtil::angleComposition(lineAngle, M_PI) : lineAngle; + placementFactor > 0.0 ? DrawUtil::angleComposition(lineAngle, pi) : lineAngle; // Find out the projection of label center on the line with given angle Base::Vector2d labelProjection( @@ -1234,7 +1236,7 @@ void QGIViewDimension::drawDistanceExecutive(const Base::Vector2d& startPoint, + Base::Vector2d::FromPolar( placementFactor * (labelRectangle.Height() * 0.5 + getIsoDimensionLineSpacing()), - lineAngle + M_PI_2)); + lineAngle + pi/2)); // Compute the dimensional line start and end crossings with (virtual) extension lines //check for isometric direction and if iso compute non-perpendicular intersection of dim line and ext lines @@ -1288,14 +1290,14 @@ void QGIViewDimension::drawDistanceExecutive(const Base::Vector2d& startPoint, Base::Vector2d extensionOrigin; Base::Vector2d extensionTarget(computeExtensionLinePoints( - endPoint, endCross, lineAngle + M_PI_2, getDefaultExtensionLineOverhang(), gapSize, + endPoint, endCross, lineAngle + std::numbers::pi/2, getDefaultExtensionLineOverhang(), gapSize, extensionOrigin)); //draw 1st extension line distancePath.moveTo(toQtGui(extensionOrigin)); distancePath.lineTo(toQtGui(extensionTarget)); if (arrowCount > 1) { - extensionTarget = computeExtensionLinePoints(startPoint, startCross, lineAngle + M_PI_2, + extensionTarget = computeExtensionLinePoints(startPoint, startCross, lineAngle + std::numbers::pi/2, getDefaultExtensionLineOverhang(), gapSize, extensionOrigin); //draw second extension line @@ -1320,6 +1322,8 @@ void QGIViewDimension::drawDistanceOverride(const Base::Vector2d& startPoint, int standardStyle, int renderExtent, bool flipArrows, double extensionAngle) const { + using std::numbers::pi; + QPainterPath distancePath; Base::Vector2d labelCenter(labelRectangle.GetCenter()); @@ -1377,9 +1381,9 @@ void QGIViewDimension::drawDistanceOverride(const Base::Vector2d& startPoint, // Orient the leader line angle correctly towards the target point double angles[2]; angles[0] = - jointPositions[0] > 0.0 ? DrawUtil::angleComposition(lineAngle, M_PI) : lineAngle; + jointPositions[0] > 0.0 ? DrawUtil::angleComposition(lineAngle, pi) : lineAngle; angles[1] = - jointPositions[1] > 0.0 ? DrawUtil::angleComposition(lineAngle, M_PI) : lineAngle; + jointPositions[1] > 0.0 ? DrawUtil::angleComposition(lineAngle, pi) : lineAngle; // Select the placement, where the label is not obscured by the leader line // or (if both behave the same) the one that bends the reference line less @@ -1436,7 +1440,7 @@ void QGIViewDimension::drawDistanceOverride(const Base::Vector2d& startPoint, // We may rotate the label so no leader and reference lines are needed double placementFactor = getIsoStandardLinePlacement(lineAngle); labelAngle = - placementFactor > 0.0 ? DrawUtil::angleComposition(lineAngle, M_PI) : lineAngle; + placementFactor > 0.0 ? DrawUtil::angleComposition(lineAngle, pi) : lineAngle; // Find out the projection of label center on the line with given angle Base::Vector2d labelProjection( @@ -1444,7 +1448,7 @@ void QGIViewDimension::drawDistanceOverride(const Base::Vector2d& startPoint, + Base::Vector2d::FromPolar( placementFactor * (labelRectangle.Height() * 0.5 + getIsoDimensionLineSpacing()), - lineAngle + M_PI_2)); + lineAngle + std::numbers::pi/2)); // Compute the dimensional line start and end crossings with (virtual) extension lines startCross = @@ -1499,14 +1503,14 @@ void QGIViewDimension::drawDistanceOverride(const Base::Vector2d& startPoint, Base::Vector2d extensionOrigin; Base::Vector2d extensionTarget(computeExtensionLinePoints( - endPoint, endCross, lineAngle + M_PI_2, getDefaultExtensionLineOverhang(), gapSize, + endPoint, endCross, lineAngle + std::numbers::pi/2, getDefaultExtensionLineOverhang(), gapSize, extensionOrigin)); //draw 1st extension line distancePath.moveTo(toQtGui(extensionOrigin)); distancePath.lineTo(toQtGui(extensionTarget)); if (arrowCount > 1) { - extensionTarget = computeExtensionLinePoints(startPoint, startCross, lineAngle + M_PI_2, + extensionTarget = computeExtensionLinePoints(startPoint, startCross, lineAngle + std::numbers::pi/2, getDefaultExtensionLineOverhang(), gapSize, extensionOrigin); //draw second extension line @@ -1528,6 +1532,8 @@ void QGIViewDimension::drawRadiusExecutive(const Base::Vector2d& centerPoint, double centerOverhang, int standardStyle, int renderExtent, bool flipArrow) const { + using std::numbers::pi; + QPainterPath radiusPath; Base::Vector2d labelCenter(labelRectangle.GetCenter()); @@ -1558,10 +1564,10 @@ void QGIViewDimension::drawRadiusExecutive(const Base::Vector2d& centerPoint, // Orient the leader line angle correctly towards the point on arc if (angleFactors[0] < 0.0) { - lineAngles[0] = DrawUtil::angleComposition(lineAngles[0], M_PI); + lineAngles[0] = DrawUtil::angleComposition(lineAngles[0], pi); } if (angleFactors[1] < 0.0) { - lineAngles[1] = DrawUtil::angleComposition(lineAngles[1], M_PI); + lineAngles[1] = DrawUtil::angleComposition(lineAngles[1], pi); } // Find the positions where the reference line attaches to the dimension line @@ -1600,9 +1606,9 @@ void QGIViewDimension::drawRadiusExecutive(const Base::Vector2d& centerPoint, if (compareAngleStraightness( 0.0, - jointPositions[0] > 0.0 ? DrawUtil::angleComposition(lineAngles[0], M_PI) + jointPositions[0] > 0.0 ? DrawUtil::angleComposition(lineAngles[0], pi) : lineAngles[0], - jointPositions[1] > 0.0 ? DrawUtil::angleComposition(lineAngles[1], M_PI) + jointPositions[1] > 0.0 ? DrawUtil::angleComposition(lineAngles[1], pi) : lineAngles[1], strikeFactors[0], strikeFactors[1]) > 0) { @@ -1653,7 +1659,7 @@ void QGIViewDimension::drawRadiusExecutive(const Base::Vector2d& centerPoint, // Is there point on the arc, where line from center intersects it perpendicularly? double angleFactor = getAnglePlacementFactor(lineAngle, endAngle, startRotation); if (angleFactor < 0.0) { - lineAngle = DrawUtil::angleComposition(lineAngle, M_PI); + lineAngle = DrawUtil::angleComposition(lineAngle, pi); } Base::Vector2d arcPoint; @@ -1673,7 +1679,7 @@ void QGIViewDimension::drawRadiusExecutive(const Base::Vector2d& centerPoint, labelRectangle.Height() * 0.5 + getIsoDimensionLineSpacing(), lineAngle, labelAngle); - lineAngle = DrawUtil::angleComposition(lineAngle, M_PI); + lineAngle = DrawUtil::angleComposition(lineAngle, pi); labelPosition = -cos(devAngle) * ((labelCenter - arcPoint).Length()); } @@ -1693,7 +1699,7 @@ void QGIViewDimension::drawRadiusExecutive(const Base::Vector2d& centerPoint, // Is there point on the arc, where line from center intersects it perpendicularly? double angleFactor = getAnglePlacementFactor(lineAngle, endAngle, startRotation); if (angleFactor < 0) { - lineAngle = DrawUtil::angleComposition(lineAngle, M_PI); + lineAngle = DrawUtil::angleComposition(lineAngle, pi); } Base::Vector2d arcPoint; @@ -1779,7 +1785,7 @@ void QGIViewDimension::drawAreaExecutive(const Base::Vector2d& centerPoint, doub labelRectangle.Height() * 0.5 + getIsoDimensionLineSpacing(), lineAngle, labelAngle); - lineAngle = lineAngle - M_PI; + lineAngle = lineAngle - std::numbers::pi; double labelPosition = -cos(devAngle) * ((labelCenter - centerPoint).Length()); drawDimensionLine(areaPath, centerPoint, lineAngle, 0.0, labelPosition, labelRectangle, 1, standardStyle, flipArrow, forcePointStyle); @@ -1818,7 +1824,7 @@ void QGIViewDimension::drawDistance(TechDraw::DrawViewDimension* dimension, lineAngle = 0.0; } else if (strcmp(dimensionType, "DistanceY") == 0) { - lineAngle = M_PI_2; + lineAngle = std::numbers::pi/2; } else { lineAngle = (fromQtApp(linePoints.second()) - fromQtApp(linePoints.first())).Angle(); @@ -1831,9 +1837,9 @@ void QGIViewDimension::drawDistance(TechDraw::DrawViewDimension* dimension, if (dimension->AngleOverride.getValue()) { drawDistanceOverride(fromQtApp(linePoints.first()), fromQtApp(linePoints.second()), - dimension->LineAngle.getValue() * M_PI / 180.0, labelRectangle, + dimension->LineAngle.getValue() * std::numbers::pi / 180.0, labelRectangle, standardStyle, renderExtent, flipArrows, - dimension->ExtensionAngle.getValue() * M_PI / 180.0); + dimension->ExtensionAngle.getValue() * std::numbers::pi / 180.0); } else { drawDistanceExecutive(fromQtApp(linePoints.extensionLineFirst()), fromQtApp(linePoints.extensionLineSecond()), @@ -1844,6 +1850,8 @@ void QGIViewDimension::drawDistance(TechDraw::DrawViewDimension* dimension, void QGIViewDimension::drawRadius(TechDraw::DrawViewDimension* dimension, ViewProviderDimension* viewProvider) const { + using std::numbers::pi; + Base::BoundBox2d labelRectangle( fromQtGui(mapRectFromItem(datumLabel, datumLabel->tightBoundingRect()))); arcPoints curvePoints = dimension->getArcPoints(); @@ -1858,12 +1866,12 @@ void QGIViewDimension::drawRadius(TechDraw::DrawViewDimension* dimension, - endAngle; if (startRotation != 0.0 && ((startRotation > 0.0) != curvePoints.arcCW)) { - startRotation += curvePoints.arcCW ? +M_2PI : -M_2PI; + startRotation += curvePoints.arcCW ? +2*pi : -2*pi; } } else {// A circle arc covers the whole plane - endAngle = M_PI; - startRotation = -M_2PI; + endAngle = pi; + startRotation = -2*pi; } drawRadiusExecutive( @@ -1875,6 +1883,8 @@ void QGIViewDimension::drawRadius(TechDraw::DrawViewDimension* dimension, void QGIViewDimension::drawDiameter(TechDraw::DrawViewDimension* dimension, ViewProviderDimension* viewProvider) const { + using std::numbers::pi; + Base::BoundBox2d labelRectangle( fromQtGui(mapRectFromItem(datumLabel, datumLabel->tightBoundingRect()))); Base::Vector2d labelCenter(labelRectangle.GetCenter()); @@ -1941,9 +1951,9 @@ void QGIViewDimension::drawDiameter(TechDraw::DrawViewDimension* dimension, int selected = 0; if (compareAngleStraightness( 0.0, - jointPositions[0] > 0.0 ? DrawUtil::angleComposition(lineAngles[0], M_PI) + jointPositions[0] > 0.0 ? DrawUtil::angleComposition(lineAngles[0], pi) : lineAngles[0], - jointPositions[1] > 0.0 ? DrawUtil::angleComposition(lineAngles[1], M_PI) + jointPositions[1] > 0.0 ? DrawUtil::angleComposition(lineAngles[1], pi) : lineAngles[1], strikeFactors[0], strikeFactors[1]) > 0) { @@ -2006,8 +2016,8 @@ void QGIViewDimension::drawDiameter(TechDraw::DrawViewDimension* dimension, Base::Vector2d startPoint(curveCenter); Base::Vector2d endPoint(curveCenter); - if ((lineAngle >= M_PI_4 && lineAngle <= 3.0 * M_PI_4) - || (lineAngle <= -M_PI_4 && lineAngle >= -3.0 * M_PI_4)) { + if ((lineAngle >= pi/4 && lineAngle <= 3.0 * pi/4) + || (lineAngle <= -pi/4 && lineAngle >= -3.0 * pi/4)) { // Horizontal dimension line startPoint.x -= curveRadius; endPoint.x += curveRadius; @@ -2016,10 +2026,10 @@ void QGIViewDimension::drawDiameter(TechDraw::DrawViewDimension* dimension, else {// Vertical dimension line startPoint.y -= curveRadius; endPoint.y += curveRadius; - lineAngle = M_PI_2; + lineAngle = pi/2; } - // lineAngle = DrawUtil::angleComposition((labelCenter - curveCenter).Angle(), +M_PI_2); + // lineAngle = DrawUtil::angleComposition((labelCenter - curveCenter).Angle(), +pi/2); // startPoint = curveCenter - Base::Vector2d::FromPolar(curveRadius, lineAngle); // endPoint = curveCenter + Base::Vector2d::FromPolar(curveRadius, lineAngle); @@ -2031,8 +2041,8 @@ void QGIViewDimension::drawDiameter(TechDraw::DrawViewDimension* dimension, ? ViewProviderDimension::REND_EXTENT_REDUCED : ViewProviderDimension::REND_EXTENT_NORMAL; - drawRadiusExecutive(curveCenter, Rez::guiX(curvePoints.midArc, true), curveRadius, M_PI, - -M_2PI, labelRectangle, getDefaultExtensionLineOverhang(), + drawRadiusExecutive(curveCenter, Rez::guiX(curvePoints.midArc, true), curveRadius, pi, + -2*pi, labelRectangle, getDefaultExtensionLineOverhang(), standardStyle, renderExtent, flipArrows); } } @@ -2040,6 +2050,8 @@ void QGIViewDimension::drawDiameter(TechDraw::DrawViewDimension* dimension, void QGIViewDimension::drawAngle(TechDraw::DrawViewDimension* dimension, ViewProviderDimension* viewProvider) const { + using std::numbers::pi; + QPainterPath anglePath; Base::BoundBox2d labelRectangle( @@ -2105,11 +2117,11 @@ void QGIViewDimension::drawAngle(TechDraw::DrawViewDimension* dimension, jointRotations[1] = handednessFactor * (jointAngles[1] - endAngle); // Compare the offset with half of the rest of 2PI minus the angle and eventually fix the values - if (fabs(jointRotations[0] - startRotation * 0.5) > M_PI) { - jointRotations[0] += jointRotations[0] < 0.0 ? +M_2PI : -M_2PI; + if (fabs(jointRotations[0] - startRotation * 0.5) > pi) { + jointRotations[0] += jointRotations[0] < 0.0 ? +2*pi : -2*pi; } - if (fabs(jointRotations[1] - startRotation * 0.5) > M_PI) { - jointRotations[1] += jointRotations[1] < 0.0 ? +M_2PI : -M_2PI; + if (fabs(jointRotations[1] - startRotation * 0.5) > pi) { + jointRotations[1] += jointRotations[1] < 0.0 ? +2*pi : -2*pi; } // Compute the strike factors so we can choose the placement where value is not obscured by dimensional arc @@ -2133,9 +2145,9 @@ void QGIViewDimension::drawAngle(TechDraw::DrawViewDimension* dimension, if (compareAngleStraightness( 0.0, DrawUtil::angleComposition( - jointAngles[0], handednessFactor * jointRotations[0] > 0.0 ? -M_PI_2 : +M_PI_2), + jointAngles[0], handednessFactor * jointRotations[0] > 0.0 ? -pi/2 : +pi/2), DrawUtil::angleComposition( - jointAngles[1], handednessFactor * jointRotations[1] > 0.0 ? -M_PI_2 : +M_PI_2), + jointAngles[1], handednessFactor * jointRotations[1] > 0.0 ? -pi/2 : +pi/2), strikeFactors[0], strikeFactors[1]) > 0) { selected = 1; @@ -2160,10 +2172,10 @@ void QGIViewDimension::drawAngle(TechDraw::DrawViewDimension* dimension, Base::Vector2d labelDirection(labelCenter - angleVertex); double radiusAngle = labelDirection.Angle(); - labelAngle = DrawUtil::angleComposition(radiusAngle, M_PI_2); + labelAngle = DrawUtil::angleComposition(radiusAngle, pi/2); double placementFactor = getIsoStandardLinePlacement(labelAngle); labelAngle = - placementFactor > 0.0 ? DrawUtil::angleComposition(labelAngle, M_PI) : labelAngle; + placementFactor > 0.0 ? DrawUtil::angleComposition(labelAngle, pi) : labelAngle; arcRadius = labelDirection.Length() - placementFactor @@ -2290,22 +2302,23 @@ Base::Vector3d QGIViewDimension::findIsoExt(Base::Vector3d dir) const Base::Vector3d isoYr(0.866, -0.5, 0.0); //iso +Y? Base::Vector3d isoZ(0.0, 1.0, 0.0); //iso Z Base::Vector3d isoZr(0.0, -1.0, 0.0); //iso -Z - if (dir.IsEqual(isoX, FLT_EPSILON)) { + constexpr float floatEpsilon = std::numeric_limits::epsilon(); + if (dir.IsEqual(isoX, floatEpsilon)) { return isoY; } - else if (dir.IsEqual(-isoX, FLT_EPSILON)) { + else if (dir.IsEqual(-isoX, floatEpsilon)) { return -isoY; } - else if (dir.IsEqual(isoY, FLT_EPSILON)) { + else if (dir.IsEqual(isoY, floatEpsilon)) { return isoZ; } - else if (dir.IsEqual(-isoY, FLT_EPSILON)) { + else if (dir.IsEqual(-isoY, floatEpsilon)) { return -isoZ; } - else if (dir.IsEqual(isoZ, FLT_EPSILON)) { + else if (dir.IsEqual(isoZ, floatEpsilon)) { return isoX; } - else if (dir.IsEqual(-isoZ, FLT_EPSILON)) { + else if (dir.IsEqual(-isoZ, floatEpsilon)) { return -isoX; } @@ -2454,11 +2467,11 @@ void QGIViewDimension::setPens() aHead2->setWidth(m_lineWidth); } -double QGIViewDimension::toDeg(double angle) { return angle * 180 / M_PI; } +double QGIViewDimension::toDeg(double angle) { return angle * 180 / std::numbers::pi; } double QGIViewDimension::toQtRad(double angle) { return -angle; } -double QGIViewDimension::toQtDeg(double angle) { return -angle * 180.0 / M_PI; } +double QGIViewDimension::toQtDeg(double angle) { return -angle * 180.0 / std::numbers::pi; } void QGIViewDimension::makeMarkC(double xPos, double yPos, QColor color) const { diff --git a/src/Mod/TechDraw/Gui/QGIViewPart.cpp b/src/Mod/TechDraw/Gui/QGIViewPart.cpp index d0259a66bb..30d395be52 100644 --- a/src/Mod/TechDraw/Gui/QGIViewPart.cpp +++ b/src/Mod/TechDraw/Gui/QGIViewPart.cpp @@ -964,7 +964,7 @@ void QGIViewPart::drawHighlight(TechDraw::DrawViewDetail* viewDetail, bool b) highlight->setPos(0.0, 0.0);//sb setPos(center.x, center.y)? Base::Vector3d center = viewDetail->AnchorPoint.getValue() * viewPart->getScale(); - double rotationRad = viewPart->Rotation.getValue() * M_PI / 180.0; + double rotationRad = viewPart->Rotation.getValue() * std::numbers::pi / 180.0; center.RotateZ(rotationRad); double radius = viewDetail->Radius.getValue() * viewPart->getScale(); diff --git a/src/Mod/TechDraw/Gui/QGIWeldSymbol.cpp b/src/Mod/TechDraw/Gui/QGIWeldSymbol.cpp index 436fcfac3e..51e785053c 100644 --- a/src/Mod/TechDraw/Gui/QGIWeldSymbol.cpp +++ b/src/Mod/TechDraw/Gui/QGIWeldSymbol.cpp @@ -549,7 +549,7 @@ std::pair QGIWeldSymbol::getLocalAxes() { auto localX = getLeader()->lastSegmentDirection(); auto localY = DU::invertY(localX); - localY.RotateZ(M_PI_2); + localY.RotateZ(std::numbers::pi/2); localY.Normalize(); localY = DU::invertY(localY); return {localX, localY}; diff --git a/src/Mod/TechDraw/Gui/QGTracker.cpp b/src/Mod/TechDraw/Gui/QGTracker.cpp index a69856f024..c1313ded2b 100644 --- a/src/Mod/TechDraw/Gui/QGTracker.cpp +++ b/src/Mod/TechDraw/Gui/QGTracker.cpp @@ -48,14 +48,14 @@ #include "Rez.h" #include "ZVALUE.h" - using namespace TechDraw; using namespace TechDrawGui; QGTracker::QGTracker(QGSPage* inScene, TrackerMode m): m_sleep(false), m_qgParent(nullptr), - m_lastClick(QPointF(FLT_MAX, FLT_MAX)) + m_lastClick(QPointF(std::numeric_limits::max(), + std::numeric_limits::max())) { setTrackerMode(m); if (inScene) { @@ -111,13 +111,8 @@ void QGTracker::mousePressEvent(QGraphicsSceneMouseEvent *event) if (!m_sleep) { double someLimit = Rez::guiX(1.0); QPointF manhat = myScenePos - m_lastClick; -// Base::Console().Message("QGT::mousePressEvent - scenePos: (%.3f, %.3f) lastClick:(%.3f, %.3f)\n", -// myScenePos.x(), myScenePos.y(), m_lastClick.x(), m_lastClick.y()); -// Base::Console().Message("QGT::mousePressEvent - manhat(%.3f, %.3f) mLength: %.3f\n", -// manhat.x(), manhat.y(), manhat.manhattanLength()); - if (manhat.manhattanLength() < someLimit) { -// Base::Console().Message("QGT::mousePressEvent - too close to last click\n"); - } else { + + if (manhat.manhattanLength() >= someLimit) { if (event->button() == Qt::LeftButton) { if (event->modifiers() & Qt::ControlModifier) { myScenePos = snapToAngle(myScenePos); @@ -143,7 +138,6 @@ void QGTracker::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) // can generate two add points void QGTracker::mouseDoubleClickEvent(QGraphicsSceneMouseEvent * event) { -// Base::Console().Message("QGT::mouseDoubleClickEvent()\n"); if (!m_sleep) { onDoubleClick(event->scenePos()); } @@ -165,7 +159,6 @@ void QGTracker::hoverMoveEvent(QGraphicsSceneHoverEvent* event) void QGTracker::keyPressEvent(QKeyEvent * event) { -// Base::Console().Message("QGT::keyPressEvent()\n"); if (event->key() == Qt::Key_Escape) { terminateDrawing(); } @@ -191,7 +184,7 @@ QPointF QGTracker::snapToAngle(QPointF dumbPt) return dumbPt; QPointF result(dumbPt); - double angleIncr = M_PI / 8.0; //15* + double angleIncr = std::numbers::pi / 8.0; //15* //mirror last clicked point and event point to get sensible coords QPointF last(m_points.back().x(), -m_points.back().y()); QPointF pt(dumbPt.x(), -dumbPt.y()); @@ -200,7 +193,7 @@ QPointF QGTracker::snapToAngle(QPointF dumbPt) QPointF qVec = last - pt; //vec from end of track to end of tail double actual = atan2(-qVec.y(), qVec.x()); if (actual < 0.0) { - actual = (2 * M_PI) + actual; //map to +ve angle + actual = (2 * std::numbers::pi) + actual; //map to +ve angle } double intPart; @@ -227,7 +220,6 @@ QPointF QGTracker::snapToAngle(QPointF dumbPt) //mouse event reactions void QGTracker::onMousePress(QPointF pos) { -// Base::Console().Message("QGT::onMousePress(%s)\n", TechDraw::DrawUtil::formatVector(pos).c_str()); m_points.push_back(pos); TrackerMode mode = getTrackerMode(); if (m_points.size() > 1) { @@ -245,14 +237,13 @@ void QGTracker::onMousePress(QPointF pos) break; case TrackerMode::Point: //do nothing -// setPoint(m_points); break; } } else if (m_points.size() == 1) { //first point selected //just return pos to caller getPickedQGIV(pos); setCursor(Qt::CrossCursor); //why cross?? -// Q_EMIT qViewPicked(pos, m_qgParent); //not in use yet. + if (mode == TrackerMode::Point) { setPoint(m_points); //first point is mouse click scene pos terminateDrawing(); @@ -290,7 +281,6 @@ void QGTracker::onMouseMove(QPointF pos) void QGTracker::onDoubleClick(QPointF pos) { -// Base::Console().Message("QGTracker::onDoubleClick()\n"); Q_UNUSED(pos); TrackerMode mode = getTrackerMode(); if (mode == TrackerMode::Point) { @@ -337,7 +327,6 @@ QPainterPath QGTracker::shape() const //actual art routines void QGTracker::drawTrackLine(QPointF pos) { -// Base::Console().Message("QGTracker::drawTrackLine()\n"); m_segEnd = pos; QPainterPath tail; if (!m_points.empty()) { @@ -351,7 +340,6 @@ void QGTracker::drawTrackLine(QPointF pos) void QGTracker::drawTrackSquare(QPointF pos) { -// Base::Console().Message("QGTracker::drawTrackSquare()\n"); m_segEnd = pos; QPainterPath tail; if (!m_points.empty()) { @@ -365,7 +353,6 @@ void QGTracker::drawTrackSquare(QPointF pos) void QGTracker::drawTrackCircle(QPointF pos) { -// Base::Console().Message("QGTracker::drawTrackCircle() - m_points: %d \n", m_points.size()); QPointF circum = pos; QPainterPath tail; if (!m_points.empty()) { @@ -385,7 +372,6 @@ void QGTracker::drawTrackPoint(QPointF pos) void QGTracker::setPathFromPoints(std::vector pts) { -// Base::Console().Message("QGTracker::setPathFromPoints()\n"); if (pts.empty()) { return; } @@ -401,7 +387,6 @@ void QGTracker::setPathFromPoints(std::vector pts) } void QGTracker::setSquareFromPoints(std::vector pts) { -// Base::Console().Message("QGTracker::setSquareFromPoints()\n"); if (pts.empty()) { return; } @@ -417,7 +402,6 @@ void QGTracker::setSquareFromPoints(std::vector pts) void QGTracker::setCircleFromPoints(std::vector pts) { -// Base::Console().Message("QGTracker::setCircleFromPoints()\n"); if (pts.empty()) { return; } @@ -434,7 +418,6 @@ void QGTracker::setCircleFromPoints(std::vector pts) void QGTracker::setPoint(std::vector pts) { -// Base::Console().Message("QGTracker::setPoint()\n"); if (pts.empty()) { Base::Console().Message("QGTracker::setPoint - no pts!\n"); return; @@ -463,7 +446,6 @@ std::vector QGTracker::convertPoints() void QGTracker::terminateDrawing() { -// Base::Console().Message("QGTracker::terminateDrawing()\n"); m_track->hide(); setCursor(Qt::ArrowCursor); Q_EMIT drawingFinished(m_points, m_qgParent); @@ -474,7 +456,6 @@ void QGTracker::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QStyleOptionGraphicsItem myOption(*option); myOption.state &= ~QStyle::State_Selected; -// painter->drawRect(boundingRect()); //good for debugging painter->drawPath(shape()); QGIPrimPath::paint(painter, &myOption, widget); diff --git a/src/Mod/TechDraw/Gui/TaskComplexSection.cpp b/src/Mod/TechDraw/Gui/TaskComplexSection.cpp index 30eab5653a..c19a4a500d 100644 --- a/src/Mod/TechDraw/Gui/TaskComplexSection.cpp +++ b/src/Mod/TechDraw/Gui/TaskComplexSection.cpp @@ -24,6 +24,7 @@ #ifndef _PreComp_ #include #include +#include #endif// #ifndef _PreComp_ #include @@ -193,7 +194,7 @@ void TaskComplexSection::setUiEdit() ui->leBaseView->setText(QString::fromStdString(m_baseView->getNameInDocument())); Base::Vector3d projectedViewDirection = m_baseView->projectPoint(sectionNormalVec, false); double viewAngle = atan2(-projectedViewDirection.y, -projectedViewDirection.x); - m_compass->setDialAngle(viewAngle * 180.0 / M_PI); + m_compass->setDialAngle(viewAngle * 180.0 / std::numbers::pi); m_viewDirectionWidget->setValueNoNotify(projectedViewDirection * -1.0); } else { @@ -308,7 +309,7 @@ void TaskComplexSection::slotViewDirectionChanged(Base::Vector3d newDirection) } projectedViewDirection.Normalize(); double viewAngle = atan2(projectedViewDirection.y, projectedViewDirection.x); - m_compass->setDialAngle(viewAngle * 180.0 / M_PI); + m_compass->setDialAngle(viewAngle * 180.0 / std::numbers::pi); checkAll(false); applyAligned(); } @@ -318,7 +319,7 @@ void TaskComplexSection::slotViewDirectionChanged(Base::Vector3d newDirection) void TaskComplexSection::slotChangeAngle(double newAngle) { // Base::Console().Message("TCS::slotAngleChanged(%.3f)\n", newAngle); - double angleRadians = newAngle * M_PI / 180.0; + double angleRadians = newAngle * std::numbers::pi / 180.0; double unitX = cos(angleRadians); double unitY = sin(angleRadians); Base::Vector3d localUnit(unitX, unitY, 0.0); diff --git a/src/Mod/TechDraw/Gui/TaskCosmeticLine.cpp b/src/Mod/TechDraw/Gui/TaskCosmeticLine.cpp index 5b83340515..11cea7d77a 100644 --- a/src/Mod/TechDraw/Gui/TaskCosmeticLine.cpp +++ b/src/Mod/TechDraw/Gui/TaskCosmeticLine.cpp @@ -113,7 +113,7 @@ void TaskCosmeticLine::setUiPrimary() setWindowTitle(QObject::tr("Create Cosmetic Line")); // double rotDeg = m_partFeat->Rotation.getValue(); - // double rotRad = rotDeg * M_PI / 180.0; + // double rotRad = rotDeg * std::numbers::pi / 180.0; Base::Vector3d centroid = m_partFeat->getCurrentCentroid(); Base::Vector3d p1, p2; if (m_is3d.front()) { diff --git a/src/Mod/TechDraw/Gui/TaskDimension.cpp b/src/Mod/TechDraw/Gui/TaskDimension.cpp index 4da35c5c67..f3794c9c9c 100644 --- a/src/Mod/TechDraw/Gui/TaskDimension.cpp +++ b/src/Mod/TechDraw/Gui/TaskDimension.cpp @@ -227,7 +227,7 @@ void TaskDimension::onEqualToleranceChanged() ui->leFormatSpecifierUnderTolerance->setDisabled(true); } else { - ui->qsbOvertolerance->setMinimum(-DBL_MAX); + ui->qsbOvertolerance->setMinimum(-std::numeric_limits::max()); if (!ui->cbTheoreticallyExact->isChecked()) { ui->qsbUndertolerance->setDisabled(false); ui->leFormatSpecifierUnderTolerance->setDisabled(false); @@ -372,14 +372,14 @@ void TaskDimension::onDimUseDefaultClicked() Base::Vector2d first2(points.first().x, -points.first().y); Base::Vector2d second2(points.second().x, -points.second().y); double lineAngle = (second2 - first2).Angle(); - ui->dsbDimAngle->setValue(lineAngle * 180.0 / M_PI); + ui->dsbDimAngle->setValue(lineAngle * 180.0 / std::numbers::pi); } void TaskDimension::onDimUseSelectionClicked() { std::pair result = getAngleFromSelection(); if (result.second) { - ui->dsbDimAngle->setValue(result.first * 180.0 / M_PI); + ui->dsbDimAngle->setValue(result.first * 180.0 / std::numbers::pi); } } @@ -392,13 +392,13 @@ void TaskDimension::onExtUseDefaultClicked() Base::Vector2d lineDirection = second2 - first2; Base::Vector2d extensionDirection(-lineDirection.y, lineDirection.x); double extensionAngle = extensionDirection.Angle(); - ui->dsbExtAngle->setValue(extensionAngle * 180.0 / M_PI); + ui->dsbExtAngle->setValue(extensionAngle * 180.0 / std::numbers::pi); } void TaskDimension::onExtUseSelectionClicked() { std::pair result = getAngleFromSelection(); if (result.second) { - ui->dsbExtAngle->setValue(result.first * 180.0 / M_PI); + ui->dsbExtAngle->setValue(result.first * 180.0 / std::numbers::pi); } } diff --git a/src/Mod/TechDraw/Gui/TaskSectionView.cpp b/src/Mod/TechDraw/Gui/TaskSectionView.cpp index 8cceb4eea7..3169ff543e 100644 --- a/src/Mod/TechDraw/Gui/TaskSectionView.cpp +++ b/src/Mod/TechDraw/Gui/TaskSectionView.cpp @@ -180,7 +180,7 @@ void TaskSectionView::setUiEdit() Base::Vector3d projectedViewDirection = m_base->projectPoint(sectionNormalVec, false); projectedViewDirection.Normalize(); double viewAngle = atan2(-projectedViewDirection.y, -projectedViewDirection.x); - m_compass->setDialAngle(viewAngle * 180.0 / M_PI); + m_compass->setDialAngle(viewAngle * 180.0 / std::numbers::pi); m_viewDirectionWidget->setValueNoNotify(sectionNormalVec * -1.0); } @@ -272,7 +272,7 @@ void TaskSectionView::slotViewDirectionChanged(Base::Vector3d newDirection) Base::Vector3d projectedViewDirection = m_base->projectPoint(newDirection, false); projectedViewDirection.Normalize(); double viewAngle = atan2(projectedViewDirection.y, projectedViewDirection.x); - m_compass->setDialAngle(viewAngle * 180.0 / M_PI); + m_compass->setDialAngle(viewAngle * 180.0 / std::numbers::pi); checkAll(false); directionChanged(true); applyAligned(); @@ -281,7 +281,7 @@ void TaskSectionView::slotViewDirectionChanged(Base::Vector3d newDirection) //the CompassWidget reports that the view direction angle has changed void TaskSectionView::slotChangeAngle(double newAngle) { - double angleRadians = newAngle * M_PI / 180.0; + double angleRadians = newAngle * std::numbers::pi / 180.0; double unitX = cos(angleRadians); double unitY = sin(angleRadians); Base::Vector3d localUnit(unitX, unitY, 0.0); diff --git a/src/Mod/TechDraw/Gui/ViewProviderAnnotation.cpp b/src/Mod/TechDraw/Gui/ViewProviderAnnotation.cpp index 9d05f13b7a..54b0db7975 100644 --- a/src/Mod/TechDraw/Gui/ViewProviderAnnotation.cpp +++ b/src/Mod/TechDraw/Gui/ViewProviderAnnotation.cpp @@ -22,8 +22,15 @@ ***************************************************************************/ #include "PreCompiled.h" +#ifndef _PreComp_ +#include +#endif #include +#include +#include +#include +#include #include #include @@ -87,3 +94,51 @@ TechDraw::DrawViewAnnotation* ViewProviderAnnotation::getViewObject() const { return dynamic_cast(pcObject); } + +bool ViewProviderAnnotation::doubleClicked() +{ + setEdit(ViewProvider::Default); + return true; +} + +bool ViewProviderAnnotation::setEdit(int ModNum) +{ + if (ModNum != ViewProvider::Default) { + return ViewProviderDrawingView::setEdit(ModNum); + } + // retrieves the PropertyEditor of the Data tab in the comboview of the + // mainwindow and opens the editor of the "text" property of the selected + // item and mimics a click of the button inside that editor, which opens + // the plain text edit dialog + // I feel quite dirty writing this but that was the cleanest way to prevent + // code duplication, because everything just works as long as the structure + // does not change. In case this modus operandi gets used more + // often, this should be delegated to a utility function that takes a property + // path and opens the "deepest" editor of that property + auto comboView = qobject_cast( + Gui::DockWindowManager::instance()->getDockWindow("Model")); + if (!comboView) { + return false; + } + auto dataPropView = comboView->findChild( + QStringLiteral("propertyEditorData")); + if (!dataPropView) { + return false; + } + auto dataPropModel = qobject_cast(dataPropView->model()); + if (!dataPropModel) { + return false; + } + // the property data is a tree model, to we need the first item in the first group + auto index = dataPropModel->propertyIndexFromPath( + {QStringLiteral("Annotation"), QStringLiteral("Text")}); + // setting the current item also opens the editor + dataPropView->setCurrentIndex(index); + dataPropView->activated(index); + // there is a button in the editor wigdet that opens a plain text dialog + auto button = dataPropView->findChild(); + if (button) { + button->click(); + } + return true; +} diff --git a/src/Mod/TechDraw/Gui/ViewProviderAnnotation.h b/src/Mod/TechDraw/Gui/ViewProviderAnnotation.h index b32402c6f6..4bdba5ccc4 100644 --- a/src/Mod/TechDraw/Gui/ViewProviderAnnotation.h +++ b/src/Mod/TechDraw/Gui/ViewProviderAnnotation.h @@ -51,6 +51,9 @@ public: std::vector claimChildren(void) const override; TechDraw::DrawViewAnnotation* getViewObject() const override; + + bool doubleClicked() override; + bool setEdit(int ModNum) override; }; }// namespace TechDrawGui diff --git a/src/Mod/TechDraw/Gui/ViewProviderCosmeticExtension.cpp b/src/Mod/TechDraw/Gui/ViewProviderCosmeticExtension.cpp index 8359f8a490..6a18181eb5 100644 --- a/src/Mod/TechDraw/Gui/ViewProviderCosmeticExtension.cpp +++ b/src/Mod/TechDraw/Gui/ViewProviderCosmeticExtension.cpp @@ -24,10 +24,7 @@ #include "PreCompiled.h" #ifndef _PreComp_ -# ifdef _MSC_VER -# define _USE_MATH_DEFINES -# include -# endif //_MSC_VER +# include #endif #include diff --git a/src/Mod/TechDraw/Gui/ViewProviderDrawingView.cpp b/src/Mod/TechDraw/Gui/ViewProviderDrawingView.cpp index c186079a6c..311e885f5d 100644 --- a/src/Mod/TechDraw/Gui/ViewProviderDrawingView.cpp +++ b/src/Mod/TechDraw/Gui/ViewProviderDrawingView.cpp @@ -28,7 +28,6 @@ #include #endif -#include #include #include @@ -404,7 +403,7 @@ void ViewProviderDrawingView::stackTop() //no view, nothing to stack return; } - int maxZ = INT_MIN; + int maxZ = std::numeric_limits::min(); auto parent = qView->parentItem(); if (parent) { //if we have a parentItem, we have to stack within the parentItem, not within the page @@ -439,7 +438,7 @@ void ViewProviderDrawingView::stackBottom() //no view, nothing to stack return; } - int minZ = INT_MAX; + int minZ = std::numeric_limits::max(); auto parent = qView->parentItem(); if (parent) { //if we have a parentItem, we have to stack within the parentItem, not within the page diff --git a/src/Mod/TechDraw/Gui/Widgets/CompassWidget.cpp b/src/Mod/TechDraw/Gui/Widgets/CompassWidget.cpp index 74e4357c6e..29999791c2 100644 --- a/src/Mod/TechDraw/Gui/Widgets/CompassWidget.cpp +++ b/src/Mod/TechDraw/Gui/Widgets/CompassWidget.cpp @@ -68,17 +68,11 @@ bool CompassWidget::eventFilter(QObject* target, QEvent* event) if (target == dsbAngle) { if (event->type() == QEvent::KeyPress) { QKeyEvent* keyEvent = static_cast(event); - if (keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) { - dsbAngle->interpretText(); - slotSpinBoxEnter(dsbAngle->rawValue()); + const auto isEnter = keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter; + if (isEnter && dsbAngle->isNormalized()) { return true; } } - else if (event->type() == QEvent::FocusOut) { - dsbAngle->interpretText(); - slotSpinBoxEnter(dsbAngle->rawValue()); - return true; - } } return QWidget::eventFilter(target, event); } @@ -133,15 +127,8 @@ void CompassWidget::buildWidget() dsbAngle = new Gui::QuantitySpinBox(this); dsbAngle->setObjectName(QStringLiteral("dsbAngle")); dsbAngle->setUnit(Base::Unit::Angle); - sizePolicy2.setHeightForWidth(dsbAngle->sizePolicy().hasHeightForWidth()); - dsbAngle->setSizePolicy(sizePolicy2); - dsbAngle->setMinimumSize(QSize(75, 26)); - dsbAngle->setMouseTracking(true); - dsbAngle->setFocusPolicy(Qt::ClickFocus); - dsbAngle->setAlignment(Qt::AlignRight | Qt::AlignTrailing | Qt::AlignVCenter); - dsbAngle->setKeyboardTracking(false); - dsbAngle->setMaximum(360.000000000000000); - dsbAngle->setMinimum(-360.000000000000000); + connect(dsbAngle, QOverload::of(&Gui::QuantitySpinBox::valueChanged), + this, &CompassWidget::slotSpinBoxEnter); compassControlLayout->addWidget(dsbAngle); diff --git a/src/Mod/Web/App/AppWeb.cpp b/src/Mod/Web/App/AppWeb.cpp index d89e3ef568..54b2a9ebbc 100644 --- a/src/Mod/Web/App/AppWeb.cpp +++ b/src/Mod/Web/App/AppWeb.cpp @@ -89,7 +89,7 @@ private: if (!PyArg_ParseTuple(args.ptr(), "|si", &addr, &port)) { throw Py::Exception(); } - if (port > USHRT_MAX) { + if (port > std::numeric_limits::max()) { throw Py::OverflowError("port number is greater than maximum"); } else if (port < 0) { @@ -121,7 +121,7 @@ private: if (!PyArg_ParseTuple(args.ptr(), "|sii", &addr, &port, &timeout)) { throw Py::Exception(); } - if (port > USHRT_MAX) { + if (port > std::numeric_limits::max()) { throw Py::OverflowError("port number is greater than maximum"); } else if (port < 0) { diff --git a/src/Tools/bindings/model/generateModel_Python.py b/src/Tools/bindings/model/generateModel_Python.py index 28d802db56..d00b4352d2 100644 --- a/src/Tools/bindings/model/generateModel_Python.py +++ b/src/Tools/bindings/model/generateModel_Python.py @@ -492,6 +492,7 @@ def _parse_class(class_node, source_code: str, path: str, imports_mapping: dict) # Parse imports to compute module metadata module_name = _get_module_from_path(path) + imported_from_module = imports_mapping[base_class_name] parent_module_name = _extract_module_name(imported_from_module, module_name) @@ -537,11 +538,8 @@ def _parse_class(class_node, source_code: str, path: str, imports_mapping: dict) # Attach sequence protocol metadata if provided. if sequence_protocol_kwargs is not None: - try: - seq_protocol = SequenceProtocol(**sequence_protocol_kwargs) - py_export.Sequence = seq_protocol - except Exception as e: - py_export.Sequence = None + seq_protocol = SequenceProtocol(**sequence_protocol_kwargs) + py_export.Sequence = seq_protocol py_export.Attribute.extend(class_attributes) py_export.Methode.extend(class_methods) diff --git a/src/Tools/bindings/model/typedModel.py b/src/Tools/bindings/model/typedModel.py index 594cf8d2dd..e27603b092 100644 --- a/src/Tools/bindings/model/typedModel.py +++ b/src/Tools/bindings/model/typedModel.py @@ -144,16 +144,16 @@ class SequenceProtocol: All attributes are required booleans. """ - sq_length: bool - sq_concat: bool - sq_repeat: bool - sq_item: bool - mp_subscript: bool - sq_ass_item: bool - mp_ass_subscript: bool - sq_contains: bool - sq_inplace_concat: bool - sq_inplace_repeat: bool + sq_length: bool = False + sq_concat: bool = False + sq_repeat: bool = False + sq_item: bool = False + mp_subscript: bool = False + sq_ass_item: bool = False + mp_ass_subscript: bool = False + sq_contains: bool = False + sq_inplace_concat: bool = False + sq_inplace_repeat: bool = False @dataclass diff --git a/src/Tools/bindings/templates/templateClassPyExport.py b/src/Tools/bindings/templates/templateClassPyExport.py index 76bcc95385..9dc65a8e42 100644 --- a/src/Tools/bindings/templates/templateClassPyExport.py +++ b/src/Tools/bindings/templates/templateClassPyExport.py @@ -37,7 +37,14 @@ def compareFiles(file1, file2): class TemplateClassPyExport(template.ModelTemplate): # TODO: This is temporary, once all XML files are migrated, this can be removed. def getPath(self, path): - if self.is_python and not self.export.ModuleName in ["Base", "App"]: + if self.is_python and not self.export.ModuleName in [ + "Base", + "App", + "Gui", + "Part", + "PartDesign", + "Material", + ]: root, ext = os.path.splitext(path) return f"{root}_{ext}" return path diff --git a/src/Tools/freecad-thumbnailer b/src/Tools/freecad-thumbnailer.in similarity index 93% rename from src/Tools/freecad-thumbnailer rename to src/Tools/freecad-thumbnailer.in index 5c89ffe12d..0a6fe451cd 100644 --- a/src/Tools/freecad-thumbnailer +++ b/src/Tools/freecad-thumbnailer.in @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/python3 """Support file to show FreeCAD thumbnails on Free Desktop Environments (like GNOME or KDE) Installation: @@ -37,6 +37,7 @@ import getopt opt, par = getopt.getopt(sys.argv[1:], "-s:") input_file = par[0] output_file = par[1] +default_icon = "@CMAKE_INSTALL_DATAROOTDIR@/icons/hicolor/48x48/apps/org.freecad.FreeCAD.png" try: # Read compressed file @@ -54,7 +55,7 @@ try: image = zfile.read(image) else: # apps should have at least 48x48 icons - freecad = open("/usr/share/icons/hicolor/48x48/apps/org.freecad.FreeCAD.png", "rb") + freecad = open(default_icon, "rb") image = freecad.read() # Write icon to output_file diff --git a/src/Tools/plugins/widget/customwidgets.cpp b/src/Tools/plugins/widget/customwidgets.cpp index 0f9107619a..b5e5dec404 100644 --- a/src/Tools/plugins/widget/customwidgets.cpp +++ b/src/Tools/plugins/widget/customwidgets.cpp @@ -33,7 +33,6 @@ #include #include #include -#include #include "customwidgets.h" @@ -449,8 +448,8 @@ ActionSelector::~ActionSelector() InputField::InputField(QWidget* parent) : QLineEdit(parent) , Value(0) - , Maximum(INT_MAX) - , Minimum(-INT_MAX) + , Maximum(std::numeric_limits::max()) + , Minimum(-std::numeric_limits::max()) , StepSize(1.0) , HistorySize(5) {} @@ -671,8 +670,8 @@ public: : validInput(true) , pendingEmit(false) , unitValue(0) - , maximum(INT_MAX) - , minimum(-INT_MAX) + , maximum(std::numeric_limits::max()) + , minimum(-std::numeric_limits::max()) , singleStep(1.0) {} ~QuantitySpinBoxPrivate() @@ -1459,7 +1458,7 @@ UnsignedValidator::UnsignedValidator(QObject* parent) : QValidator(parent) { b = 0; - t = UINT_MAX; + t = std::numeric_limits::max(); } UnsignedValidator::UnsignedValidator(uint minimum, uint maximum, QObject* parent) @@ -1522,39 +1521,41 @@ public: {} uint mapToUInt(int v) const { + using limits = std::numeric_limits; uint ui; - if (v == INT_MIN) { + if (v == limits::min()) { ui = 0; } - else if (v == INT_MAX) { - ui = UINT_MAX; + else if (v == limits::max()) { + ui = limits::max(); } else if (v < 0) { - v -= INT_MIN; - ui = (uint)v; + v -= limits::min(); + ui = static_cast(v); } else { - ui = (uint)v; - ui -= INT_MIN; + ui = static_cast(v); + ui -= limits::min(); } return ui; } int mapToInt(uint v) const { + using limits = std::numeric_limits; int in; - if (v == UINT_MAX) { - in = INT_MAX; + if (v == limits::max()) { + in = limits::max(); } else if (v == 0) { - in = INT_MIN; + in = limits::min(); } - else if (v > INT_MAX) { - v += INT_MIN; - in = (int)v; + else if (v > limits::max()) { + v += limits::min(); + in = static_cast(v); } else { in = v; - in += INT_MIN; + in += limits::min(); } return in; } diff --git a/src/XDGData/CMakeLists.txt b/src/XDGData/CMakeLists.txt index ec4609243a..837381554a 100644 --- a/src/XDGData/CMakeLists.txt +++ b/src/XDGData/CMakeLists.txt @@ -30,7 +30,20 @@ install( DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/mime/packages ) +configure_file( + FreeCAD.thumbnailer.in + ${CMAKE_BINARY_DIR}/FreeCAD.thumbnailer +) install( - FILES FreeCAD.thumbnailer + FILES ${CMAKE_BINARY_DIR}/FreeCAD.thumbnailer DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/thumbnailers ) + +configure_file( + ${CMAKE_SOURCE_DIR}/src/Tools/freecad-thumbnailer.in + ${CMAKE_BINARY_DIR}/freecad-thumbnailer +) +install( + PROGRAMS ${CMAKE_BINARY_DIR}/freecad-thumbnailer + TYPE BIN +) diff --git a/src/XDGData/FreeCAD.thumbnailer b/src/XDGData/FreeCAD.thumbnailer deleted file mode 100644 index 005dd0ae28..0000000000 --- a/src/XDGData/FreeCAD.thumbnailer +++ /dev/null @@ -1,4 +0,0 @@ -[Thumbnailer Entry] -TryExec=freecad-thumbnailer -Exec=freecad-thumbnailer -s %s %i %o -MimeType=application/x-extension-fcstd; diff --git a/src/XDGData/FreeCAD.thumbnailer.in b/src/XDGData/FreeCAD.thumbnailer.in new file mode 100644 index 0000000000..f334368d4a --- /dev/null +++ b/src/XDGData/FreeCAD.thumbnailer.in @@ -0,0 +1,4 @@ +[Thumbnailer Entry] +TryExec=@CMAKE_INSTALL_BINDIR@/freecad-thumbnailer +Exec=@CMAKE_INSTALL_BINDIR@/freecad-thumbnailer -s %s %i %o +MimeType=application/x-extension-fcstd; diff --git a/src/XDGData/org.freecad.FreeCAD.desktop b/src/XDGData/org.freecad.FreeCAD.desktop index e7fb3ca0e0..fc9c3019b2 100644 --- a/src/XDGData/org.freecad.FreeCAD.desktop +++ b/src/XDGData/org.freecad.FreeCAD.desktop @@ -1,16 +1,14 @@ [Desktop Entry] Name=FreeCAD -Name[de]=FreeCAD -Name[ko]=FreeCAD -Name[pl]=FreeCAD -Name[ru]=FreeCAD Comment=Feature based Parametric Modeler Comment[de]=Feature-basierter parametrischer Modellierer +Comment[es]=Modelador paramétrico basado en operaciones Comment[ko]=형상 기반 파라메트릭 모델링 도구 Comment[pl]=Modeler parametryczny oparty na cechach Comment[ru]=Система автоматизированного проектирования GenericName=CAD Application GenericName[de]=CAD-Anwendung +GenericName[es]=Aplicación CAD GenericName[ko]=CAD 응용프로그램 GenericName[pl]=Aplikacja CAD GenericName[ru]=Система автоматизированного проектирования diff --git a/tests/src/Base/Tools.cpp b/tests/src/Base/Tools.cpp index 6bcb0b4c0b..39fc056fd9 100644 --- a/tests/src/Base/Tools.cpp +++ b/tests/src/Base/Tools.cpp @@ -39,16 +39,16 @@ TEST(Tools, TestSignum) TEST(Tools, TestRadian) { EXPECT_EQ(Base::toRadians(90), 1); - EXPECT_DOUBLE_EQ(Base::toRadians(180), M_PI); - EXPECT_DOUBLE_EQ(Base::toRadians(90.0), M_PI / 2.0); + EXPECT_DOUBLE_EQ(Base::toRadians(180), std::numbers::pi); + EXPECT_DOUBLE_EQ(Base::toRadians(90.0), std::numbers::pi / 2.0); EXPECT_DOUBLE_EQ(Base::toRadians(0.0), 0.0); } TEST(Tools, TestDegree) { EXPECT_EQ(Base::toDegrees(3), 171); - EXPECT_DOUBLE_EQ(Base::toDegrees(M_PI), 180.0); - EXPECT_DOUBLE_EQ(Base::toDegrees(M_PI / 2.0), 90.0); + EXPECT_DOUBLE_EQ(Base::toDegrees(std::numbers::pi), 180.0); + EXPECT_DOUBLE_EQ(Base::toDegrees(std::numbers::pi / 2.0), 90.0); EXPECT_DOUBLE_EQ(Base::toDegrees(0.0), 0.0); } diff --git a/tests/src/Mod/Material/App/TestMaterialCards.cpp b/tests/src/Mod/Material/App/TestMaterialCards.cpp index 435ba19822..a4c9c4be50 100644 --- a/tests/src/Mod/Material/App/TestMaterialCards.cpp +++ b/tests/src/Mod/Material/App/TestMaterialCards.cpp @@ -57,12 +57,12 @@ protected: QDir libDir(libPath); libDir.removeRecursively(); // Clear old run data libDir.mkdir(libPath); - _library = std::make_shared(QStringLiteral("Testing"), + _library = std::make_shared(QStringLiteral("Testing"), libPath, QStringLiteral(":/icons/preferences-general.svg"), false); - _modelManager = new Materials::ModelManager(); - _materialManager = new Materials::MaterialManager(); + _modelManager = &(Materials::ModelManager::getManager()); + _materialManager = &(Materials::MaterialManager::getManager()); _testMaterialUUID = QStringLiteral("c6c64159-19c1-40b5-859c-10561f20f979"); } @@ -70,7 +70,7 @@ protected: // void TearDown() override {} Materials::ModelManager* _modelManager; Materials::MaterialManager* _materialManager; - std::shared_ptr _library; + std::shared_ptr _library; QString _testMaterialUUID; }; @@ -187,17 +187,17 @@ TEST_F(TestMaterialCards, TestColumns) EXPECT_TRUE(testMaterial->hasPhysicalProperty(QStringLiteral("TestArray2D"))); auto array2d = testMaterial->getPhysicalProperty(QStringLiteral("TestArray2D"))->getMaterialValue(); EXPECT_TRUE(array2d); - EXPECT_EQ(dynamic_cast(*array2d).columns(), 2); + EXPECT_EQ(dynamic_cast(*array2d).columns(), 2); EXPECT_TRUE(testMaterial->hasPhysicalProperty(QStringLiteral("TestArray2D3Column"))); auto array2d3Column = testMaterial->getPhysicalProperty(QStringLiteral("TestArray2D3Column"))->getMaterialValue(); EXPECT_TRUE(array2d3Column); - EXPECT_EQ(dynamic_cast(*array2d3Column).columns(), 3); + EXPECT_EQ(dynamic_cast(*array2d3Column).columns(), 3); EXPECT_TRUE(testMaterial->hasPhysicalProperty(QStringLiteral("TestArray3D"))); auto array3d = testMaterial->getPhysicalProperty(QStringLiteral("TestArray3D"))->getMaterialValue(); EXPECT_TRUE(array3d); - EXPECT_EQ(dynamic_cast(*array3d).columns(), 2); + EXPECT_EQ(dynamic_cast(*array3d).columns(), 2); } // clang-format on diff --git a/tests/src/Mod/Material/App/TestMaterialFilter.cpp b/tests/src/Mod/Material/App/TestMaterialFilter.cpp index f79c12bc96..c84c91d6ef 100644 --- a/tests/src/Mod/Material/App/TestMaterialFilter.cpp +++ b/tests/src/Mod/Material/App/TestMaterialFilter.cpp @@ -52,8 +52,8 @@ protected: } void SetUp() override { - _modelManager = new Materials::ModelManager(); - _materialManager = new Materials::MaterialManager(); + _modelManager = &(Materials::ModelManager::getManager()); + _materialManager = &(Materials::MaterialManager::getManager()); // Use our test files as a custom directory ParameterGrp::handle hGrp = @@ -74,7 +74,7 @@ protected: _materialManager->refresh(); - _library = _materialManager->getLibrary(QLatin1String("Custom")); + _library = _materialManager->getLibrary(QStringLiteral("Custom")); } void TearDown() override { @@ -153,7 +153,7 @@ TEST_F(TestMaterialFilter, TestFilters) ASSERT_EQ(tree->size(), 5); // Create a basic rendering filter - filter->setName(QLatin1String("Basic Appearance")); + filter->setName(QStringLiteral("Basic Appearance")); filter->addRequiredComplete(Materials::ModelUUIDs::ModelUUID_Rendering_Basic); options.setIncludeLegacy(false); @@ -166,7 +166,7 @@ TEST_F(TestMaterialFilter, TestFilters) // Create an advanced rendering filter filter->clear(); - filter->setName(QLatin1String("Advanced Appearance")); + filter->setName(QStringLiteral("Advanced Appearance")); filter->addRequiredComplete(Materials::ModelUUIDs::ModelUUID_Rendering_Advanced); options.setIncludeLegacy(false); @@ -179,7 +179,7 @@ TEST_F(TestMaterialFilter, TestFilters) // Create a Density filter filter->clear(); - filter->setName(QLatin1String("Density")); + filter->setName(QStringLiteral("Density")); filter->addRequiredComplete(Materials::ModelUUIDs::ModelUUID_Mechanical_Density); options.setIncludeLegacy(false); @@ -192,7 +192,7 @@ TEST_F(TestMaterialFilter, TestFilters) // Create a Hardness filter filter->clear(); - filter->setName(QLatin1String("Hardness")); + filter->setName(QStringLiteral("Hardness")); filter->addRequiredComplete(Materials::ModelUUIDs::ModelUUID_Mechanical_Hardness); options.setIncludeLegacy(false); @@ -205,7 +205,7 @@ TEST_F(TestMaterialFilter, TestFilters) // Create a Density and Basic Rendering filter filter->clear(); - filter->setName(QLatin1String("Density and Basic Rendering")); + filter->setName(QStringLiteral("Density and Basic Rendering")); filter->addRequiredComplete(Materials::ModelUUIDs::ModelUUID_Rendering_Basic); filter->addRequiredComplete(Materials::ModelUUIDs::ModelUUID_Mechanical_Density); options.setIncludeLegacy(false); @@ -219,7 +219,7 @@ TEST_F(TestMaterialFilter, TestFilters) // Create a Linear Elastic filter filter->clear(); - filter->setName(QLatin1String("Linear Elastic")); + filter->setName(QStringLiteral("Linear Elastic")); filter->addRequiredComplete(Materials::ModelUUIDs::ModelUUID_Mechanical_LinearElastic); options.setIncludeLegacy(false); @@ -231,7 +231,7 @@ TEST_F(TestMaterialFilter, TestFilters) ASSERT_EQ(tree->size(), 0); filter->clear(); - filter->setName(QLatin1String("Linear Elastic")); + filter->setName(QStringLiteral("Linear Elastic")); filter->addRequired(Materials::ModelUUIDs::ModelUUID_Mechanical_LinearElastic); options.setIncludeLegacy(false); diff --git a/tests/src/Mod/Material/App/TestMaterialProperties.cpp b/tests/src/Mod/Material/App/TestMaterialProperties.cpp index c1f4a2a26e..6fd60c6709 100644 --- a/tests/src/Mod/Material/App/TestMaterialProperties.cpp +++ b/tests/src/Mod/Material/App/TestMaterialProperties.cpp @@ -130,9 +130,9 @@ TEST_F(TestMaterialProperties, TestEmpty) TEST_F(TestMaterialProperties, TestSingle) { - Materials::MaterialProperty prop(modelProp1, QLatin1String("sampleUUID")); + Materials::MaterialProperty prop(modelProp1, QStringLiteral("sampleUUID")); EXPECT_EQ(prop.getType(), Materials::MaterialValue::Quantity); - EXPECT_EQ(prop.getModelUUID(), QLatin1String("sampleUUID")); + EXPECT_EQ(prop.getModelUUID(), QStringLiteral("sampleUUID")); EXPECT_TRUE(prop.isNull()); auto variant = prop.getValue(); EXPECT_TRUE(variant.canConvert()); @@ -146,9 +146,9 @@ TEST_F(TestMaterialProperties, TestSingle) void check2DArray(Materials::MaterialProperty& prop) { EXPECT_EQ(prop.getType(), Materials::MaterialValue::Array2D); - EXPECT_EQ(prop.getModelUUID(), QLatin1String("sampleUUID")); + EXPECT_EQ(prop.getModelUUID(), QStringLiteral("sampleUUID")); EXPECT_TRUE(prop.isNull()); - auto array = std::static_pointer_cast(prop.getMaterialValue()); + auto array = std::static_pointer_cast(prop.getMaterialValue()); EXPECT_EQ(array->rows(), 0); auto variant = prop.getValue(); // Throw an error? EXPECT_FALSE(variant.canConvert()); @@ -162,20 +162,20 @@ void check2DArray(Materials::MaterialProperty& prop) TEST_F(TestMaterialProperties, Test2DArray) { - Materials::MaterialProperty prop(modelProp, QLatin1String("sampleUUID")); + Materials::MaterialProperty prop(modelProp, QStringLiteral("sampleUUID")); check2DArray(prop); } TEST_F(TestMaterialProperties, Test2DArrayCopy) { - Materials::MaterialProperty propBase(modelProp, QLatin1String("sampleUUID")); + Materials::MaterialProperty propBase(modelProp, QStringLiteral("sampleUUID")); Materials::MaterialProperty prop(propBase); check2DArray(prop); } TEST_F(TestMaterialProperties, Test2DArrayAssignment) { - Materials::MaterialProperty propBase(modelProp, QLatin1String("sampleUUID")); + Materials::MaterialProperty propBase(modelProp, QStringLiteral("sampleUUID")); Materials::MaterialProperty prop; prop = propBase; @@ -185,9 +185,9 @@ TEST_F(TestMaterialProperties, Test2DArrayAssignment) void check3DArray(Materials::MaterialProperty& prop) { EXPECT_EQ(prop.getType(), Materials::MaterialValue::Array3D); - EXPECT_EQ(prop.getModelUUID(), QLatin1String("sampleUUID")); + EXPECT_EQ(prop.getModelUUID(), QStringLiteral("sampleUUID")); EXPECT_TRUE(prop.isNull()); - auto array = std::static_pointer_cast(prop.getMaterialValue()); + auto array = std::static_pointer_cast(prop.getMaterialValue()); EXPECT_EQ(array->depth(), 0); auto variant = prop.getValue(); // Throw an error? EXPECT_FALSE(variant.canConvert()); @@ -201,20 +201,20 @@ void check3DArray(Materials::MaterialProperty& prop) TEST_F(TestMaterialProperties, Test3DArray) { - Materials::MaterialProperty prop(model3DProp, QLatin1String("sampleUUID")); + Materials::MaterialProperty prop(model3DProp, QStringLiteral("sampleUUID")); check3DArray(prop); } TEST_F(TestMaterialProperties, Test3DArrayCopy) { - Materials::MaterialProperty propBase(model3DProp, QLatin1String("sampleUUID")); + Materials::MaterialProperty propBase(model3DProp, QStringLiteral("sampleUUID")); Materials::MaterialProperty prop(propBase); check3DArray(prop); } TEST_F(TestMaterialProperties, Test3DArrayAssignment) { - Materials::MaterialProperty propBase(model3DProp, QLatin1String("sampleUUID")); + Materials::MaterialProperty propBase(model3DProp, QStringLiteral("sampleUUID")); Materials::MaterialProperty prop; prop = propBase; diff --git a/tests/src/Mod/Material/App/TestMaterialValue.cpp b/tests/src/Mod/Material/App/TestMaterialValue.cpp index b31fd82d99..1a5d1d12e4 100644 --- a/tests/src/Mod/Material/App/TestMaterialValue.cpp +++ b/tests/src/Mod/Material/App/TestMaterialValue.cpp @@ -172,7 +172,7 @@ TEST_F(TestMaterialValue, TestArray2DType) { EXPECT_THROW(auto mat1 = Materials::MaterialValue(Materials::MaterialValue::Array2D), Materials::InvalidMaterialType); - auto mat2 = Materials::Material2DArray(); + auto mat2 = Materials::Array2D(); EXPECT_EQ(mat2.getType(), Materials::MaterialValue::Array2D); EXPECT_TRUE(mat2.isNull()); EXPECT_EQ(mat2.rows(), 0); @@ -182,7 +182,7 @@ TEST_F(TestMaterialValue, TestArray3DType) { EXPECT_THROW(auto mat1 = Materials::MaterialValue(Materials::MaterialValue::Array3D), Materials::InvalidMaterialType); - auto mat2 = Materials::Material3DArray(); + auto mat2 = Materials::Array3D(); mat2.setColumns(2); EXPECT_EQ(mat2.getType(), Materials::MaterialValue::Array3D); EXPECT_TRUE(mat2.isNull()); diff --git a/tests/src/Mod/Material/App/TestMaterials.cpp b/tests/src/Mod/Material/App/TestMaterials.cpp index b1940c7d2e..7c3b31a6cf 100644 --- a/tests/src/Mod/Material/App/TestMaterials.cpp +++ b/tests/src/Mod/Material/App/TestMaterials.cpp @@ -31,8 +31,8 @@ #include #include -#include #include +#include #include #include @@ -58,8 +58,8 @@ class TestMaterial : public ::testing::Test { void SetUp() override { Base::Interpreter().runString("import Part"); - _modelManager = new Materials::ModelManager(); - _materialManager = new Materials::MaterialManager(); + _modelManager = &(Materials::ModelManager::getManager()); + _materialManager = &(Materials::MaterialManager::getManager()); } // void TearDown() override {} @@ -72,11 +72,11 @@ TEST_F(TestMaterial, TestInstallation) ASSERT_NE(_modelManager, nullptr); // We should have loaded at least the system library - auto libraries = _materialManager->getMaterialLibraries(); + auto libraries = _materialManager->getLibraries(); ASSERT_GT(libraries->size(), 0); // We should have at least one material - auto materials = _materialManager->getMaterials(); + auto materials = _materialManager->getLocalMaterials(); ASSERT_GT(materials->size(), 0); } @@ -97,7 +97,7 @@ TEST_F(TestMaterial, TestMaterialsWithModel) // All LinearElastic models should be in IsotropicLinearElastic since it is inherited EXPECT_LE(materialsLinearElastic->size(), materials->size()); - for (auto itp : *materialsLinearElastic) { + for (auto &itp : *materialsLinearElastic) { auto mat = itp.first; EXPECT_NO_THROW(materials->at(mat)); } @@ -365,17 +365,17 @@ TEST_F(TestMaterial, TestColumns) EXPECT_TRUE(testMaterial.hasPhysicalProperty(QStringLiteral("TestArray2D"))); auto array2d = testMaterial.getPhysicalProperty(QStringLiteral("TestArray2D"))->getMaterialValue(); EXPECT_TRUE(array2d); - EXPECT_EQ(dynamic_cast(*array2d).columns(), 2); + EXPECT_EQ(dynamic_cast(*array2d).columns(), 2); EXPECT_TRUE(testMaterial.hasPhysicalProperty(QStringLiteral("TestArray2D3Column"))); auto array2d3Column = testMaterial.getPhysicalProperty(QStringLiteral("TestArray2D3Column"))->getMaterialValue(); EXPECT_TRUE(array2d3Column); - EXPECT_EQ(dynamic_cast(*array2d3Column).columns(), 3); + EXPECT_EQ(dynamic_cast(*array2d3Column).columns(), 3); EXPECT_TRUE(testMaterial.hasPhysicalProperty(QStringLiteral("TestArray3D"))); auto array3d = testMaterial.getPhysicalProperty(QStringLiteral("TestArray3D"))->getMaterialValue(); EXPECT_TRUE(array3d); - EXPECT_EQ(dynamic_cast(*array3d).columns(), 2); + EXPECT_EQ(dynamic_cast(*array3d).columns(), 2); } // clang-format on diff --git a/tests/src/Mod/Material/App/TestModel.cpp b/tests/src/Mod/Material/App/TestModel.cpp index d4db24220a..dde62d476d 100644 --- a/tests/src/Mod/Material/App/TestModel.cpp +++ b/tests/src/Mod/Material/App/TestModel.cpp @@ -46,7 +46,7 @@ class TestModel : public ::testing::Test { } void SetUp() override { - _modelManager = new Materials::ModelManager(); + _modelManager = &(Materials::ModelManager::getManager()); } // void TearDown() override {} @@ -76,7 +76,7 @@ TEST_F(TestModel, TestInstallation) ASSERT_NE(_modelManager, nullptr); // We should have loaded at least the system library - auto libraries = _modelManager->getModelLibraries(); + auto libraries = _modelManager->getLibraries(); ASSERT_GT(libraries->size(), 0); // We should have at least one model diff --git a/tests/src/Mod/Part/App/FeatureExtrusion.cpp b/tests/src/Mod/Part/App/FeatureExtrusion.cpp index 4529d7def6..18c017f0a3 100644 --- a/tests/src/Mod/Part/App/FeatureExtrusion.cpp +++ b/tests/src/Mod/Part/App/FeatureExtrusion.cpp @@ -172,7 +172,7 @@ TEST_F(FeatureExtrusionTest, testExecuteAngled) { // Arrange const double ang = 30; - const double tangent = tan(ang / 180.0 * M_PI); + const double tangent = tan(ang / 180.0 * std::numbers::pi); // The shape is a truncated pyramid elongated by a truncated triangular prism in the middle. // Calc the volume of full size pyramid and prism, and subtract top volumes to truncate. @@ -209,7 +209,7 @@ TEST_F(FeatureExtrusionTest, testExecuteAngledRev) { // Arrange const double ang = 30; - const double tangent = tan(ang / 180.0 * M_PI); + const double tangent = tan(ang / 180.0 * std::numbers::pi); // The shape is a truncated pyramid elongated by a truncated triangular prism in the middle, // plus a rectangular prism. // Calc the volume of full size pyramid and prism, and subtract top volumes to truncate. @@ -249,7 +249,7 @@ TEST_F(FeatureExtrusionTest, testExecuteEdge) { // Arrange const double ang = 30; - const double tangent = tan(ang / 180.0 * M_PI); + const double tangent = tan(ang / 180.0 * std::numbers::pi); BRepBuilderAPI_MakeEdge e1(gp_Pnt(0, 0, 0), gp_Pnt(ext1, ext1, ext1)); auto edge = _doc->addObject("Edge"); edge->Shape.setValue(e1); @@ -270,7 +270,7 @@ TEST_F(FeatureExtrusionTest, testExecuteEdge) TEST_F(FeatureExtrusionTest, testExecuteDir) { // Arrange - const double sin45 = sin(45 / 180.0 * M_PI); + const double sin45 = sin(45 / 180.0 * std::numbers::pi); _extrusion->Dir.setValue(Base::Vector3d(0, 1, 1)); _extrusion->DirMode.setValue((long)0); // Act @@ -319,8 +319,10 @@ TEST_F(FeatureExtrusionTest, testFaceWithHoles) double volume = PartTestHelpers::getVolume(ts.getShape()); Base::BoundBox3d bb = ts.getBoundBox(); // Assert - EXPECT_FLOAT_EQ(volume, len * wid * ext1 - radius * radius * M_PI * ext1); + EXPECT_FLOAT_EQ(volume, len * wid * ext1 - radius * radius * std::numbers::pi * ext1); EXPECT_TRUE(PartTestHelpers::boxesMatch(bb, Base::BoundBox3d(0, 0, 0, len, wid, ext1))); - EXPECT_FLOAT_EQ(PartTestHelpers::getArea(face1), len * wid + radius * radius * M_PI); - EXPECT_FLOAT_EQ(PartTestHelpers::getArea(face2), len * wid - radius * radius * M_PI); + EXPECT_FLOAT_EQ(PartTestHelpers::getArea(face1), + len * wid + radius * radius * std::numbers::pi); + EXPECT_FLOAT_EQ(PartTestHelpers::getArea(face2), + len * wid - radius * radius * std::numbers::pi); } diff --git a/tests/src/Mod/Part/App/FeatureRevolution.cpp b/tests/src/Mod/Part/App/FeatureRevolution.cpp index 38eeafa562..90247e11b3 100644 --- a/tests/src/Mod/Part/App/FeatureRevolution.cpp +++ b/tests/src/Mod/Part/App/FeatureRevolution.cpp @@ -46,7 +46,7 @@ protected: TEST_F(FeatureRevolutionTest, testExecute) { // Arrange - double puckVolume = len * len * M_PI * wid; // Area is PIr2; apply height + double puckVolume = len * len * std::numbers::pi * wid; // Area is PIr2; apply height // Act _revolution->execute(); Part::TopoShape ts = _revolution->Shape.getValue(); @@ -62,8 +62,8 @@ TEST_F(FeatureRevolutionTest, testExecuteBase) // Arrange double rad = len + 1.0; double rad2 = 1.0; - double outerPuckVolume = rad * rad * M_PI * wid; // Area is PIr2; apply height - double innerPuckVolume = rad2 * rad2 * M_PI * wid; // Area is PIr2; apply height + double outerPuckVolume = rad * rad * std::numbers::pi * wid; // Area is PIr2; apply height + double innerPuckVolume = rad2 * rad2 * std::numbers::pi * wid; // Area is PIr2; apply height _revolution->Base.setValue(Base::Vector3d(len + 1, 0, 0)); // Act _revolution->execute(); @@ -79,7 +79,7 @@ TEST_F(FeatureRevolutionTest, testExecuteBase) TEST_F(FeatureRevolutionTest, testAxis) { // Arrange - double puckVolume = wid * wid * M_PI * len; // Area is PIr2 times height + double puckVolume = wid * wid * std::numbers::pi * len; // Area is PIr2 times height _revolution->Axis.setValue(Base::Vector3d(1, 0, 0)); // Act _revolution->execute(); @@ -98,7 +98,7 @@ TEST_F(FeatureRevolutionTest, testAxisLink) auto edge = _doc->addObject("Edge"); edge->Shape.setValue(e1); _revolution->AxisLink.setValue(edge); - // double puckVolume = wid * wid * M_PI * len; // Area is PIr2; apply height + // double puckVolume = wid * wid * std::numbers::pi * len; // Area is PIr2; apply height // Act _revolution->execute(); Part::TopoShape ts = _revolution->Shape.getValue(); @@ -115,7 +115,7 @@ TEST_F(FeatureRevolutionTest, testAxisLink) TEST_F(FeatureRevolutionTest, testSymmetric) { // Arrange - double puckVolume = len * len * M_PI * wid; // Area is PIr2 times height + double puckVolume = len * len * std::numbers::pi * wid; // Area is PIr2 times height _revolution->Symmetric.setValue(true); // Act _revolution->execute(); @@ -130,8 +130,8 @@ TEST_F(FeatureRevolutionTest, testSymmetric) TEST_F(FeatureRevolutionTest, testAngle) { // Arrange - double puckVolume = len * len * M_PI * wid; // Area is PIr2 times height - _revolution->Angle.setValue(90); // NOLINT magic number + double puckVolume = len * len * std::numbers::pi * wid; // Area is PIr2 times height + _revolution->Angle.setValue(90); // NOLINT magic number // Act _revolution->execute(); Part::TopoShape ts = _revolution->Shape.getValue(); diff --git a/tests/src/Mod/Part/App/TopoShapeExpansion.cpp b/tests/src/Mod/Part/App/TopoShapeExpansion.cpp index 81a2f4ee5a..ba6046852f 100644 --- a/tests/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/tests/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -608,10 +608,10 @@ TEST_F(TopoShapeExpansionTest, makeElementFaceNull) double area3 = getArea(topoShape.getShape()); // Assert EXPECT_FALSE(face1.IsEqual(newFace.getShape())); - EXPECT_DOUBLE_EQ(area, Len * Wid + M_PI * Rad * Rad); - EXPECT_DOUBLE_EQ(area1, Len * Wid + M_PI * Rad * Rad); - EXPECT_DOUBLE_EQ(area2, Len * Wid - M_PI * Rad * Rad); - EXPECT_DOUBLE_EQ(area3, Len * Wid + M_PI * Rad * Rad); + EXPECT_DOUBLE_EQ(area, Len * Wid + std::numbers::pi * Rad * Rad); + EXPECT_DOUBLE_EQ(area1, Len * Wid + std::numbers::pi * Rad * Rad); + EXPECT_DOUBLE_EQ(area2, Len * Wid - std::numbers::pi * Rad * Rad); + EXPECT_DOUBLE_EQ(area3, Len * Wid + std::numbers::pi * Rad * Rad); EXPECT_STREQ(newFace.shapeName().c_str(), "Face"); } @@ -632,8 +632,8 @@ TEST_F(TopoShapeExpansionTest, makeElementFaceSimple) // Assert EXPECT_TRUE(newFace.getShape().IsEqual(topoShape.getShape())); // topoShape was altered EXPECT_FALSE(face1.IsEqual(newFace.getShape())); - EXPECT_DOUBLE_EQ(area, Len * Wid + M_PI * Rad * Rad); - EXPECT_DOUBLE_EQ(area1, Len * Wid + M_PI * Rad * Rad); + EXPECT_DOUBLE_EQ(area, Len * Wid + std::numbers::pi * Rad * Rad); + EXPECT_DOUBLE_EQ(area1, Len * Wid + std::numbers::pi * Rad * Rad); EXPECT_DOUBLE_EQ(area2, Len * Wid); EXPECT_DOUBLE_EQ(area3, Len * Wid); EXPECT_STREQ(newFace.shapeName().c_str(), "Face"); @@ -656,8 +656,8 @@ TEST_F(TopoShapeExpansionTest, makeElementFaceParams) // Assert EXPECT_TRUE(newFace.getShape().IsEqual(topoShape.getShape())); // topoShape was altered EXPECT_FALSE(face1.IsEqual(newFace.getShape())); - EXPECT_DOUBLE_EQ(area, Len * Wid + M_PI * Rad * Rad); - EXPECT_DOUBLE_EQ(area1, Len * Wid + M_PI * Rad * Rad); + EXPECT_DOUBLE_EQ(area, Len * Wid + std::numbers::pi * Rad * Rad); + EXPECT_DOUBLE_EQ(area1, Len * Wid + std::numbers::pi * Rad * Rad); EXPECT_DOUBLE_EQ(area2, Len * Wid); EXPECT_DOUBLE_EQ(area3, Len * Wid); EXPECT_STREQ(newFace.shapeName().c_str(), "Face"); @@ -680,10 +680,10 @@ TEST_F(TopoShapeExpansionTest, makeElementFaceFromFace) // Assert EXPECT_TRUE(newFace.getShape().IsEqual(topoShape.getShape())); // topoShape was altered EXPECT_FALSE(face1.IsEqual(newFace.getShape())); - EXPECT_DOUBLE_EQ(area, Len * Wid + M_PI * Rad * Rad); - EXPECT_DOUBLE_EQ(area1, Len * Wid + M_PI * Rad * Rad); - EXPECT_DOUBLE_EQ(area2, Len * Wid - M_PI * Rad * Rad); - EXPECT_DOUBLE_EQ(area3, Len * Wid - M_PI * Rad * Rad); + EXPECT_DOUBLE_EQ(area, Len * Wid + std::numbers::pi * Rad * Rad); + EXPECT_DOUBLE_EQ(area1, Len * Wid + std::numbers::pi * Rad * Rad); + EXPECT_DOUBLE_EQ(area2, Len * Wid - std::numbers::pi * Rad * Rad); + EXPECT_DOUBLE_EQ(area3, Len * Wid - std::numbers::pi * Rad * Rad); EXPECT_STREQ(newFace.shapeName().c_str(), "Face"); } @@ -705,8 +705,8 @@ TEST_F(TopoShapeExpansionTest, makeElementFaceOpenWire) // Assert EXPECT_TRUE(newFace.getShape().IsEqual(topoShape.getShape())); // topoShape was altered EXPECT_FALSE(face1.IsEqual(newFace.getShape())); - EXPECT_DOUBLE_EQ(area, Len * Wid + M_PI * Rad * Rad); - EXPECT_DOUBLE_EQ(area1, 0); // Len * Wid - M_PI * Rad * Rad); + EXPECT_DOUBLE_EQ(area, Len * Wid + std::numbers::pi * Rad * Rad); + EXPECT_DOUBLE_EQ(area1, 0); // Len * Wid - std::numbers::pi * Rad * Rad); EXPECT_DOUBLE_EQ(area2, Len * Wid); EXPECT_DOUBLE_EQ(area3, Len * Wid); EXPECT_STREQ(newFace.shapeName().c_str(), "Face"); @@ -730,10 +730,10 @@ TEST_F(TopoShapeExpansionTest, makeElementFaceClosedWire) // Assert EXPECT_TRUE(newFace.getShape().IsEqual(topoShape.getShape())); // topoShape was altered EXPECT_FALSE(face1.IsEqual(newFace.getShape())); - EXPECT_DOUBLE_EQ(area, Len * Wid + M_PI * Rad * Rad); - EXPECT_DOUBLE_EQ(area1, 0); // Len * Wid - M_PI * Rad * Rad); - EXPECT_DOUBLE_EQ(area2, M_PI * Rad * Rad); - EXPECT_DOUBLE_EQ(area3, M_PI * Rad * Rad); + EXPECT_DOUBLE_EQ(area, Len * Wid + std::numbers::pi * Rad * Rad); + EXPECT_DOUBLE_EQ(area1, 0); // Len * Wid - std::numbers::pi * Rad * Rad); + EXPECT_DOUBLE_EQ(area2, std::numbers::pi * Rad * Rad); + EXPECT_DOUBLE_EQ(area3, std::numbers::pi * Rad * Rad); EXPECT_STREQ(newFace.shapeName().c_str(), "Face"); } @@ -837,7 +837,7 @@ TEST_F(TopoShapeExpansionTest, splitWires) // Assert EXPECT_EQ(inner.size(), 1); EXPECT_DOUBLE_EQ(getLength(wire.getShape()), 2 + 2 + 3 + 3); - EXPECT_DOUBLE_EQ(getLength(inner.front().getShape()), M_PI * Rad * 2); + EXPECT_DOUBLE_EQ(getLength(inner.front().getShape()), std::numbers::pi * Rad * 2); EXPECT_EQ(wire.getShape().Orientation(), TopAbs_REVERSED); for (TopoShape& shape : inner) { EXPECT_EQ(shape.getShape().Orientation(), TopAbs_FORWARD); @@ -1284,7 +1284,7 @@ TEST_F(TopoShapeExpansionTest, makeElementShellOpen) const float Wid = 2; auto [face1, wire1, edge1, edge2, edge3, edge4] = CreateRectFace(Len, Wid); auto transform {gp_Trsf()}; - transform.SetRotation(gp_Ax1(gp_Pnt(0, 0, 0), gp_Dir(1, 0, 0)), M_PI / 2); + transform.SetRotation(gp_Ax1(gp_Pnt(0, 0, 0), gp_Dir(1, 0, 0)), std::numbers::pi / 2); auto face2 = face1; // Shallow copy face2.Move(TopLoc_Location(transform)); TopoDS_Compound compound1; @@ -1562,7 +1562,7 @@ TEST_F(TopoShapeExpansionTest, makeElementDraft) std::vector subShapes = cube1TS.getSubTopoShapes(TopAbs_FACE); std::vector faces {subShapes[0], subShapes[1], subShapes[2], subShapes[3]}; const gp_Dir pullDirection {0, 0, 1}; - double angle {M_PI * 10 + double angle {std::numbers::pi * 10 / 8}; // Angle should be between Pi and Pi * 1.5 ( 180 and 270 degrees ) const gp_Pln plane {}; // Act @@ -1580,7 +1580,7 @@ TEST_F(TopoShapeExpansionTest, makeElementDraftTopoShapes) // Arrange auto [cube1TS, cube2TS] = CreateTwoTopoShapeCubes(); const gp_Dir pullDirection {0, 0, 1}; - double angle {M_PI * 10 + double angle {std::numbers::pi * 10 / 8}; // Angle should be between Pi and Pi * 1.5 ( 180 and 270 degrees ) const gp_Pln plane {}; // Act diff --git a/tests/src/Mod/Sketcher/App/SketchObject.cpp b/tests/src/Mod/Sketcher/App/SketchObject.cpp index 954308353c..36c1db0f26 100644 --- a/tests/src/Mod/Sketcher/App/SketchObject.cpp +++ b/tests/src/Mod/Sketcher/App/SketchObject.cpp @@ -237,7 +237,7 @@ TEST_F(SketchObjectTest, testGetPointFromGeomArcOfCircle) { // Arrange Base::Vector3d coordsCenter(1.0, 2.0, 0.0); - double radius = 3.0, startParam = M_PI / 3, endParam = M_PI * 1.5; + double radius = 3.0, startParam = std::numbers::pi / 3, endParam = std::numbers::pi * 1.5; Part::GeomArcOfCircle arcOfCircle; arcOfCircle.setCenter(coordsCenter); arcOfCircle.setRadius(radius); @@ -267,7 +267,7 @@ TEST_F(SketchObjectTest, testGetPointFromGeomArcOfEllipse) Base::Vector3d coordsCenter(1.0, 2.0, 0.0); double majorRadius = 4.0; double minorRadius = 3.0; - double startParam = M_PI / 3, endParam = M_PI * 1.5; + double startParam = std::numbers::pi / 3, endParam = std::numbers::pi * 1.5; Part::GeomArcOfEllipse arcOfEllipse; arcOfEllipse.setCenter(coordsCenter); arcOfEllipse.setMajorRadius(majorRadius); @@ -298,7 +298,7 @@ TEST_F(SketchObjectTest, testGetPointFromGeomArcOfHyperbola) Base::Vector3d coordsCenter(1.0, 2.0, 0.0); double majorRadius = 4.0; double minorRadius = 3.0; - double startParam = M_PI / 3, endParam = M_PI * 1.5; + double startParam = std::numbers::pi / 3, endParam = std::numbers::pi * 1.5; Part::GeomArcOfHyperbola arcOfHyperbola; arcOfHyperbola.setCenter(coordsCenter); arcOfHyperbola.setMajorRadius(majorRadius); @@ -330,7 +330,7 @@ TEST_F(SketchObjectTest, testGetPointFromGeomArcOfParabola) // Arrange Base::Vector3d coordsCenter(1.0, 2.0, 0.0); double focal = 3.0; - double startParam = M_PI / 3, endParam = M_PI * 1.5; + double startParam = std::numbers::pi / 3, endParam = std::numbers::pi * 1.5; Part::GeomArcOfParabola arcOfParabola; arcOfParabola.setCenter(coordsCenter); arcOfParabola.setFocal(focal); diff --git a/tests/src/Mod/Sketcher/App/SketchObjectChanges.cpp b/tests/src/Mod/Sketcher/App/SketchObjectChanges.cpp index 2b0f69aa76..5d2519bea6 100644 --- a/tests/src/Mod/Sketcher/App/SketchObjectChanges.cpp +++ b/tests/src/Mod/Sketcher/App/SketchObjectChanges.cpp @@ -846,7 +846,7 @@ TEST_F(SketchObjectTest, testJoinCurves) // Arrange // Make two curves Base::Vector3d coordsCenter(0.0, 0.0, 0.0); - double radius = 3.0, startParam = M_PI / 2, endParam = M_PI; + double radius = 3.0, startParam = std::numbers::pi / 2, endParam = std::numbers::pi; Part::GeomArcOfCircle arcOfCircle; arcOfCircle.setCenter(coordsCenter); arcOfCircle.setRadius(radius); @@ -877,7 +877,7 @@ TEST_F(SketchObjectTest, testJoinCurvesWhenTangent) // Arrange // Make two curves Base::Vector3d coordsCenter(0.0, 0.0, 0.0); - double radius = 3.0, startParam = M_PI / 2, endParam = M_PI; + double radius = 3.0, startParam = std::numbers::pi / 2, endParam = std::numbers::pi; Part::GeomArcOfCircle arcOfCircle; arcOfCircle.setCenter(coordsCenter); arcOfCircle.setRadius(radius); diff --git a/tests/src/Mod/Sketcher/App/SketcherTestHelpers.cpp b/tests/src/Mod/Sketcher/App/SketcherTestHelpers.cpp index ae4fb2414e..51aade677f 100644 --- a/tests/src/Mod/Sketcher/App/SketcherTestHelpers.cpp +++ b/tests/src/Mod/Sketcher/App/SketcherTestHelpers.cpp @@ -56,7 +56,7 @@ void setupArcOfCircle(Part::GeomArcOfCircle& arcOfCircle) { Base::Vector3d coordsCenter(1.0, 2.0, 0.0); double radius = 3.0; - double startParam = M_PI / 3, endParam = M_PI * 1.5; + double startParam = std::numbers::pi / 3, endParam = std::numbers::pi * 1.5; arcOfCircle.setCenter(coordsCenter); arcOfCircle.setRadius(radius); arcOfCircle.setRange(startParam, endParam, true); @@ -77,7 +77,7 @@ void setupArcOfHyperbola(Part::GeomArcOfHyperbola& arcOfHyperbola) Base::Vector3d coordsCenter(1.0, 2.0, 0.0); double majorRadius = 4.0; double minorRadius = 3.0; - double startParam = M_PI / 3, endParam = M_PI * 1.5; + double startParam = std::numbers::pi / 3, endParam = std::numbers::pi * 1.5; arcOfHyperbola.setCenter(coordsCenter); arcOfHyperbola.setMajorRadius(majorRadius); arcOfHyperbola.setMinorRadius(minorRadius); @@ -88,7 +88,7 @@ void setupArcOfParabola(Part::GeomArcOfParabola& aop) { Base::Vector3d coordsCenter(1.0, 2.0, 0.0); double focal = 3.0; - double startParam = -M_PI * 1.5, endParam = M_PI * 1.5; + double startParam = -std::numbers::pi * 1.5, endParam = std::numbers::pi * 1.5; aop.setCenter(coordsCenter); aop.setFocal(focal); aop.setRange(startParam, endParam, true); diff --git a/tests/src/Mod/Sketcher/App/planegcs/Constraints.cpp b/tests/src/Mod/Sketcher/App/planegcs/Constraints.cpp index 4f0b90b997..89cb81dc12 100644 --- a/tests/src/Mod/Sketcher/App/planegcs/Constraints.cpp +++ b/tests/src/Mod/Sketcher/App/planegcs/Constraints.cpp @@ -1,6 +1,7 @@ // SPDX-License-Identifier: LGPL-2.1-or-later #include +#include #include @@ -54,8 +55,8 @@ TEST_F(ConstraintsTest, tangentBSplineAndArc) // NOLINT arcEnd.y = &arcEndY; arcCenter.x = &arcCenterX; arcCenter.y = &arcCenterY; - double arcRadius = 5.0, arcStartAngle = 0.0, arcEndAngle = M_PI / 2; - double desiredAngle = M_PI; + double arcRadius = 5.0, arcStartAngle = 0.0, arcEndAngle = std::numbers::pi / 2; + double desiredAngle = std::numbers::pi; double bSplineStartX = 0.0, bSplineEndX = 16.0; double bSplineStartY = 10.0, bSplineEndY = -10.0; GCS::Point bSplineStart, bSplineEnd;