PartDesign: New features AdditiveHelix and SubtractiveHelix

These features, based on the code for the Pipe class, allow the user
to simply create a helical sweep within PartDesign workbench.

Sample application is threads, springs, coils, augers, etc.

Also, remove needless requirement for positive cone angle on helixes.

Thanks to @bitacovir for helping with the icons
Thanks to @chennes for review
Thanks to @vosk for review
Thanks to @wwmayer for review

Enforce that links stay within scope for ProfileBased features
This also ensures that the Body itself is not used for creating features within
the body, causing a "Graph not a DAG" error.
This commit is contained in:
David Osterberg
2020-12-25 12:42:03 +01:00
committed by wwmayer
parent 7999536858
commit 59ec3cb141
23 changed files with 4927 additions and 47 deletions

View File

@@ -55,6 +55,7 @@
#include <Mod/PartDesign/App/Body.h>
#include <Mod/PartDesign/App/FeatureGroove.h>
#include <Mod/PartDesign/App/FeatureRevolution.h>
#include <Mod/PartDesign/App/FeatureTransformed.h>
#include <Mod/PartDesign/App/FeatureMultiTransform.h>
#include <Mod/PartDesign/App/DatumPoint.h>
@@ -384,7 +385,7 @@ void CmdPartDesignSubShapeBinder::activated(int iMsg)
}
values = std::move(links);
}
PartDesign::SubShapeBinder *binder = 0;
try {
openCommand(QT_TRANSLATE_NOOP("Command", "Create SubShapeBinder"));
@@ -403,7 +404,7 @@ void CmdPartDesignSubShapeBinder::activated(int iMsg)
commitCommand();
} catch (Base::Exception &e) {
e.ReportException();
QMessageBox::critical(Gui::getMainWindow(),
QMessageBox::critical(Gui::getMainWindow(),
QObject::tr("Sub-Shape Binder"), QString::fromUtf8(e.what()));
abortCommand();
}
@@ -994,7 +995,7 @@ void prepareProfileBased(PartDesign::Body *pcActiveBody, Gui::Command* cmd, cons
FCMD_OBJ_CMD(pcActiveBody,"newObject('PartDesign::" << which << "','" << FeatName << "')");
auto Feat = pcActiveBody->getDocument()->getObject(FeatName.c_str());
auto objCmd = Gui::Command::getObjectCmd(feature);
if (feature->isDerivedFrom(Part::Part2DObject::getClassTypeId()) || subs.empty()) {
FCMD_OBJ_CMD(Feat,"Profile = " << objCmd);
@@ -1003,7 +1004,7 @@ void prepareProfileBased(PartDesign::Body *pcActiveBody, Gui::Command* cmd, cons
std::ostringstream ss;
for (auto &s : subs)
ss << "'" << s << "',";
FCMD_OBJ_CMD(Feat,"Profile = (" << objCmd << ", [" << ss.str() << "])");
FCMD_OBJ_CMD(Feat,"Profile = (" << objCmd << ", [" << ss.str() << "])");
}
//for additive and subtractive lofts allow the user to preselect the sections
@@ -1042,10 +1043,44 @@ void prepareProfileBased(PartDesign::Body *pcActiveBody, Gui::Command* cmd, cons
func(static_cast<Part::Feature*>(feature), Feat);
};
// in case of subtractive types, check that there is something to subtract from
if ((which.find("Subtractive") != std::string::npos) ||
(which.compare("Groove") == 0) ||
(which.compare("Pocket") == 0)) {
if (!pcActiveBody->isSolid()) {
QMessageBox msgBox;
msgBox.setText(QObject::tr("Cannot use this command as there is no solid to subtract from."));
msgBox.setInformativeText(QObject::tr("Ensure that the body contains a feature before attempting a subtractive command."));
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setDefaultButton(QMessageBox::Ok);
msgBox.exec();
return;
}
}
//if a profile is selected we can make our life easy and fast
std::vector<Gui::SelectionObject> selection = cmd->getSelection().getSelectionEx();
if (!selection.empty()) {
base_worker(selection.front().getObject(), selection.front().getSubNames());
bool onlyAllowed = true;
for (auto it = selection.begin(); it!=selection.end(); ++it){
if (PartDesign::Body::findBodyOf((*it).getObject()) != pcActiveBody) { // the selected objects must belong to the body
onlyAllowed = false;
break;
}
}
if (!onlyAllowed) {
QMessageBox msgBox;
msgBox.setText(QObject::tr("Cannot use selected object. Selected object must belong to the active body"));
msgBox.setInformativeText(QObject::tr("Consider using a ShapeBinder or a BaseFeature to reference external geometry in a body."));
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setDefaultButton(QMessageBox::Ok);
msgBox.exec();
} else {
base_worker(selection.front().getObject(), selection.front().getSubNames());
}
return;
}
@@ -1418,7 +1453,7 @@ void CmdPartDesignGroove::activated(int iMsg)
else {
FCMD_OBJ_CMD(Feat,"ReferenceAxis = ("<<getObjectCmd(pcActiveBody->getOrigin()->getY())<<",[''])");
}
FCMD_OBJ_CMD(Feat,"Angle = 360.0");
try {
@@ -1643,6 +1678,119 @@ bool CmdPartDesignSubtractiveLoft::isActive(void)
return hasActiveDocument();
}
//===========================================================================
// PartDesign_Additive_Helix
//===========================================================================
DEF_STD_CMD_A(CmdPartDesignAdditiveHelix)
CmdPartDesignAdditiveHelix::CmdPartDesignAdditiveHelix()
: Command("PartDesign_AdditiveHelix")
{
sAppModule = "PartDesign";
sGroup = QT_TR_NOOP("PartDesign");
sMenuText = QT_TR_NOOP("Additive helix");
sToolTipText = QT_TR_NOOP("Sweep a selected sketch along a helix");
sWhatsThis = "PartDesign_AdditiveHelix";
sStatusTip = sToolTipText;
sPixmap = "PartDesign_Additive_Helix";
}
void CmdPartDesignAdditiveHelix::activated(int iMsg)
{
Q_UNUSED(iMsg);
App::Document *doc = getDocument();
if (!PartDesignGui::assureModernWorkflow(doc))
return;
PartDesign::Body *pcActiveBody = PartDesignGui::getBody(true);
if (!pcActiveBody)
return;
Gui::Command* cmd = this;
auto worker = [cmd, &pcActiveBody](Part::Feature* sketch, App::DocumentObject *Feat) {
if (!Feat) return;
// specific parameters for helix
Gui::Command::updateActive();
if (sketch->isDerivedFrom(Part::Part2DObject::getClassTypeId())) {
FCMD_OBJ_CMD(Feat,"ReferenceAxis = (" << getObjectCmd(sketch) << ",['V_Axis'])");
}
else {
FCMD_OBJ_CMD(Feat,"ReferenceAxis = (" << getObjectCmd(pcActiveBody->getOrigin()->getY()) << ",[''])");
}
finishProfileBased(cmd, sketch, Feat);
cmd->adjustCameraPosition();
};
prepareProfileBased(pcActiveBody, this, "AdditiveHelix", worker);
}
bool CmdPartDesignAdditiveHelix::isActive(void)
{
return hasActiveDocument();
}
//===========================================================================
// PartDesign_Subtractive_Helix
//===========================================================================
DEF_STD_CMD_A(CmdPartDesignSubtractiveHelix)
CmdPartDesignSubtractiveHelix::CmdPartDesignSubtractiveHelix()
: Command("PartDesign_SubtractiveHelix")
{
sAppModule = "PartDesign";
sGroup = QT_TR_NOOP("PartDesign");
sMenuText = QT_TR_NOOP("Subtractive helix");
sToolTipText = QT_TR_NOOP("Sweep a selected sketch along a helix and remove it from the body");
sWhatsThis = "PartDesign_SubtractiveHelix";
sStatusTip = sToolTipText;
sPixmap = "PartDesign_Subtractive_Helix";
}
void CmdPartDesignSubtractiveHelix::activated(int iMsg)
{
Q_UNUSED(iMsg);
App::Document *doc = getDocument();
if (!PartDesignGui::assureModernWorkflow(doc))
return;
PartDesign::Body *pcActiveBody = PartDesignGui::getBody(true);
if (!pcActiveBody)
return;
Gui::Command* cmd = this;
auto worker = [cmd, &pcActiveBody](Part::Feature* sketch, App::DocumentObject *Feat) {
if (!Feat) return;
// specific parameters for helix
Gui::Command::updateActive();
if (sketch->isDerivedFrom(Part::Part2DObject::getClassTypeId())) {
FCMD_OBJ_CMD(Feat,"ReferenceAxis = (" << getObjectCmd(sketch) << ",['V_Axis'])");
}
else {
FCMD_OBJ_CMD(Feat,"ReferenceAxis = (" << getObjectCmd(pcActiveBody->getOrigin()->getY()) << ",[''])");
}
finishProfileBased(cmd, sketch, Feat);
cmd->adjustCameraPosition();
};
prepareProfileBased(pcActiveBody, this, "SubtractiveHelix", worker);
}
bool CmdPartDesignSubtractiveHelix::isActive(void)
{
return hasActiveDocument();
}
//===========================================================================
// Common utility functions for Dressup features
//===========================================================================
@@ -2176,7 +2324,7 @@ void CmdPartDesignPolarPattern::activated(int iMsg)
}
if (!direction) {
auto body = static_cast<PartDesign::Body*>(Part::BodyBase::findBodyOf(features.front()));
if (body) {
if (body) {
FCMD_OBJ_CMD(Feat,"Axis = ("<<Gui::Command::getObjectCmd(body->getOrigin()->getZ())<<",[''])");
}
}
@@ -2397,7 +2545,7 @@ void CmdPartDesignBoolean::activated(int iMsg)
std::string FeatName = getUniqueObjectName("Boolean",pcActiveBody);
FCMD_OBJ_CMD(pcActiveBody,"newObject('PartDesign::Boolean','"<<FeatName<<"')");
auto Feat = pcActiveBody->getDocument()->getObject(FeatName.c_str());
// If we don't add an object to the boolean group then don't update the body
// as otherwise this will fail and it will be marked as invalid
bool updateDocument = false;
@@ -2456,6 +2604,8 @@ void CreatePartDesignCommands(void)
rcCmdMgr.addCommand(new CmdPartDesignSubtractivePipe);
rcCmdMgr.addCommand(new CmdPartDesignAdditiveLoft);
rcCmdMgr.addCommand(new CmdPartDesignSubtractiveLoft);
rcCmdMgr.addCommand(new CmdPartDesignAdditiveHelix);
rcCmdMgr.addCommand(new CmdPartDesignSubtractiveHelix);
rcCmdMgr.addCommand(new CmdPartDesignFillet());
rcCmdMgr.addCommand(new CmdPartDesignDraft());