// SPDX-License-Identifier: LGPL-2.1-or-later /*************************************************************************** * 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 #include #include #include #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: explicit EdgeSelection(Surface::FeatureBlendCurve* editedObject) : Gui::SelectionFilterGate(nullPointer()) , editedObject(editedObject) {} ~EdgeSelection() override = default; EdgeSelection(const EdgeSelection&) = delete; EdgeSelection& operator=(const EdgeSelection&) = delete; EdgeSelection(EdgeSelection&&) = delete; EdgeSelection& operator=(EdgeSelection&&) = delete; /** * Allow the user to pick only edges. */ bool allow(App::Document* doc, App::DocumentObject* pObj, const char* sSubName) override { (void)doc; // 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(); bindProperties(); } 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::bindProperties() { auto fea = vp->getObject(); ui->paramFirstEdge->bind(fea->StartParameter); ui->sizeFirstEdge->bind(fea->StartSize); ui->paramSecondEdge->bind(fea->EndParameter); ui->sizeSecondEdge->bind(fea->EndSize); } 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::Command::abortCommand(); Gui::cmdGuiDocument(vp->getObject(), "resetEdit()"); Gui::Command::updateActive(); 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(); }