From 5d2037c820584c99a09427576f967e5ad869f1bf Mon Sep 17 00:00:00 2001 From: PaddleStroke Date: Sun, 10 Aug 2025 16:06:01 +0200 Subject: [PATCH] PartDesign: Transform rework --- src/Gui/ComboLinks.cpp | 75 ++- src/Gui/ComboLinks.h | 16 +- src/Mod/Part/Gui/PatternParametersWidget.cpp | 182 ++++---- src/Mod/Part/Gui/PatternParametersWidget.h | 17 +- src/Mod/Part/Gui/PatternParametersWidget.ui | 198 ++++---- .../PartDesign/App/FeatureLinearPattern.cpp | 335 +++++++++++--- src/Mod/PartDesign/App/FeatureLinearPattern.h | 35 +- .../PartDesign/App/FeaturePolarPattern.cpp | 96 +++- src/Mod/PartDesign/App/FeaturePolarPattern.h | 9 +- src/Mod/PartDesign/Gui/CMakeLists.txt | 12 +- src/Mod/PartDesign/Gui/Command.cpp | 1 + .../Gui/TaskLinearPatternParameters.cpp | 438 ------------------ .../Gui/TaskLinearPatternParameters.ui | 188 -------- .../PartDesign/Gui/TaskMirroredParameters.cpp | 35 +- .../Gui/TaskMultiTransformParameters.cpp | 19 +- .../PartDesign/Gui/TaskPatternParameters.cpp | 383 +++++++++++++++ ...rnParameters.h => TaskPatternParameters.h} | 74 ++- .../PartDesign/Gui/TaskPatternParameters.ui | 39 ++ .../Gui/TaskPolarPatternParameters.cpp | 432 ----------------- .../Gui/TaskPolarPatternParameters.h | 111 ----- .../Gui/TaskPolarPatternParameters.ui | 200 -------- .../Gui/TaskTransformedParameters.cpp | 9 +- .../Gui/TaskTransformedParameters.h | 4 +- .../Gui/ViewProviderLinearPattern.cpp | 2 +- .../Gui/ViewProviderPolarPattern.cpp | 4 +- 25 files changed, 1097 insertions(+), 1817 deletions(-) delete mode 100644 src/Mod/PartDesign/Gui/TaskLinearPatternParameters.cpp delete mode 100644 src/Mod/PartDesign/Gui/TaskLinearPatternParameters.ui create mode 100644 src/Mod/PartDesign/Gui/TaskPatternParameters.cpp rename src/Mod/PartDesign/Gui/{TaskLinearPatternParameters.h => TaskPatternParameters.h} (58%) create mode 100644 src/Mod/PartDesign/Gui/TaskPatternParameters.ui delete mode 100644 src/Mod/PartDesign/Gui/TaskPolarPatternParameters.cpp delete mode 100644 src/Mod/PartDesign/Gui/TaskPolarPatternParameters.h delete mode 100644 src/Mod/PartDesign/Gui/TaskPolarPatternParameters.ui diff --git a/src/Gui/ComboLinks.cpp b/src/Gui/ComboLinks.cpp index 7e1f3ebf40..a1dc242f35 100644 --- a/src/Gui/ComboLinks.cpp +++ b/src/Gui/ComboLinks.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: LGPL-2.1-or-later /**************************************************************************** - * * + * Copyright (c) 2012 Jan Rheinländer jrheinlaender@users.sourceforge.net * * Copyright (c) 2025 AstoCAD * * * * This file is part of FreeCAD. * @@ -36,35 +36,34 @@ namespace Gui { - ComboLinks::ComboLinks(QComboBox& combo) - : _combo(&combo) + ComboLinks::ComboLinks(QComboBox* combo) + : combo(combo) { - if (_combo) { - _combo->clear(); + if (combo) { + combo->clear(); } } ComboLinks::~ComboLinks() { clear(); // Deletes owned pointers in linksInList - _combo = nullptr; // Don't delete, not owned } - void ComboLinks::setCombo(QComboBox& combo) + void ComboLinks::setCombo(QComboBox* combobox) { clear(); // Clear old state if any - _combo = &combo; - if (_combo) { - _combo->clear(); + combo = combobox; + if (combo) { + combo->clear(); } } int ComboLinks::addLink(const App::PropertyLinkSub& lnk, const QString& itemText, int userData) { - if (!_combo) { + if (!combo) { return -1; } - int newIndex = _combo->count(); + int newIndex = combo->count(); int finalUserData = (userData == -1) ? newIndex : userData; // Store link internally (create a copy) @@ -73,7 +72,7 @@ namespace Gui { linksInList.push_back(newLink); // Add to combo box - _combo->addItem(itemText, QVariant(finalUserData)); + combo->addItem(itemText, QVariant(finalUserData)); // Track document context from the first valid object link if (!doc && newLink->getValue()) { @@ -96,13 +95,13 @@ namespace Gui { int ComboLinks::addLinkBefore(const App::PropertyLinkSub& lnk, const QString& itemText, int targetUserData, int userData) { - if (!_combo) { + if (!combo) { return -1; } int insertPos = -1; - for (int i = 0; i < _combo->count(); ++i) { - if (_combo->itemData(i).toInt() == targetUserData) { + for (int i = 0; i < combo->count(); ++i) { + if (combo->itemData(i).toInt() == targetUserData) { insertPos = i; break; } @@ -116,20 +115,20 @@ namespace Gui { if (insertPos != -1) { linksInList.insert(linksInList.begin() + insertPos, newLink); - _combo->insertItem(insertPos, itemText, QVariant(finalUserData)); + combo->insertItem(insertPos, itemText, QVariant(finalUserData)); } else { // Target not found, append to end - insertPos = _combo->count(); + insertPos = combo->count(); linksInList.push_back(newLink); - _combo->addItem(itemText, QVariant(finalUserData)); + combo->addItem(itemText, QVariant(finalUserData)); } // Update user data for subsequent items if default (-1) was used and inserting if (userData == -1 && insertPos != -1 && insertPos < count() - 1) { for (int i = insertPos + 1; i < count(); ++i) { - if (_combo->itemData(i).toInt() == i - 1) { // Check if it was using default index - _combo->setItemData(i, QVariant(i)); + if (combo->itemData(i).toInt() == i - 1) { // Check if it was using default index + combo->setItemData(i, QVariant(i)); } } } @@ -145,12 +144,10 @@ namespace Gui { void ComboLinks::clear() { - if (_combo) { + if (combo) { // Block signals while clearing to prevent issues if connected elsewhere - bool wasBlocked = _combo->signalsBlocked(); - _combo->blockSignals(true); - _combo->clear(); - _combo->blockSignals(wasBlocked); + QSignalBlocker blocker(combo); + combo->clear(); } for (App::PropertyLinkSub* linkPtr : linksInList) { delete linkPtr; // Delete the objects pointed to @@ -174,41 +171,39 @@ namespace Gui { App::PropertyLinkSub& ComboLinks::getCurrentLink() const { - assert(_combo); - return getLink(_combo->currentIndex()); + assert(combo); + return getLink(combo->currentIndex()); } int ComboLinks::getUserData(int index) const { - if (!_combo || index < 0 || index >= _combo->count()) { + if (!combo || index < 0 || index >= combo->count()) { return -1; // Indicate invalid index or no combo } - return _combo->itemData(index).toInt(); + return combo->itemData(index).toInt(); } int ComboLinks::getCurrentUserData() const { - if (!_combo) { + if (!combo) { return -1; } - return _combo->currentData().toInt(); + return combo->currentData().toInt(); } int ComboLinks::setCurrentLink(const App::PropertyLinkSub& lnk) { - if (!_combo) { + if (!combo) { return -1; } for (size_t i = 0; i < linksInList.size(); ++i) { - const App::PropertyLinkSub& it = *(linksInList[i]); + const App::PropertyLinkSub* it = linksInList[i]; // Compare object pointer and sub-values vector - if (lnk.getValue() == it.getValue() && lnk.getSubValues() == it.getSubValues()) { - bool wasBlocked = _combo->signalsBlocked(); - _combo->blockSignals(true); - _combo->setCurrentIndex(static_cast(i)); - _combo->blockSignals(wasBlocked); + if (lnk.getValue() == it->getValue() && lnk.getSubValues() == it->getSubValues()) { + QSignalBlocker blocker(combo); + combo->setCurrentIndex(static_cast(i)); return static_cast(i); } } @@ -217,7 +212,7 @@ namespace Gui { int ComboLinks::count() const { - return _combo ? _combo->count() : 0; + return combo ? combo->count() : 0; } } // namespace Gui diff --git a/src/Gui/ComboLinks.h b/src/Gui/ComboLinks.h index cbc8939a68..b3361fa4d9 100644 --- a/src/Gui/ComboLinks.h +++ b/src/Gui/ComboLinks.h @@ -27,8 +27,8 @@ #include #include -#include // For PropertyLinkSub -#include // For exceptions +#include +#include // Forward declarations class QComboBox; @@ -53,7 +53,7 @@ namespace Gui { * @param combo The combo box to manage. It will be cleared upon binding. * Do not add/remove items directly to/from the combo after binding. */ - explicit ComboLinks(QComboBox& combo); + explicit ComboLinks(QComboBox* combo); /** * @brief Default constructor. Use setCombo() later. @@ -65,17 +65,13 @@ namespace Gui { */ ~ComboLinks(); - // Disable copy/move semantics for simplicity with pointer management - ComboLinks(const ComboLinks&) = delete; - ComboLinks& operator=(const ComboLinks&) = delete; - ComboLinks(ComboLinks&&) = delete; - ComboLinks& operator=(ComboLinks&&) = delete; + FC_DISABLE_COPY_MOVE(ComboLinks) /** * @brief Binds the helper to a QComboBox. Clears the combo box. * @param combo The combo box to manage. */ - void setCombo(QComboBox& combo); + void setCombo(QComboBox* combo); /** * @brief Adds an item to the combo box associated with a PropertyLinkSub. @@ -162,7 +158,7 @@ namespace Gui { private: - QComboBox* _combo = nullptr; + QComboBox* combo = nullptr; App::Document* doc = nullptr; // Document context for validation std::vector linksInList; // Owned pointers }; diff --git a/src/Mod/Part/Gui/PatternParametersWidget.cpp b/src/Mod/Part/Gui/PatternParametersWidget.cpp index 50201ab499..04c7d4ca4e 100644 --- a/src/Mod/Part/Gui/PatternParametersWidget.cpp +++ b/src/Mod/Part/Gui/PatternParametersWidget.cpp @@ -29,13 +29,17 @@ #include #include #include +#include #endif #include "ui_PatternParametersWidget.h" #include "PatternParametersWidget.h" +#include #include #include +#include +#include #include #include #include @@ -54,27 +58,27 @@ PatternParametersWidget::PatternParametersWidget(PatternType type, QWidget* pare connectSignals(); } -PatternParametersWidget::~PatternParametersWidget() -{ - // ui is managed by unique_ptr - // Bound properties are managed externally - // Dynamic spacing rows are deleted by clearDynamicSpacingRows or Qt's parent mechanism -} +PatternParametersWidget::~PatternParametersWidget() = default; void PatternParametersWidget::setupUiElements() { // Configure UI elements if needed (e.g., icons for mode) - QIcon icon1 = Gui::BitmapFactory().iconFromTheme("Part_LinearPattern_extent"); - QIcon icon2 = Gui::BitmapFactory().iconFromTheme("Part_LinearPattern_spacing"); - ui->comboMode->setItemIcon(0, icon1); - ui->comboMode->setItemIcon(1, icon2); + QIcon iconExtent = Gui::BitmapFactory().iconFromTheme("Part_LinearPattern_extent"); + QIcon iconSpacing = Gui::BitmapFactory().iconFromTheme("Part_LinearPattern_spacing"); + + ui->comboMode->setItemIcon(0, iconExtent); + ui->comboMode->setItemIcon(1, iconSpacing); if (type == PatternType::Polar) { setTitle(tr("Axis")); } // Set combo box helper - dirLinks.setCombo(*(ui->comboDirection)); + dirLinks.setCombo(ui->comboDirection); + + ParameterGrp::handle hPart = App::GetApplication().GetParameterGroupByPath( + "User parameter:BaseApp/Preferences/Mod/Part"); + ui->addSpacingButton->setVisible(hPart->GetBool("ExperimentalFeatures", false)); } void PatternParametersWidget::connectSignals() @@ -100,23 +104,23 @@ void PatternParametersWidget::connectSignals() // Note: Connections for dynamic rows are done in addSpacingRow() } -void PatternParametersWidget::bindProperties(App::PropertyLinkSub& directionProp, - App::PropertyBool& reversedProp, - App::PropertyEnumeration& modeProp, - App::PropertyQuantity& lengthProp, - App::PropertyQuantity& offsetProp, - App::PropertyFloatList& spacingPatternProp, - App::PropertyIntegerConstraint& occurrencesProp, +void PatternParametersWidget::bindProperties(App::PropertyLinkSub* directionProp, + App::PropertyBool* reversedProp, + App::PropertyEnumeration* modeProp, + App::PropertyQuantity* lengthProp, + App::PropertyQuantity* offsetProp, + App::PropertyFloatList* spacingPatternProp, + App::PropertyIntegerConstraint* occurrencesProp, App::DocumentObject* feature) { // Store pointers to the properties - m_directionProp = &directionProp; - m_reversedProp = &reversedProp; - m_modeProp = &modeProp; - m_extentProp = &lengthProp; - m_spacingProp = &offsetProp; - m_spacingPatternProp = &spacingPatternProp; - m_occurrencesProp = &occurrencesProp; + m_directionProp = directionProp; + m_reversedProp = reversedProp; + m_modeProp = modeProp; + m_extentProp = lengthProp; + m_spacingProp = offsetProp; + m_spacingPatternProp = spacingPatternProp; + m_occurrencesProp = occurrencesProp; m_feature = feature; // Store feature for context (units, etc.) ui->spinExtent->bind(*m_extentProp); @@ -152,15 +156,15 @@ void PatternParametersWidget::updateUI() if (blockUpdate || !m_feature) { // Need properties to be bound return; } - blockUpdate = true; + Base::StateLocker locker(blockUpdate, true); // Update direction combo if (dirLinks.setCurrentLink(*m_directionProp) == -1) { // failed to set current, because the link isn't in the list yet if (m_directionProp->getValue()) { - QString refStr = QString::fromLatin1(m_directionProp->getValue()->getNameInDocument()) + QStringLiteral(":") + - QString::fromLatin1(m_directionProp->getSubValues().front().c_str()); - + QString refStr = + QStringLiteral("%1:%2").arg(QString::fromLatin1(m_directionProp->getValue()->getNameInDocument()), + QString::fromLatin1(m_directionProp->getSubValues().front().c_str())); dirLinks.addLink(*m_directionProp, refStr); dirLinks.setCurrentLink(*m_directionProp); } @@ -175,21 +179,22 @@ void PatternParametersWidget::updateUI() rebuildDynamicSpacingUI(); adaptVisibilityToMode(); - - blockUpdate = false; } void PatternParametersWidget::adaptVisibilityToMode() { - if (!m_modeProp) return; + if (!m_modeProp) { + return; + } // Use the enum names defined in FeatureLinearPattern.h auto mode = static_cast(m_modeProp->getValue()); - ui->extentWrapper->setVisible(mode == PartGui::PatternMode::Extent); - ui->spacingWrapper->setVisible(mode == PartGui::PatternMode::Spacing); - ui->spacingPatternWidget->setVisible(mode == PartGui::PatternMode::Spacing); - ui->addSpacingButton->setVisible(mode == PartGui::PatternMode::Spacing); + ui->formLayout->labelForField(ui->spinExtent)->setVisible(mode == PartGui::PatternMode::Extent); + ui->spinExtent->setVisible(mode == PartGui::PatternMode::Extent); + ui->formLayout->labelForField(ui->spacingControlsWidget) + ->setVisible(mode == PartGui::PatternMode::Spacing); + ui->spacingControlsWidget->setVisible(mode == PartGui::PatternMode::Spacing); } const App::PropertyLinkSub& PatternParametersWidget::getCurrentDirectionLink() const @@ -206,16 +211,17 @@ void PatternParametersWidget::setTitle(const QString& title) { ui->groupBox->setTitle(title); } + void PatternParametersWidget::setCheckable(bool on) { ui->groupBox->setCheckable(on); } + void PatternParametersWidget::setChecked(bool on) { ui->groupBox->setChecked(on); } - // --- Slots --- void PatternParametersWidget::onDirectionChanged(int /*index*/) @@ -285,68 +291,77 @@ void PatternParametersWidget::onOccurrencesChanged(unsigned int value) } -// --- Dynamic Spacing Logic (Copied and adapted) --- +// --- Dynamic Spacing Logic --- void PatternParametersWidget::clearDynamicSpacingRows() { - for (Gui::QuantitySpinBox* spinBox : dynamicSpacingSpinBoxes) { - spinBox->blockSignals(true); // Block signals during deletion + for (QWidget* fieldWidget : dynamicSpacingRows) { + ui->formLayout->removeRow(fieldWidget); } - qDeleteAll(dynamicSpacingRows); dynamicSpacingRows.clear(); dynamicSpacingSpinBoxes.clear(); } void PatternParametersWidget::addSpacingRow(double value) { - if (!m_spacingProp) return; // Need context for units + if (!m_spacingProp) { + return; // Need context for units + } - QWidget* rowWidget = new QWidget(ui->spacingPatternWidget); - QHBoxLayout* rowLayout = new QHBoxLayout(rowWidget); - rowLayout->setContentsMargins(0, 0, 0, 0); - rowLayout->setSpacing(3); + // Find position to insert before "Occurrences" + int insertPos = -1; + QFormLayout::ItemRole role; + ui->formLayout->getWidgetPosition(ui->spinOccurrences, &insertPos, &role); + if (insertPos == -1) { + insertPos = ui->formLayout->rowCount(); // Fallback to appending + } int newIndex = dynamicSpacingRows.count(); - // Use qApp->translate for dynamic labels - QLabel* label = new QLabel(qApp->translate("PartGui::PatternParameters", "Spacing %1").arg(newIndex + 2), rowWidget); + QLabel* label = new QLabel(tr("Spacing %1").arg(newIndex + 2), this); - Gui::QuantitySpinBox* spinBox = new Gui::QuantitySpinBox(rowWidget); + // Create the field widget (spinbox + remove button) + QWidget* fieldWidget = new QWidget(this); + QHBoxLayout* fieldLayout = new QHBoxLayout(fieldWidget); + fieldLayout->setContentsMargins(0, 0, 0, 0); + fieldLayout->setSpacing(3); + + Gui::QuantitySpinBox* spinBox = new Gui::QuantitySpinBox(fieldWidget); Base::Unit unit = type == PatternType::Linear ? Base::Unit::Length : Base::Unit::Angle; spinBox->setUnit(unit); spinBox->setKeyboardTracking(false); - spinBox->setValue(value); // Set initial value + spinBox->setValue(value); // Set initial value - QToolButton* removeButton = new QToolButton(rowWidget); + QToolButton* removeButton = new QToolButton(fieldWidget); removeButton->setIcon(Gui::BitmapFactory().iconFromTheme("list-remove")); - removeButton->setToolTip(qApp->translate("PartGui::PatternParameters", "Remove this spacing definition.")); + removeButton->setToolTip(tr("Remove this spacing definition.")); removeButton->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred); - rowLayout->addWidget(label); - rowLayout->addWidget(spinBox); - rowLayout->addWidget(removeButton); - // No need to explicitly call rowWidget->setLayout(rowLayout); QHBoxLayout constructor handles it + fieldLayout->addWidget(spinBox); + fieldLayout->addWidget(removeButton); - ui->spacingPatternLayout->addWidget(rowWidget); - dynamicSpacingRows.append(rowWidget); + ui->formLayout->insertRow(insertPos, label, fieldWidget); + dynamicSpacingRows.append(fieldWidget); dynamicSpacingSpinBoxes.append(spinBox); // Connect signals for the new row - connect(spinBox, qOverload(&Gui::QuantitySpinBox::valueChanged), this, &PatternParametersWidget::onDynamicSpacingChanged); - connect(removeButton, &QToolButton::clicked, this, [this, rowWidget]() { - this->onRemoveSpacingButtonClicked(rowWidget); + connect(spinBox, + qOverload(&Gui::QuantitySpinBox::valueChanged), + this, + &PatternParametersWidget::onDynamicSpacingChanged); + connect(removeButton, &QToolButton::clicked, this, [this, fieldWidget]() { + this->onRemoveSpacingButtonClicked(fieldWidget); }); - - // rowWidget->show(); // Usually not needed when added to a visible layout } void PatternParametersWidget::rebuildDynamicSpacingUI() { - if (!m_spacingPatternProp) return; + if (!m_spacingPatternProp) { + return; + } + + clearDynamicSpacingRows(); // Clear existing dynamic UI first std::vector currentSpacings = m_spacingPatternProp->getValues(); - - clearDynamicSpacingRows(); // Clear existing dynamic UI first - // Start from index 1, as index 0 corresponds to ui->spinSpacing for (size_t i = 1; i < currentSpacings.size(); ++i) { // Values in PropertyFloatList are unitless. Assume they match the Offset unit. @@ -372,32 +387,33 @@ void PatternParametersWidget::onDynamicSpacingChanged() updateSpacingPatternProperty(); // This will emit parametersChanged } -void PatternParametersWidget::onRemoveSpacingButtonClicked(QWidget* rowWidget) +void PatternParametersWidget::onRemoveSpacingButtonClicked(QWidget* fieldWidget) { - if (blockUpdate || !m_spacingPatternProp) return; + if (blockUpdate || !m_spacingPatternProp) { + return; + } - int indexToRemove = dynamicSpacingRows.indexOf(rowWidget); - if (indexToRemove == -1) return; + int indexToRemove = dynamicSpacingRows.indexOf(fieldWidget); + if (indexToRemove == -1) { + return; + } + + // removeRow also deletes the widgets (label and field) + ui->formLayout->removeRow(fieldWidget); - // Remove from UI lists dynamicSpacingRows.removeAt(indexToRemove); - dynamicSpacingSpinBoxes.removeAt(indexToRemove); // Just remove, widget deleted below - - // Remove from layout and delete - ui->spacingPatternLayout->removeWidget(rowWidget); - delete rowWidget; // Deletes children too + dynamicSpacingSpinBoxes.removeAt(indexToRemove); // Update labels of subsequent rows - for (int i = indexToRemove; i < dynamicSpacingRows.count(); ++i) { - QWidget* row = dynamicSpacingRows.at(i); - QLabel* label = row->findChild(); - if (label) { - label->setText(qApp->translate("PartGui::PatternParameters", "Spacing %1").arg(i + 2)); + for (int i = indexToRemove; i < dynamicSpacingRows.size(); ++i) { + if (auto* label = + qobject_cast(ui->formLayout->labelForField(dynamicSpacingRows[i]))) { + label->setText(tr("Spacing %1").arg(i + 2)); } } // Update the underlying property list - updateSpacingPatternProperty(); // This will emit parametersChanged + updateSpacingPatternProperty(); // This will emit parametersChanged } void PatternParametersWidget::updateSpacingPatternProperty() diff --git a/src/Mod/Part/Gui/PatternParametersWidget.h b/src/Mod/Part/Gui/PatternParametersWidget.h index 797a7bbd42..d9ef3d8fd3 100644 --- a/src/Mod/Part/Gui/PatternParametersWidget.h +++ b/src/Mod/Part/Gui/PatternParametersWidget.h @@ -90,13 +90,13 @@ namespace PartGui { * @param occurrencesProp Reference to the Occurrences property (PropertyIntegerConstraint). * @param feature The feature object itself, needed for context (e.g., units). */ - void bindProperties(App::PropertyLinkSub& directionProp, - App::PropertyBool& reversedProp, - App::PropertyEnumeration& modeProp, - App::PropertyQuantity& lengthProp, - App::PropertyQuantity& offsetProp, - App::PropertyFloatList& spacingPatternProp, - App::PropertyIntegerConstraint& occurrencesProp, + void bindProperties(App::PropertyLinkSub* directionProp, + App::PropertyBool* reversedProp, + App::PropertyEnumeration* modeProp, + App::PropertyQuantity* lengthProp, + App::PropertyQuantity* offsetProp, + App::PropertyFloatList* spacingPatternProp, + App::PropertyIntegerConstraint* occurrencesProp, App::DocumentObject* feature); // Pass feature for context @@ -201,9 +201,6 @@ namespace PartGui { bool blockUpdate = false; // Prevents signal loops - // Helpers for direction combo box - static const int SELECT_REFERENCE_INDEX = -10; // Special index for "Select reference..." - // Store pointers to dynamically created widgets for removal and access QList dynamicSpacingRows; QList dynamicSpacingSpinBoxes; diff --git a/src/Mod/Part/Gui/PatternParametersWidget.ui b/src/Mod/Part/Gui/PatternParametersWidget.ui index d488b0f716..3059a2ae8c 100644 --- a/src/Mod/Part/Gui/PatternParametersWidget.ui +++ b/src/Mod/Part/Gui/PatternParametersWidget.ui @@ -28,7 +28,7 @@ Direction - + @@ -54,15 +54,15 @@ - - + + Mode - + @@ -76,125 +76,85 @@ - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Length - - - - - - - false - - - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Spacing - - - - - - - false - - - - - - - - 0 - 0 - - - - Add spacing to create spacing patterns. - - - - - - - :/icons/list-add.svg:/icons/list-add.svg - - - - - - - - - - - 3 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - + + - Occurrences + Length - - + + + + false + + + + + + Spacing + + + + + + + + 3 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + false + + + + + + + + 0 + 0 + + + + Add spacing to create spacing patterns. + + + + + + + :/icons/list-add.svg:/icons/list-add.svg + + + + + + + + + + Occurrences + + + + + + @@ -230,4 +190,4 @@ - \ No newline at end of file + diff --git a/src/Mod/PartDesign/App/FeatureLinearPattern.cpp b/src/Mod/PartDesign/App/FeatureLinearPattern.cpp index d03dba8209..8d492cc5db 100644 --- a/src/Mod/PartDesign/App/FeatureLinearPattern.cpp +++ b/src/Mod/PartDesign/App/FeatureLinearPattern.cpp @@ -35,6 +35,7 @@ #include #include +#include #include #include @@ -53,21 +54,35 @@ PROPERTY_SOURCE(PartDesign::LinearPattern, PartDesign::Transformed) const App::PropertyIntegerConstraint::Constraints LinearPattern::intOccurrences = { 1, std::numeric_limits::max(), 1 }; -const char* LinearPattern::ModeEnums[] = { "length", "offset", nullptr }; +const char* LinearPattern::ModeEnums[] = { "Extent", "Spacing", nullptr }; LinearPattern::LinearPattern() { - auto initialMode = LinearPatternMode::length; + auto initialMode = LinearPatternMode::Extent; - ADD_PROPERTY_TYPE(Direction,(nullptr),"LinearPattern",(App::PropertyType)(App::Prop_None),"Direction"); - ADD_PROPERTY(Reversed,(0)); - ADD_PROPERTY(Mode, (long(initialMode))); - ADD_PROPERTY(Length,(100.0)); - ADD_PROPERTY(Offset,(10.0)); - ADD_PROPERTY(Occurrences,(3)); + ADD_PROPERTY_TYPE(Direction,(nullptr),"Direction 1",(App::PropertyType)(App::Prop_None),"Direction"); + ADD_PROPERTY_TYPE(Reversed,(0), "Direction 1", (App::PropertyType)(App::Prop_None), "Direction"); + ADD_PROPERTY_TYPE(Mode, (long(initialMode)), "Direction 1", (App::PropertyType)(App::Prop_None), "Direction"); + ADD_PROPERTY_TYPE(Length,(100.0), "Direction 1", (App::PropertyType)(App::Prop_None), "Direction"); + ADD_PROPERTY_TYPE(Offset,(10.0), "Direction 1", (App::PropertyType)(App::Prop_None), "Direction"); + ADD_PROPERTY_TYPE(Spacings, ({ -1.0 }), "Direction 1", (App::PropertyType)(App::Prop_None), "Direction"); + ADD_PROPERTY_TYPE(SpacingPattern, ({}), "Direction 1", (App::PropertyType)(App::Prop_None), "Direction"); + ADD_PROPERTY_TYPE(Occurrences,(2), "Direction 1", (App::PropertyType)(App::Prop_None), "Direction"); Occurrences.setConstraints(&intOccurrences); Mode.setEnums(ModeEnums); - setReadWriteStatusForMode(initialMode); + setReadWriteStatusForMode(LinearPatternDirection::First); + + ADD_PROPERTY_TYPE(Direction2,(nullptr),"Direction 2",(App::PropertyType)(App::Prop_None),"Direction"); + ADD_PROPERTY_TYPE(Reversed2,(0), "Direction 2", (App::PropertyType)(App::Prop_None), "Direction"); + ADD_PROPERTY_TYPE(Mode2, (long(initialMode)), "Direction 2", (App::PropertyType)(App::Prop_None), "Direction"); + ADD_PROPERTY_TYPE(Length2,(100.0), "Direction 2", (App::PropertyType)(App::Prop_None), "Direction"); + ADD_PROPERTY_TYPE(Offset2,(10.0), "Direction 2", (App::PropertyType)(App::Prop_None), "Direction"); + ADD_PROPERTY_TYPE(Spacings2, ({}), "Direction 2", (App::PropertyType)(App::Prop_None), "Direction"); + ADD_PROPERTY_TYPE(SpacingPattern2, ({}), "Direction 2", (App::PropertyType)(App::Prop_None), "Direction"); + ADD_PROPERTY_TYPE(Occurrences2,(1), "Direction 2", (App::PropertyType)(App::Prop_None), "Direction"); + Occurrences2.setConstraints(&intOccurrences); + Mode2.setEnums(ModeEnums); + setReadWriteStatusForMode(LinearPatternDirection::Second); } short LinearPattern::mustExecute() const @@ -78,42 +93,170 @@ short LinearPattern::mustExecute() const // Length and Offset are mutually exclusive, only one could be updated at once Length.isTouched() || Offset.isTouched() || - Occurrences.isTouched()) + Spacings.isTouched() || + SpacingPattern.isTouched() || + Occurrences.isTouched() || + Direction2.isTouched() || + Reversed2.isTouched() || + Mode2.isTouched() || + Length2.isTouched() || + Offset2.isTouched() || + Spacings2.isTouched() || + SpacingPattern2.isTouched() || + Occurrences2.isTouched()) return 1; return Transformed::mustExecute(); } -void LinearPattern::setReadWriteStatusForMode(LinearPatternMode mode) +void LinearPattern::setReadWriteStatusForMode(LinearPatternDirection dir) { - Length.setReadOnly(mode != LinearPatternMode::length); - Offset.setReadOnly(mode != LinearPatternMode::offset); + bool isExtentMode = false; + if (dir == LinearPatternDirection::First) { + isExtentMode = (Mode.getValue() == static_cast(LinearPatternMode::Extent)); + Length.setReadOnly(!isExtentMode); + Offset.setReadOnly(isExtentMode); + } + else { + isExtentMode = (Mode2.getValue() == static_cast(LinearPatternMode::Extent)); + Length2.setReadOnly(!isExtentMode); + Offset2.setReadOnly(isExtentMode); + } } const std::list LinearPattern::getTransformations(const std::vector) { int occurrences = Occurrences.getValue(); - if (occurrences < 1) + int occurrences2 = Occurrences2.getValue(); + if (occurrences < 1 || occurrences2 < 1) { throw Base::ValueError("At least one occurrence required"); + } - if (occurrences == 1) + if (occurrences == 1 && occurrences2 == 1) { return {gp_Trsf()}; + } - double distance = Length.getValue(); - if (distance < Precision::Confusion()) + // make sure spacings are correct size : + updateSpacings(); + + // Calculate the base offset vector and final step positions for each direction + gp_Vec offset1 = calculateOffsetVector(LinearPatternDirection::First); + std::vector steps1 = calculateSteps(LinearPatternDirection::First, offset1); + + gp_Vec offset2 = calculateOffsetVector(LinearPatternDirection::Second); + std::vector steps2 = calculateSteps(LinearPatternDirection::Second, offset2); + + // Combine the steps from both directions + std::list transformations; + for (const auto& step1 : steps1) { + for (const auto& step2 : steps2) { + gp_Trsf trans; + trans.SetTranslation(step1 + step2); + transformations.push_back(trans); + } + } + + return transformations; +} + +gp_Vec LinearPattern::calculateOffsetVector(LinearPatternDirection dir) const +{ + const auto& occurrencesProp = + (dir == LinearPatternDirection::First) ? Occurrences : Occurrences2; + int occurrences = occurrencesProp.getValue(); + if (occurrences <= 1) { + return gp_Vec(); // Return zero vector if no transformation is needed + } + + const auto& dirProp = (dir == LinearPatternDirection::First) ? Direction : Direction2; + const auto& reversedProp = (dir == LinearPatternDirection::First) ? Reversed : Reversed2; + const auto& modeProp = (dir == LinearPatternDirection::First) ? Mode : Mode2; + const auto& lengthProp = (dir == LinearPatternDirection::First) ? Length : Length2; + + double distance = lengthProp.getValue(); + if (distance < Precision::Confusion()) { throw Base::ValueError("Pattern length too small"); - bool reversed = Reversed.getValue(); + } - App::DocumentObject* refObject = Direction.getValue(); - if (!refObject) - throw Base::ValueError("No direction reference specified"); + gp_Vec offset = getDirectionFromProperty(dirProp); + if (reversedProp.getValue()) { + offset.Reverse(); + } - std::vector subStrings = Direction.getSubValues(); - if (subStrings.empty()) + // For 'Extent' mode, the vector represents one full step. + // For 'Spacing' mode, it's just a normalized direction vector. + if (static_cast(modeProp.getValue()) == LinearPatternMode::Extent) { + offset *= distance / (occurrences - 1); + } + + return offset; +} + +std::vector LinearPattern::calculateSteps(LinearPatternDirection dir, + const gp_Vec& offsetVector) const +{ + const auto& occurrencesProp = + (dir == LinearPatternDirection::First) ? Occurrences : Occurrences2; + const auto& modeProp = (dir == LinearPatternDirection::First) ? Mode : Mode2; + const auto& offsetValueProp = (dir == LinearPatternDirection::First) ? Offset : Offset2; + const auto& spacingsProp = (dir == LinearPatternDirection::First) ? Spacings : Spacings2; + const auto& spacingPatternProp = + (dir == LinearPatternDirection::First) ? SpacingPattern : SpacingPattern2; + + int occurrences = occurrencesProp.getValue(); + std::vector steps {gp_Vec()}; // First step is always zero + steps.reserve(occurrences); + + if (occurrences <= 1) { + return steps; + } + + if (modeProp.getValue() == static_cast(LinearPatternMode::Spacing)) { + const std::vector spacings = spacingsProp.getValues(); + const std::vector pattern = spacingPatternProp.getValues(); + bool usePattern = pattern.size() > 1; + double cumulativeDistance = 0.0; + + // Spacing priority: individual spacing > pattern > global offset + const auto spacingAt = [&](unsigned i) { + if (spacings.at(i - 1) != -1.0) { + return spacings.at(i - 1); + } + + if (usePattern) { + return pattern.at(static_cast(fmod(i - 1, pattern.size()))); + } + + return offsetValueProp.getValue(); + }; + + for (int i = 1; i < occurrences; ++i) { + cumulativeDistance += spacingAt(i); + steps.push_back(offsetVector * cumulativeDistance); + } + } + else { // Extent Mode + for (int i = 1; i < occurrences; ++i) { + steps.push_back(offsetVector * i); + } + } + + return steps; +} + +gp_Dir LinearPattern::getDirectionFromProperty(const App::PropertyLinkSub& dirProp) const +{ + App::DocumentObject* refObject = dirProp.getValue(); + if (!refObject) { throw Base::ValueError("No direction reference specified"); + } + + std::vector subStrings = dirProp.getSubValues(); + if (subStrings.empty()) { + throw Base::ValueError("No direction reference specified"); + } gp_Dir dir; - if (refObject->isDerivedFrom()) { - Part::Part2DObject* refSketch = static_cast(refObject); + if (auto* refSketch = freecad_cast(refObject)) { Base::Axis axis; if (subStrings[0] == "H_Axis") { axis = refSketch->getAxis(Part::Part2DObject::H_Axis); @@ -128,7 +271,7 @@ const std::list LinearPattern::getTransformations(const std::vectorPlacement.getValue(); } else if (subStrings[0].compare(0, 4, "Axis") == 0) { - int AxId = std::atoi(subStrings[0].substr(4,4000).c_str()); + int AxId = std::atoi(subStrings[0].substr(4, 4000).c_str()); if (AxId >= 0 && AxId < refSketch->getAxisCount()) { axis = refSketch->getAxis(AxId); axis *= refSketch->Placement.getValue(); @@ -153,22 +296,23 @@ const std::list LinearPattern::getTransformations(const std::vectorisDerivedFrom()) { - PartDesign::Plane* plane = static_cast(refObject); + } + else if (auto* plane = freecad_cast(refObject)) { Base::Vector3d d = plane->getNormal(); dir = gp_Dir(d.x, d.y, d.z); - } else if (refObject->isDerivedFrom()) { - PartDesign::Line* line = static_cast(refObject); + } + else if (auto* line = freecad_cast(refObject)) { Base::Vector3d d = line->getDirection(); dir = gp_Dir(d.x, d.y, d.z); - } else if (refObject->isDerivedFrom()) { - App::Line* line = static_cast(refObject); + } + else if (auto* line = freecad_cast(refObject)) { Base::Vector3d d = line->getDirection(); dir = gp_Dir(d.x, d.y, d.z); - } else if (refObject->isDerivedFrom()) { - if (subStrings[0].empty()) + } + else if (auto* refFeature = freecad_cast(refObject)) { + if (subStrings[0].empty()) { throw Base::ValueError("No direction reference specified"); - Part::Feature* refFeature = static_cast(refObject); + } Part::TopoShape refShape = refFeature->Shape.getShape(); TopoDS_Shape ref = refShape.getSubShape(subStrings[0].c_str()); @@ -181,7 +325,8 @@ const std::list LinearPattern::getTransformations(const std::vector LinearPattern::getTransformations(const std::vectorgetLocation().Inverted(); dir.Transform(invObjLoc.Transformation()); - gp_Vec offset(dir.X(), dir.Y(), dir.Z()); + return Base::convertTo(dir); +} - switch (static_cast(Mode.getValue())) { - case LinearPatternMode::length: - offset *= distance / (occurrences - 1); - break; +void LinearPattern::updateSpacings() +{ + updateSpacings(LinearPatternDirection::First); + updateSpacings(LinearPatternDirection::Second); +} - case LinearPatternMode::offset: - offset *= Offset.getValue(); - break; +void LinearPattern::updateSpacings(LinearPatternDirection dir) +{ + bool isSecondDir = dir == LinearPatternDirection::Second; - default: - throw Base::ValueError("Invalid mode"); + App::PropertyFloatList& spacingsProp = isSecondDir ? Spacings2 : Spacings; + App::PropertyLength& offsetProp = isSecondDir ? Offset2 : Offset; + const App::PropertyIntegerConstraint& occurrencesProp = isSecondDir ? Occurrences2 : Occurrences; + + std::vector spacings = spacingsProp.getValues(); + size_t targetCount = occurrencesProp.getValue() - 1; // 1 less spacing than there are occurrences. + + for (auto& spacing : spacings) { + if (spacing == offsetProp.getValue()) { + spacing = -1.0; + } } - if (reversed) - offset.Reverse(); - - std::list transformations; - gp_Trsf trans; - transformations.push_back(trans); - - // NOTE: The original feature is already included in the list of transformations! - // Therefore we start with occurrence number 1 - for (int i = 1; i < occurrences; i++) { - trans.SetTranslation(offset * i); - transformations.push_back(trans); + if (spacings.size() == targetCount) { + return; + } + else if (spacings.size() < targetCount) { + spacings.reserve(targetCount); + while (spacings.size() < targetCount) { + spacings.push_back(-1.0); + } + } + else if ((int)spacings.size() > targetCount) { + spacings.resize(targetCount); } - return transformations; + spacingsProp.setValues(spacings); } void LinearPattern::handleChangedPropertyType(Base::XMLReader& reader, const char* TypeName, App::Property* prop) @@ -249,23 +407,60 @@ void LinearPattern::handleChangedPropertyType(Base::XMLReader& reader, const cha void LinearPattern::onChanged(const App::Property* prop) { auto mode = static_cast(Mode.getValue()); + auto mode2 = static_cast(Mode2.getValue()); if (prop == &Mode) { - setReadWriteStatusForMode(mode); + setReadWriteStatusForMode(LinearPatternDirection::First); + } + else if (prop == &Occurrences) { + updateSpacings(LinearPatternDirection::First); + syncLengthAndOffset(LinearPatternDirection::First); + } + else if (prop == &Offset && mode == LinearPatternMode::Spacing) { + syncLengthAndOffset(LinearPatternDirection::First); + } + else if (prop == &Length && mode == LinearPatternMode::Extent) { + syncLengthAndOffset(LinearPatternDirection::First); } - // Keep Length in sync with Offset, catch Occurrences changes - if (mode == LinearPatternMode::offset && (prop == &Offset || prop == &Occurrences) - && !Length.testStatus(App::Property::Status::Immutable)) { - Length.setValue(Offset.getValue() * (Occurrences.getValue() - 1)); + else if (prop == &Mode2) { + setReadWriteStatusForMode(LinearPatternDirection::Second); } - - if (mode == LinearPatternMode::length && (prop == &Length || prop == &Occurrences) - && !Offset.testStatus(App::Property::Status::Immutable)) { - Offset.setValue(Length.getValue() / (Occurrences.getValue() - 1)); + else if (prop == &Occurrences2) { + updateSpacings(LinearPatternDirection::Second); + syncLengthAndOffset(LinearPatternDirection::Second); + } + else if (prop == &Offset2 && mode2 == LinearPatternMode::Spacing) { + syncLengthAndOffset(LinearPatternDirection::Second); + } + else if (prop == &Length2 && mode2 == LinearPatternMode::Extent) { + syncLengthAndOffset(LinearPatternDirection::Second); } Transformed::onChanged(prop); } +void LinearPattern::syncLengthAndOffset(LinearPatternDirection dir) +{ + // Get references to the correct properties based on the direction + auto& modeProp = (dir == LinearPatternDirection::First) ? Mode : Mode2; + auto& lengthProp = (dir == LinearPatternDirection::First) ? Length : Length2; + auto& offsetProp = (dir == LinearPatternDirection::First) ? Offset : Offset2; + auto& occurrencesProp = (dir == LinearPatternDirection::First) ? Occurrences : Occurrences2; + + auto mode = static_cast(modeProp.getValue()); + int occurrences = occurrencesProp.getValue(); + occurrences = occurrences <= 1 ? 1 : occurrences - 1; + + if (mode == LinearPatternMode::Spacing + && !lengthProp.testStatus(App::Property::Status::Immutable)) { + lengthProp.setValue(offsetProp.getValue() * occurrences); + } + else if (mode == LinearPatternMode::Extent + && !offsetProp.testStatus(App::Property::Status::Immutable)) { + offsetProp.setValue(lengthProp.getValue() / occurrences); + } } + +} + diff --git a/src/Mod/PartDesign/App/FeatureLinearPattern.h b/src/Mod/PartDesign/App/FeatureLinearPattern.h index 8876f2646a..41faecf336 100644 --- a/src/Mod/PartDesign/App/FeatureLinearPattern.h +++ b/src/Mod/PartDesign/App/FeatureLinearPattern.h @@ -27,11 +27,19 @@ #include #include "FeatureTransformed.h" +class gp_Vec; + namespace PartDesign { enum class LinearPatternMode { - length, - offset + Extent, + Spacing +}; + +enum class LinearPatternDirection : std::uint8_t +{ + First, + Second }; class PartDesignExport LinearPattern : public PartDesign::Transformed @@ -47,6 +55,18 @@ public: App::PropertyLength Length; App::PropertyLength Offset; App::PropertyIntegerConstraint Occurrences; + App::PropertyFloatList Spacings; + App::PropertyFloatList SpacingPattern; + + App::PropertyLinkSub Direction2; + App::PropertyBool Reversed2; + App::PropertyEnumeration Mode2; + App::PropertyLength Length2; + App::PropertyLength Offset2; + App::PropertyIntegerConstraint Occurrences2; + App::PropertyFloatList Spacings2; + App::PropertyFloatList SpacingPattern2; + /** @name methods override feature */ //@{ @@ -85,7 +105,16 @@ protected: private: static const char* ModeEnums[]; - void setReadWriteStatusForMode(LinearPatternMode mode); + gp_Dir getDirectionFromProperty(const App::PropertyLinkSub& dirProp) const; + + void setReadWriteStatusForMode(LinearPatternDirection dir); + void syncLengthAndOffset(LinearPatternDirection dir); + + void updateSpacings(); + void updateSpacings(LinearPatternDirection dir); + + gp_Vec calculateOffsetVector(LinearPatternDirection dir) const; + std::vector calculateSteps(LinearPatternDirection dir, const gp_Vec& offsetVector) const; }; } //namespace PartDesign diff --git a/src/Mod/PartDesign/App/FeaturePolarPattern.cpp b/src/Mod/PartDesign/App/FeaturePolarPattern.cpp index 9a5aa64c91..46c5a45c18 100644 --- a/src/Mod/PartDesign/App/FeaturePolarPattern.cpp +++ b/src/Mod/PartDesign/App/FeaturePolarPattern.cpp @@ -53,11 +53,11 @@ 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}; +const char* PolarPattern::ModeEnums[] = { "Extent", "Spacing", nullptr}; PolarPattern::PolarPattern() { - auto initialMode = PolarPatternMode::angle; + auto initialMode = PolarPatternMode::Extent; ADD_PROPERTY_TYPE(Axis, (nullptr), "PolarPattern", (App::PropertyType)(App::Prop_None), "Direction"); ADD_PROPERTY(Reversed, (0)); @@ -67,6 +67,8 @@ PolarPattern::PolarPattern() ADD_PROPERTY(Offset, (120.0)); Angle.setConstraints(&floatAngle); Offset.setConstraints(&floatAngle); + ADD_PROPERTY(Spacings, ({ -1.0, -1.0, -1.0 })); + ADD_PROPERTY(SpacingPattern, ({})); ADD_PROPERTY(Occurrences, (3)); Occurrences.setConstraints(&intOccurrences); @@ -80,7 +82,9 @@ short PolarPattern::mustExecute() const Mode.isTouched() || // Angle and Offset are mutually exclusive, only one could be updated at once Angle.isTouched() || - Offset.isTouched() || + Offset.isTouched() || + Spacings.isTouched() || + SpacingPattern.isTouched() || Occurrences.isTouched()) return 1; return Transformed::mustExecute(); @@ -173,38 +177,54 @@ const std::list PolarPattern::getTransformations(const std::vector(Mode.getValue())) { - case PolarPatternMode::angle: - angle = Angle.getValue(); + if (Mode.getValue() == (int)PolarPatternMode::Extent) { + angle = Angle.getValue(); - if (std::fabs(angle - 360.0) < Precision::Confusion()) - angle /= occurrences; // Because e.g. two occurrences in 360 degrees need to be 180 degrees apart - else - angle /= occurrences - 1; + if (std::fabs(angle - 360.0) < Precision::Confusion()) + angle /= occurrences; // Because e.g. two occurrences in 360 degrees need to be 180 degrees apart + else + angle /= occurrences - 1; - break; - - case PolarPatternMode::offset: - angle = Offset.getValue(); - break; - - default: - throw Base::ValueError("Invalid mode"); + angle = Base::toRadians(angle); + if (angle < Precision::Angular()){ + throw Base::ValueError("Pattern angle too small"); + } } - double offset = Base::toRadians(angle); + // make sure spacings are correct size : + updateSpacings(); - if (offset < Precision::Angular()) - throw Base::ValueError("Pattern angle too small"); + const std::vector spacings = Spacings.getValues(); + const std::vector pattern = SpacingPattern.getValues(); + bool usePattern = pattern.size() > 1; + + double cumulativeSpacings = 0.0; std::list transformations; gp_Trsf trans; transformations.push_back(trans); + // Note: The original feature is already included in the list of transformations! // Therefore we start with occurrence number 1 for (int i = 1; i < occurrences; i++) { - trans.SetRotation(axis.Axis(), i * offset); + if (Mode.getValue() == (int)PolarPatternMode::Spacing) { + double spacing; + if (spacings[i] != -1.0) { + spacing = spacings[i]; + } + else if (usePattern) { + spacing = pattern[(size_t)fmod(i, pattern.size())]; + } + else { + spacing = Offset.getValue(); + } + cumulativeSpacings += Base::toRadians(spacing); + trans.SetRotation(axis.Axis(), cumulativeSpacings); + } + else { + trans.SetRotation(axis.Axis(), angle * i); + } transformations.push_back(trans); } @@ -239,8 +259,36 @@ void PolarPattern::onChanged(const App::Property* prop) void PolarPattern::setReadWriteStatusForMode(PolarPatternMode mode) { - Offset.setReadOnly(mode != PolarPatternMode::offset); - Angle.setReadOnly(mode != PolarPatternMode::angle); + Offset.setReadOnly(mode != PolarPatternMode::Spacing); + Angle.setReadOnly(mode != PolarPatternMode::Extent); +} + + +void PolarPattern::updateSpacings() +{ + std::vector spacings = Spacings.getValues(); + size_t targetCount = Occurrences.getValue(); + + for (auto& spacing : spacings) { + if (spacing == Offset.getValue()) { + spacing = -1.0; + } + } + + if (spacings.size() == targetCount) { + return; + } + else if (spacings.size() < targetCount) { + spacings.reserve(targetCount); + while (spacings.size() < targetCount) { + spacings.push_back(-1.0); + } + } + else if ((int)spacings.size() > targetCount) { + spacings.resize(targetCount); + } + + Spacings.setValues(spacings); } } diff --git a/src/Mod/PartDesign/App/FeaturePolarPattern.h b/src/Mod/PartDesign/App/FeaturePolarPattern.h index f250820fec..4798f5cb12 100644 --- a/src/Mod/PartDesign/App/FeaturePolarPattern.h +++ b/src/Mod/PartDesign/App/FeaturePolarPattern.h @@ -32,8 +32,8 @@ namespace PartDesign { enum class PolarPatternMode { - angle, - offset + Extent, + Spacing }; class PartDesignExport PolarPattern : public PartDesign::Transformed @@ -49,6 +49,9 @@ public: App::PropertyAngle Angle; App::PropertyAngle Offset; App::PropertyIntegerConstraint Occurrences; + App::PropertyFloatList Spacings; + App::PropertyFloatList SpacingPattern; + /** @name methods override feature */ //@{ @@ -92,6 +95,8 @@ private: static const char* ModeEnums[]; void setReadWriteStatusForMode(PolarPatternMode mode); + + void updateSpacings(); }; } //namespace PartDesign diff --git a/src/Mod/PartDesign/Gui/CMakeLists.txt b/src/Mod/PartDesign/Gui/CMakeLists.txt index 91ba4e6679..6416857e71 100644 --- a/src/Mod/PartDesign/Gui/CMakeLists.txt +++ b/src/Mod/PartDesign/Gui/CMakeLists.txt @@ -39,8 +39,7 @@ set(PartDesignGui_UIC_SRCS TaskTransformedMessages.ui TaskTransformedParameters.ui TaskMirroredParameters.ui - TaskLinearPatternParameters.ui - TaskPolarPatternParameters.ui + TaskPatternParameters.ui TaskScaledParameters.ui TaskMultiTransformParameters.ui TaskShapeBinder.ui @@ -165,12 +164,9 @@ SET(PartDesignGuiTaskDlgs_SRCS TaskMirroredParameters.ui TaskMirroredParameters.cpp TaskMirroredParameters.h - TaskLinearPatternParameters.ui - TaskLinearPatternParameters.cpp - TaskLinearPatternParameters.h - TaskPolarPatternParameters.ui - TaskPolarPatternParameters.cpp - TaskPolarPatternParameters.h + TaskPatternParameters.ui + TaskPatternParameters.cpp + TaskPatternParameters.h TaskScaledParameters.ui TaskScaledParameters.cpp TaskScaledParameters.h diff --git a/src/Mod/PartDesign/Gui/Command.cpp b/src/Mod/PartDesign/Gui/Command.cpp index 5df71477e4..1f6f1ab260 100644 --- a/src/Mod/PartDesign/Gui/Command.cpp +++ b/src/Mod/PartDesign/Gui/Command.cpp @@ -2064,6 +2064,7 @@ void CmdPartDesignLinearPattern::activated(int iMsg) Part::Part2DObject *sketch = (static_cast(features.front()))->getVerifiedSketch(/* silent =*/ true); if (sketch) { FCMD_OBJ_CMD(Feat,"Direction = ("< * - * * - * 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 -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ui_TaskLinearPatternParameters.h" -#include "TaskLinearPatternParameters.h" -#include "ReferenceSelection.h" -#include "TaskMultiTransformParameters.h" - - -using namespace PartDesignGui; -using namespace Gui; - -/* TRANSLATOR PartDesignGui::TaskLinearPatternParameters */ - -TaskLinearPatternParameters::TaskLinearPatternParameters(ViewProviderTransformed* TransformedView, - QWidget* parent) - : TaskTransformedParameters(TransformedView, parent) - , ui(new Ui_TaskLinearPatternParameters) -{ - setupUI(); -} - -TaskLinearPatternParameters::TaskLinearPatternParameters(TaskMultiTransformParameters* parentTask, - QWidget* parameterWidget) - : TaskTransformedParameters(parentTask) - , ui(new Ui_TaskLinearPatternParameters) -{ - setupParameterUI(parameterWidget); -} - -void TaskLinearPatternParameters::setupParameterUI(QWidget* widget) -{ - ui->setupUi(widget); - QMetaObject::connectSlotsByName(this); - - // Get the feature data - auto pcLinearPattern = getObject(); - - ui->spinLength->bind(pcLinearPattern->Length); - ui->spinOffset->bind(pcLinearPattern->Offset); - ui->spinOccurrences->bind(pcLinearPattern->Occurrences); - ui->spinOccurrences->setMaximum(pcLinearPattern->Occurrences.getMaximum()); - ui->spinOccurrences->setMinimum(pcLinearPattern->Occurrences.getMinimum()); - - ui->comboDirection->setEnabled(true); - ui->checkReverse->setEnabled(true); - ui->comboMode->setEnabled(true); - ui->spinLength->blockSignals(true); - ui->spinLength->setEnabled(true); - ui->spinLength->setUnit(Base::Unit::Length); - ui->spinLength->blockSignals(false); - ui->spinOffset->blockSignals(true); - ui->spinOffset->setEnabled(true); - ui->spinOffset->setUnit(Base::Unit::Length); - ui->spinOffset->blockSignals(false); - ui->spinOccurrences->setEnabled(true); - - dirLinks.setCombo(*(ui->comboDirection)); - App::DocumentObject* sketch = getSketchObject(); - if (sketch && sketch->isDerivedFrom()) { - this->fillAxisCombo(dirLinks, static_cast(sketch)); - } - else { - this->fillAxisCombo(dirLinks, nullptr); - } - - // show the parts coordinate system axis for selection - PartDesign::Body* body = PartDesign::Body::findBodyOf(getObject()); - if (body) { - try { - App::Origin* origin = body->getOrigin(); - auto vpOrigin = static_cast( - Gui::Application::Instance->getViewProvider(origin)); - vpOrigin->setTemporaryVisibility(Gui::DatumElement::Axes); - } - catch (const Base::Exception& ex) { - Base::Console().error("%s\n", ex.what()); - } - } - - adaptVisibilityToMode(); - - updateViewTimer = new QTimer(this); - updateViewTimer->setSingleShot(true); - updateViewTimer->setInterval(getUpdateViewTimeout()); - connect(updateViewTimer, - &QTimer::timeout, - this, - &TaskLinearPatternParameters::onUpdateViewTimer); - - connect(ui->comboDirection, - qOverload(&QComboBox::activated), - this, - &TaskLinearPatternParameters::onDirectionChanged); - connect(ui->checkReverse, - &QCheckBox::toggled, - this, - &TaskLinearPatternParameters::onCheckReverse); - connect(ui->comboMode, - qOverload(&QComboBox::activated), - this, - &TaskLinearPatternParameters::onModeChanged); - connect(ui->spinLength, - qOverload(&Gui::QuantitySpinBox::valueChanged), - this, - &TaskLinearPatternParameters::onLength); - connect(ui->spinOffset, - qOverload(&Gui::QuantitySpinBox::valueChanged), - this, - &TaskLinearPatternParameters::onOffset); - connect(ui->spinOccurrences, - &Gui::UIntSpinBox::unsignedChanged, - this, - &TaskLinearPatternParameters::onOccurrences); -} - -void TaskLinearPatternParameters::retranslateParameterUI(QWidget* widget) -{ - ui->retranslateUi(widget); -} - -void TaskLinearPatternParameters::updateUI() -{ - if (blockUpdate) { - return; - } - blockUpdate = true; - - auto pcLinearPattern = getObject(); - auto mode = static_cast(pcLinearPattern->Mode.getValue()); - - bool reverse = pcLinearPattern->Reversed.getValue(); - double length = pcLinearPattern->Length.getValue(); - double offset = pcLinearPattern->Offset.getValue(); - unsigned occurrences = pcLinearPattern->Occurrences.getValue(); - - if (dirLinks.setCurrentLink(pcLinearPattern->Direction) == -1) { - // failed to set current, because the link isn't in the list yet - dirLinks.addLink(pcLinearPattern->Direction, - getRefStr(pcLinearPattern->Direction.getValue(), - pcLinearPattern->Direction.getSubValues())); - dirLinks.setCurrentLink(pcLinearPattern->Direction); - } - - // Note: This block of code would trigger change signal handlers (e.g. onOccurrences()) - // and another updateUI() if we didn't check for blockUpdate - ui->checkReverse->setChecked(reverse); - ui->comboMode->setCurrentIndex(static_cast(mode)); - ui->spinLength->setValue(length); - ui->spinOffset->setValue(offset); - ui->spinOccurrences->setValue(occurrences); - - blockUpdate = false; -} - -void TaskLinearPatternParameters::adaptVisibilityToMode() -{ - auto pcLinearPattern = getObject(); - auto mode = static_cast(pcLinearPattern->Mode.getValue()); - - ui->lengthWrapper->setVisible(mode == PartDesign::LinearPatternMode::length); - ui->offsetWrapper->setVisible(mode == PartDesign::LinearPatternMode::offset); - - updateUI(); -} - -void TaskLinearPatternParameters::onUpdateViewTimer() -{ - setupTransaction(); - recomputeFeature(); -} - -void TaskLinearPatternParameters::kickUpdateViewTimer() const -{ - updateViewTimer->start(); -} - -void TaskLinearPatternParameters::onSelectionChanged(const Gui::SelectionChanges& msg) -{ - if (selectionMode != SelectionMode::None && msg.Type == Gui::SelectionChanges::AddSelection) { - if (originalSelected(msg)) { - exitSelectionMode(); - } - else { - auto pcLinearPattern = getObject(); - - std::vector directions; - App::DocumentObject* selObj = nullptr; - getReferencedSelection(pcLinearPattern, msg, selObj, directions); - if (!selObj) { - return; - } - - // Note: ReferenceSelection has already checked the selection for validity - if (selectionMode == SelectionMode::Reference || selObj->isDerivedFrom()) { - setupTransaction(); - pcLinearPattern->Direction.setValue(selObj, directions); - recomputeFeature(); - updateUI(); - } - exitSelectionMode(); - } - } -} - -void TaskLinearPatternParameters::onCheckReverse(const bool on) -{ - if (blockUpdate) { - return; - } - auto pcLinearPattern = getObject(); - pcLinearPattern->Reversed.setValue(on); - - exitSelectionMode(); - kickUpdateViewTimer(); -} - -void TaskLinearPatternParameters::onModeChanged(const int mode) -{ - if (blockUpdate) { - return; - } - auto pcLinearPattern = getObject(); - pcLinearPattern->Mode.setValue(mode); - - adaptVisibilityToMode(); - - exitSelectionMode(); - kickUpdateViewTimer(); -} - -void TaskLinearPatternParameters::onLength(const double length) -{ - if (blockUpdate) { - return; - } - auto pcLinearPattern = getObject(); - pcLinearPattern->Length.setValue(length); - - exitSelectionMode(); - kickUpdateViewTimer(); -} - -void TaskLinearPatternParameters::onOffset(const double offset) -{ - if (blockUpdate) { - return; - } - auto pcLinearPattern = getObject(); - pcLinearPattern->Offset.setValue(offset); - - exitSelectionMode(); - kickUpdateViewTimer(); -} - -void TaskLinearPatternParameters::onOccurrences(const uint number) -{ - if (blockUpdate) { - return; - } - auto pcLinearPattern = getObject(); - pcLinearPattern->Occurrences.setValue(number); - - exitSelectionMode(); - kickUpdateViewTimer(); -} - -void TaskLinearPatternParameters::onDirectionChanged(int /*num*/) -{ - if (blockUpdate) { - return; - } - auto pcLinearPattern = getObject(); - try { - if (!dirLinks.getCurrentLink().getValue()) { - // enter reference selection mode - hideObject(); - showBase(); - selectionMode = SelectionMode::Reference; - Gui::Selection().clearSelection(); - addReferenceSelectionGate(AllowSelection::EDGE | AllowSelection::FACE - | AllowSelection::PLANAR); - } - else { - exitSelectionMode(); - pcLinearPattern->Direction.Paste(dirLinks.getCurrentLink()); - } - } - catch (Base::Exception& e) { - QMessageBox::warning(nullptr, tr("Error"), QApplication::translate("Exception", e.what())); - } - - kickUpdateViewTimer(); -} - -void TaskLinearPatternParameters::onUpdateView(bool on) -{ - blockUpdate = !on; - if (on) { - // Do the same like in TaskDlgLinearPatternParameters::accept() but without doCommand - auto pcLinearPattern = getObject(); - std::vector directions; - App::DocumentObject* obj = nullptr; - - setupTransaction(); - getDirection(obj, directions); - pcLinearPattern->Direction.setValue(obj, directions); - pcLinearPattern->Reversed.setValue(getReverse()); - pcLinearPattern->Length.setValue(getLength()); - pcLinearPattern->Offset.setValue(getOffset()); - pcLinearPattern->Occurrences.setValue(getOccurrences()); - - recomputeFeature(); - } -} - -void TaskLinearPatternParameters::getDirection(App::DocumentObject*& obj, - std::vector& sub) const -{ - const App::PropertyLinkSub& lnk = dirLinks.getCurrentLink(); - obj = lnk.getValue(); - sub = lnk.getSubValues(); -} - -bool TaskLinearPatternParameters::getReverse() const -{ - return ui->checkReverse->isChecked(); -} - -int TaskLinearPatternParameters::getMode() const -{ - return ui->comboMode->currentIndex(); -} - -double TaskLinearPatternParameters::getLength() const -{ - return ui->spinLength->value().getValue(); -} - -double TaskLinearPatternParameters::getOffset() const -{ - return ui->spinOffset->value().getValue(); -} - -unsigned TaskLinearPatternParameters::getOccurrences() const -{ - return ui->spinOccurrences->value(); -} - -TaskLinearPatternParameters::~TaskLinearPatternParameters() -{ - try { - // hide the parts coordinate system axis for selection - PartDesign::Body* body = PartDesign::Body::findBodyOf(getObject()); - if (body) { - App::Origin* origin = body->getOrigin(); - auto vpOrigin = static_cast( - Gui::Application::Instance->getViewProvider(origin)); - vpOrigin->resetTemporaryVisibility(); - } - } - catch (const Base::Exception& ex) { - Base::Console().error("%s\n", ex.what()); - } -} - -void TaskLinearPatternParameters::apply() -{ - std::vector directions; - App::DocumentObject* obj = nullptr; - getDirection(obj, directions); - std::string direction = buildLinkSingleSubPythonStr(obj, directions); - - auto tobj = getObject(); - FCMD_OBJ_CMD(tobj, "Direction = " << direction); - FCMD_OBJ_CMD(tobj, "Reversed = " << getReverse()); - FCMD_OBJ_CMD(tobj, "Mode = " << getMode()); - ui->spinLength->apply(); - ui->spinOffset->apply(); - ui->spinOccurrences->apply(); -} - -//************************************************************************** -//************************************************************************** -// TaskDialog -//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - -TaskDlgLinearPatternParameters::TaskDlgLinearPatternParameters( - ViewProviderLinearPattern* LinearPatternView) - : TaskDlgTransformedParameters(LinearPatternView) -{ - parameter = new TaskLinearPatternParameters(LinearPatternView); - - Content.push_back(parameter); - Content.push_back(preview); -} - -#include "moc_TaskLinearPatternParameters.cpp" diff --git a/src/Mod/PartDesign/Gui/TaskLinearPatternParameters.ui b/src/Mod/PartDesign/Gui/TaskLinearPatternParameters.ui deleted file mode 100644 index 9390eb9fdc..0000000000 --- a/src/Mod/PartDesign/Gui/TaskLinearPatternParameters.ui +++ /dev/null @@ -1,188 +0,0 @@ - - - PartDesignGui::TaskLinearPatternParameters - - - - 0 - 0 - 270 - 188 - - - - Form - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - Direction - - - - - - - - - - - - Reverse direction - - - - - - - - - Mode - - - - - - - - Overall length - - - - - Offset - - - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Length - - - - - - - false - - - mm - - - 100.000000000000000 - - - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Offset - - - - - - - false - - - mm - - - 10.000000000000000 - - - - - - - - - - - - Occurrences - - - - - - - - - - - - - Gui::QuantitySpinBox - QWidget -
Gui/QuantitySpinBox.h
-
- - Gui::UIntSpinBox - QSpinBox -
Gui/SpinBox.h
- 1 -
-
- - comboDirection - checkReverse - spinLength - spinOccurrences - - - -
diff --git a/src/Mod/PartDesign/Gui/TaskMirroredParameters.cpp b/src/Mod/PartDesign/Gui/TaskMirroredParameters.cpp index 3d6267c869..0ec657f121 100644 --- a/src/Mod/PartDesign/Gui/TaskMirroredParameters.cpp +++ b/src/Mod/PartDesign/Gui/TaskMirroredParameters.cpp @@ -74,7 +74,7 @@ void TaskMirroredParameters::setupParameterUI(QWidget* widget) this, &TaskMirroredParameters::onPlaneChanged); - this->planeLinks.setCombo(*(ui->comboPlane)); + this->planeLinks.setCombo(ui->comboPlane); ui->comboPlane->setEnabled(true); App::DocumentObject* sketch = getSketchObject(); @@ -131,27 +131,22 @@ void TaskMirroredParameters::onSelectionChanged(const Gui::SelectionChanges& msg { if (selectionMode != SelectionMode::None && msg.Type == Gui::SelectionChanges::AddSelection) { - if (originalSelected(msg)) { - exitSelectionMode(); - } - else { - auto pcMirrored = getObject(); + auto pcMirrored = getObject(); - std::vector mirrorPlanes; - App::DocumentObject* selObj = nullptr; - getReferencedSelection(pcMirrored, msg, selObj, mirrorPlanes); - if (!selObj) { - return; - } - - if (selectionMode == SelectionMode::Reference || selObj->isDerivedFrom()) { - setupTransaction(); - pcMirrored->MirrorPlane.setValue(selObj, mirrorPlanes); - recomputeFeature(); - updateUI(); - } - exitSelectionMode(); + std::vector mirrorPlanes; + App::DocumentObject* selObj = nullptr; + getReferencedSelection(pcMirrored, msg, selObj, mirrorPlanes); + if (!selObj) { + return; } + + if (selectionMode == SelectionMode::Reference || selObj->isDerivedFrom()) { + setupTransaction(); + pcMirrored->MirrorPlane.setValue(selObj, mirrorPlanes); + recomputeFeature(); + updateUI(); + } + exitSelectionMode(); } } diff --git a/src/Mod/PartDesign/Gui/TaskMultiTransformParameters.cpp b/src/Mod/PartDesign/Gui/TaskMultiTransformParameters.cpp index 94e564a197..e4e25ae351 100644 --- a/src/Mod/PartDesign/Gui/TaskMultiTransformParameters.cpp +++ b/src/Mod/PartDesign/Gui/TaskMultiTransformParameters.cpp @@ -44,8 +44,7 @@ #include "ui_TaskMultiTransformParameters.h" #include "TaskMultiTransformParameters.h" #include "TaskMirroredParameters.h" -#include "TaskLinearPatternParameters.h" -#include "TaskPolarPatternParameters.h" +#include "TaskPatternParameters.h" #include "TaskScaledParameters.h" #include "Utils.h" @@ -170,6 +169,10 @@ void TaskMultiTransformParameters::closeSubTask() subTask->apply(); } + delete subTask; + subTask = nullptr; + subFeature = nullptr; + // Remove all parameter ui widgets and layout ui->subFeatureWidget->setUpdatesEnabled(false); qDeleteAll( @@ -177,10 +180,6 @@ void TaskMultiTransformParameters::closeSubTask() qDeleteAll( ui->subFeatureWidget->findChildren(QString(), Qt::FindDirectChildrenOnly)); ui->subFeatureWidget->setUpdatesEnabled(true); - - delete subTask; - subTask = nullptr; - subFeature = nullptr; } } @@ -230,11 +229,9 @@ void TaskMultiTransformParameters::onTransformEdit() if (subFeature->is()) { subTask = new TaskMirroredParameters(this, ui->subFeatureWidget); } - else if (subFeature->is()) { - subTask = new TaskLinearPatternParameters(this, ui->subFeatureWidget); - } - else if (subFeature->is()) { - subTask = new TaskPolarPatternParameters(this, ui->subFeatureWidget); + else if (subFeature->is() + || subFeature->is()) { + subTask = new TaskPatternParameters(this, ui->subFeatureWidget); } else if (subFeature->is()) { subTask = new TaskScaledParameters(this, ui->subFeatureWidget); diff --git a/src/Mod/PartDesign/Gui/TaskPatternParameters.cpp b/src/Mod/PartDesign/Gui/TaskPatternParameters.cpp new file mode 100644 index 0000000000..e7354a0cb9 --- /dev/null +++ b/src/Mod/PartDesign/Gui/TaskPatternParameters.cpp @@ -0,0 +1,383 @@ +/****************************************************************************** + * Copyright (c) 2012 Jan Rheinländer * + * * + * 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 +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ui_TaskPatternParameters.h" +#include "TaskPatternParameters.h" +#include "ReferenceSelection.h" +#include "TaskMultiTransformParameters.h" + + +using namespace PartDesignGui; +using namespace Gui; + +/* TRANSLATOR PartDesignGui::TaskPatternParameters */ + +TaskPatternParameters::TaskPatternParameters(ViewProviderTransformed* TransformedView, + QWidget* parent) + : TaskTransformedParameters(TransformedView, parent) + , ui(new Ui_TaskPatternParameters) +{ + setupUI(); +} + +TaskPatternParameters::TaskPatternParameters(TaskMultiTransformParameters* parentTask, + QWidget* parameterWidget) + : TaskTransformedParameters(parentTask) + , ui(new Ui::TaskPatternParameters) +{ + setupParameterUI(parameterWidget); +} + +void TaskPatternParameters::setupParameterUI(QWidget* widget) +{ + ui->setupUi(widget); // Setup the Task's own minimal UI (placeholder) + QMetaObject::connectSlotsByName(this); + + // --- Create and Embed the Parameter Widget --- + auto pattern = getObject(); + if (!pattern) { + return; + } + PartGui::PatternType type = pattern->isDerivedFrom() ? + PartGui::PatternType::Linear : PartGui::PatternType::Polar; + + // Set first direction widget + parametersWidget = new PartGui::PatternParametersWidget(type, widget); + + auto* placeholderLayout = new QVBoxLayout(ui->parametersWidgetPlaceholder); + placeholderLayout->setContentsMargins(0, 0, 0, 0); + placeholderLayout->addWidget(parametersWidget); + ui->parametersWidgetPlaceholder->setLayout(placeholderLayout); + + auto* sketch = dynamic_cast(getSketchObject()); + this->fillAxisCombo(parametersWidget->dirLinks, sketch); + connect(parametersWidget, &PartGui::PatternParametersWidget::requestReferenceSelection, + this, &TaskPatternParameters::onParameterWidgetRequestReferenceSelection); + connect(parametersWidget, &PartGui::PatternParametersWidget::parametersChanged, + this, &TaskPatternParameters::onParameterWidgetParametersChanged); + + // Add second direction widget if necessary + if (type == PartGui::PatternType::Linear) { + parametersWidget2 = new PartGui::PatternParametersWidget(type, widget); + auto* placeholderLayout2 = new QVBoxLayout(ui->parametersWidgetPlaceholder2); + placeholderLayout2->setContentsMargins(0, 0, 0, 0); + placeholderLayout2->addWidget(parametersWidget2); + ui->parametersWidgetPlaceholder2->setLayout(placeholderLayout2); + + this->fillAxisCombo(parametersWidget2->dirLinks, sketch); + connect(parametersWidget2, &PartGui::PatternParametersWidget::requestReferenceSelection, + this, &TaskPatternParameters::onParameterWidgetRequestReferenceSelection2); + connect(parametersWidget2, &PartGui::PatternParametersWidget::parametersChanged, + this, &TaskPatternParameters::onParameterWidgetParametersChanged); + parametersWidget2->setTitle(tr("Direction 2")); + } + + bindProperties(); + + // --- Task Specific Setup --- + showOriginAxes(true); // Show origin helper axes + + updateViewTimer = new QTimer(this); + updateViewTimer->setSingleShot(true); + updateViewTimer->setInterval(getUpdateViewTimeout()); + connect(updateViewTimer, &QTimer::timeout, this, &TaskPatternParameters::onUpdateViewTimer); +} + +void TaskPatternParameters::bindProperties() +{ + auto pattern = getObject(); + if (!pattern) { + return; + } + + if (pattern->isDerivedFrom()) { + auto* linear = static_cast(pattern); + parametersWidget->bindProperties(&linear->Direction, + &linear->Reversed, + &linear->Mode, + &linear->Length, + &linear->Offset, + &linear->SpacingPattern, + &linear->Occurrences, + linear); + parametersWidget2->bindProperties(&linear->Direction2, + &linear->Reversed2, + &linear->Mode2, + &linear->Length2, + &linear->Offset2, + &linear->SpacingPattern2, + &linear->Occurrences2, + linear); + } + else if (pattern->isDerivedFrom()) { + auto* polar = static_cast(pattern); + parametersWidget->bindProperties(&polar->Axis, + &polar->Reversed, + &polar->Mode, + &polar->Angle, + &polar->Offset, + &polar->SpacingPattern, + &polar->Occurrences, + polar); + } + else{ + Base::Console().warning("PatternParametersWidget property binding failed. Something is wrong please report.\n"); + } +} + +void TaskPatternParameters::retranslateParameterUI(QWidget* widget) +{ + ui->retranslateUi(widget); +} + +void TaskPatternParameters::updateUI() +{ + if (parametersWidget) { + parametersWidget->updateUI(); + } + if (parametersWidget2) { + parametersWidget2->updateUI(); + } +} + +// --- Task-Specific Logic --- + +void TaskPatternParameters::showOriginAxes(bool show) +{ + PartDesign::Body* body = PartDesign::Body::findBodyOf(getObject()); + if (body) { + try { + App::Origin* origin = body->getOrigin(); + auto vpOrigin = static_cast( + Gui::Application::Instance->getViewProvider(origin)); + if (show) { + vpOrigin->setTemporaryVisibility(Gui::DatumElement::Axes); + } + else { + vpOrigin->resetTemporaryVisibility(); + } + } + catch (const Base::Exception& ex) { + Base::Console().error("TaskPatternParameters: Error accessing origin axes: %s\n", ex.what()); + } + } +} + +void TaskPatternParameters::enterReferenceSelectionMode() +{ + if (selectionMode == SelectionMode::Reference) return; + + hideObject(); // Hide the pattern feature itself + showBase(); // Show the base features/body + Gui::Selection().clearSelection(); + // Add selection gate (allow edges, faces, potentially datums) + addReferenceSelectionGate(AllowSelection::EDGE | AllowSelection::FACE | AllowSelection::PLANAR); + Gui::getMainWindow()->showMessage(tr("Select a direction reference (edge, face, datum line)")); // User feedback +} + +void TaskPatternParameters::exitReferenceSelectionMode() +{ + exitSelectionMode(); + + hideBase(); + Gui::getMainWindow()->showMessage(QString()); + activeDirectionWidget = nullptr; +} + + +// --- SLOTS --- + +void TaskPatternParameters::onUpdateViewTimer() +{ + // Recompute is triggered when parameters change and this timer fires + setupTransaction(); // Group potential property changes + recomputeFeature(); + + updateUI(); +} + +void TaskPatternParameters::onParameterWidgetRequestReferenceSelection() +{ + // The embedded widget wants to enter reference selection mode + activeDirectionWidget = parametersWidget; + enterReferenceSelectionMode(); + selectionMode = SelectionMode::Reference; +} + +void TaskPatternParameters::onParameterWidgetRequestReferenceSelection2() +{ + // The embedded widget wants to enter reference selection mode + activeDirectionWidget = parametersWidget2; + enterReferenceSelectionMode(); + selectionMode = SelectionMode::Reference; +} + +void TaskPatternParameters::onParameterWidgetParametersChanged() +{ + // A parameter in the embedded widget changed, trigger a recompute + if (blockUpdate) return; // Avoid loops if change originated from Task update + kickUpdateViewTimer(); // Debounce recompute +} + +void TaskPatternParameters::onUpdateView(bool on) +{ + // This might be less relevant now if recomputes are triggered by parametersChanged + blockUpdate = !on; + if (on) { + kickUpdateViewTimer(); + } +} + +void TaskPatternParameters::kickUpdateViewTimer() const +{ + updateViewTimer->start(); +} + +void TaskPatternParameters::onSelectionChanged(const Gui::SelectionChanges& msg) +{ + // Handle selection ONLY when in reference selection mode + if (selectionMode == SelectionMode::None || msg.Type != Gui::SelectionChanges::AddSelection) { + return; + } + + auto patternObj = getObject(); + if (!patternObj) return; + + std::vector directions; + App::DocumentObject* selObj = nullptr; + getReferencedSelection(patternObj, msg, selObj, directions); + if (!selObj) { + Base::Console().warning(tr("Invalid selection. Select an edge, planar face, or datum line.").toStdString().c_str()); + return; + } + + // Note: ReferenceSelection has already checked the selection for validity + if (selectionMode == SelectionMode::Reference + || selObj->isDerivedFrom()) { + setupTransaction(); + + if (patternObj->isDerivedFrom()) { + auto* linearPattern = static_cast(patternObj); + if (activeDirectionWidget == parametersWidget) { + linearPattern->Direction.setValue(selObj, directions); + } + else { + linearPattern->Direction2.setValue(selObj, directions); + } + } + else if(patternObj->isDerivedFrom()) { + auto* polarPattern = static_cast(patternObj); + polarPattern->Axis.setValue(selObj, directions); + } + recomputeFeature(); + updateUI(); + } + exitReferenceSelectionMode(); +} + +TaskPatternParameters::~TaskPatternParameters() +{ + showOriginAxes(false); // Clean up temporary visibility + exitReferenceSelectionMode(); // Ensure gates are removed etc. + // ui unique_ptr handles deletion + // parametersWidget is deleted by Qt parent mechanism if added to layout correctly +} + +void TaskPatternParameters::apply() +{ + auto pattern = getObject(); + if (!pattern || !parametersWidget) { + return; + } + + std::vector dirs; + App::DocumentObject* obj = nullptr; + parametersWidget->getAxis(obj, dirs); + std::string direction = buildLinkSingleSubPythonStr(obj, dirs); + + bool isLinear = pattern->isDerivedFrom(); + const char* propName = isLinear ? "Direction = " : "Axis = "; + + FCMD_OBJ_CMD(pattern, propName << direction.c_str()); + FCMD_OBJ_CMD(pattern, "Reversed = " << parametersWidget->getReverse()); + FCMD_OBJ_CMD(pattern, "Mode = " << parametersWidget->getMode()); + parametersWidget->applyQuantitySpinboxes(); + + FCMD_OBJ_CMD(pattern, "SpacingPattern = " << parametersWidget->getSpacingPatternsAsString()); + + if (!parametersWidget2) { + return; + } + + parametersWidget2->getAxis(obj, dirs); + direction = buildLinkSingleSubPythonStr(obj, dirs); + + FCMD_OBJ_CMD(pattern, "Direction2 = " << direction.c_str()); + FCMD_OBJ_CMD(pattern, "Reversed2 = " << parametersWidget2->getReverse()); + FCMD_OBJ_CMD(pattern, "Mode2 = " << parametersWidget2->getMode()); + parametersWidget2->applyQuantitySpinboxes(); + + FCMD_OBJ_CMD(pattern, "SpacingPattern2 = " << parametersWidget2->getSpacingPatternsAsString()); +} + +//************************************************************************** +// TaskDialog Implementation (Remains largely the same) +//************************************************************************** + +TaskDlgLinearPatternParameters::TaskDlgLinearPatternParameters( + ViewProviderTransformed* LinearPatternView) + : TaskDlgTransformedParameters(LinearPatternView) // Use base class constructor +{ + // Create the specific parameter task panel + parameter = new TaskPatternParameters(LinearPatternView); + // Add it to the dialog's content list + Content.push_back(parameter); + Content.push_back(preview); +} + +#include "moc_TaskPatternParameters.cpp" + diff --git a/src/Mod/PartDesign/Gui/TaskLinearPatternParameters.h b/src/Mod/PartDesign/Gui/TaskPatternParameters.h similarity index 58% rename from src/Mod/PartDesign/Gui/TaskLinearPatternParameters.h rename to src/Mod/PartDesign/Gui/TaskPatternParameters.h index 81b38aaa82..1c3424d851 100644 --- a/src/Mod/PartDesign/Gui/TaskLinearPatternParameters.h +++ b/src/Mod/PartDesign/Gui/TaskPatternParameters.h @@ -1,4 +1,4 @@ -/****************************************************************************** + /****************************************************************************** * Copyright (c) 2012 Jan Rheinländer * * * * This file is part of the FreeCAD CAx development system. * @@ -20,24 +20,18 @@ * * ******************************************************************************/ -#ifndef GUI_TASKVIEW_TaskLinearPatternParameters_H -#define GUI_TASKVIEW_TaskLinearPatternParameters_H +#ifndef GUI_TASKVIEW_TaskPatternParameters_H +#define GUI_TASKVIEW_TaskPatternParameters_H #include "TaskTransformedParameters.h" -#include "ViewProviderLinearPattern.h" +#include "ViewProviderTransformed.h" class QTimer; -class Ui_TaskLinearPatternParameters; +class Ui_TaskPatternParameters; -namespace App -{ -class Property; -} - -namespace Gui -{ -class ViewProvider; +namespace PartGui { + class PatternParametersWidget; } namespace PartDesignGui @@ -45,17 +39,17 @@ namespace PartDesignGui class TaskMultiTransformParameters; -class TaskLinearPatternParameters: public TaskTransformedParameters +class TaskPatternParameters : public TaskTransformedParameters { Q_OBJECT public: /// Constructor for task with ViewProvider - explicit TaskLinearPatternParameters(ViewProviderTransformed* TransformedView, + explicit TaskPatternParameters(ViewProviderTransformed* TransformedView, QWidget* parent = nullptr); /// Constructor for task with parent task (MultiTransform mode) - TaskLinearPatternParameters(TaskMultiTransformParameters* parentTask, QWidget* parameterWidget); - ~TaskLinearPatternParameters() override; + TaskPatternParameters(TaskMultiTransformParameters* parentTask, QWidget* parameterWidget); + ~TaskPatternParameters() override; void apply() override; @@ -64,47 +58,49 @@ protected: private Q_SLOTS: void onUpdateViewTimer(); - void onDirectionChanged(int num); - void onCheckReverse(bool on); - void onModeChanged(int mode); - void onLength(double length); - void onOffset(double offset); - void onOccurrences(uint number); - void onUpdateView(bool /*unused*/) override; + // Slot to handle reference selection request from the widget + void onParameterWidgetRequestReferenceSelection(); + void onParameterWidgetRequestReferenceSelection2(); + // Slot to handle parameter changes from the widget + void onParameterWidgetParametersChanged(); + // Update view signal (might be redundant now) + void onUpdateView(bool on) override; + private: void setupParameterUI(QWidget* widget) override; void retranslateParameterUI(QWidget* widget) override; - void connectSignals(); void updateUI(); - void adaptVisibilityToMode(); void kickUpdateViewTimer() const; - void getDirection(App::DocumentObject*& obj, std::vector& sub) const; - bool getReverse() const; - int getMode() const; - double getLength() const; - double getOffset() const; - unsigned getOccurrences() const; + void bindProperties(); -private: - std::unique_ptr ui; + // Task-specific logic remains + void showOriginAxes(bool show); + void enterReferenceSelectionMode(); + void exitReferenceSelectionMode(); // Ensure this clears gates etc. + + PartGui::PatternParametersWidget* parametersWidget = nullptr; + PartGui::PatternParametersWidget* parametersWidget2 = nullptr; + + PartGui::PatternParametersWidget* activeDirectionWidget = nullptr; + + std::unique_ptr ui; QTimer* updateViewTimer = nullptr; - - Gui::ComboLinks dirLinks; }; /// simulation dialog for the TaskView -class TaskDlgLinearPatternParameters: public TaskDlgTransformedParameters +class TaskDlgLinearPatternParameters : public TaskDlgTransformedParameters { Q_OBJECT public: - explicit TaskDlgLinearPatternParameters(ViewProviderLinearPattern* LinearPatternView); + explicit TaskDlgLinearPatternParameters(ViewProviderTransformed* LinearPatternView); }; } // namespace PartDesignGui -#endif // GUI_TASKVIEW_TASKAPPERANCE_H +#endif // GUI_TASKVIEW_TaskPatternParameters_H + diff --git a/src/Mod/PartDesign/Gui/TaskPatternParameters.ui b/src/Mod/PartDesign/Gui/TaskPatternParameters.ui new file mode 100644 index 0000000000..fd2cf4e2ad --- /dev/null +++ b/src/Mod/PartDesign/Gui/TaskPatternParameters.ui @@ -0,0 +1,39 @@ + + + PartDesignGui::TaskPatternParameters + + + + 0 + 0 + 270 + 200 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + + diff --git a/src/Mod/PartDesign/Gui/TaskPolarPatternParameters.cpp b/src/Mod/PartDesign/Gui/TaskPolarPatternParameters.cpp deleted file mode 100644 index e37bbeacdc..0000000000 --- a/src/Mod/PartDesign/Gui/TaskPolarPatternParameters.cpp +++ /dev/null @@ -1,432 +0,0 @@ -/****************************************************************************** - * Copyright (c) 2012 Jan Rheinländer * - * * - * 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 -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ReferenceSelection.h" -#include "TaskMultiTransformParameters.h" -#include "Utils.h" - -#include "ui_TaskPolarPatternParameters.h" -#include "TaskPolarPatternParameters.h" - -using namespace PartDesignGui; -using namespace Gui; - -/* TRANSLATOR PartDesignGui::TaskPolarPatternParameters */ - -TaskPolarPatternParameters::TaskPolarPatternParameters(ViewProviderTransformed* TransformedView, - QWidget* parent) - : TaskTransformedParameters(TransformedView, parent) - , ui(new Ui_TaskPolarPatternParameters) -{ - setupUI(); -} - -TaskPolarPatternParameters::TaskPolarPatternParameters(TaskMultiTransformParameters* parentTask, - QWidget* parameterWidget) - : TaskTransformedParameters(parentTask) - , ui(new Ui_TaskPolarPatternParameters) -{ - setupParameterUI(parameterWidget); -} - -void TaskPolarPatternParameters::setupParameterUI(QWidget* widget) -{ - ui->setupUi(widget); - QMetaObject::connectSlotsByName(this); - - // Get the feature data - auto pcPolarPattern = getObject(); - - ui->polarAngle->bind(pcPolarPattern->Angle); - ui->angleOffset->bind(pcPolarPattern->Offset); - - ui->spinOccurrences->bind(pcPolarPattern->Occurrences); - ui->spinOccurrences->setMaximum(pcPolarPattern->Occurrences.getMaximum()); - ui->spinOccurrences->setMinimum(pcPolarPattern->Occurrences.getMinimum()); - - ui->comboAxis->setEnabled(true); - ui->comboMode->setEnabled(true); - ui->checkReverse->setEnabled(true); - ui->polarAngle->setEnabled(true); - ui->spinOccurrences->setEnabled(true); - - this->axesLinks.setCombo(*(ui->comboAxis)); - App::DocumentObject* sketch = getSketchObject(); - if (sketch && sketch->isDerivedFrom()) { - this->fillAxisCombo(axesLinks, static_cast(sketch)); - } - else { - this->fillAxisCombo(axesLinks, nullptr); - } - - // show the parts coordinate system axis for selection - PartDesign::Body* body = PartDesign::Body::findBodyOf(getObject()); - - if (body) { - try { - App::Origin* origin = body->getOrigin(); - auto vpOrigin = static_cast( - Gui::Application::Instance->getViewProvider(origin)); - vpOrigin->setTemporaryVisibility(Gui::DatumElement::Axes); - } - catch (const Base::Exception& ex) { - Base::Console().error("%s\n", ex.what()); - } - } - - adaptVisibilityToMode(); - updateUI(); - - updateViewTimer = new QTimer(this); - updateViewTimer->setSingleShot(true); - updateViewTimer->setInterval(getUpdateViewTimeout()); - connect(updateViewTimer, - &QTimer::timeout, - this, - &TaskPolarPatternParameters::onUpdateViewTimer); - connect(ui->comboAxis, - qOverload(&QComboBox::activated), - this, - &TaskPolarPatternParameters::onAxisChanged); - connect(ui->comboMode, - qOverload(&QComboBox::activated), - this, - &TaskPolarPatternParameters::onModeChanged); - connect(ui->checkReverse, - &QCheckBox::toggled, - this, - &TaskPolarPatternParameters::onCheckReverse); - connect(ui->polarAngle, - qOverload(&Gui::QuantitySpinBox::valueChanged), - this, - &TaskPolarPatternParameters::onAngle); - connect(ui->angleOffset, - qOverload(&Gui::QuantitySpinBox::valueChanged), - this, - &TaskPolarPatternParameters::onOffset); - connect(ui->spinOccurrences, - &Gui::UIntSpinBox::unsignedChanged, - this, - &TaskPolarPatternParameters::onOccurrences); -} - -void TaskPolarPatternParameters::retranslateParameterUI(QWidget* widget) -{ - ui->retranslateUi(widget); -} - -void TaskPolarPatternParameters::updateUI() -{ - if (blockUpdate) { - return; - } - blockUpdate = true; - - auto pcPolarPattern = getObject(); - - auto mode = static_cast(pcPolarPattern->Mode.getValue()); - bool reverse = pcPolarPattern->Reversed.getValue(); - double angle = pcPolarPattern->Angle.getValue(); - double offset = pcPolarPattern->Offset.getValue(); - unsigned occurrences = pcPolarPattern->Occurrences.getValue(); - - if (axesLinks.setCurrentLink(pcPolarPattern->Axis) == -1) { - // failed to set current, because the link isn't in the list yet - axesLinks.addLink( - pcPolarPattern->Axis, - getRefStr(pcPolarPattern->Axis.getValue(), pcPolarPattern->Axis.getSubValues())); - axesLinks.setCurrentLink(pcPolarPattern->Axis); - } - - // Note: This block of code would trigger change signal handlers (e.g. onOccurrences()) - // and another updateUI() if we didn't check for blockUpdate - ui->checkReverse->setChecked(reverse); - ui->comboMode->setCurrentIndex(static_cast(mode)); - ui->polarAngle->setValue(angle); - ui->angleOffset->setValue(offset); - ui->spinOccurrences->setValue(occurrences); - - blockUpdate = false; -} - -void TaskPolarPatternParameters::onUpdateViewTimer() -{ - setupTransaction(); - recomputeFeature(); -} - -void TaskPolarPatternParameters::kickUpdateViewTimer() const -{ - updateViewTimer->start(); -} - -void TaskPolarPatternParameters::adaptVisibilityToMode() -{ - auto pcLinearPattern = getObject(); - auto mode = static_cast(pcLinearPattern->Mode.getValue()); - - ui->polarAngleWrapper->setVisible(mode == PartDesign::PolarPatternMode::angle); - ui->angleOffsetWrapper->setVisible(mode == PartDesign::PolarPatternMode::offset); -} - -void TaskPolarPatternParameters::onSelectionChanged(const Gui::SelectionChanges& msg) -{ - if (selectionMode != SelectionMode::None && msg.Type == Gui::SelectionChanges::AddSelection) { - if (originalSelected(msg)) { - exitSelectionMode(); - } - else { - auto pcPolarPattern = getObject(); - - std::vector axes; - App::DocumentObject* selObj = nullptr; - getReferencedSelection(pcPolarPattern, msg, selObj, axes); - if (!selObj) { - return; - } - - if (selectionMode == SelectionMode::Reference || selObj->isDerivedFrom()) { - setupTransaction(); - pcPolarPattern->Axis.setValue(selObj, axes); - recomputeFeature(); - updateUI(); - } - exitSelectionMode(); - } - } -} - -void TaskPolarPatternParameters::onCheckReverse(const bool on) -{ - if (blockUpdate) { - return; - } - auto pcPolarPattern = getObject(); - pcPolarPattern->Reversed.setValue(on); - - exitSelectionMode(); - kickUpdateViewTimer(); -} - -void TaskPolarPatternParameters::onModeChanged(const int mode) -{ - if (blockUpdate) { - return; - } - auto pcPolarPattern = getObject(); - pcPolarPattern->Mode.setValue(mode); - - adaptVisibilityToMode(); - - exitSelectionMode(); - kickUpdateViewTimer(); -} - -void TaskPolarPatternParameters::onAngle(const double angle) -{ - if (blockUpdate) { - return; - } - auto pcPolarPattern = getObject(); - pcPolarPattern->Angle.setValue(angle); - - exitSelectionMode(); - kickUpdateViewTimer(); -} - -void TaskPolarPatternParameters::onOffset(const double offset) -{ - if (blockUpdate) { - return; - } - auto pcPolarPattern = getObject(); - pcPolarPattern->Offset.setValue(offset); - - exitSelectionMode(); - kickUpdateViewTimer(); -} - -void TaskPolarPatternParameters::onOccurrences(const uint n) -{ - if (blockUpdate) { - return; - } - auto pcPolarPattern = getObject(); - pcPolarPattern->Occurrences.setValue(n); - - exitSelectionMode(); - kickUpdateViewTimer(); -} - -void TaskPolarPatternParameters::onAxisChanged(int /*num*/) -{ - if (blockUpdate) { - return; - } - auto pcPolarPattern = getObject(); - - try { - if (!axesLinks.getCurrentLink().getValue()) { - // enter reference selection mode - hideObject(); - showBase(); - selectionMode = SelectionMode::Reference; - Gui::Selection().clearSelection(); - addReferenceSelectionGate(AllowSelection::EDGE | AllowSelection::CIRCLE); - } - else { - exitSelectionMode(); - pcPolarPattern->Axis.Paste(axesLinks.getCurrentLink()); - } - } - catch (Base::Exception& e) { - QMessageBox::warning(nullptr, tr("Error"), QApplication::translate("Exception", e.what())); - } - - kickUpdateViewTimer(); -} - -void TaskPolarPatternParameters::onUpdateView(bool on) -{ - blockUpdate = !on; - if (on) { - // Do the same like in TaskDlgPolarPatternParameters::accept() but without doCommand - auto pcPolarPattern = getObject(); - std::vector axes; - App::DocumentObject* obj = nullptr; - - setupTransaction(); - getAxis(obj, axes); - pcPolarPattern->Axis.setValue(obj, axes); - pcPolarPattern->Reversed.setValue(getReverse()); - pcPolarPattern->Angle.setValue(getAngle()); - pcPolarPattern->Occurrences.setValue(getOccurrences()); - - recomputeFeature(); - } -} - -void TaskPolarPatternParameters::getAxis(App::DocumentObject*& obj, - std::vector& sub) const -{ - const App::PropertyLinkSub& lnk = axesLinks.getCurrentLink(); - obj = lnk.getValue(); - sub = lnk.getSubValues(); -} - -bool TaskPolarPatternParameters::getReverse() const -{ - return ui->checkReverse->isChecked(); -} - -int TaskPolarPatternParameters::getMode() const -{ - return ui->comboMode->currentIndex(); -} - -double TaskPolarPatternParameters::getAngle() const -{ - return ui->polarAngle->value().getValue(); -} - -unsigned TaskPolarPatternParameters::getOccurrences() const -{ - return ui->spinOccurrences->value(); -} - - -TaskPolarPatternParameters::~TaskPolarPatternParameters() -{ - // hide the parts coordinate system axis for selection - try { - PartDesign::Body* body = PartDesign::Body::findBodyOf(getObject()); - if (body) { - App::Origin* origin = body->getOrigin(); - auto vpOrigin = static_cast( - Gui::Application::Instance->getViewProvider(origin)); - vpOrigin->resetTemporaryVisibility(); - } - } - catch (const Base::Exception& ex) { - Base::Console().error("%s\n", ex.what()); - } -} - -void TaskPolarPatternParameters::apply() -{ - std::vector axes; - App::DocumentObject* obj = nullptr; - getAxis(obj, axes); - std::string axis = buildLinkSingleSubPythonStr(obj, axes); - - auto tobj = getObject(); - FCMD_OBJ_CMD(tobj, "Axis = " << axis.c_str()); - FCMD_OBJ_CMD(tobj, "Reversed = " << getReverse()); - FCMD_OBJ_CMD(tobj, "Mode = " << getMode()); - ui->polarAngle->apply(); - ui->angleOffset->apply(); - ui->spinOccurrences->apply(); -} - -//************************************************************************** -//************************************************************************** -// TaskDialog -//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - -TaskDlgPolarPatternParameters::TaskDlgPolarPatternParameters( - ViewProviderPolarPattern* PolarPatternView) - : TaskDlgTransformedParameters(PolarPatternView) -{ - parameter = new TaskPolarPatternParameters(PolarPatternView); - - Content.push_back(parameter); - Content.push_back(preview); -} - -#include "moc_TaskPolarPatternParameters.cpp" diff --git a/src/Mod/PartDesign/Gui/TaskPolarPatternParameters.h b/src/Mod/PartDesign/Gui/TaskPolarPatternParameters.h deleted file mode 100644 index b961fd67b9..0000000000 --- a/src/Mod/PartDesign/Gui/TaskPolarPatternParameters.h +++ /dev/null @@ -1,111 +0,0 @@ -/****************************************************************************** - * Copyright (c) 2012 Jan Rheinländer * - * * - * 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_TASKVIEW_TaskPolarPatternParameters_H -#define GUI_TASKVIEW_TaskPolarPatternParameters_H - -#include "TaskTransformedParameters.h" -#include "ViewProviderPolarPattern.h" - - -class QTimer; -class Ui_TaskPolarPatternParameters; - -namespace App -{ -class Property; -} - -namespace Gui -{ -class ViewProvider; -} - -namespace PartDesignGui -{ - -class TaskMultiTransformParameters; - -class TaskPolarPatternParameters: public TaskTransformedParameters -{ - Q_OBJECT - -public: - /// Constructor for task with ViewProvider - explicit TaskPolarPatternParameters(ViewProviderTransformed* TransformedView, - QWidget* parent = nullptr); - /// Constructor for task with parent task (MultiTransform mode) - TaskPolarPatternParameters(TaskMultiTransformParameters* parentTask, QWidget* parameterWidget); - ~TaskPolarPatternParameters() override; - - void apply() override; - -protected: - void onSelectionChanged(const Gui::SelectionChanges& msg) override; - -private Q_SLOTS: - void onUpdateViewTimer(); - void onAxisChanged(int num); - void onModeChanged(int mode); - void onCheckReverse(bool on); - void onAngle(double angle); - void onOffset(double offset); - void onOccurrences(uint number); - void onUpdateView(bool /*unused*/) override; - -private: - void setupParameterUI(QWidget* widget) override; - void retranslateParameterUI(QWidget* widget) override; - - void connectSignals(); - void updateUI(); - void kickUpdateViewTimer() const; - void adaptVisibilityToMode(); - - void getAxis(App::DocumentObject*& obj, std::vector& sub) const; - const std::string getStdAxis() const; - const std::string getAxis() const; - bool getReverse() const; - int getMode() const; - double getAngle() const; - unsigned getOccurrences() const; - -private: - std::unique_ptr ui; - QTimer* updateViewTimer = nullptr; - - Gui::ComboLinks axesLinks; -}; - - -/// simulation dialog for the TaskView -class TaskDlgPolarPatternParameters: public TaskDlgTransformedParameters -{ - Q_OBJECT - -public: - explicit TaskDlgPolarPatternParameters(ViewProviderPolarPattern* PolarPatternView); -}; - -} // namespace PartDesignGui - -#endif // GUI_TASKVIEW_TASKAPPERANCE_H diff --git a/src/Mod/PartDesign/Gui/TaskPolarPatternParameters.ui b/src/Mod/PartDesign/Gui/TaskPolarPatternParameters.ui deleted file mode 100644 index ea1ba5896d..0000000000 --- a/src/Mod/PartDesign/Gui/TaskPolarPatternParameters.ui +++ /dev/null @@ -1,200 +0,0 @@ - - - PartDesignGui::TaskPolarPatternParameters - - - - 0 - 0 - 253 - 206 - - - - Form - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - Axis - - - - - - - - - - - - Reverse direction - - - - - - - - - Mode - - - - - - - - Overall angle - - - - - Offset angle - - - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Angle - - - - - - - false - - - deg - - - 0.000000000000000 - - - 360.000000000000000 - - - 360.000000000000000 - - - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Offset - - - - - - - false - - - deg - - - 0.000000000000000 - - - 360.000000000000000 - - - 360.000000000000000 - - - - - - - - - - - - Occurrences - - - - - - - - - - - - - Gui::QuantitySpinBox - QWidget -
Gui/QuantitySpinBox.h
-
- - Gui::UIntSpinBox - QSpinBox -
Gui/SpinBox.h
- 1 -
-
- - comboAxis - checkReverse - polarAngle - spinOccurrences - - - -
diff --git a/src/Mod/PartDesign/Gui/TaskTransformedParameters.cpp b/src/Mod/PartDesign/Gui/TaskTransformedParameters.cpp index d08005e4be..90d6bdd523 100644 --- a/src/Mod/PartDesign/Gui/TaskTransformedParameters.cpp +++ b/src/Mod/PartDesign/Gui/TaskTransformedParameters.cpp @@ -393,15 +393,15 @@ void TaskTransformedParameters::removeItemFromListWidget(QListWidget* widget, } } -void TaskTransformedParameters::fillAxisCombo(ComboLinks& combolinks, Part::Part2DObject* sketch) +void TaskTransformedParameters::fillAxisCombo(Gui::ComboLinks& combolinks, Part::Part2DObject* sketch) { combolinks.clear(); // add sketch axes if (sketch) { - combolinks.addLink(sketch, "N_Axis", tr("Normal sketch axis")); - combolinks.addLink(sketch, "V_Axis", tr("Vertical sketch axis")); combolinks.addLink(sketch, "H_Axis", tr("Horizontal sketch axis")); + combolinks.addLink(sketch, "V_Axis", tr("Vertical sketch axis")); + combolinks.addLink(sketch, "N_Axis", tr("Normal sketch axis")); for (int i = 0; i < sketch->getAxisCount(); i++) { QString itemText = tr("Construction line %1").arg(i + 1); std::stringstream sub; @@ -430,7 +430,8 @@ void TaskTransformedParameters::fillAxisCombo(ComboLinks& combolinks, Part::Part combolinks.addLink(nullptr, std::string(), tr("Select reference…")); } -void TaskTransformedParameters::fillPlanesCombo(ComboLinks& combolinks, Part::Part2DObject* sketch) +void TaskTransformedParameters::fillPlanesCombo(Gui::ComboLinks& combolinks, + Part::Part2DObject* sketch) { combolinks.clear(); diff --git a/src/Mod/PartDesign/Gui/TaskTransformedParameters.h b/src/Mod/PartDesign/Gui/TaskTransformedParameters.h index 23aaa39110..2aab1f5a5a 100644 --- a/src/Mod/PartDesign/Gui/TaskTransformedParameters.h +++ b/src/Mod/PartDesign/Gui/TaskTransformedParameters.h @@ -139,9 +139,9 @@ protected: void onSelectionChanged(const Gui::SelectionChanges& msg) override; /// Fill combobox with the axis from the sketch and the own bodys origin axis - void fillAxisCombo(ComboLinks& combolinks, Part::Part2DObject* sketch); + void fillAxisCombo(Gui::ComboLinks& combolinks, Part::Part2DObject* sketch); /// Fill combobox with the planes from the sketch and the own bodys origin planes - void fillPlanesCombo(ComboLinks& combolinks, Part::Part2DObject* sketch); + void fillPlanesCombo(Gui::ComboLinks& combolinks, Part::Part2DObject* sketch); /** * Returns the base transformed objectfromStdString diff --git a/src/Mod/PartDesign/Gui/ViewProviderLinearPattern.cpp b/src/Mod/PartDesign/Gui/ViewProviderLinearPattern.cpp index 49e311795f..d4e20fafbd 100644 --- a/src/Mod/PartDesign/Gui/ViewProviderLinearPattern.cpp +++ b/src/Mod/PartDesign/Gui/ViewProviderLinearPattern.cpp @@ -24,7 +24,7 @@ #include "PreCompiled.h" #include "ViewProviderLinearPattern.h" -#include "TaskLinearPatternParameters.h" +#include "TaskPatternParameters.h" using namespace PartDesignGui; diff --git a/src/Mod/PartDesign/Gui/ViewProviderPolarPattern.cpp b/src/Mod/PartDesign/Gui/ViewProviderPolarPattern.cpp index c94ad1ca6d..53f33bffc6 100644 --- a/src/Mod/PartDesign/Gui/ViewProviderPolarPattern.cpp +++ b/src/Mod/PartDesign/Gui/ViewProviderPolarPattern.cpp @@ -23,14 +23,14 @@ #include "PreCompiled.h" #include "ViewProviderPolarPattern.h" -#include "TaskPolarPatternParameters.h" +#include "TaskPatternParameters.h" using namespace PartDesignGui; PROPERTY_SOURCE(PartDesignGui::ViewProviderPolarPattern,PartDesignGui::ViewProviderTransformed) TaskDlgFeatureParameters *ViewProviderPolarPattern::getEditDialog() { - return new TaskDlgPolarPatternParameters (this); + return new TaskDlgLinearPatternParameters(this); } void ViewProviderPolarPattern::setupContextMenu(QMenu* menu, QObject* receiver, const char* member)