diff --git a/src/Mod/PartDesign/App/FeatureBoolean.cpp b/src/Mod/PartDesign/App/FeatureBoolean.cpp index b2b42feebe..3338112517 100644 --- a/src/Mod/PartDesign/App/FeatureBoolean.cpp +++ b/src/Mod/PartDesign/App/FeatureBoolean.cpp @@ -164,21 +164,55 @@ App::DocumentObjectExecReturn *Boolean::execute() } this->Shape.setValue(getSolid(result)); - return App::DocumentObject::StdReturn; + + return StdReturn; +} + +void Boolean::updatePreviewShape() +{ + if (strcmp(Type.getValueAsString(), "Cut") == 0) { + TopoShape base = getBaseTopoShape(true).moved(getLocation().Inverted()); + TopoShape result = Shape.getShape(); + + PreviewShape.setValue(base.makeElementCut(result.getShape())); + return; + } + + if (strcmp(Type.getValueAsString(), "Fuse") == 0) { + std::vector shapes; + + for (auto& obj : Group.getValues()) { + shapes.push_back(getTopoShape(obj, Part::ShapeOption::ResolveLink | Part::ShapeOption::Transform)); + } + + TopoShape result; + result.makeCompound(shapes); + + PreviewShape.setValue(result.getShape()); + return; + } + + PreviewShape.setValue(Shape.getShape()); } void Boolean::onChanged(const App::Property* prop) { - if(strcmp(prop->getName(), "Group") == 0) + if (strcmp(prop->getName(), "Group") == 0) { touch(); + } - PartDesign::Feature::onChanged(prop); + if (strcmp(prop->getName(), "Shape") == 0) { + updatePreviewShape(); + } + + Feature::onChanged(prop); } void Boolean::handleChangedPropertyName(Base::XMLReader &reader, const char * TypeName, const char *PropName) { // The App::PropertyLinkList property was Bodies in the past Base::Type type = Base::Type::fromName(TypeName); + if (Group.getClassTypeId() == type && strcmp(PropName, "Bodies") == 0) { Group.Restore(reader); } diff --git a/src/Mod/PartDesign/App/FeatureBoolean.h b/src/Mod/PartDesign/App/FeatureBoolean.h index 5fcffdac2b..686e312f5f 100644 --- a/src/Mod/PartDesign/App/FeatureBoolean.h +++ b/src/Mod/PartDesign/App/FeatureBoolean.h @@ -52,6 +52,7 @@ public: //@{ /// Recalculate the feature App::DocumentObjectExecReturn *execute() override; + void updatePreviewShape() override; short mustExecute() const override; /// returns the type name of the view provider const char* getViewProviderName() const override { diff --git a/src/Mod/PartDesign/Gui/StyleParameters.h b/src/Mod/PartDesign/Gui/StyleParameters.h index 7d54f4178c..8e35383d01 100644 --- a/src/Mod/PartDesign/Gui/StyleParameters.h +++ b/src/Mod/PartDesign/Gui/StyleParameters.h @@ -29,6 +29,7 @@ namespace PartDesignGui::StyleParameters { DEFINE_STYLE_PARAMETER(PreviewAdditiveColor, Base::Color(0.0F, 1.0F, 0.6F)); DEFINE_STYLE_PARAMETER(PreviewSubtractiveColor, Base::Color(1.0F, 0.0F, 0.0F)); + DEFINE_STYLE_PARAMETER(PreviewCommonColor, Base::Color(1.0F, 1.0F, 0.0F)); DEFINE_STYLE_PARAMETER(PreviewDressUpColor, Base::Color(1.0F, 0.0F, 1.0F)); DEFINE_STYLE_PARAMETER(PreviewErrorColor, Base::Color(1.0F, 0.0F, 0.0F)); diff --git a/src/Mod/PartDesign/Gui/TaskBooleanParameters.cpp b/src/Mod/PartDesign/Gui/TaskBooleanParameters.cpp index 6af54e3ef7..c1cb0ea8ce 100644 --- a/src/Mod/PartDesign/Gui/TaskBooleanParameters.cpp +++ b/src/Mod/PartDesign/Gui/TaskBooleanParameters.cpp @@ -352,13 +352,14 @@ void TaskBooleanParameters::exitSelectionMode() //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ TaskDlgBooleanParameters::TaskDlgBooleanParameters(ViewProviderBoolean* BooleanView) - : TaskDialog() + : TaskDlgFeatureParameters(BooleanView) , BooleanView(BooleanView) { assert(BooleanView); parameter = new TaskBooleanParameters(BooleanView); Content.push_back(parameter); + Content.push_back(preview); } TaskDlgBooleanParameters::~TaskDlgBooleanParameters() = default; diff --git a/src/Mod/PartDesign/Gui/TaskBooleanParameters.h b/src/Mod/PartDesign/Gui/TaskBooleanParameters.h index 85d4b04378..526837447a 100644 --- a/src/Mod/PartDesign/Gui/TaskBooleanParameters.h +++ b/src/Mod/PartDesign/Gui/TaskBooleanParameters.h @@ -24,6 +24,9 @@ #ifndef GUI_TASKVIEW_TaskBooleanParameters_H #define GUI_TASKVIEW_TaskBooleanParameters_H +#include "TaskFeatureParameters.h" + + #include #include @@ -78,7 +81,7 @@ private: }; /// simulation dialog for the TaskView -class TaskDlgBooleanParameters : public Gui::TaskView::TaskDialog +class TaskDlgBooleanParameters : public TaskDlgFeatureParameters { Q_OBJECT diff --git a/src/Mod/PartDesign/Gui/ViewProviderBoolean.cpp b/src/Mod/PartDesign/Gui/ViewProviderBoolean.cpp index b5bd5a0938..7346f45e42 100644 --- a/src/Mod/PartDesign/Gui/ViewProviderBoolean.cpp +++ b/src/Mod/PartDesign/Gui/ViewProviderBoolean.cpp @@ -1,6 +1,7 @@ /*************************************************************************** * Copyright (c) 2013 Jan Rheinländer * * * + * Copyright (c) 2025 Kacper Donat * * * * This file is part of the FreeCAD CAx development system. * * * @@ -26,30 +27,39 @@ #ifndef _PreComp_ # include -# include +# include #endif #include "ViewProviderBoolean.h" + +#include "StyleParameters.h" #include "TaskBooleanParameters.h" + +#include #include #include #include #include -#include #include +#include +#include +#include using namespace PartDesignGui; PROPERTY_SOURCE_WITH_EXTENSIONS(PartDesignGui::ViewProviderBoolean,PartDesignGui::ViewProvider) -const char* PartDesignGui::ViewProviderBoolean::DisplayEnum[] = {"Result","Tools",nullptr}; +const char* PartDesignGui::ViewProviderBoolean::DisplayEnum[] = {"Result", "Tools", nullptr}; ViewProviderBoolean::ViewProviderBoolean() + : pcToolsPreview(new SoGroup) + , pcBasePreviewToggle(new SoToggleSwitch) { sPixmap = "PartDesign_Boolean.svg"; - Gui::ViewProviderGeoFeatureGroupExtension::initExtension(this); + + ViewProviderGeoFeatureGroupExtension::initExtension(this); ADD_PROPERTY(Display,((long)0)); Display.setEnums(DisplayEnum); @@ -57,62 +67,19 @@ ViewProviderBoolean::ViewProviderBoolean() ViewProviderBoolean::~ViewProviderBoolean() = default; - void ViewProviderBoolean::setupContextMenu(QMenu* menu, QObject* receiver, const char* member) { addDefaultAction(menu, QObject::tr("Edit Boolean")); - PartDesignGui::ViewProvider::setupContextMenu(menu, receiver, member); -} -bool ViewProviderBoolean::setEdit(int ModNum) -{ - if (ModNum == ViewProvider::Default ) { - // When double-clicking on the item for this fillet the - // object unsets and sets its edit mode without closing - // the task panel - Gui::TaskView::TaskDialog *dlg = Gui::Control().activeDialog(); - TaskDlgBooleanParameters *booleanDlg = qobject_cast(dlg); - if (booleanDlg && booleanDlg->getBooleanView() != this) - booleanDlg = nullptr; // another pad left open its task panel - if (dlg && !booleanDlg) { - QMessageBox msgBox(Gui::getMainWindow()); - msgBox.setText(QObject::tr("A dialog is already open in the task panel")); - msgBox.setInformativeText(QObject::tr("Close this dialog?")); - msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); - msgBox.setDefaultButton(QMessageBox::Yes); - int ret = msgBox.exec(); - if (ret == QMessageBox::Yes) - Gui::Control().closeDialog(); - else - return false; - } - - // clear the selection (convenience) - Gui::Selection().clearSelection(); - - // always change to PartDesign WB, remember where we come from - oldWb = Gui::Command::assureWorkbench("PartDesignWorkbench"); - - // start the edit dialog - if (booleanDlg) - Gui::Control().showDialog(booleanDlg); - else - Gui::Control().showDialog(new TaskDlgBooleanParameters(this)); - - return true; - } - else { - return PartGui::ViewProviderPart::setEdit(ModNum); // clazy:exclude=skipped-base-method - } + ViewProvider::setupContextMenu(menu, receiver, member); } bool ViewProviderBoolean::onDelete(const std::vector &s) { - PartDesign::Boolean* pcBoolean = getObject(); + auto* feature = getObject(); // if abort command deleted the object the bodies are visible again - std::vector bodies = pcBoolean->Group.getValues(); - for (auto body : bodies) { + for (auto body : feature->Group.getValues()) { if (auto vp = Gui::Application::Instance->getViewProvider(body)) { vp->show(); } @@ -121,11 +88,6 @@ bool ViewProviderBoolean::onDelete(const std::vector &s) return ViewProvider::onDelete(s); } -void ViewProviderBoolean::attach(App::DocumentObject* obj) -{ - PartGui::ViewProviderPartExt::attach(obj); -} - const char* ViewProviderBoolean::getDefaultDisplayMode() const { return "Flat Lines"; @@ -133,18 +95,140 @@ const char* ViewProviderBoolean::getDefaultDisplayMode() const void ViewProviderBoolean::onChanged(const App::Property* prop) { - PartDesignGui::ViewProvider::onChanged(prop); + ViewProvider::onChanged(prop); - if(prop == &Display) { + if (prop == &Display) { + const auto getDisplayMode = [this]() { + if (Display.getValue() != 0) { + return "Group"; + } - if(Display.getValue() == 0) { - auto vp = getBodyViewProvider(); - if(vp) - setDisplayMode(vp->DisplayMode.getValueAsString()); - else - setDisplayMode("Flat Lines"); - } else { - setDisplayMode("Group"); - } + if (auto bodyViewProvider = getBodyViewProvider()) { + return bodyViewProvider->DisplayMode.getValueAsString(); + } + + return getDefaultDisplayMode(); + }; + + setDisplayMode(getDisplayMode()); + } + + if (prop == &Visibility) { + updateBasePreviewVisibility(); } } + +void ViewProviderBoolean::updateData(const App::Property* prop) +{ + auto feature = getObject(); + + if (prop == &feature->Type) { + const auto* styleParameterManager = Base::provideService(); + const auto type = feature->Type.getValueAsString(); + + const std::map> lookup { + {"Cut", StyleParameters::PreviewSubtractiveColor}, + {"Common", StyleParameters::PreviewCommonColor}, + {"Fuse", StyleParameters::PreviewAdditiveColor}, + }; + + if (lookup.contains(type)) { + PreviewColor.setValue(styleParameterManager->resolve(lookup.at(type))); + } + + updateBasePreviewVisibility(); + } + + ViewProvider::updateData(prop); +} + +void ViewProviderBoolean::attachPreview() +{ + ViewProvider::attachPreview(); + + pcPreviewRoot->addChild(this->pcToolsPreview); + pcPreviewRoot->addChild(this->pcBasePreviewToggle); +} + +void ViewProviderBoolean::updatePreview() +{ + const auto* styleParameterManager = Base::provideService(); + const float toolTransparency = static_cast(styleParameterManager->resolve(StyleParameters::PreviewToolTransparency).value); + + auto boolean = getObject(); + + if (!boolean) { + return; + } + + const auto addToolPreview = [this, toolTransparency](App::DocumentObject* tool) { + const auto feature = freecad_cast(tool); + + if (!feature) { + return; + } + + Part::TopoShape toolShape = feature->Shape.getShape(); + + auto pcToolPreview = new PartGui::SoPreviewShape; + updatePreviewShape(toolShape, pcToolPreview); + + pcToolPreview->transparency.setValue(toolTransparency); + pcToolPreview->color.connectFrom(&pcPreviewShape->color); + pcToolPreview->lineWidth.connectFrom(&pcPreviewShape->lineWidth); + + pcToolsPreview->addChild(pcToolPreview); + }; + + const auto addBaseShapePreview = [this, toolTransparency, boolean]() { + auto baseFeature = dynamic_cast(boolean->BaseFeature.getValue()); + if (!baseFeature) { + return; + } + + auto baseFeatureViewProvider = freecad_cast(Gui::Application::Instance->getViewProvider(baseFeature)); + if (!baseFeatureViewProvider) { + return; + } + + auto pcBaseShapePreview = new PartGui::SoPreviewShape; + updatePreviewShape(baseFeature->Shape.getShape(), pcBaseShapePreview); + + pcBaseShapePreview->transparency.setValue(toolTransparency); + pcBaseShapePreview->color.setValue(baseFeatureViewProvider->ShapeAppearance.getDiffuseColor().asValue()); + pcBaseShapePreview->lineWidth.connectFrom(&pcPreviewShape->lineWidth); + + pcBasePreviewToggle->addChild(pcBaseShapePreview); + }; + + try { + const auto& tools = boolean->Group.getValues(); + + if (tools.empty()) { + return; + } + + Gui::coinRemoveAllChildren(pcToolsPreview); + Gui::coinRemoveAllChildren(pcBasePreviewToggle); + + addBaseShapePreview(); + std::ranges::for_each(tools, addToolPreview); + } catch (const Base::Exception& e) { + e.reportException(); + } + + ViewProvider::updatePreview(); +} + +TaskDlgFeatureParameters* ViewProviderBoolean::getEditDialog() +{ + return new TaskDlgBooleanParameters(this); +} + +void ViewProviderBoolean::updateBasePreviewVisibility() +{ + auto feature = getObject(); + + // enable base preview for Common operation only and when the final result is shown + pcBasePreviewToggle->on = strcmp(feature->Type.getValueAsString(), "Common") == 0 && Visibility.getValue(); +} diff --git a/src/Mod/PartDesign/Gui/ViewProviderBoolean.h b/src/Mod/PartDesign/Gui/ViewProviderBoolean.h index 1f8a114d0a..d7532e5fbe 100644 --- a/src/Mod/PartDesign/Gui/ViewProviderBoolean.h +++ b/src/Mod/PartDesign/Gui/ViewProviderBoolean.h @@ -1,6 +1,7 @@ /*************************************************************************** * Copyright (c) 2013 Jan Rheinländer * * * + * Copyright (c) 2025 Kacper Donat * * * * This file is part of the FreeCAD CAx development system. * * * @@ -27,6 +28,7 @@ #include "ViewProvider.h" #include +#include namespace PartDesignGui { @@ -48,15 +50,24 @@ public: void setupContextMenu(QMenu*, QObject*, const char*) override; bool onDelete(const std::vector &) override; - void attach(App::DocumentObject*) override; const char* getDefaultDisplayMode() const override; void onChanged(const App::Property* prop) override; protected: - bool setEdit(int ModNum) override; + void updateData(const App::Property* prop) override; + + void attachPreview() override; + void updatePreview() override; + + TaskDlgFeatureParameters* getEditDialog() override; static const char* DisplayEnum[]; +private: + void updateBasePreviewVisibility(); + + Gui::CoinPtr pcToolsPreview; + Gui::CoinPtr pcBasePreviewToggle; }; } // namespace PartDesignGui