PD: Add offset / overall length modes for LinearPattern

This commit adds support for two separate modes of defining distance
between elements in PD's Linear Pattern.

1. Overall Length - which works exactly like it works before,
2. Spacing - which allows user to explicitly define distance (offset) between
   features.
This commit is contained in:
al
2022-05-12 00:52:05 +12:00
committed by Kacper Donat
parent 9dd4779252
commit b7da44f57b
5 changed files with 221 additions and 22 deletions

View File

@@ -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<gp_Trsf> LinearPattern::getTransformations(const std::vector<App::DocumentObject*>)
{
int occurrences = Occurrences.getValue();
@@ -173,7 +190,20 @@ const std::list<gp_Trsf> LinearPattern::getTransformations(const std::vector<App
dir.Transform(invObjLoc.Transformation());
gp_Vec offset(dir.X(), dir.Y(), dir.Z());
offset *= distance / (occurrences - 1);
switch (static_cast<LinearPatternMode>(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<LinearPatternMode>(Mode.getValue());
setReadWriteStatusForMode(mode);
}
Transformed::onChanged(prop);
}
}

View File

@@ -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<gp_Trsf> getTransformations(const std::vector<App::DocumentObject*> ) 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

View File

@@ -127,8 +127,12 @@ void TaskLinearPatternParameters::connectSignals()
this, &TaskLinearPatternParameters::onDirectionChanged);
connect(ui->checkReverse, &QCheckBox::toggled,
this, &TaskLinearPatternParameters::onCheckReverse);
connect(ui->comboMode, qOverload<int>(&QComboBox::activated),
this, &TaskLinearPatternParameters::onModeChanged);
connect(ui->spinLength, qOverload<double>(&Gui::QuantitySpinBox::valueChanged),
this, &TaskLinearPatternParameters::onLength);
connect(ui->spinOffset, qOverload<double>(&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<PartDesign::LinearPattern*>(getObject());
PartDesign::LinearPatternMode mode = static_cast<PartDesign::LinearPatternMode>(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<PartDesign::LinearPattern*>(getObject());
auto mode = static_cast<PartDesign::LinearPatternMode>(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<PartDesign::LinearPattern*>(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<PartDesign::LinearPattern*>(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();
}

View File

@@ -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<std::string>& 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:

View File

@@ -70,29 +70,106 @@
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<layout class="QHBoxLayout">
<item>
<widget class="QLabel" name="label_2">
<widget class="QLabel" name="labelMode">
<property name="text">
<string>Length</string>
<string>Mode</string>
</property>
</widget>
</item>
<item>
<widget class="Gui::QuantitySpinBox" name="spinLength">
<property name="keyboardTracking">
<bool>false</bool>
</property>
<property name="unit" stdset="0">
<string notr="true">mm</string>
</property>
<property name="value">
<double>100.000000000000000</double>
</property>
<widget class="QComboBox" name="comboMode">
<item>
<property name="text">
<string>Overall Length</string>
</property>
</item>
<item>
<property name="text">
<string>Offset</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QWidget" name="lengthWrapper" native="true">
<layout class="QHBoxLayout" name="_3">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="labelLength">
<property name="text">
<string>Length</string>
</property>
</widget>
</item>
<item>
<widget class="Gui::QuantitySpinBox" name="spinLength" native="true">
<property name="keyboardTracking" stdset="0">
<bool>false</bool>
</property>
<property name="unit" stdset="0">
<string notr="true">mm</string>
</property>
<property name="value" stdset="0">
<double>100.000000000000000</double>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="offsetWrapper" native="true">
<layout class="QHBoxLayout" name="_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="labelOffset">
<property name="text">
<string>Offset</string>
</property>
</widget>
</item>
<item>
<widget class="Gui::QuantitySpinBox" name="spinOffset" native="true">
<property name="keyboardTracking" stdset="0">
<bool>false</bool>
</property>
<property name="unit" stdset="0">
<string notr="true">mm</string>
</property>
<property name="value" stdset="0">
<double>10.000000000000000</double>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>