diff --git a/src/Mod/PartDesign/App/FeatureLinearPattern.cpp b/src/Mod/PartDesign/App/FeatureLinearPattern.cpp index fe862b5099..7826abf175 100644 --- a/src/Mod/PartDesign/App/FeatureLinearPattern.cpp +++ b/src/Mod/PartDesign/App/FeatureLinearPattern.cpp @@ -51,25 +51,42 @@ PROPERTY_SOURCE(PartDesign::LinearPattern, PartDesign::Transformed) const App::PropertyIntegerConstraint::Constraints LinearPattern::intOccurrences = { 1, INT_MAX, 1 }; +const char* LinearPattern::ModeEnums[] = { "length", "offset", nullptr }; + LinearPattern::LinearPattern() { + auto initialMode = LinearPatternMode::length; + 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)); Occurrences.setConstraints(&intOccurrences); + Mode.setEnums(ModeEnums); + setReadWriteStatusForMode(initialMode); } short LinearPattern::mustExecute() const { if (Direction.isTouched() || Reversed.isTouched() || - Length.isTouched() || + Mode.isTouched() || + // Length and Offset are mutually exclusive, only one could be updated at once + Length.isTouched() || + Offset.isTouched() || Occurrences.isTouched()) return 1; return Transformed::mustExecute(); } +void LinearPattern::setReadWriteStatusForMode(LinearPatternMode mode) +{ + Length.setReadOnly(mode != LinearPatternMode::length); + Offset.setReadOnly(mode != LinearPatternMode::offset); +} + const std::list LinearPattern::getTransformations(const std::vector) { int occurrences = Occurrences.getValue(); @@ -173,7 +190,20 @@ const std::list LinearPattern::getTransformations(const std::vector(Mode.getValue())) { + case LinearPatternMode::length: + offset *= distance / (occurrences - 1); + break; + + case LinearPatternMode::offset: + offset *= Offset.getValue(); + break; + + default: + throw Base::ValueError("Invalid mode"); + } + if (reversed) offset.Reverse(); @@ -206,4 +236,14 @@ void LinearPattern::handleChangedPropertyType(Base::XMLReader& reader, const cha } } +void LinearPattern::onChanged(const App::Property* prop) +{ + if (prop == &Mode) { + auto mode = static_cast(Mode.getValue()); + setReadWriteStatusForMode(mode); + } + + Transformed::onChanged(prop); +} + } diff --git a/src/Mod/PartDesign/App/FeatureLinearPattern.h b/src/Mod/PartDesign/App/FeatureLinearPattern.h index 19e6d860be..f538335958 100644 --- a/src/Mod/PartDesign/App/FeatureLinearPattern.h +++ b/src/Mod/PartDesign/App/FeatureLinearPattern.h @@ -29,6 +29,10 @@ namespace PartDesign { +enum class LinearPatternMode { + length, + offset +}; class PartDesignExport LinearPattern : public PartDesign::Transformed { @@ -37,9 +41,11 @@ class PartDesignExport LinearPattern : public PartDesign::Transformed public: LinearPattern(); - App::PropertyLinkSub Direction; - App::PropertyBool Reversed; - App::PropertyLength Length; + App::PropertyLinkSub Direction; + App::PropertyBool Reversed; + App::PropertyEnumeration Mode; + App::PropertyLength Length; + App::PropertyLength Offset; App::PropertyIntegerConstraint Occurrences; /** @name methods override feature */ @@ -54,11 +60,18 @@ public: /** Create transformations * Returns a list of (Occurrences - 1) transformations since the first, untransformed instance - * is not counted. Each transformation will move the shape it is applied to by the distance - * (Length / (Occurrences - 1)) so that the transformations will cover the total Length. + * is not counted. + * + * Depending on Mode selection list will be constructed differently: + * 1. For "Overall Length" each transformation will move the shape it is applied to by the distance + * (Length / (Occurrences - 1)) so that the transformations will cover the total Length. + * 2. For "Spacing" each transformation will move the shape by the distance explicitly given in + * the Offset parameter. + * * If Direction contains a feature and a face name, then the transformation direction will be * the normal of the given face, which must be planar. If it contains an edge name, then the * transformation direction will be parallel to the given edge, which must be linear + * * If Reversed is true, the direction of transformation will be opposite */ const std::list getTransformations(const std::vector ) override; @@ -66,6 +79,12 @@ public: protected: void handleChangedPropertyType(Base::XMLReader& reader, const char* TypeName, App::Property* prop) override; static const App::PropertyIntegerConstraint::Constraints intOccurrences; + void onChanged(const App::Property* prop) override; + +private: + static const char* ModeEnums[]; + + void setReadWriteStatusForMode(LinearPatternMode mode); }; } //namespace PartDesign diff --git a/src/Mod/PartDesign/Gui/TaskLinearPatternParameters.cpp b/src/Mod/PartDesign/Gui/TaskLinearPatternParameters.cpp index f28fcb9e5d..8ed53aac18 100644 --- a/src/Mod/PartDesign/Gui/TaskLinearPatternParameters.cpp +++ b/src/Mod/PartDesign/Gui/TaskLinearPatternParameters.cpp @@ -127,8 +127,12 @@ void TaskLinearPatternParameters::connectSignals() 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); connect(ui->checkBoxUpdateView, &QCheckBox::toggled, @@ -153,16 +157,22 @@ void TaskLinearPatternParameters::setupUI() // --------------------- 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)); @@ -187,6 +197,7 @@ void TaskLinearPatternParameters::setupUI() } } + adaptVisibilityToMode(); updateUI(); connectSignals(); } @@ -198,9 +209,11 @@ void TaskLinearPatternParameters::updateUI() blockUpdate = true; PartDesign::LinearPattern* pcLinearPattern = static_cast(getObject()); + PartDesign::LinearPatternMode 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){ @@ -210,15 +223,26 @@ void TaskLinearPatternParameters::updateUI() dirLinks.setCurrentLink(pcLinearPattern->Direction); } - // Note: These three lines would trigger onLength(), on Occurrences() and another updateUI() if we - // didn't check for blockUpdate + // 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((long)mode); ui->spinLength->setValue(length); + ui->spinOffset->setValue(offset); ui->spinOccurrences->setValue(occurrences); blockUpdate = false; } +void TaskLinearPatternParameters::adaptVisibilityToMode() +{ + auto pcLinearPattern = static_cast(getObject()); + auto mode = static_cast(pcLinearPattern->Mode.getValue()); + + ui->lengthWrapper->setVisible(mode == PartDesign::LinearPatternMode::length); + ui->offsetWrapper->setVisible(mode == PartDesign::LinearPatternMode::offset); +} + void TaskLinearPatternParameters::onUpdateViewTimer() { setupTransaction(); @@ -294,6 +318,18 @@ void TaskLinearPatternParameters::onCheckReverse(const bool on) { kickUpdateViewTimer(); } +void TaskLinearPatternParameters::onModeChanged(const int mode) { + if (blockUpdate) + return; + PartDesign::LinearPattern* pcLinearPattern = static_cast(getObject()); + pcLinearPattern->Mode.setValue(mode); + + adaptVisibilityToMode(); + + exitSelectionMode(); + kickUpdateViewTimer(); +} + void TaskLinearPatternParameters::onLength(const double l) { if (blockUpdate) return; @@ -304,6 +340,16 @@ void TaskLinearPatternParameters::onLength(const double l) { kickUpdateViewTimer(); } +void TaskLinearPatternParameters::onOffset(const double o) { + if (blockUpdate) + return; + PartDesign::LinearPattern* pcLinearPattern = static_cast(getObject()); + pcLinearPattern->Offset.setValue(o); + + exitSelectionMode(); + kickUpdateViewTimer(); +} + void TaskLinearPatternParameters::onOccurrences(const uint n) { if (blockUpdate) return; @@ -352,6 +398,7 @@ void TaskLinearPatternParameters::onUpdateView(bool on) pcLinearPattern->Direction.setValue(obj,directions); pcLinearPattern->Reversed.setValue(getReverse()); pcLinearPattern->Length.setValue(getLength()); + pcLinearPattern->Offset.setValue(getOffset()); pcLinearPattern->Occurrences.setValue(getOccurrences()); recomputeFeature(); @@ -386,11 +433,21 @@ 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(); @@ -436,6 +493,7 @@ void TaskLinearPatternParameters::apply() FCMD_OBJ_CMD(tobj,"Reversed = " << getReverse()); ui->spinLength->apply(); + ui->spinOffset->apply(); ui->spinOccurrences->apply(); } diff --git a/src/Mod/PartDesign/Gui/TaskLinearPatternParameters.h b/src/Mod/PartDesign/Gui/TaskLinearPatternParameters.h index 7edc47f5d0..41cb67733e 100644 --- a/src/Mod/PartDesign/Gui/TaskLinearPatternParameters.h +++ b/src/Mod/PartDesign/Gui/TaskLinearPatternParameters.h @@ -59,7 +59,9 @@ private Q_SLOTS: void onUpdateViewTimer(); void onDirectionChanged(int num); void onCheckReverse(const bool on); + void onModeChanged(const int mode); void onLength(const double l); + void onOffset(const double o); void onOccurrences(const uint n); void onUpdateView(bool) override; void onFeatureDeleted() override; @@ -72,13 +74,16 @@ protected: void clearButtons() override; void getDirection(App::DocumentObject*& obj, std::vector& sub) const; bool getReverse() const; + int getMode() const; double getLength() const; + double getOffset() const; unsigned getOccurrences() const; private: void connectSignals(); void setupUI(); void updateUI(); + void adaptVisibilityToMode(); void kickUpdateViewTimer() const; private: diff --git a/src/Mod/PartDesign/Gui/TaskLinearPatternParameters.ui b/src/Mod/PartDesign/Gui/TaskLinearPatternParameters.ui index 751eccc4b0..7c97a94511 100644 --- a/src/Mod/PartDesign/Gui/TaskLinearPatternParameters.ui +++ b/src/Mod/PartDesign/Gui/TaskLinearPatternParameters.ui @@ -70,29 +70,106 @@ - + - + - Length + Mode - - - false - - - mm - - - 100.000000000000000 - + + + + Overall Length + + + + + Offset + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Length + + + + + + + false + + + mm + + + 100.000000000000000 + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Offset + + + + + + + false + + + mm + + + 10.000000000000000 + + + + + +