From 327222f78099f900c048efd87d64849f798da673 Mon Sep 17 00:00:00 2001 From: wmayer Date: Thu, 22 May 2025 18:11:45 +0200 Subject: [PATCH] Surface: Add task panel for blending curve --- .../Surface/Gui/Blending/TaskBlendCurve.cpp | 432 ++++++++++++++++++ src/Mod/Surface/Gui/Blending/TaskBlendCurve.h | 128 ++++++ .../Surface/Gui/Blending/TaskBlendCurve.ui | 226 +++++++++ .../Gui/Blending/ViewProviderBlendCurve.cpp | 43 ++ .../Gui/Blending/ViewProviderBlendCurve.h | 5 + src/Mod/Surface/Gui/CMakeLists.txt | 3 + 6 files changed, 837 insertions(+) create mode 100644 src/Mod/Surface/Gui/Blending/TaskBlendCurve.cpp create mode 100644 src/Mod/Surface/Gui/Blending/TaskBlendCurve.h create mode 100644 src/Mod/Surface/Gui/Blending/TaskBlendCurve.ui diff --git a/src/Mod/Surface/Gui/Blending/TaskBlendCurve.cpp b/src/Mod/Surface/Gui/Blending/TaskBlendCurve.cpp new file mode 100644 index 0000000000..aa49d5e686 --- /dev/null +++ b/src/Mod/Surface/Gui/Blending/TaskBlendCurve.cpp @@ -0,0 +1,432 @@ +/*************************************************************************** + * Copyright (c) 2025 Werner Mayer * + * * + * 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 +#include +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "TaskBlendCurve.h" +#include "ui_TaskBlendCurve.h" +#include "ViewProviderBlendCurve.h" + + +using namespace SurfaceGui; + +class BlendCurvePanel::EdgeSelection: public Gui::SelectionFilterGate +{ +public: + EdgeSelection(Surface::FeatureBlendCurve* editedObject) + : Gui::SelectionFilterGate(nullPointer()) + , editedObject(editedObject) + {} + ~EdgeSelection() override = default; + /** + * Allow the user to pick only edges. + */ + bool allow(App::Document*, App::DocumentObject* pObj, const char* sSubName) override + { + // don't allow references to itself + if (pObj == editedObject) { + return false; + } + if (!pObj->isDerivedFrom()) { + return false; + } + + if (Base::Tools::isNullOrEmpty(sSubName)) { + return false; + } + + std::string element(sSubName); + return (element.substr(0, 4) == "Edge"); + } + +private: + Surface::FeatureBlendCurve* editedObject; +}; + +// ---------------------------------------------------------------------------- + +BlendCurvePanel::BlendCurvePanel(ViewProviderBlendCurve* vp) + : ui(new Ui_BlendCurve()) + , vp(vp) +{ + ui->setupUi(this); + + initControls(); + setupConnections(); +} + +BlendCurvePanel::~BlendCurvePanel() = default; + +void BlendCurvePanel::setupConnections() +{ + // clang-format off + connect(ui->buttonFirstEdge, + &QToolButton::toggled, + this, + &BlendCurvePanel::onFirstEdgeButton); + connect(ui->buttonSecondEdge, + &QToolButton::toggled, + this, + &BlendCurvePanel::onSecondEdgeButton); + connect(ui->contFirstEdge, + qOverload(&QComboBox::currentIndexChanged), + this, + &BlendCurvePanel::onFirstEdgeContChanged); + connect(ui->contSecondEdge, + qOverload(&QComboBox::currentIndexChanged), + this, + &BlendCurvePanel::onSecondEdgeContChanged); + connect(ui->paramFirstEdge, + qOverload(&QDoubleSpinBox::valueChanged), + this, + &BlendCurvePanel::onFirstEdgeParameterChanged); + connect(ui->paramSecondEdge, + qOverload(&QDoubleSpinBox::valueChanged), + this, + &BlendCurvePanel::onSecondEdgeParameterChanged); + connect(ui->sizeFirstEdge, + qOverload(&QDoubleSpinBox::valueChanged), + this, + &BlendCurvePanel::onFirstEdgeSizeChanged); + connect(ui->sizeSecondEdge, + qOverload(&QDoubleSpinBox::valueChanged), + this, + &BlendCurvePanel::onSecondEdgeSizeChanged); + // clang-format on +} + +void BlendCurvePanel::initControls() +{ + initSubLinks(); + initContinuity(); + initParameter(); + initSize(); +} + +void BlendCurvePanel::initSubLinks() +{ + auto fea = vp->getObject(); + + ui->firstEdgeEdit->setText(linkToString(fea->StartEdge)); + ui->secondEdgeEdit->setText(linkToString(fea->EndEdge)); +} + +void BlendCurvePanel::initContinuity() +{ + auto fea = vp->getObject(); + + constexpr long maxCont = 4; + // clang-format off + ui->contFirstEdge->setCurrentIndex(static_cast(std::min(maxCont, fea->StartContinuity.getValue()))); + ui->contSecondEdge->setCurrentIndex(static_cast(std::min(maxCont, fea->EndContinuity.getValue()))); + // clang-format on +} + +void BlendCurvePanel::initParameter() +{ + auto fea = vp->getObject(); + + const double minPara = fea->StartParameter.getMinimum(); + const double maxPara = fea->StartParameter.getMaximum(); + const double stepsPara = fea->StartParameter.getStepSize(); + ui->paramFirstEdge->setRange(minPara, maxPara); + ui->paramFirstEdge->setSingleStep(stepsPara); + ui->paramSecondEdge->setRange(minPara, maxPara); + ui->paramSecondEdge->setSingleStep(stepsPara); + ui->paramFirstEdge->setValue(fea->StartParameter.getValue()); + ui->paramSecondEdge->setValue(fea->EndParameter.getValue()); +} + +void BlendCurvePanel::initSize() +{ + auto fea = vp->getObject(); + + const double minSize = fea->StartSize.getMinimum(); + const double maxSize = fea->StartSize.getMaximum(); + const double stepsSize = fea->StartSize.getStepSize(); + ui->sizeFirstEdge->setRange(minSize, maxSize); + ui->sizeFirstEdge->setSingleStep(stepsSize); + ui->sizeSecondEdge->setRange(minSize, maxSize); + ui->sizeSecondEdge->setSingleStep(stepsSize); + ui->sizeFirstEdge->setValue(fea->StartSize.getValue()); + ui->sizeSecondEdge->setValue(fea->EndSize.getValue()); +} + +void BlendCurvePanel::onFirstEdgeButton(bool checked) +{ + if (checked) { + onStartSelection(); + selectionMode = StartEdge; + onUncheckSecondEdgeButton(); + } + else { + exitSelectionMode(); + } +} + +void BlendCurvePanel::onSecondEdgeButton(bool checked) +{ + if (checked) { + onStartSelection(); + selectionMode = EndEdge; + onUncheckFirstEdgeButton(); + } + else { + exitSelectionMode(); + } +} + +void BlendCurvePanel::onUncheckFirstEdgeButton() +{ + QSignalBlocker block(ui->buttonFirstEdge); + ui->buttonFirstEdge->setChecked(false); +} + +void BlendCurvePanel::onUncheckSecondEdgeButton() +{ + QSignalBlocker block(ui->buttonSecondEdge); + ui->buttonSecondEdge->setChecked(false); +} + +void BlendCurvePanel::onFirstEdgeContChanged(int index) +{ + if (vp.expired()) { + return; + } + + auto fea = vp->getObject(); + fea->StartContinuity.setValue(index); + fea->recomputeFeature(); +} + +void BlendCurvePanel::onSecondEdgeContChanged(int index) +{ + if (vp.expired()) { + return; + } + + auto fea = vp->getObject(); + fea->EndContinuity.setValue(index); + fea->recomputeFeature(); +} + +void BlendCurvePanel::onFirstEdgeParameterChanged(double value) +{ + if (vp.expired()) { + return; + } + + auto fea = vp->getObject(); + fea->StartParameter.setValue(value); + fea->recomputeFeature(); +} + +void BlendCurvePanel::onSecondEdgeParameterChanged(double value) +{ + if (vp.expired()) { + return; + } + + auto fea = vp->getObject(); + fea->EndParameter.setValue(value); + fea->recomputeFeature(); +} + +void BlendCurvePanel::onFirstEdgeSizeChanged(double value) +{ + if (vp.expired()) { + return; + } + + auto fea = vp->getObject(); + fea->StartSize.setValue(value); + fea->recomputeFeature(); +} + +void BlendCurvePanel::onSecondEdgeSizeChanged(double value) +{ + if (vp.expired()) { + return; + } + + auto fea = vp->getObject(); + fea->EndSize.setValue(value); + fea->recomputeFeature(); +} + +void BlendCurvePanel::onStartSelection() +{ + if (vp.expired()) { + return; + } + + auto gate = new EdgeSelection(vp->getObject()); + Gui::Selection().addSelectionGate(gate); +} + +void BlendCurvePanel::exitSelectionMode() +{ + Gui::Selection().clearSelection(); + Gui::Selection().rmvSelectionGate(); + selectionMode = None; +} + +void BlendCurvePanel::onSelectionChanged(const Gui::SelectionChanges& msg) +{ + if (selectionMode == None) { + return; + } + + if (msg.Type != Gui::SelectionChanges::AddSelection) { + return; + } + + if (selectionMode == StartEdge) { + setStartEdge(msg.Object.getObject(), msg.Object.getSubName()); + onUncheckFirstEdgeButton(); + } + else if (selectionMode == EndEdge) { + setEndEdge(msg.Object.getObject(), msg.Object.getSubName()); + onUncheckSecondEdgeButton(); + } + + QTimer::singleShot(50, this, &BlendCurvePanel::exitSelectionMode); +} + +QString BlendCurvePanel::linkToString(const App::PropertyLinkSub& link) +{ + auto obj = link.getValue(); + const auto& sub = link.getSubValues(); + std::string name = sub.empty() ? "" : sub.front(); + + return QString::fromLatin1("%1 [%2]").arg(QString::fromLatin1(obj->Label.getValue()), + QString::fromStdString(name)); +} + +void BlendCurvePanel::setStartEdge(App::DocumentObject* obj, const std::string& subname) +{ + if (vp.expired()) { + return; + } + + auto fea = vp->getObject(); + fea->StartEdge.setValue(obj, {{subname}}); + fea->recomputeFeature(); + ui->firstEdgeEdit->setText(linkToString(fea->StartEdge)); +} + +void BlendCurvePanel::setEndEdge(App::DocumentObject* obj, const std::string& subname) +{ + if (vp.expired()) { + return; + } + + auto fea = vp->getObject(); + fea->EndEdge.setValue(obj, {{subname}}); + fea->recomputeFeature(); + ui->secondEdgeEdit->setText(linkToString(fea->EndEdge)); +} + +void BlendCurvePanel::changeEvent(QEvent* e) +{ + if (e->type() == QEvent::LanguageChange) { + ui->retranslateUi(this); + } + else { + QWidget::changeEvent(e); + } +} + +void BlendCurvePanel::open() +{ + checkOpenCommand(); + clearSelection(); +} + +void BlendCurvePanel::clearSelection() +{ + Gui::Selection().clearSelection(); +} + +void BlendCurvePanel::checkOpenCommand() +{ + if (!Gui::Command::hasPendingCommand()) { + Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Edit blending curve")); + } +} + +bool BlendCurvePanel::accept() +{ + Gui::cmdGuiDocument(vp->getObject(), "resetEdit()"); + Gui::Command::commitCommand(); + Gui::Command::updateActive(); + return true; +} + +bool BlendCurvePanel::reject() +{ + Gui::cmdGuiDocument(vp->getObject(), "resetEdit()"); + Gui::Command::abortCommand(); + return true; +} + +// ---------------------------------------------------------------------------- + +TaskBlendCurve::TaskBlendCurve(ViewProviderBlendCurve* vp) + : widget {new BlendCurvePanel(vp)} +{ + addTaskBox(Gui::BitmapFactory().pixmap("Surface_BlendCurve"), widget); +} + +void TaskBlendCurve::open() +{ + widget->open(); +} + +bool TaskBlendCurve::accept() +{ + return widget->accept(); +} + +bool TaskBlendCurve::reject() +{ + return widget->reject(); +} + +#include "moc_TaskBlendCurve.cpp" diff --git a/src/Mod/Surface/Gui/Blending/TaskBlendCurve.h b/src/Mod/Surface/Gui/Blending/TaskBlendCurve.h new file mode 100644 index 0000000000..3c98849c70 --- /dev/null +++ b/src/Mod/Surface/Gui/Blending/TaskBlendCurve.h @@ -0,0 +1,128 @@ +/*************************************************************************** + * Copyright (c) 2025 Werner Mayer * + * * + * 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 SURFACEGUI_TASKBLENDCURVE_H +#define SURFACEGUI_TASKBLENDCURVE_H + +#include + +#include +#include +#include + + +class QListWidgetItem; + +namespace Gui +{ +class ButtonGroup; +} + +namespace SurfaceGui +{ + +class ViewProviderBlendCurve; +class Ui_BlendCurve; + +class BlendCurvePanel: public QWidget, public Gui::SelectionObserver +{ + Q_OBJECT + Q_DISABLE_COPY_MOVE(BlendCurvePanel) + +private: + std::unique_ptr ui; + Gui::WeakPtrT vp; + +public: + explicit BlendCurvePanel(ViewProviderBlendCurve* vp); + ~BlendCurvePanel() override; + + void open(); + void checkOpenCommand(); + bool accept(); + bool reject(); + +protected: + void changeEvent(QEvent* e) override; + void onSelectionChanged(const Gui::SelectionChanges& msg) override; + +private: + void setupConnections(); + void initControls(); + void initSubLinks(); + void initContinuity(); + void initParameter(); + void initSize(); + void onFirstEdgeButton(bool checked); + void onSecondEdgeButton(bool checked); + void onUncheckFirstEdgeButton(); + void onUncheckSecondEdgeButton(); + + void onFirstEdgeContChanged(int index); + void onSecondEdgeContChanged(int index); + + void onFirstEdgeParameterChanged(double value); + void onSecondEdgeParameterChanged(double value); + + void onFirstEdgeSizeChanged(double value); + void onSecondEdgeSizeChanged(double value); + + void onStartSelection(); + void clearSelection(); + void exitSelectionMode(); + void setStartEdge(App::DocumentObject* obj, const std::string& subname); + void setEndEdge(App::DocumentObject* obj, const std::string& subname); + static QString linkToString(const App::PropertyLinkSub& link); + + class EdgeSelection; + enum SelectionMode + { + None, + StartEdge, + EndEdge + }; + SelectionMode selectionMode = None; +}; + +class TaskBlendCurve: public Gui::TaskView::TaskDialog +{ + Q_OBJECT + +public: + TaskBlendCurve(ViewProviderBlendCurve* vp); + +public: + void open() override; + bool accept() override; + bool reject() override; + + QDialogButtonBox::StandardButtons getStandardButtons() const override + { + return QDialogButtonBox::Ok | QDialogButtonBox::Cancel; + } + +private: + BlendCurvePanel* widget; +}; + +} // namespace SurfaceGui + +#endif // SURFACEGUI_TASKBLENDCURVE_H diff --git a/src/Mod/Surface/Gui/Blending/TaskBlendCurve.ui b/src/Mod/Surface/Gui/Blending/TaskBlendCurve.ui new file mode 100644 index 0000000000..8b53aef23a --- /dev/null +++ b/src/Mod/Surface/Gui/Blending/TaskBlendCurve.ui @@ -0,0 +1,226 @@ + + + SurfaceGui::BlendCurve + + + + 0 + 0 + 297 + 352 + + + + Blending curve + + + + + + Start edge + + + + + + Edge + + + true + + + + + + + true + + + + + + + Continuity + + + + + + + + C0 + + + + + G1 + + + + + G2 + + + + + G3 + + + + + G4 + + + + + + + + Parameter + + + + + + + 1.000000000000000 + + + 0.050000000000000 + + + + + + + Size + + + + + + + 0.100000000000000 + + + + + + + + + + End edge + + + + + + Edge + + + true + + + + + + + true + + + + + + + Continuity + + + + + + + + C0 + + + + + G1 + + + + + G2 + + + + + G3 + + + + + G4 + + + + + + + + Parameter + + + + + + + 1.000000000000000 + + + 0.050000000000000 + + + + + + + Size + + + + + + + 0.100000000000000 + + + + + + + + + + + Gui::DoubleSpinBox + QDoubleSpinBox +
Gui/SpinBox.h
+
+
+ + buttonFirstEdge + firstEdgeEdit + contFirstEdge + paramFirstEdge + sizeFirstEdge + buttonSecondEdge + secondEdgeEdit + contSecondEdge + paramSecondEdge + sizeSecondEdge + + + +
diff --git a/src/Mod/Surface/Gui/Blending/ViewProviderBlendCurve.cpp b/src/Mod/Surface/Gui/Blending/ViewProviderBlendCurve.cpp index 72f73dd9ad..51bf3c159d 100644 --- a/src/Mod/Surface/Gui/Blending/ViewProviderBlendCurve.cpp +++ b/src/Mod/Surface/Gui/Blending/ViewProviderBlendCurve.cpp @@ -21,9 +21,15 @@ ***************************************************************************/ #include "PreCompiled.h" +#ifndef _PreComp_ +#include +#endif #include "ViewProviderBlendCurve.h" +#include "TaskBlendCurve.h" +#include #include +#include PROPERTY_SOURCE(SurfaceGui::ViewProviderBlendCurve, PartGui::ViewProviderSpline) @@ -35,4 +41,41 @@ QIcon ViewProviderBlendCurve::getIcon() const return Gui::BitmapFactory().pixmap("Surface_BlendCurve"); } +void ViewProviderBlendCurve::setupContextMenu(QMenu* menu, QObject* receiver, const char* member) +{ + auto func = new Gui::ActionFunction(menu); + QAction* act = menu->addAction( + QObject::tr("Edit %1").arg(QString::fromUtf8(getObject()->Label.getValue()))); + act->setData(QVariant((int)ViewProvider::Default)); + func->trigger(act, [this]() { + this->startDefaultEditMode(); + }); + + ViewProviderSpline::setupContextMenu(menu, receiver, member); +} + +bool ViewProviderBlendCurve::setEdit(int ModNum) +{ + if (ModNum == ViewProvider::Default) { + if (Gui::Control().activeDialog()) { + return false; + } + auto dlg = new TaskBlendCurve(this); + Gui::Control().showDialog(dlg); + return true; + } + + return ViewProviderSpline::setEdit(ModNum); +} + +void ViewProviderBlendCurve::unsetEdit(int ModNum) +{ + if (ModNum == ViewProvider::Default) { + Gui::Control().closeDialog(); + } + else { + ViewProviderSpline::unsetEdit(ModNum); + } +} + } // namespace SurfaceGui diff --git a/src/Mod/Surface/Gui/Blending/ViewProviderBlendCurve.h b/src/Mod/Surface/Gui/Blending/ViewProviderBlendCurve.h index f5ce6d186e..ce866d07e7 100644 --- a/src/Mod/Surface/Gui/Blending/ViewProviderBlendCurve.h +++ b/src/Mod/Surface/Gui/Blending/ViewProviderBlendCurve.h @@ -34,6 +34,11 @@ class ViewProviderBlendCurve: public PartGui::ViewProviderSpline public: QIcon getIcon() const override; + void setupContextMenu(QMenu* menu, QObject* receiver, const char* member) override; + +protected: + bool setEdit(int ModNum) override; + void unsetEdit(int ModNum) override; }; } // namespace SurfaceGui diff --git a/src/Mod/Surface/Gui/CMakeLists.txt b/src/Mod/Surface/Gui/CMakeLists.txt index a32f3500b2..301ba27cad 100644 --- a/src/Mod/Surface/Gui/CMakeLists.txt +++ b/src/Mod/Surface/Gui/CMakeLists.txt @@ -20,9 +20,12 @@ SET(SurfaceGui_UIC_SRCS TaskFillingVertex.ui TaskGeomFillSurface.ui TaskSections.ui + Blending/TaskBlendCurve.ui ) SET(BlendingGui_SRCS + Blending/TaskBlendCurve.cpp + Blending/TaskBlendCurve.h Blending/ViewProviderBlendCurve.cpp Blending/ViewProviderBlendCurve.h )