From 39efc8ec0ff0c3b14870df38f22e19fd359ee463 Mon Sep 17 00:00:00 2001 From: Joao Matos Date: Sun, 18 May 2025 18:05:57 +0100 Subject: [PATCH] Part: Add OCCT progress support to Part boolean and shape builders Introduce a `Build(const Message_ProgressRange&)` overload in `FCBRepAlgoAPI_BooleanOperation` (guarded by `OCC_VERSION_HEX < 0x070600`) and route user-abort checks around the operation. Add a new `OCCTProgressIndicator` adapter (`OCCTProgressIndicator.h`) that bridges `Message_ProgressIndicator` to `Base::ProgressIndicator`. Include `OCCTProgressIndicator.h` where needed and update all `Build()` calls in `TopoShape` and `TopoShapeExpansion` to pass `OCCTProgressIndicator().Start()`, enabling singleton-based progress reporting and user-break handling. --- src/App/Application.h | 6 ++ .../App/FCBRepAlgoAPI_BooleanOperation.cpp | 14 ++++ .../Part/App/FCBRepAlgoAPI_BooleanOperation.h | 8 ++ src/Mod/Part/App/OCCTProgressIndicator.h | 73 +++++++++++++++++++ src/Mod/Part/App/TopoShape.cpp | 56 +++++++++++++- src/Mod/Part/App/TopoShapeExpansion.cpp | 48 +++++++++++- 6 files changed, 200 insertions(+), 5 deletions(-) create mode 100644 src/Mod/Part/App/OCCTProgressIndicator.h diff --git a/src/App/Application.h b/src/App/Application.h index 4aa70c85e0..1c79cbb1a6 100644 --- a/src/App/Application.h +++ b/src/App/Application.h @@ -36,6 +36,7 @@ #include #include +#include // forward declarations using PyObject = struct _object; @@ -475,6 +476,9 @@ public: bool hasLinksTo(const DocumentObject *obj) const; //@} + /// Gets the base progress indicator instance. + Base::ProgressIndicator& getProgressIndicator() { return _progressIndicator; } + friend class App::Document; protected: @@ -662,6 +666,8 @@ private: int _activeTransactionGuard{0}; bool _activeTransactionTmpName{false}; + Base::ProgressIndicator _progressIndicator; + static Base::ConsoleObserverStd *_pConsoleObserverStd; static Base::ConsoleObserverFile *_pConsoleObserverFile; }; diff --git a/src/Mod/Part/App/FCBRepAlgoAPI_BooleanOperation.cpp b/src/Mod/Part/App/FCBRepAlgoAPI_BooleanOperation.cpp index b49317ab71..decee22b2b 100644 --- a/src/Mod/Part/App/FCBRepAlgoAPI_BooleanOperation.cpp +++ b/src/Mod/Part/App/FCBRepAlgoAPI_BooleanOperation.cpp @@ -82,7 +82,14 @@ void FCBRepAlgoAPIHelper::setAutoFuzzy(BRepAlgoAPI_BuilderAlgo* op) { } void FCBRepAlgoAPI_BooleanOperation::Build() { + Message_ProgressRange progressRange; + Build(progressRange); +} +void FCBRepAlgoAPI_BooleanOperation::Build(const Message_ProgressRange& progressRange) { + if (progressRange.UserBreak()) { + Standard_ConstructionError::Raise("User aborted"); + } if (myOperation == BOPAlgo_CUT && myArguments.Size() == 1 && myTools.Size() == 1 && myTools.First().ShapeType() == TopAbs_COMPOUND) { // cut argument and compound tool TopTools_ListOfShape myOriginalArguments = myArguments; @@ -98,7 +105,14 @@ void FCBRepAlgoAPI_BooleanOperation::Build() { myArguments = myOriginalArguments; } else { +#if OCC_VERSION_HEX >= 0x070600 + BRepAlgoAPI_BooleanOperation::Build(progressRange); +#else BRepAlgoAPI_BooleanOperation::Build(); +#endif + } + if (progressRange.UserBreak()) { + Standard_ConstructionError::Raise("User aborted"); } } diff --git a/src/Mod/Part/App/FCBRepAlgoAPI_BooleanOperation.h b/src/Mod/Part/App/FCBRepAlgoAPI_BooleanOperation.h index b009d98e2e..6ab1fb5ae3 100644 --- a/src/Mod/Part/App/FCBRepAlgoAPI_BooleanOperation.h +++ b/src/Mod/Part/App/FCBRepAlgoAPI_BooleanOperation.h @@ -28,7 +28,9 @@ #ifndef FCREPALGOAPIBOOLEANOPERATION_H #define FCREPALGOAPIBOOLEANOPERATION_H + #include +#include class FCBRepAlgoAPIHelper { @@ -52,6 +54,12 @@ public: // not an override - real Build() has optionals, sadly type of those optionals that are differs between OCCT versions Standard_EXPORT virtual void Build(); // NOLINT(clang-diagnostic-overloaded-virtual, -Woverloaded-virtual) +#if OCC_VERSION_HEX >= 0x070600 + Standard_EXPORT void Build(const Message_ProgressRange& progressRange) Standard_OVERRIDE; +#else + Standard_EXPORT virtual void Build(const Message_ProgressRange& progressRange); +#endif + protected: //! @name Constructors //! Constructor to perform Boolean operation on only two arguments. diff --git a/src/Mod/Part/App/OCCTProgressIndicator.h b/src/Mod/Part/App/OCCTProgressIndicator.h new file mode 100644 index 0000000000..203aa54193 --- /dev/null +++ b/src/Mod/Part/App/OCCTProgressIndicator.h @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/*************************************************************************** + * Copyright (c) 2025 James Stanley * + * * + * 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 PART_OCCTPROGRESSINDICATOR_H +#define PART_OCCTPROGRESSINDICATOR_H + +#include +#include +#include + +#include + +namespace Part { + +class PartExport OCCTProgressIndicator : public Message_ProgressIndicator +{ + Base::ProgressIndicator& baseIndicator; + +public: + OCCTProgressIndicator(Base::ProgressIndicator& indicator) + : baseIndicator(indicator) {} + + Standard_Boolean UserBreak() override { + return baseIndicator.userBreak(); + } + + void Show(const Message_ProgressScope& scope, const Standard_Boolean isForce) override { + float pos = -1; // negative means indeterminate + if (!scope.IsInfinite()) { + pos = static_cast(GetPosition()); + } + using ShowFlags = Base::ProgressIndicator::ShowFlags; + baseIndicator.show(pos, isForce ? ShowFlags::Force : ShowFlags::None); + } + + static OCCTProgressIndicator getAppIndicator() { + return {App::GetApplication().getProgressIndicator()}; + } +}; + + +#if OCC_VERSION_HEX < 0x070600 +// Stubs out OCCT Message_ProgressRange for OCCT versions below 7.5 +class Message_ProgressRange +{ +public: + bool UserBreak() { return false; } + void Show([[maybe_unused]] float position, [[maybe_unused]] bool isForce) {} +}; +#endif + +} // namespace Part + +#endif // PART_OCCTPROGRESSINDICATOR_H diff --git a/src/Mod/Part/App/TopoShape.cpp b/src/Mod/Part/App/TopoShape.cpp index 20254c7270..6793e16210 100644 --- a/src/Mod/Part/App/TopoShape.cpp +++ b/src/Mod/Part/App/TopoShape.cpp @@ -189,7 +189,7 @@ #include "TopoShapeSolidPy.h" #include "TopoShapeVertexPy.h" #include "TopoShapeWirePy.h" - +#include "OCCTProgressIndicator.h" FC_LOG_LEVEL_INIT("TopoShape",true,true) @@ -1695,7 +1695,11 @@ TopoDS_Shape TopoShape::cut(const std::vector& shapes, Standard_Re } else if (tolerance < 0.0) { mkCut.setAutoFuzzy(); } +#if OCC_VERSION_HEX >= 0x070600 + mkCut.Build(OCCTProgressIndicator::getAppIndicator().Start()); +#else mkCut.Build(); +#endif if (!mkCut.IsDone()) throw Base::RuntimeError("Multi cut failed"); @@ -1734,7 +1738,11 @@ TopoDS_Shape TopoShape::common(const std::vector& shapes, Standard } else if (tolerance < 0.0) { mkCommon.setAutoFuzzy(); } +#if OCC_VERSION_HEX >= 0x070600 + mkCommon.Build(OCCTProgressIndicator::getAppIndicator().Start()); +#else mkCommon.Build(); +#endif if (!mkCommon.IsDone()) throw Base::RuntimeError("Multi common failed"); @@ -1773,7 +1781,11 @@ TopoDS_Shape TopoShape::fuse(const std::vector& shapes, Standard_R } else if (tolerance < 0.0) { mkFuse.setAutoFuzzy(); } +#if OCC_VERSION_HEX >= 0x070600 + mkFuse.Build(OCCTProgressIndicator::getAppIndicator().Start()); +#else mkFuse.Build(); +#endif if (!mkFuse.IsDone()) throw Base::RuntimeError("Multi fuse failed"); @@ -1792,7 +1804,11 @@ TopoDS_Shape TopoShape::section(TopoDS_Shape shape, Standard_Boolean approximate mkSection.Init1(this->_Shape); mkSection.Init2(shape); mkSection.Approximation(approximate); +#if OCC_VERSION_HEX >= 0x070600 + mkSection.Build(OCCTProgressIndicator::getAppIndicator().Start()); +#else mkSection.Build(); +#endif if (!mkSection.IsDone()) throw Base::RuntimeError("Section failed"); return mkSection.Shape(); @@ -1823,7 +1839,11 @@ TopoDS_Shape TopoShape::section(const std::vector& shapes, } else if (tolerance < 0.0) { mkSection.setAutoFuzzy(); } +#if OCC_VERSION_HEX >= 0x070600 + mkSection.Build(OCCTProgressIndicator::getAppIndicator().Start()); +#else mkSection.Build(); +#endif if (!mkSection.IsDone()) throw Base::RuntimeError("Multi section failed"); @@ -1886,7 +1906,11 @@ TopoDS_Shape TopoShape::generalFuse(const std::vector &sOthers, St FCBRepAlgoAPIHelper::setAutoFuzzy(&mkGFA); } mkGFA.SetNonDestructive(Standard_True); +#if OCC_VERSION_HEX >= 0x070600 + mkGFA.Build(OCCTProgressIndicator::getAppIndicator().Start()); +#else mkGFA.Build(); +#endif if (!mkGFA.IsDone()) throw BooleanException("MultiFusion failed"); TopoDS_Shape resShape = mkGFA.Shape(); @@ -1940,8 +1964,11 @@ TopoDS_Shape TopoShape::makePipeShell(const TopTools_ListOfShape& profiles, if (!mkPipeShell.IsReady()) throw Standard_Failure("shape is not ready to build"); +#if OCC_VERSION_HEX >= 0x070600 + mkPipeShell.Build(OCCTProgressIndicator::getAppIndicator().Start()); +#else mkPipeShell.Build(); - +#endif if (make_solid) mkPipeShell.MakeSolid(); @@ -2431,7 +2458,11 @@ TopoDS_Shape TopoShape::makeLoft(const TopTools_ListOfShape& profiles, Standard_Boolean anIsCheck = Standard_True; aGenerator.CheckCompatibility (anIsCheck); // use BRepFill_CompatibleWires on profiles. force #edges, orientation, "origin" to match. +#if OCC_VERSION_HEX >= 0x070600 + aGenerator.Build(OCCTProgressIndicator::getAppIndicator().Start()); +#else aGenerator.Build(); +#endif if (!aGenerator.IsDone()) Standard_Failure::Raise("Failed to create loft face"); @@ -2573,7 +2604,11 @@ TopoDS_Shape TopoShape::makeOffsetShape(double offset, double tol, bool intersec BRepOffsetAPI_ThruSections aGenerator; aGenerator.AddWire(originalWire); aGenerator.AddWire(offsetWire); +#if OCC_VERSION_HEX >= 0x070600 + aGenerator.Build(OCCTProgressIndicator::getAppIndicator().Start()); +#else aGenerator.Build(); +#endif if (!aGenerator.IsDone()) { Standard_Failure::Raise("ThruSections failed"); @@ -2880,8 +2915,11 @@ TopoDS_Shape TopoShape::makeOffset2D(double offset, short joinType, bool fill, b //add final joining edge mkWire.Add(BRepBuilderAPI_MakeEdge(v3,v1).Edge()); +#if OCC_VERSION_HEX >= 0x070600 + mkWire.Build(OCCTProgressIndicator::getAppIndicator().Start()); +#else mkWire.Build(); - +#endif wiresForMakingFaces.push_front(mkWire.Wire()); } } @@ -2893,7 +2931,11 @@ TopoDS_Shape TopoShape::makeOffset2D(double offset, short joinType, bool fill, b for(TopoDS_Wire &w : wiresForMakingFaces){ mkFace.addWire(w); } +#if OCC_VERSION_HEX >= 0x070600 + mkFace.Build(OCCTProgressIndicator::getAppIndicator().Start()); +#else mkFace.Build(); +#endif if (mkFace.Shape().IsNull()) throw Base::CADKernelError("makeOffset2D: making face failed (null shape returned)."); TopoDS_Shape result = mkFace.Shape(); @@ -3633,7 +3675,11 @@ TopoDS_Shape TopoShape::defeaturing(const std::vector& s) const defeat.SetShape(this->_Shape); for (const auto & it : s) defeat.AddFaceToRemove(it); +#if OCC_VERSION_HEX >= 0x070600 + defeat.Build(OCCTProgressIndicator::getAppIndicator().Start()); +#else defeat.Build(); +#endif if (!defeat.IsDone()) { // error treatment Standard_SStream aSStream; @@ -3854,7 +3900,11 @@ TopoShape &TopoShape::makeFace(const std::vector &shapes, const char else if (s.getShape().ShapeType() != TopAbs_VERTEX) mkFace->addShape(s.getShape()); } +#if OCC_VERSION_HEX >= 0x070600 + mkFace->Build(OCCTProgressIndicator::getAppIndicator().Start()); +#else mkFace->Build(); +#endif _Shape = mkFace->Shape(); return *this; } diff --git a/src/Mod/Part/App/TopoShapeExpansion.cpp b/src/Mod/Part/App/TopoShapeExpansion.cpp index ea9babd627..dcedb5dff3 100644 --- a/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -95,8 +95,10 @@ #include "FaceMaker.h" #include "Geometry.h" #include "BRepOffsetAPI_MakeOffsetFix.h" -#include "Base/Tools.h" #include "Base/BoundBox.h" +#include "Base/Exception.h" +#include "Base/Tools.h" +#include "OCCTProgressIndicator.h" #include #include @@ -2428,7 +2430,11 @@ TopoShape& TopoShape::makeElementPipeShell(const std::vector& shapes, FC_THROWM(Base::CADKernelError, "shape is not ready to build"); } else { +#if OCC_VERSION_HEX >= 0x070600 + mkPipeShell.Build(OCCTProgressIndicator::getAppIndicator().Start()); +#else mkPipeShell.Build(); +#endif } if (make_solid == MakeSolid::makeSolid) { @@ -2534,7 +2540,11 @@ TopoShape& TopoShape::makeElementOffset(const TopoShape& shape, BRepOffsetAPI_ThruSections aGenerator; aGenerator.AddWire(TopoDS::Wire(originalWire.getShape())); aGenerator.AddWire(offsetWire); +#if OCC_VERSION_HEX >= 0x070600 + aGenerator.Build(OCCTProgressIndicator::getAppIndicator().Start()); +#else aGenerator.Build(); +#endif if (!aGenerator.IsDone()) { FC_THROWM(Base::CADKernelError, "ThruSections failed"); } @@ -2949,8 +2959,11 @@ TopoShape& TopoShape::makeElementOffset2D(const TopoShape& shape, } // add final joining edge mkWire.Add(BRepBuilderAPI_MakeEdge(v3, v1).Edge()); - +#if OCC_VERSION_HEX >= 0x070600 + mkWire.Build(OCCTProgressIndicator::getAppIndicator().Start()); +#else mkWire.Build(); +#endif wiresForMakingFaces.push_back( TopoShape(Tag, Hasher).makeElementShape(mkWire, openWires, op)); @@ -3820,7 +3833,11 @@ TopoShape& TopoShape::makeElementFilledFace(const std::vector& _shape } } +#if OCC_VERSION_HEX >= 0x070600 + maker.Build(OCCTProgressIndicator::getAppIndicator().Start()); +#else maker.Build(); +#endif if (!maker.IsDone()) { FC_THROWM(Base::CADKernelError, "Failed to created face by filling edges"); } @@ -4089,7 +4106,11 @@ TopoShape& TopoShape::makeElementGeneralFuse(const std::vector& _shap FCBRepAlgoAPIHelper::setAutoFuzzy(&mkGFA); } mkGFA.SetNonDestructive(Standard_True); +#if OCC_VERSION_HEX >= 0x070600 + mkGFA.Build(OCCTProgressIndicator::getAppIndicator().Start()); +#else mkGFA.Build(); +#endif if (!mkGFA.IsDone()) { FC_THROWM(Base::CADKernelError, "GeneralFuse failed"); } @@ -4228,7 +4249,11 @@ TopoShape& TopoShape::makeElementLoft(const std::vector& shapes, aGenerator.CheckCompatibility(anIsCheck); // use BRepFill_CompatibleWires on profiles. force // #edges, orientation, "origin" to match. +#if OCC_VERSION_HEX >= 0x070600 + aGenerator.Build(OCCTProgressIndicator::getAppIndicator().Start()); +#else aGenerator.Build(); +#endif return makeShapeWithElementMap(aGenerator.Shape(), MapperThruSections(aGenerator, profiles), shapes, @@ -4555,7 +4580,11 @@ TopoShape& TopoShape::makeElementDraft(const TopoShape& shape, } } while (retry && !done); +#if OCC_VERSION_HEX >= 0x070600 + mkDraft.Build(OCCTProgressIndicator::getAppIndicator().Start()); +#else mkDraft.Build(); +#endif return makeElementShape(mkDraft, shape, op); } @@ -4600,7 +4629,11 @@ TopoShape& TopoShape::makeElementFace(const std::vector& shapes, mkFace->addTopoShape(shape); } } +#if OCC_VERSION_HEX >= 0x070600 + mkFace->Build(OCCTProgressIndicator::getAppIndicator().Start()); +#else mkFace->Build(); +#endif const auto& ret = mkFace->getTopoShape(); setShape(ret._Shape); @@ -5587,6 +5620,10 @@ TopoShape& TopoShape::makeElementBoolean(const char* maker, FC_THROWM(NullShapeException, "Null shape"); } + if (OCCTProgressIndicator::getAppIndicator().UserBreak()) { + FC_THROWM(Base::CADKernelError, "User aborted"); + } + if (strcmp(maker, Part::OpCodes::Compound) == 0) { return makeElementCompound(shapes, op, SingleShapeCompoundCreationPolicy::returnShape); } @@ -5750,7 +5787,14 @@ TopoShape& TopoShape::makeElementBoolean(const char* maker, } else if (tolerance < 0.0) { FCBRepAlgoAPIHelper::setAutoFuzzy(mk.get()); } +#if OCC_VERSION_HEX >= 0x070600 + mk->Build(OCCTProgressIndicator::getAppIndicator().Start()); +#else mk->Build(); +#endif + if (OCCTProgressIndicator::getAppIndicator().UserBreak()) { + FC_THROWM(Base::CADKernelError, "User aborted"); + } makeElementShape(*mk, inputs, op); if (buildShell) {