FreeCAD/src/Mod/PartDesign/Gui/Command.cpp
Alexander Golubev 55f3f52f54 PartDesign/Feature: add an optional parameter to getBaseObject() to prevent it from throwing exceptions
In most cases getBaseObject() is used with an exception handler which
only detects if it failed but not the reason. This modification allows
to use it without excess exception handlers.
Also add the same parameter to SketchBased::getVerifiedSketch().
2016-04-12 18:12:15 +02:00

2206 lines
86 KiB
C++

/***************************************************************************
* Copyright (c) 2008 Jürgen Riegel (juergen.riegel@web.de) *
* *
* 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 <Standard_Failure.hxx>
# include <TopoDS_Shape.hxx>
# include <TopoDS_Face.hxx>
# include <TopoDS.hxx>
# include <BRepAdaptor_Surface.hxx>
# include <QApplication>
# include <QInputDialog>
# include <BRep_Tool.hxx>
# include <TopExp_Explorer.hxx>
# include <TopoDS.hxx>
# include <TopoDS_Edge.hxx>
# include <TopoDS_Shape.hxx>
# include <TopoDS_Face.hxx>
# include <TopExp.hxx>
# include <TopTools_ListOfShape.hxx>
# include <TopTools_IndexedDataMapOfShapeListOfShape.hxx>
# include <TopTools_IndexedMapOfShape.hxx>
# include <BRepAdaptor_Surface.hxx>
# include <QMessageBox>
#endif
#include <sstream>
#include <algorithm>
#include <App/DocumentObjectGroup.h>
#include <App/Plane.h>
#include <App/Part.h>
#include <Gui/Application.h>
#include <Gui/Command.h>
#include <Gui/Control.h>
#include <Gui/Selection.h>
#include <Gui/MainWindow.h>
#include <Gui/FileDialog.h>
#include <Gui/SelectionFilter.h>
#include <Gui/SelectionObject.h>
#include <Gui/ViewProvider.h>
#include <Gui/Tree.h>
#include <Gui/Document.h>
#include <Gui/ViewProviderDocumentObject.h>
#include <Mod/Part/App/Part2DObject.h>
#include <Mod/PartDesign/App/Body.h>
#include <Mod/Sketcher/App/SketchObject.h>
#include <Mod/Sketcher/Gui/SketchOrientationDialog.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>
#include <Mod/PartDesign/App/DatumLine.h>
#include <Mod/PartDesign/App/DatumPlane.h>
#include "Workbench.h"
using namespace std;
#include "TaskFeaturePick.h"
#include "ReferenceSelection.h"
//===========================================================================
// PartDesign_Part
//===========================================================================
DEF_STD_CMD_A(CmdPartDesignPart);
CmdPartDesignPart::CmdPartDesignPart()
: Command("PartDesign_Part")
{
sAppModule = "PartDesign";
sGroup = QT_TR_NOOP("PartDesign");
sMenuText = QT_TR_NOOP("Create part");
sToolTipText = QT_TR_NOOP("Create a new part and make it active");
sWhatsThis = sToolTipText;
sStatusTip = sToolTipText;
sPixmap = "Tree_Annotation";
}
void CmdPartDesignPart::activated(int iMsg)
{
openCommand("Add a part");
std::string FeatName = getUniqueObjectName("Part");
std::string PartName;
PartName = getUniqueObjectName("Part");
doCommand(Doc,"App.activeDocument().Tip = App.activeDocument().addObject('App::Part','%s')",PartName.c_str());
doCommand(Doc,"App.activeDocument().ActiveObject.Label = '%s'", QObject::tr(PartName.c_str()).toStdString().c_str());
PartDesignGui::Workbench::setUpPart(dynamic_cast<App::Part *>(getDocument()->getObject(PartName.c_str())));
doCommand(Gui::Command::Gui, "Gui.activeView().setActiveObject('%s', App.activeDocument().%s)", PARTKEY, PartName.c_str());
updateActive();
}
bool CmdPartDesignPart::isActive(void)
{
return hasActiveDocument();
}
//===========================================================================
// PartDesign_Body
//===========================================================================
DEF_STD_CMD_A(CmdPartDesignBody);
CmdPartDesignBody::CmdPartDesignBody()
: Command("PartDesign_Body")
{
sAppModule = "PartDesign";
sGroup = QT_TR_NOOP("PartDesign");
sMenuText = QT_TR_NOOP("Create body");
sToolTipText = QT_TR_NOOP("Create a new body and make it active");
sWhatsThis = sToolTipText;
sStatusTip = sToolTipText;
sPixmap = "PartDesign_Body_Create_New";
}
void CmdPartDesignBody::activated(int iMsg)
{
openCommand("Add a body");
std::string FeatName = getUniqueObjectName("Body");
// first check if Part is already created:
App::Part *actPart = getDocument()->Tip.getValue<App::Part *>();
std::string PartName;
if(!actPart){
// if not, creating a part and set it up by calling the appropiated function in Workbench
//if we create a new part we automaticly get a new body, there is no need to create a second one
PartName = getUniqueObjectName("Part");
doCommand(Doc,"App.activeDocument().Tip = App.activeDocument().addObject('App::Part','%s')",PartName.c_str());
doCommand(Doc,"App.activeDocument().ActiveObject.Label = '%s'", QObject::tr(PartName.c_str()).toStdString().c_str());
PartDesignGui::Workbench::setUpPart(dynamic_cast<App::Part *>(getDocument()->getObject(PartName.c_str())));
doCommand(Gui::Command::Gui, "Gui.activeView().setActiveObject('%s', App.activeDocument().%s)", PARTKEY, PartName.c_str());
} else {
PartName = actPart->getNameInDocument();
// add the Body feature itself, and make it active
doCommand(Doc,"App.activeDocument().addObject('PartDesign::Body','%s')",FeatName.c_str());
//doCommand(Doc,"App.activeDocument().%s.Model = []",FeatName.c_str());
//doCommand(Doc,"App.activeDocument().%s.Tip = None",FeatName.c_str());
addModule(Gui,"PartDesignGui"); // import the Gui module only once a session
doCommand(Gui::Command::Gui, "Gui.activeView().setActiveObject('%s', App.activeDocument().%s)", PDBODYKEY, FeatName.c_str());
// Make the "Create sketch" prompt appear in the task panel
doCommand(Gui,"Gui.Selection.clearSelection()");
doCommand(Gui,"Gui.Selection.addSelection(App.ActiveDocument.%s)", FeatName.c_str());
doCommand(Doc,"App.activeDocument().%s.addObject(App.ActiveDocument.%s)",PartName.c_str(),FeatName.c_str());
}
updateActive();
}
bool CmdPartDesignBody::isActive(void)
{
return hasActiveDocument();
}
//===========================================================================
// PartDesign_MoveTip
//===========================================================================
DEF_STD_CMD_A(CmdPartDesignMoveTip);
CmdPartDesignMoveTip::CmdPartDesignMoveTip()
: Command("PartDesign_MoveTip")
{
sAppModule = "PartDesign";
sGroup = QT_TR_NOOP("PartDesign");
sMenuText = QT_TR_NOOP("Insert here");
sToolTipText = QT_TR_NOOP("Move insert point to selected feature");
sWhatsThis = sToolTipText;
sStatusTip = sToolTipText;
sPixmap = "PartDesign_MoveTip";
}
void CmdPartDesignMoveTip::activated(int iMsg)
{
PartDesign::Body *pcActiveBody = PartDesignGui::getBody(/*messageIfNot = */true);
if(!pcActiveBody) return;
std::vector<App::DocumentObject*> features = getSelection().getObjectsOfType(Part::Feature::getClassTypeId());
App::DocumentObject* selFeature;
if (features.empty()) {
// Insert at the beginning of this body
selFeature = NULL;
} else {
selFeature = features.front();
if (selFeature->getTypeId().isDerivedFrom(PartDesign::Body::getClassTypeId())) {
// Insert at the beginning of this body
selFeature = NULL;
} else if (!pcActiveBody->hasFeature(selFeature)) {
// Switch to other body
pcActiveBody = static_cast<PartDesign::Body*>(Part::BodyBase::findBodyOf(selFeature));
if (pcActiveBody != NULL)
Gui::Command::doCommand(Gui::Command::Gui, "Gui.activeView().setActiveObject('%s',App.activeDocument().%s)",
PDBODYKEY, pcActiveBody->getNameInDocument());
else
return;
}
}
openCommand("Move insert point to selected feature");
App::DocumentObject* oldTip = pcActiveBody->Tip.getValue();
if (oldTip != NULL) {
if (!oldTip->getTypeId().isDerivedFrom(Part::Datum::getClassTypeId()))
doCommand(Gui,"Gui.activeDocument().hide(\"%s\")", oldTip->getNameInDocument());
App::DocumentObject* prevSolidFeature = pcActiveBody->getPrevSolidFeature();
if (prevSolidFeature != NULL)
doCommand(Gui,"Gui.activeDocument().hide(\"%s\")", prevSolidFeature->getNameInDocument());
}
if (selFeature == NULL) {
doCommand(Doc,"App.activeDocument().%s.Tip = None", pcActiveBody->getNameInDocument());
} else {
doCommand(Doc,"App.activeDocument().%s.Tip = App.activeDocument().%s",pcActiveBody->getNameInDocument(), selFeature->getNameInDocument());
// Adjust visibility to show only the Tip feature and (if the Tip feature is not solid) the solid feature prior to the Tip
doCommand(Gui,"Gui.activeDocument().show(\"%s\")", selFeature->getNameInDocument());
App::DocumentObject* prevSolidFeature = pcActiveBody->getPrevSolidFeature();
if ((prevSolidFeature != NULL) && !PartDesign::Body::isSolidFeature(selFeature))
doCommand(Gui,"Gui.activeDocument().show(\"%s\")", prevSolidFeature->getNameInDocument());
}
// TOOD: Hide all datum features after the Tip feature? But the user might have already hidden some and wants to see
// others, so we would have to remember their state somehow
}
bool CmdPartDesignMoveTip::isActive(void)
{
return hasActiveDocument();
}
//===========================================================================
// PartDesign_DuplicateSelection
//===========================================================================
DEF_STD_CMD_A(CmdPartDesignDuplicateSelection);
CmdPartDesignDuplicateSelection::CmdPartDesignDuplicateSelection()
:Command("PartDesign_DuplicateSelection")
{
sAppModule = "PartDesign";
sGroup = QT_TR_NOOP("PartDesign");
sMenuText = QT_TR_NOOP("Duplicate selected object");
sToolTipText = QT_TR_NOOP("Duplicates the selected object and adds it to the active body");
sWhatsThis = sToolTipText;
sStatusTip = sToolTipText;
sPixmap = "";
}
void CmdPartDesignDuplicateSelection::activated(int iMsg)
{
PartDesign::Body *pcActiveBody = PartDesignGui::getBody(/*messageIfNot = */true);
if(!pcActiveBody) return;
std::vector<App::DocumentObject*> features = getSelection().getObjectsOfType(Part::Feature::getClassTypeId());
if (features.empty()) return;
App::DocumentObject* selFeature = features.front();
if (!pcActiveBody->hasFeature(selFeature)) {
// NOTE: We assume all selected features will be in the same document
// Switch to other body
pcActiveBody = static_cast<PartDesign::Body*>(Part::BodyBase::findBodyOf(selFeature));
if (pcActiveBody != NULL)
Gui::Command::doCommand(Gui::Command::Gui, "Gui.activeView().setActiveObject('%s', App.activeDocument().%s)",
PDBODYKEY, pcActiveBody->getNameInDocument());
else
return;
}
std::vector<App::DocumentObject*> beforeFeatures = getDocument()->getObjects();
openCommand("Duplicate a PartDesign object");
doCommand(Doc,"FreeCADGui.runCommand('Std_DuplicateSelection')");
// Find the features that were added
std::vector<App::DocumentObject*> afterFeatures = getDocument()->getObjects();
std::vector<App::DocumentObject*> newFeatures;
std::sort(beforeFeatures.begin(), beforeFeatures.end());
std::sort(afterFeatures.begin(), afterFeatures.end());
std::set_difference(afterFeatures.begin(), afterFeatures.end(), beforeFeatures.begin(), beforeFeatures.end(),
std::back_inserter(newFeatures));
for (std::vector<App::DocumentObject*>::const_iterator f = newFeatures.begin(); f != newFeatures.end(); f++) {
if (PartDesign::Body::isAllowed(*f)) {
doCommand(Doc,"App.activeDocument().%s.addFeature(App.activeDocument().%s)",
pcActiveBody->getNameInDocument(), (*f)->getNameInDocument());
doCommand(Gui,"Gui.activeDocument().hide(\"%s\")", (*f)->getNameInDocument());
}
}
// Adjust visibility of features
doCommand(Gui,"Gui.activeDocument().show(\"%s\")", newFeatures.back()->getNameInDocument());
App::DocumentObject* prevSolidFeature = pcActiveBody->getPrevSolidFeature();
if ((prevSolidFeature != NULL) && !PartDesign::Body::isSolidFeature(selFeature))
doCommand(Gui,"Gui.activeDocument().show(\"%s\")", prevSolidFeature->getNameInDocument());
}
bool CmdPartDesignDuplicateSelection::isActive(void)
{
if (getActiveGuiDocument())
return true;
else
return false;
}
//===========================================================================
// PartDesign_MoveFeature
//===========================================================================
DEF_STD_CMD_A(CmdPartDesignMoveFeature);
CmdPartDesignMoveFeature::CmdPartDesignMoveFeature()
:Command("PartDesign_MoveFeature")
{
sAppModule = "PartDesign";
sGroup = QT_TR_NOOP("PartDesign");
sMenuText = QT_TR_NOOP("Move object to other body");
sToolTipText = QT_TR_NOOP("Moves the selected object to another body");
sWhatsThis = sToolTipText;
sStatusTip = sToolTipText;
sPixmap = "";
}
void CmdPartDesignMoveFeature::activated(int iMsg)
{
PartDesign::Body *pcActiveBody = PartDesignGui::getBody(/*messageIfNot = */true);
if(!pcActiveBody) return;
std::vector<App::DocumentObject*> features = getSelection().getObjectsOfType(Part::Feature::getClassTypeId());
if (features.empty()) return;
// Create a list of all bodies in this part
std::vector<App::DocumentObject*> bodies = getDocument()->getObjectsOfType(Part::BodyBase::getClassTypeId());
// Ask user to select the target body
bool ok;
QStringList items;
for (std::vector<App::DocumentObject*>::iterator it = bodies.begin(); it != bodies.end(); ++it)
items.push_back(QString::fromUtf8((*it)->Label.getValue()));
QString text = QInputDialog::getItem(Gui::getMainWindow(),
qApp->translate(className(), "Select body"),
qApp->translate(className(), "Select a body from the list"),
items, 0, false, &ok);
if (!ok) return;
int index = items.indexOf(text);
PartDesign::Body* target = static_cast<PartDesign::Body*>(bodies[index]);
openCommand("Move an object");
for (std::vector<App::DocumentObject*>::const_iterator f = features.begin(); f != features.end(); f++) {
// Find body of this feature
Part::BodyBase* source = PartDesign::Body::findBodyOf(*f);
bool featureIsTip = false;
if (source == target) continue;
// Remove from the source body if the feature belonged to a body
if (source) {
featureIsTip = (source->Tip.getValue() == *f);
doCommand(Doc,"App.activeDocument().%s.removeFeature(App.activeDocument().%s)",
source->getNameInDocument(), (*f)->getNameInDocument());
}
// Add to target body (always at the Tip)
doCommand(Doc,"App.activeDocument().%s.addFeature(App.activeDocument().%s)",
target->getNameInDocument(), (*f)->getNameInDocument());
// Recompute to update the shape
doCommand(Gui,"App.activeDocument().recompute()");
// Adjust visibility of features
if (PartDesign::Body::isSolidFeature(*f)) {
// If we removed the tip of the source body, make the new tip visible
if (featureIsTip) {
App::DocumentObject* prevSolidFeature = source->getPrevSolidFeature();
if (prevSolidFeature) {
doCommand(Gui,"Gui.activeDocument().show(\"%s\")", prevSolidFeature->getNameInDocument());
}
}
// Hide old tip and show new tip (the moved feature) of the target body
App::DocumentObject* prevSolidFeature = target->getPrevSolidFeature();
doCommand(Gui,"Gui.activeDocument().hide(\"%s\")", prevSolidFeature->getNameInDocument());
doCommand(Gui,"Gui.activeDocument().show(\"%s\")", (*f)->getNameInDocument());
} else if ((*f)->getTypeId().isDerivedFrom(Sketcher::SketchObject::getClassTypeId())) {
Sketcher::SketchObject *sketch = static_cast<Sketcher::SketchObject*>(*f);
try {
PartDesignGui::Workbench::fixSketchSupport(sketch);
} catch (Base::Exception &) {
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Sketch plane cannot be migrated"),
QObject::tr("Please edit '%1' and redefine it to use a Base or Datum plane as the sketch plane.").
arg(QString::fromAscii(sketch->getNameInDocument()) ) );
}
}
}
}
bool CmdPartDesignMoveFeature::isActive(void)
{
if (getActiveGuiDocument())
return true;
else
return false;
}
DEF_STD_CMD_A(CmdPartDesignMoveFeatureInTree);
CmdPartDesignMoveFeatureInTree::CmdPartDesignMoveFeatureInTree()
:Command("PartDesign_MoveFeatureInTree")
{
sAppModule = "PartDesign";
sGroup = QT_TR_NOOP("PartDesign");
sMenuText = QT_TR_NOOP("Move object after other object");
sToolTipText = QT_TR_NOOP("Moves the selected object and insert it after another object");
sWhatsThis = sToolTipText;
sStatusTip = sToolTipText;
sPixmap = "";
}
void CmdPartDesignMoveFeatureInTree::activated(int iMsg)
{
PartDesign::Body *pcActiveBody = PartDesignGui::getBody(/*messageIfNot = */true);
if(!pcActiveBody) return;
std::vector<App::DocumentObject*> features = getSelection().getObjectsOfType(Part::Feature::getClassTypeId());
if (features.empty()) return;
// Create a list of all features in this body
std::vector<App::DocumentObject*> model = pcActiveBody->Model.getValues();
// Ask user to select the target feature
bool ok;
QStringList items;
for (std::vector<App::DocumentObject*>::iterator it = model.begin(); it != model.end(); ++it)
items.push_back(QString::fromUtf8((*it)->Label.getValue()));
QString text = QInputDialog::getItem(Gui::getMainWindow(),
qApp->translate(className(), "Select feature"),
qApp->translate(className(), "Select a feature from the list"),
items, 0, false, &ok);
if (!ok) return;
int index = items.indexOf(text);
PartDesign::Feature* target = static_cast<PartDesign::Feature*>(model[index]);
openCommand("Move an object inside tree");
for (std::vector<App::DocumentObject*>::const_iterator f = features.begin(); f != features.end(); f++) {
if (*f == target) continue;
// Remove and re-insert the feature from the Body
// Note: If the tip was moved then the new tip will be at the moved position, that is, at the same
// feature as before!
doCommand(Doc,"App.activeDocument().%s.removeFeature(App.activeDocument().%s)",
pcActiveBody->getNameInDocument(), (*f)->getNameInDocument());
doCommand(Doc,
"App.activeDocument().%s.insertFeature(App.activeDocument().%s, App.activeDocument().%s, True)",
pcActiveBody->getNameInDocument(), (*f)->getNameInDocument(), target->getNameInDocument());
}
// Recompute to update the shape
doCommand(Gui,"App.activeDocument().recompute()");
}
bool CmdPartDesignMoveFeatureInTree::isActive(void)
{
if (getActiveGuiDocument())
return true;
else
return false;
}
//===========================================================================
// PartDesign_Datum
//===========================================================================
/**
* @brief UnifiedDatumCommand is a common routine called by datum plane, line and point commands
* @param cmd (i/o) command, to have shortcuts to doCommand, etc.
* @param type (input)
* @param name (input). Is used to generate new name for an object, and to fill undo messages.
*
*/
void UnifiedDatumCommand(Gui::Command &cmd, Base::Type type, std::string name)
{
try{
std::string fullTypeName (type.getName());
App::PropertyLinkSubList support;
cmd.getSelection().getAsPropertyLinkSubList(support);
bool bEditSelected = false;
if (support.getSize() == 1 && support.getValue() ){
if (support.getValue()->isDerivedFrom(type))
bEditSelected = true;
}
if (bEditSelected) {
std::string tmp = std::string("Edit ")+name;
cmd.openCommand(tmp.c_str());
cmd.doCommand(Gui::Command::Gui,"Gui.activeDocument().setEdit('%s')",support.getValue()->getNameInDocument());
} else {
PartDesign::Body *pcActiveBody = PartDesignGui::getBody(/*messageIfNot = */false);
std::string FeatName = cmd.getUniqueObjectName(name.c_str());
std::string tmp = std::string("Create ")+name;
cmd.openCommand(tmp.c_str());
cmd.doCommand(Gui::Command::Doc,"App.activeDocument().addObject('%s','%s')",fullTypeName.c_str(),FeatName.c_str());
//test if current selection fits a mode.
if (support.getSize() > 0) {
AttachableObject* pcDatum = static_cast<AttachableObject*>(cmd.getDocument()->getObject(FeatName.c_str()));
pcDatum->attacher().references.Paste(support);
eSuggestResult msg;
eMapMode suggMode = pcDatum->attacher().listMapModes(msg);
if (msg == srOK) {
//fits some mode. Populate support property.
cmd.doCommand(Gui::Command::Doc,"App.activeDocument().%s.Support = %s",FeatName.c_str(),support.getPyReprString().c_str());
cmd.doCommand(Gui::Command::Doc,"App.activeDocument().%s.MapMode = '%s'",FeatName.c_str(),AttachEngine::eMapModeStrings[suggMode]);
} else {
QMessageBox::information(Gui::getMainWindow(),QObject::tr("Invalid selection"), QObject::tr("There are no attachment modes that fit seleted objects. Select something else."));
}
}
if (pcActiveBody) {
cmd.doCommand(Gui::Command::Doc,"App.activeDocument().%s.addFeature(App.activeDocument().%s)",
pcActiveBody->getNameInDocument(), FeatName.c_str());
}
cmd.doCommand(Gui::Command::Doc,"App.activeDocument().recompute()"); // recompute the feature based on its references
cmd.doCommand(Gui::Command::Gui,"Gui.activeDocument().setEdit('%s')",FeatName.c_str());
}
} catch (Base::Exception &e) {
QMessageBox::warning(Gui::getMainWindow(),QObject::tr("Error"),QString::fromLatin1(e.what()));
} catch (Standard_Failure &e) {
QMessageBox::warning(Gui::getMainWindow(),QObject::tr("Error"),QString::fromLatin1(e.GetMessageString()));
}
}
/* Datum feature commands =======================================================*/
DEF_STD_CMD_A(CmdPartDesignPlane);
CmdPartDesignPlane::CmdPartDesignPlane()
:Command("PartDesign_Plane")
{
sAppModule = "PartDesign";
sGroup = QT_TR_NOOP("PartDesign");
sMenuText = QT_TR_NOOP("Create a datum plane");
sToolTipText = QT_TR_NOOP("Create a new datum plane");
sWhatsThis = sToolTipText;
sStatusTip = sToolTipText;
sPixmap = "PartDesign_Plane";
}
void CmdPartDesignPlane::activated(int iMsg)
{
UnifiedDatumCommand(*this, Base::Type::fromName("PartDesign::Plane"),"DatumPlane");
}
bool CmdPartDesignPlane::isActive(void)
{
if (getActiveGuiDocument())
return true;
else
return false;
}
DEF_STD_CMD_A(CmdPartDesignLine);
CmdPartDesignLine::CmdPartDesignLine()
:Command("PartDesign_Line")
{
sAppModule = "PartDesign";
sGroup = QT_TR_NOOP("PartDesign");
sMenuText = QT_TR_NOOP("Create a datum line");
sToolTipText = QT_TR_NOOP("Create a new datum line");
sWhatsThis = sToolTipText;
sStatusTip = sToolTipText;
sPixmap = "PartDesign_Line";
}
void CmdPartDesignLine::activated(int iMsg)
{
UnifiedDatumCommand(*this, Base::Type::fromName("PartDesign::Line"),"DatumLine");
}
bool CmdPartDesignLine::isActive(void)
{
if (getActiveGuiDocument())
return true;
else
return false;
}
DEF_STD_CMD_A(CmdPartDesignPoint);
CmdPartDesignPoint::CmdPartDesignPoint()
:Command("PartDesign_Point")
{
sAppModule = "PartDesign";
sGroup = QT_TR_NOOP("PartDesign");
sMenuText = QT_TR_NOOP("Create a datum point");
sToolTipText = QT_TR_NOOP("Create a new datum point");
sWhatsThis = sToolTipText;
sStatusTip = sToolTipText;
sPixmap = "PartDesign_Point";
}
void CmdPartDesignPoint::activated(int iMsg)
{
UnifiedDatumCommand(*this, Base::Type::fromName("PartDesign::Point"),"DatumPoint");
}
bool CmdPartDesignPoint::isActive(void)
{
if (getActiveGuiDocument())
return true;
else
return false;
}
//===========================================================================
// PartDesign_Sketch
//===========================================================================
/* Sketch commands =======================================================*/
DEF_STD_CMD_A(CmdPartDesignNewSketch);
CmdPartDesignNewSketch::CmdPartDesignNewSketch()
:Command("PartDesign_NewSketch")
{
sAppModule = "PartDesign";
sGroup = QT_TR_NOOP("PartDesign");
sMenuText = QT_TR_NOOP("Create sketch");
sToolTipText = QT_TR_NOOP("Create a new sketch");
sWhatsThis = sToolTipText;
sStatusTip = sToolTipText;
sPixmap = "Sketcher_NewSketch";
}
void CmdPartDesignNewSketch::activated(int iMsg)
{
PartDesign::Body *pcActiveBody = PartDesignGui::getBody(/*messageIfNot = */false);
// No PartDesign feature without Body past FreeCAD 0.13
if(!pcActiveBody) {
Gui::CommandManager &rcCmdMgr = Gui::Application::Instance->commandManager();
rcCmdMgr.runCommandByName("Sketcher_NewSketch");
return;
}
Gui::SelectionFilter SketchFilter("SELECT Sketcher::SketchObject COUNT 1");
Gui::SelectionFilter FaceFilter ("SELECT Part::Feature SUBELEMENT Face COUNT 1");
Gui::SelectionFilter PlaneFilter ("SELECT App::Plane COUNT 1");
Gui::SelectionFilter PlaneFilter2 ("SELECT PartDesign::Plane COUNT 1");
if (PlaneFilter2.match())
PlaneFilter = PlaneFilter2;
if (SketchFilter.match()) {
Sketcher::SketchObject *Sketch = static_cast<Sketcher::SketchObject*>(SketchFilter.Result[0][0].getObject());
openCommand("Edit Sketch");
doCommand(Gui,"Gui.activeDocument().setEdit('%s')",Sketch->getNameInDocument());
}
else if (FaceFilter.match() || PlaneFilter.match()) {
// get the selected object
std::string supportString;
App::DocumentObject* obj;
if (FaceFilter.match()) {
obj = FaceFilter.Result[0][0].getObject();
if(!obj->isDerivedFrom(Part::Feature::getClassTypeId()))
return;
Part::Feature* feat = static_cast<Part::Feature*>(obj);
const std::vector<std::string> &sub = FaceFilter.Result[0][0].getSubNames();
if (sub.size() > 1){
// No assert for wrong user input!
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Several sub-elements selected"),
QObject::tr("You have to select a single face as support for a sketch!"));
return;
}
// get the selected sub shape (a Face)
const Part::TopoShape &shape = feat->Shape.getValue();
TopoDS_Shape sh = shape.getSubShape(sub[0].c_str());
const TopoDS_Face& face = TopoDS::Face(sh);
if (face.IsNull()){
// No assert for wrong user input!
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("No support face selected"),
QObject::tr("You have to select a face as support for a sketch!"));
return;
}
BRepAdaptor_Surface adapt(face);
if (adapt.GetType() != GeomAbs_Plane){
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("No planar support"),
QObject::tr("You need a planar face as support for a sketch!"));
return;
}
supportString = FaceFilter.Result[0][0].getAsPropertyLinkSubString();
} else {
obj = static_cast<Part::Feature*>(PlaneFilter.Result[0][0].getObject());
supportString = std::string("(App.activeDocument().") + obj->getNameInDocument() + ", '')";
}
if (!pcActiveBody->hasFeature(obj)) {
bool isBasePlane = false;
if(obj->isDerivedFrom(App::Plane::getClassTypeId())) {
App::Plane* pfeat = static_cast<App::Plane*>(obj);
for (unsigned i = 0; i < 3; i++) {
if (strcmp(App::Part::BaseplaneTypes[i], pfeat->PlaneType.getValue()) == 0) {
isBasePlane = true;
break;
}
}
}
if (!isBasePlane) {
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Selection from other body"),
QObject::tr("You have to select a face or plane from the active body!"));
return;
}
} else if (!obj->getTypeId().isDerivedFrom(Part::Datum::getClassTypeId())
&& pcActiveBody->getNextSolidFeature() != obj) {
// TOOD: checkme why it's forbidden!?
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Selection from inactive feature"),
QObject::tr("You can only use the last solid feature as sketch support"));
return;
}
// create Sketch on Face or Plane
std::string FeatName = getUniqueObjectName("Sketch");
openCommand("Create a Sketch on Face");
doCommand(Doc,"App.activeDocument().addObject('Sketcher::SketchObject','%s')",FeatName.c_str());
doCommand(Doc,"App.activeDocument().%s.Support = %s",FeatName.c_str(),supportString.c_str());
doCommand(Doc,"App.activeDocument().%s.MapMode = '%s'",FeatName.c_str(),Attacher::AttachEngine::eMapModeStrings[Attacher::mmFlatFace]);
doCommand(Doc,"App.activeDocument().%s.addFeature(App.activeDocument().%s)",
pcActiveBody->getNameInDocument(), FeatName.c_str());
doCommand(Gui,"App.activeDocument().recompute()"); // recompute the sketch placement based on its support
//doCommand(Gui,"Gui.activeDocument().activeView().setCamera('%s')",cam.c_str());
doCommand(Gui,"Gui.activeDocument().setEdit('%s')",FeatName.c_str());
}
else {
// Get a valid plane from the user
std::vector<PartDesignGui::TaskFeaturePick::featureStatus> status;
std::vector<App::DocumentObject*> planes = getDocument()->getObjectsOfType(App::Plane::getClassTypeId());
std::vector<App::DocumentObject*> planestmp = getDocument()->getObjectsOfType(PartDesign::Plane::getClassTypeId());
planes.insert(planes.end(), planestmp.begin(), planestmp.end());
unsigned validPlanes = 0;
std::vector<App::DocumentObject*>::const_iterator firstValidPlane = planes.end();
App::Part* pcActivePart = Gui::Application::Instance->activeView()->getActiveObject<App::Part*>(PARTKEY);
for (std::vector<App::DocumentObject*>::iterator p = planes.begin(); p != planes.end(); p++) {
// Check whether this plane is a base plane
bool base = false;
if((*p)->isDerivedFrom(App::Plane::getClassTypeId())) {
App::Plane* pfeat = static_cast<App::Plane*>(*p);
for (unsigned i = 0; i < 3; i++) {
if (strcmp(App::Part::BaseplaneTypes[i], pfeat->PlaneType.getValue()) == 0) {
if(pcActivePart->hasObject(pfeat, true))
status.push_back(PartDesignGui::TaskFeaturePick::basePlane);
else
status.push_back(PartDesignGui::TaskFeaturePick::invalidShape);
if (firstValidPlane == planes.end())
firstValidPlane = p;
validPlanes++;
base = true;
break;
}
}
}
if (base) continue;
// Check whether this plane belongs to the active body
if (!pcActiveBody->hasFeature(*p)) {
if(pcActivePart->hasObject(*p, true))
status.push_back(PartDesignGui::TaskFeaturePick::otherBody);
else
status.push_back(PartDesignGui::TaskFeaturePick::otherPart);
continue;
} else {
if (pcActiveBody->isAfterTip(*p)){
status.push_back(PartDesignGui::TaskFeaturePick::afterTip);
continue;
}
}
// All checks passed - found a valid plane
if (firstValidPlane == planes.end())
firstValidPlane = p;
validPlanes++;
status.push_back(PartDesignGui::TaskFeaturePick::validFeature);
}
if (validPlanes == 0) {
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("No valid planes in this document"),
QObject::tr("Please create a plane first or select a face to sketch on"));
return;
}
auto accepter = [=](const std::vector<App::DocumentObject*>& features) -> bool {
if(features.empty())
return false;
return true;
};
auto worker = [=](const std::vector<App::DocumentObject*>& features) {
App::Plane* plane = static_cast<App::Plane*>(features.front());
std::string FeatName = getUniqueObjectName("Sketch");
std::string supportString = std::string("(App.activeDocument().") + plane->getNameInDocument() +
", [''])";
Gui::Command::openCommand("Create a new Sketch");
Gui::Command::doCommand(Doc,"App.activeDocument().addObject('Sketcher::SketchObject','%s')",FeatName.c_str());
Gui::Command::doCommand(Doc,"App.activeDocument().%s.Support = %s",FeatName.c_str(),supportString.c_str());
Gui::Command::doCommand(Doc,"App.activeDocument().%s.MapMode = '%s'",FeatName.c_str(),Attacher::AttachEngine::eMapModeStrings[Attacher::mmFlatFace]);
Gui::Command::updateActive(); // Make sure the Support's Placement property is updated
Gui::Command::doCommand(Doc,"App.activeDocument().%s.addFeature(App.activeDocument().%s)",
pcActiveBody->getNameInDocument(), FeatName.c_str());
//doCommand(Gui,"Gui.activeDocument().activeView().setCamera('%s')",cam.c_str());
Gui::Command::doCommand(Gui,"Gui.activeDocument().setEdit('%s')",FeatName.c_str());
};
// If there is more than one possibility, show dialog and let user pick plane
bool reversed = false;
if (validPlanes > 1) {
Gui::TaskView::TaskDialog *dlg = Gui::Control().activeDialog();
PartDesignGui::TaskDlgFeaturePick *pickDlg = qobject_cast<PartDesignGui::TaskDlgFeaturePick *>(dlg);
if (dlg && !pickDlg) {
QMessageBox msgBox;
msgBox.setText(QObject::tr("A dialog is already open in the task panel"));
msgBox.setInformativeText(QObject::tr("Do you want to close this dialog?"));
msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
msgBox.setDefaultButton(QMessageBox::Yes);
int ret = msgBox.exec();
if (ret == QMessageBox::Yes)
Gui::Control().closeDialog();
else
return;
}
if(dlg)
Gui::Control().closeDialog();
Gui::Selection().clearSelection();
Gui::Control().showDialog(new PartDesignGui::TaskDlgFeaturePick(planes, status, accepter, worker));
}
else {
worker(planes);
}
}
}
bool CmdPartDesignNewSketch::isActive(void)
{
if (getActiveGuiDocument())
return true;
else
return false;
}
//===========================================================================
// Common utility functions for all features creating solids
//===========================================================================
void finishFeature(const Gui::Command* cmd, const std::string& FeatName,
App::DocumentObject* prevSolidFeature = nullptr, const bool hidePrevSolid = true)
{
PartDesign::Body *pcActiveBody;
if (prevSolidFeature) {
pcActiveBody = PartDesignGui::getBodyFor(prevSolidFeature, /*messageIfNot = */false);
} else { // insert into the same body as the given previous one
pcActiveBody = PartDesignGui::getBody(/*messageIfNot = */false);
}
if (pcActiveBody) {
App::DocumentObject* lastSolidFeature = pcActiveBody->getPrevSolidFeature(NULL, true);
if (!prevSolidFeature || prevSolidFeature == lastSolidFeature) {
// If the previous feature not given or is the Tip add Feature after it.
cmd->doCommand(cmd->Doc,"App.activeDocument().%s.addFeature(App.activeDocument().%s)",
pcActiveBody->getNameInDocument(), FeatName.c_str());
prevSolidFeature = lastSolidFeature;
} else {
// Insert the feature into the body after the given one.
cmd->doCommand(cmd->Doc,
"App.activeDocument().%s.insertFeature(App.activeDocument().%s, App.activeDocument().%s, True)",
pcActiveBody->getNameInDocument(), FeatName.c_str(), prevSolidFeature->getNameInDocument());
}
}
if (hidePrevSolid && prevSolidFeature && (prevSolidFeature != NULL))
cmd->doCommand(cmd->Gui,"Gui.activeDocument().hide(\"%s\")", prevSolidFeature->getNameInDocument());
cmd->updateActive();
// #0001721: use '0' as edit value to avoid switching off selection in
// ViewProviderGeometryObject::setEditViewer
cmd->doCommand(cmd->Gui,"Gui.activeDocument().setEdit('%s', 0)", FeatName.c_str());
cmd->doCommand(cmd->Gui,"Gui.Selection.clearSelection()");
//cmd->doCommand(cmd->Gui,"Gui.Selection.addSelection(App.ActiveDocument.ActiveObject)");
if (pcActiveBody) {
cmd->copyVisual(FeatName.c_str(), "ShapeColor", pcActiveBody->getNameInDocument());
cmd->copyVisual(FeatName.c_str(), "LineColor", pcActiveBody->getNameInDocument());
cmd->copyVisual(FeatName.c_str(), "PointColor", pcActiveBody->getNameInDocument());
}
}
//===========================================================================
// Common utility functions for SketchBased features
//===========================================================================
// Take a list of Part2DObjects and erase those which are not eligible for creating a
// SketchBased feature.
const unsigned validateSketches(std::vector<App::DocumentObject*>& sketches,
std::vector<PartDesignGui::TaskFeaturePick::featureStatus>& status,
std::vector<App::DocumentObject*>::iterator& firstValidSketch)
{
PartDesign::Body* pcActiveBody = PartDesignGui::getBody(false);
App::Part* pcActivePart = PartDesignGui::getPartFor(pcActiveBody, false);
// TODO: If the user previously opted to allow multiple use of sketches or use of sketches from other bodies,
// then count these as valid sketches!
unsigned validSketches = 0;
firstValidSketch = sketches.end();
for (std::vector<App::DocumentObject*>::iterator s = sketches.begin(); s != sketches.end(); s++) {
if (!pcActiveBody) {
// We work in the old style outside any body
if (PartDesign::Body::findBodyOf (*s)) {
status.push_back(PartDesignGui::TaskFeaturePick::otherPart);
++validSketches;
continue;
}
} else if (!pcActiveBody->hasFeature(*s)) {
// Check whether this plane belongs to the active body
if(pcActivePart && pcActivePart->hasObject(*s, true)) {
status.push_back(PartDesignGui::TaskFeaturePick::otherBody);
} else if (PartDesign::Body::findBodyOf(*s)) {
status.push_back(PartDesignGui::TaskFeaturePick::otherPart);
} else {
status.push_back(PartDesignGui::TaskFeaturePick::notInBody);
}
++validSketches;
continue;
}
//Base::Console().Error("Checking sketch %s\n", (*s)->getNameInDocument());
// Check whether this sketch is already being used by another feature
// Body features don't count...
std::vector<App::DocumentObject*> inList = (*s)->getInList();
std::vector<App::DocumentObject*>::iterator o = inList.begin();
while (o != inList.end()) {
//Base::Console().Error("Inlist: %s\n", (*o)->getNameInDocument());
if ((*o)->getTypeId().isDerivedFrom(PartDesign::Body::getClassTypeId()))
o = inList.erase(o); //ignore bodies
else if (!( (*o)->getTypeId().isDerivedFrom(PartDesign::Feature::getClassTypeId()) ))
o = inList.erase(o); //ignore non-partDesign
else
++o;
}
if (inList.size() > 0) {
status.push_back(PartDesignGui::TaskFeaturePick::isUsed);
continue;
}
if (pcActiveBody && pcActiveBody->isAfterTip(*s)){
status.push_back(PartDesignGui::TaskFeaturePick::afterTip);
continue;
}
// Check whether the sketch shape is valid
Part::Part2DObject* sketch = static_cast<Part::Part2DObject*>(*s);
const TopoDS_Shape& shape = sketch->Shape.getValue();
if (shape.IsNull()) {
status.push_back(PartDesignGui::TaskFeaturePick::invalidShape);
continue;
}
// count free wires
int ctWires=0;
TopExp_Explorer ex;
for (ex.Init(shape, TopAbs_WIRE); ex.More(); ex.Next()) {
ctWires++;
}
if (ctWires == 0) {
status.push_back(PartDesignGui::TaskFeaturePick::noWire);
continue;
}
// All checks passed - found a valid sketch
if (firstValidSketch == sketches.end())
firstValidSketch = s;
validSketches++;
status.push_back(PartDesignGui::TaskFeaturePick::validFeature);
}
return validSketches;
}
void prepareSketchBased(Gui::Command* cmd, const std::string& which,
boost::function<void (Part::Part2DObject*, std::string)> func)
{
bool bNoSketchWasSelected = false;
// Get a valid sketch from the user
// First check selections
std::vector<App::DocumentObject*> sketches = cmd->getSelection().getObjectsOfType(Part::Part2DObject::getClassTypeId());
if (sketches.size() == 0) {//no sketches were selected. Let user pick an object from valid ones available in document
sketches = cmd->getDocument()->getObjectsOfType(Part::Part2DObject::getClassTypeId());
bNoSketchWasSelected = true;
}
std::vector<PartDesignGui::TaskFeaturePick::featureStatus> status;
std::vector<App::DocumentObject*>::iterator firstValidSketch;
unsigned validSketches = validateSketches(sketches, status, firstValidSketch);
if (validSketches == 0) {
if (bNoSketchWasSelected) {
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("No sketch to work on"),
QObject::tr("No sketch was selected. None of the sketches in the document is free."));
return;
} else {
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("No valid sketches selected"),
QObject::tr("Attention: none of selected sketches/2D objects is free."));
}
}
auto accepter = [=](const std::vector<App::DocumentObject*>& features) -> bool {
if(features.empty())
return false;
return true;
};
auto worker = [which, cmd, func](std::vector<App::DocumentObject*> features) {
auto firstValidSketch = features.begin();
Part::Part2DObject* sketch = static_cast<Part::Part2DObject*>(*firstValidSketch);
std::string FeatName = cmd->getUniqueObjectName(which.c_str());
Gui::Command::openCommand((std::string("Make ") + which).c_str());
Gui::Command::doCommand(Gui::Command::Doc,"App.activeDocument().addObject(\"PartDesign::%s\",\"%s\")",
which.c_str(), FeatName.c_str());
Gui::Command::doCommand(Gui::Command::Doc,"App.activeDocument().%s.Sketch = App.activeDocument().%s",
FeatName.c_str(), sketch->getNameInDocument());
func(sketch, FeatName);
};
//if there is a sketch selected which is from annother body or part we need to bring up the
//pick task dialog to decide how those are handled
bool ext = std::find_if( status.begin(), status.end(),
[] (const PartDesignGui::TaskFeaturePick::featureStatus& s) {
return s == PartDesignGui::TaskFeaturePick::otherBody ||
s == PartDesignGui::TaskFeaturePick::otherPart ||
s == PartDesignGui::TaskFeaturePick::notInBody;
}
) != status.end();
// If there is more than one selection/possibility, show dialog and let user pick sketch
if ((bNoSketchWasSelected && validSketches > 1) ||
(!bNoSketchWasSelected && sketches.size() > 1) ||
ext ) {
Gui::TaskView::TaskDialog *dlg = Gui::Control().activeDialog();
PartDesignGui::TaskDlgFeaturePick *pickDlg = qobject_cast<PartDesignGui::TaskDlgFeaturePick *>(dlg);
if (dlg && !pickDlg) {
QMessageBox msgBox;
msgBox.setText(QObject::tr("A dialog is already open in the task panel"));
msgBox.setInformativeText(QObject::tr("Do you want to close this dialog?"));
msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
msgBox.setDefaultButton(QMessageBox::Yes);
int ret = msgBox.exec();
if (ret == QMessageBox::Yes)
Gui::Control().closeDialog();
else
return;
}
if(dlg)
Gui::Control().closeDialog();
Gui::Selection().clearSelection();
pickDlg = new PartDesignGui::TaskDlgFeaturePick(sketches, status, accepter, worker);
if(ext)
pickDlg->showExternal(true);
Gui::Control().showDialog(pickDlg);
}
else {
std::vector<App::DocumentObject*> theSketch;
theSketch.reserve(1);
if (bNoSketchWasSelected && validSketches == 1){
theSketch.push_back(*firstValidSketch);
} else if(!bNoSketchWasSelected && sketches.size() == 1) {
theSketch = sketches;
}
worker(theSketch);
}
}
void finishSketchBased(const Gui::Command* cmd, const Part::Part2DObject* sketch, const std::string& FeatName)
{
cmd->doCommand(cmd->Gui,"Gui.activeDocument().hide(\"%s\")", sketch->getNameInDocument());
finishFeature(cmd, FeatName);
}
//===========================================================================
// PartDesign_Pad
//===========================================================================
DEF_STD_CMD_A(CmdPartDesignPad);
CmdPartDesignPad::CmdPartDesignPad()
: Command("PartDesign_Pad")
{
sAppModule = "PartDesign";
sGroup = QT_TR_NOOP("PartDesign");
sMenuText = QT_TR_NOOP("Pad");
sToolTipText = QT_TR_NOOP("Pad a selected sketch");
sWhatsThis = "PartDesign_Pad";
sStatusTip = sToolTipText;
sPixmap = "PartDesign_Pad";
}
void CmdPartDesignPad::activated(int iMsg)
{
Gui::Command* cmd = this;
auto worker = [cmd](Part::Part2DObject* sketch, std::string FeatName) {
if (FeatName.empty()) return;
// specific parameters for Pad
Gui::Command::doCommand(Doc,"App.activeDocument().%s.Length = 10.0",FeatName.c_str());
App::DocumentObjectGroup* grp = sketch->getGroup();
if (grp) {
Gui::Command::doCommand(Doc,"App.activeDocument().%s.addObject(App.activeDocument().%s)"
,grp->getNameInDocument(),FeatName.c_str());
Gui::Command::doCommand(Doc,"App.activeDocument().%s.removeObject(App.activeDocument().%s)"
,grp->getNameInDocument(),sketch->getNameInDocument());
}
Gui::Command::updateActive();
finishSketchBased(cmd, sketch, FeatName);
cmd->adjustCameraPosition();
};
prepareSketchBased(this, "Pad", worker);
}
bool CmdPartDesignPad::isActive(void)
{
return hasActiveDocument();
}
//===========================================================================
// PartDesign_Pocket
//===========================================================================
DEF_STD_CMD_A(CmdPartDesignPocket);
CmdPartDesignPocket::CmdPartDesignPocket()
: Command("PartDesign_Pocket")
{
sAppModule = "PartDesign";
sGroup = QT_TR_NOOP("PartDesign");
sMenuText = QT_TR_NOOP("Pocket");
sToolTipText = QT_TR_NOOP("Create a pocket with the selected sketch");
sWhatsThis = "PartDesign_Pocket";
sStatusTip = sToolTipText;
sPixmap = "PartDesign_Pocket";
}
void CmdPartDesignPocket::activated(int iMsg)
{
Gui::Command* cmd = this;
auto worker = [cmd](Part::Part2DObject* sketch, std::string FeatName) {
if (FeatName.empty()) return;
Gui::Command::doCommand(Doc,"App.activeDocument().%s.Length = 5.0",FeatName.c_str());
finishSketchBased(cmd, sketch, FeatName);
cmd->adjustCameraPosition();
};
prepareSketchBased(this, "Pocket", worker);
}
bool CmdPartDesignPocket::isActive(void)
{
return hasActiveDocument();
}
//===========================================================================
// PartDesign_Revolution
//===========================================================================
DEF_STD_CMD_A(CmdPartDesignRevolution);
CmdPartDesignRevolution::CmdPartDesignRevolution()
: Command("PartDesign_Revolution")
{
sAppModule = "PartDesign";
sGroup = QT_TR_NOOP("PartDesign");
sMenuText = QT_TR_NOOP("Revolution");
sToolTipText = QT_TR_NOOP("Revolve a selected sketch");
sWhatsThis = "PartDesign_Revolution";
sStatusTip = sToolTipText;
sPixmap = "PartDesign_Revolution";
}
void CmdPartDesignRevolution::activated(int iMsg)
{
Gui::Command* cmd = this;
auto worker = [cmd](Part::Part2DObject* sketch, std::string FeatName) {
if (FeatName.empty()) return;
Gui::Command::doCommand(Doc,"App.activeDocument().%s.ReferenceAxis = (App.activeDocument().%s,['V_Axis'])",
FeatName.c_str(), sketch->getNameInDocument());
Gui::Command::doCommand(Doc,"App.activeDocument().%s.Angle = 360.0",FeatName.c_str());
PartDesign::Revolution* pcRevolution = static_cast<PartDesign::Revolution*>(cmd->getDocument()->getObject(FeatName.c_str()));
if (pcRevolution && pcRevolution->suggestReversed())
Gui::Command::doCommand(Doc,"App.activeDocument().%s.Reversed = 1",FeatName.c_str());
finishSketchBased(cmd, sketch, FeatName);
cmd->adjustCameraPosition();
};
prepareSketchBased(this, "Revolution", worker);
}
bool CmdPartDesignRevolution::isActive(void)
{
return hasActiveDocument();
}
//===========================================================================
// PartDesign_Groove
//===========================================================================
DEF_STD_CMD_A(CmdPartDesignGroove);
CmdPartDesignGroove::CmdPartDesignGroove()
: Command("PartDesign_Groove")
{
sAppModule = "PartDesign";
sGroup = QT_TR_NOOP("PartDesign");
sMenuText = QT_TR_NOOP("Groove");
sToolTipText = QT_TR_NOOP("Groove a selected sketch");
sWhatsThis = "PartDesign_Groove";
sStatusTip = sToolTipText;
sPixmap = "PartDesign_Groove";
}
void CmdPartDesignGroove::activated(int iMsg)
{
Gui::Command* cmd = this;
auto worker = [cmd](Part::Part2DObject* sketch, std::string FeatName) {
if (FeatName.empty()) return;
Gui::Command::doCommand(Doc,"App.activeDocument().%s.ReferenceAxis = (App.activeDocument().%s,['V_Axis'])",
FeatName.c_str(), sketch->getNameInDocument());
Gui::Command::doCommand(Doc,"App.activeDocument().%s.Angle = 360.0",FeatName.c_str());
PartDesign::Groove* pcGroove = static_cast<PartDesign::Groove*>(cmd->getDocument()->getObject(FeatName.c_str()));
if (pcGroove && pcGroove->suggestReversed())
Gui::Command::doCommand(Doc,"App.activeDocument().%s.Reversed = 1",FeatName.c_str());
finishSketchBased(cmd, sketch, FeatName);
cmd->adjustCameraPosition();
};
prepareSketchBased(this, "Groove", worker);
}
bool CmdPartDesignGroove::isActive(void)
{
return hasActiveDocument();
}
//===========================================================================
// PartDesign_Additive_Pipe
//===========================================================================
DEF_STD_CMD_A(CmdPartDesignAdditivePipe);
CmdPartDesignAdditivePipe::CmdPartDesignAdditivePipe()
: Command("PartDesign_AdditivePipe")
{
sAppModule = "PartDesign";
sGroup = QT_TR_NOOP("PartDesign");
sMenuText = QT_TR_NOOP("Additive pipe");
sToolTipText = QT_TR_NOOP("Sweep a selected sketch along a path or to other profiles");
sWhatsThis = "PartDesign_Additive_Pipe";
sStatusTip = sToolTipText;
sPixmap = "PartDesign_Additive_Pipe";
}
void CmdPartDesignAdditivePipe::activated(int iMsg)
{
Gui::Command* cmd = this;
auto worker = [cmd](Part::Part2DObject* sketch, std::string FeatName) {
if (FeatName.empty()) return;
// specific parameters for pipe
Gui::Command::updateActive();
finishSketchBased(cmd, sketch, FeatName);
cmd->adjustCameraPosition();
};
prepareSketchBased(this, "AdditivePipe", worker);
}
bool CmdPartDesignAdditivePipe::isActive(void)
{
return hasActiveDocument();
}
//===========================================================================
// PartDesign_Subtractive_Pipe
//===========================================================================
DEF_STD_CMD_A(CmdPartDesignSubtractivePipe);
CmdPartDesignSubtractivePipe::CmdPartDesignSubtractivePipe()
: Command("PartDesign_SubtractivePipe")
{
sAppModule = "PartDesign";
sGroup = QT_TR_NOOP("PartDesign");
sMenuText = QT_TR_NOOP("Subtractive pipe");
sToolTipText = QT_TR_NOOP("Sweep a selected sketch along a path or to other profiles and remove it from the body");
sWhatsThis = "PartDesign_Subtractive_Pipe";
sStatusTip = sToolTipText;
sPixmap = "PartDesign_Subtractive_Pipe";
}
void CmdPartDesignSubtractivePipe::activated(int iMsg)
{
Gui::Command* cmd = this;
auto worker = [cmd](Part::Part2DObject* sketch, std::string FeatName) {
if (FeatName.empty()) return;
// specific parameters for pipe
Gui::Command::updateActive();
finishSketchBased(cmd, sketch, FeatName);
cmd->adjustCameraPosition();
};
prepareSketchBased(this, "SubtractivePipe", worker);
}
bool CmdPartDesignSubtractivePipe::isActive(void)
{
return hasActiveDocument();
}
//===========================================================================
// PartDesign_Additive_Loft
//===========================================================================
DEF_STD_CMD_A(CmdPartDesignAdditiveLoft);
CmdPartDesignAdditiveLoft::CmdPartDesignAdditiveLoft()
: Command("PartDesign_AdditiveLoft")
{
sAppModule = "PartDesign";
sGroup = QT_TR_NOOP("PartDesign");
sMenuText = QT_TR_NOOP("Additive loft");
sToolTipText = QT_TR_NOOP("Sweep a selected sketch along a path or to other profiles");
sWhatsThis = "PartDesign_Additive_Loft";
sStatusTip = sToolTipText;
sPixmap = "PartDesign_Additive_Loft";
}
void CmdPartDesignAdditiveLoft::activated(int iMsg)
{
Gui::Command* cmd = this;
auto worker = [cmd](Part::Part2DObject* sketch, std::string FeatName) {
if (FeatName.empty()) return;
// specific parameters for pipe
Gui::Command::updateActive();
finishSketchBased(cmd, sketch, FeatName);
cmd->adjustCameraPosition();
};
prepareSketchBased(this, "AdditiveLoft", worker);
}
bool CmdPartDesignAdditiveLoft::isActive(void)
{
return hasActiveDocument();
}
//===========================================================================
// PartDesign_Subtractive_Loft
//===========================================================================
DEF_STD_CMD_A(CmdPartDesignSubtractiveLoft);
CmdPartDesignSubtractiveLoft::CmdPartDesignSubtractiveLoft()
: Command("PartDesign_SubtractiveLoft")
{
sAppModule = "PartDesign";
sGroup = QT_TR_NOOP("PartDesign");
sMenuText = QT_TR_NOOP("Subtractive loft");
sToolTipText = QT_TR_NOOP("Sweep a selected sketch along a path or to other profiles and remove it from the body");
sWhatsThis = "PartDesign_Subtractive_Loft";
sStatusTip = sToolTipText;
sPixmap = "PartDesign_Subtractive_Loft";
}
void CmdPartDesignSubtractiveLoft::activated(int iMsg)
{
Gui::Command* cmd = this;
auto worker = [cmd](Part::Part2DObject* sketch, std::string FeatName) {
if (FeatName.empty()) return;
// specific parameters for pipe
Gui::Command::updateActive();
finishSketchBased(cmd, sketch, FeatName);
cmd->adjustCameraPosition();
};
prepareSketchBased(this, "SubtractiveLoft", worker);
}
bool CmdPartDesignSubtractiveLoft::isActive(void)
{
return hasActiveDocument();
}
//===========================================================================
// Common utility functions for Dressup features
//===========================================================================
bool dressupGetSelected(Gui::Command* cmd, const std::string& which,
Gui::SelectionObject &selected)
{
std::vector<Gui::SelectionObject> selection = cmd->getSelection().getSelectionEx();
selection = cmd->getSelection().getSelectionEx();
if (selection.size() == 0) {
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
QObject::tr("Select an edge, face or body."));
return false;
} else if (selection.size() != 1) {
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
QObject::tr("Select an edge, face or body from a single body."));
return false;
}
Gui::Selection().clearSelection();
// set the
selected = selection[0];
if (!selected.isObjectTypeOf(Part::Feature::getClassTypeId())) {
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong object type"),
QObject::tr("%1 works only on parts.").arg(QString::fromStdString(which)));
return false;
}
Part::Feature *base = static_cast<Part::Feature*>(selected.getObject());
const Part::TopoShape& TopShape = base->Shape.getShape();
if (TopShape._Shape.IsNull()){
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
QObject::tr("Shape of the selected Part is empty"));
return false;
}
return true;
}
void finishDressupFeature(const Gui::Command* cmd, const std::string& which,
Part::Feature *base, const std::vector<std::string> & SubNames)
{
if (SubNames.size() == 0) {
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
QString::fromStdString(which) + QObject::tr(" not possible on selected faces/edges."));
return;
}
std::string SelString;
SelString += "(App.";
SelString += "ActiveDocument";
SelString += ".";
SelString += base->getNameInDocument();
SelString += ",[";
for(std::vector<std::string>::const_iterator it = SubNames.begin();it!=SubNames.end();++it){
SelString += "\"";
SelString += *it;
SelString += "\"";
if(it != --SubNames.end())
SelString += ",";
}
SelString += "])";
std::string FeatName = cmd->getUniqueObjectName(which.c_str());
cmd->openCommand((std::string("Make ") + which).c_str());
cmd->doCommand(cmd->Doc,"App.activeDocument().addObject(\"PartDesign::%s\",\"%s\")",which.c_str(), FeatName.c_str());
cmd->doCommand(cmd->Doc,"App.activeDocument().%s.Base = %s",FeatName.c_str(),SelString.c_str());
doCommand(Gui,"Gui.Selection.clearSelection()");
finishFeature(cmd, FeatName, base);
}
void makeChamferOrFillet(Gui::Command* cmd, const std::string& which)
{
Gui::SelectionObject selected;
if (!dressupGetSelected ( cmd, which, selected))
return;
Part::Feature *base = static_cast<Part::Feature*>(selected.getObject());
std::vector<std::string> SubNames = std::vector<std::string>(selected.getSubNames());
finishDressupFeature (cmd, which, base, SubNames);
}
//===========================================================================
// PartDesign_Fillet
//===========================================================================
DEF_STD_CMD_A(CmdPartDesignFillet);
CmdPartDesignFillet::CmdPartDesignFillet()
:Command("PartDesign_Fillet")
{
sAppModule = "PartDesign";
sGroup = QT_TR_NOOP("PartDesign");
sMenuText = QT_TR_NOOP("Fillet");
sToolTipText = QT_TR_NOOP("Make a fillet on an edge, face or body");
sWhatsThis = "PartDesign_Fillet";
sStatusTip = sToolTipText;
sPixmap = "PartDesign_Fillet";
}
void CmdPartDesignFillet::activated(int iMsg)
{
makeChamferOrFillet(this, "Fillet");
}
bool CmdPartDesignFillet::isActive(void)
{
return hasActiveDocument();
}
//===========================================================================
// PartDesign_Chamfer
//===========================================================================
DEF_STD_CMD_A(CmdPartDesignChamfer);
CmdPartDesignChamfer::CmdPartDesignChamfer()
:Command("PartDesign_Chamfer")
{
sAppModule = "PartDesign";
sGroup = QT_TR_NOOP("PartDesign");
sMenuText = QT_TR_NOOP("Chamfer");
sToolTipText = QT_TR_NOOP("Chamfer the selected edges of a shape");
sWhatsThis = "PartDesign_Chamfer";
sStatusTip = sToolTipText;
sPixmap = "PartDesign_Chamfer";
}
void CmdPartDesignChamfer::activated(int iMsg)
{
makeChamferOrFillet(this, "Chamfer");
doCommand(Gui,"Gui.Selection.clearSelection()");
}
bool CmdPartDesignChamfer::isActive(void)
{
return hasActiveDocument();
}
//===========================================================================
// PartDesign_Draft
//===========================================================================
DEF_STD_CMD_A(CmdPartDesignDraft);
CmdPartDesignDraft::CmdPartDesignDraft()
:Command("PartDesign_Draft")
{
sAppModule = "PartDesign";
sGroup = QT_TR_NOOP("PartDesign");
sMenuText = QT_TR_NOOP("Draft");
sToolTipText = QT_TR_NOOP("Make a draft on a face");
sWhatsThis = "PartDesign_Draft";
sStatusTip = sToolTipText;
sPixmap = "PartDesign_Draft";
}
void CmdPartDesignDraft::activated(int iMsg)
{
Gui::SelectionObject selected;
if (!dressupGetSelected ( this, "Draft", selected))
return;
Part::Feature *base = static_cast<Part::Feature*>(selected.getObject());
std::vector<std::string> SubNames = std::vector<std::string>(selected.getSubNames());
const Part::TopoShape& TopShape = base->Shape.getShape();
size_t i = 0;
// filter out the edges
while(i < SubNames.size())
{
std::string aSubName = static_cast<std::string>(SubNames.at(i));
if(aSubName.size() > 4 && aSubName.substr(0,4) == "Face") {
// Check for valid face types
TopoDS_Face face = TopoDS::Face(TopShape.getSubShape(aSubName.c_str()));
BRepAdaptor_Surface sf(face);
if ((sf.GetType() != GeomAbs_Plane) && (sf.GetType() != GeomAbs_Cylinder) && (sf.GetType() != GeomAbs_Cone))
SubNames.erase(SubNames.begin()+i);
} else {
// empty name or any other sub-element
SubNames.erase(SubNames.begin()+i);
}
i++;
}
finishDressupFeature (this, "Draft", base, SubNames);
}
bool CmdPartDesignDraft::isActive(void)
{
return hasActiveDocument();
}
//===========================================================================
// PartDesign_Thickness
//===========================================================================
DEF_STD_CMD_A(CmdPartDesignThickness);
CmdPartDesignThickness::CmdPartDesignThickness()
:Command("PartDesign_Thickness")
{
sAppModule = "PartDesign";
sGroup = QT_TR_NOOP("PartDesign");
sMenuText = QT_TR_NOOP("Thickness");
sToolTipText = QT_TR_NOOP("Make a thick solid");
sWhatsThis = "PartDesign_Thickness";
sStatusTip = sToolTipText;
sPixmap = "PartDesign_Thickness";
}
void CmdPartDesignThickness::activated(int iMsg)
{
Gui::SelectionObject selected;
if (!dressupGetSelected ( this, "Thickness", selected))
return;
Part::Feature *base = static_cast<Part::Feature*>(selected.getObject());
std::vector<std::string> SubNames = std::vector<std::string>(selected.getSubNames());
size_t i = 0;
// filter out the edges
while(i < SubNames.size())
{
std::string aSubName = static_cast<std::string>(SubNames.at(i));
if(aSubName.size() > 4 && aSubName.substr(0,4) != "Face") {
// empty name or any other sub-element
SubNames.erase(SubNames.begin()+i);
}
i++;
}
finishDressupFeature (this, "Thickness", base, SubNames);
}
bool CmdPartDesignThickness::isActive(void)
{
return hasActiveDocument();
}
//===========================================================================
// Common functions for all Transformed features
//===========================================================================
void prepareTransformed(Gui::Command* cmd, const std::string& which,
boost::function<void(std::string, std::vector<App::DocumentObject*>)> func)
{
std::string FeatName = cmd->getUniqueObjectName(which.c_str());
auto accepter = [=](std::vector<App::DocumentObject*> features) -> bool{
if(features.empty())
return false;
return true;
};
auto worker = [=](std::vector<App::DocumentObject*> features) {
std::stringstream str;
str << "App.activeDocument()." << FeatName << ".Originals = [";
for (std::vector<App::DocumentObject*>::iterator it = features.begin(); it != features.end(); ++it){
str << "App.activeDocument()." << (*it)->getNameInDocument() << ",";
}
str << "]";
Gui::Command::openCommand((std::string("Make ") + which + " feature").c_str());
Gui::Command::doCommand(Gui::Command::Doc,"App.activeDocument().addObject(\"PartDesign::%s\",\"%s\")",which.c_str(), FeatName.c_str());
// FIXME: There seems to be kind of a race condition here, leading to sporadic errors like
// Exception (Thu Sep 6 11:52:01 2012): 'App.Document' object has no attribute 'Mirrored'
Gui::Command::updateActive(); // Helps to ensure that the object already exists when the next command comes up
Gui::Command::doCommand(Gui::Command::Doc, str.str().c_str());
func(FeatName, features);
};
// Get a valid original from the user
// First check selections
std::vector<App::DocumentObject*> features = cmd->getSelection().getObjectsOfType(PartDesign::FeatureAddSub::getClassTypeId());
// Next create a list of all eligible objects
if (features.size() == 0) {
features = cmd->getDocument()->getObjectsOfType(PartDesign::FeatureAddSub::getClassTypeId());
// If there is more than one selected or eligible object, show dialog and let user pick one
if (features.size() > 1) {
std::vector<PartDesignGui::TaskFeaturePick::featureStatus> status;
for (unsigned i = 0; i < features.size(); i++)
status.push_back(PartDesignGui::TaskFeaturePick::validFeature);
Gui::TaskView::TaskDialog *dlg = Gui::Control().activeDialog();
PartDesignGui::TaskDlgFeaturePick *pickDlg = qobject_cast<PartDesignGui::TaskDlgFeaturePick *>(dlg);
if (dlg && !pickDlg) {
QMessageBox msgBox;
msgBox.setText(QObject::tr("A dialog is already open in the task panel"));
msgBox.setInformativeText(QObject::tr("Do you want to close this dialog?"));
msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
msgBox.setDefaultButton(QMessageBox::Yes);
int ret = msgBox.exec();
if (ret == QMessageBox::Yes)
Gui::Control().closeDialog();
else
return;
}
if(dlg)
Gui::Control().closeDialog();
Gui::Selection().clearSelection();
Gui::Control().showDialog(new PartDesignGui::TaskDlgFeaturePick(features, status, accepter, worker));
} else {
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("No valid features in this document"),
QObject::tr("Please create a subtractive or additive feature first."));
return;
}
}
else {
worker(features);
}
}
void finishTransformed(Gui::Command* cmd, std::string& FeatName)
{
finishFeature(cmd, FeatName);
}
//===========================================================================
// PartDesign_Mirrored
//===========================================================================
DEF_STD_CMD_A(CmdPartDesignMirrored);
CmdPartDesignMirrored::CmdPartDesignMirrored()
: Command("PartDesign_Mirrored")
{
sAppModule = "PartDesign";
sGroup = QT_TR_NOOP("PartDesign");
sMenuText = QT_TR_NOOP("Mirrored");
sToolTipText = QT_TR_NOOP("create a mirrored feature");
sWhatsThis = "PartDesign_Mirrored";
sStatusTip = sToolTipText;
sPixmap = "PartDesign_Mirrored";
}
void CmdPartDesignMirrored::activated(int iMsg)
{
Gui::Command* cmd = this;
auto worker = [cmd](std::string FeatName, std::vector<App::DocumentObject*> features) {
if (features.empty())
return;
if(features.front()->isDerivedFrom(PartDesign::SketchBased::getClassTypeId())) {
Part::Part2DObject *sketch = (static_cast<PartDesign::SketchBased*>(features.front()))->getVerifiedSketch(/* silent =*/ true);
if (sketch)
Gui::Command::doCommand(Doc,"App.activeDocument().%s.MirrorPlane = (App.activeDocument().%s, [\"V_Axis\"])",
FeatName.c_str(), sketch->getNameInDocument());
}
else {
doCommand(Doc,"App.activeDocument().%s.MirrorPlane = (App.activeDocument().%s, [\"\"])", FeatName.c_str(),
App::Part::BaseplaneTypes[0]);
}
finishTransformed(cmd, FeatName);
};
prepareTransformed(this, "Mirrored", worker);
}
bool CmdPartDesignMirrored::isActive(void)
{
return hasActiveDocument();
}
//===========================================================================
// PartDesign_LinearPattern
//===========================================================================
DEF_STD_CMD_A(CmdPartDesignLinearPattern);
CmdPartDesignLinearPattern::CmdPartDesignLinearPattern()
: Command("PartDesign_LinearPattern")
{
sAppModule = "PartDesign";
sGroup = QT_TR_NOOP("PartDesign");
sMenuText = QT_TR_NOOP("LinearPattern");
sToolTipText = QT_TR_NOOP("Create a linear pattern feature");
sWhatsThis = "PartDesign_LinearPattern";
sStatusTip = sToolTipText;
sPixmap = "PartDesign_LinearPattern";
}
void CmdPartDesignLinearPattern::activated(int iMsg)
{
Gui::Command* cmd = this;
auto worker = [cmd](std::string FeatName, std::vector<App::DocumentObject*> features) {
if (features.empty())
return;
if(features.front()->isDerivedFrom(PartDesign::SketchBased::getClassTypeId())) {
Part::Part2DObject *sketch = (static_cast<PartDesign::SketchBased*>(features.front()))->getVerifiedSketch(/* silent =*/ true);
if (sketch)
doCommand(Doc,"App.activeDocument().%s.Direction = (App.activeDocument().%s, [\"H_Axis\"])",
FeatName.c_str(), sketch->getNameInDocument());
}
else {
doCommand(Doc,"App.activeDocument().%s.Direction = (App.activeDocument().%s, [\"\"])", FeatName.c_str(),
App::Part::BaselineTypes[0]);
}
doCommand(Doc,"App.activeDocument().%s.Length = 100", FeatName.c_str());
doCommand(Doc,"App.activeDocument().%s.Occurrences = 2", FeatName.c_str());
finishTransformed(cmd, FeatName);
};
prepareTransformed(this, "LinearPattern", worker);
}
bool CmdPartDesignLinearPattern::isActive(void)
{
return hasActiveDocument();
}
//===========================================================================
// PartDesign_PolarPattern
//===========================================================================
DEF_STD_CMD_A(CmdPartDesignPolarPattern);
CmdPartDesignPolarPattern::CmdPartDesignPolarPattern()
: Command("PartDesign_PolarPattern")
{
sAppModule = "PartDesign";
sGroup = QT_TR_NOOP("PartDesign");
sMenuText = QT_TR_NOOP("PolarPattern");
sToolTipText = QT_TR_NOOP("Create a polar pattern feature");
sWhatsThis = "PartDesign_PolarPattern";
sStatusTip = sToolTipText;
sPixmap = "PartDesign_PolarPattern";
}
void CmdPartDesignPolarPattern::activated(int iMsg)
{
Gui::Command* cmd = this;
auto worker = [cmd](std::string FeatName, std::vector<App::DocumentObject*> features) {
if (features.empty())
return;
if(features.front()->isDerivedFrom(PartDesign::SketchBased::getClassTypeId())) {
Part::Part2DObject *sketch = (static_cast<PartDesign::SketchBased*>(features.front()))->getVerifiedSketch(/* silent =*/ true);
if (sketch)
doCommand(Doc,"App.activeDocument().%s.Axis = (App.activeDocument().%s, [\"N_Axis\"])",
FeatName.c_str(), sketch->getNameInDocument());
}
else {
doCommand(Doc,"App.activeDocument().%s.Axis = (App.activeDocument().%s, [\"\"])", FeatName.c_str(),
App::Part::BaselineTypes[0]);
}
doCommand(Doc,"App.activeDocument().%s.Angle = 360", FeatName.c_str());
doCommand(Doc,"App.activeDocument().%s.Occurrences = 2", FeatName.c_str());
finishTransformed(cmd, FeatName);
};
prepareTransformed(this, "PolarPattern", worker);
}
bool CmdPartDesignPolarPattern::isActive(void)
{
return hasActiveDocument();
}
//===========================================================================
// PartDesign_Scaled
//===========================================================================
DEF_STD_CMD_A(CmdPartDesignScaled);
CmdPartDesignScaled::CmdPartDesignScaled()
: Command("PartDesign_Scaled")
{
sAppModule = "PartDesign";
sGroup = QT_TR_NOOP("PartDesign");
sMenuText = QT_TR_NOOP("Scaled");
sToolTipText = QT_TR_NOOP("Create a scaled feature");
sWhatsThis = "PartDesign_Scaled";
sStatusTip = sToolTipText;
sPixmap = "PartDesign_Scaled";
}
void CmdPartDesignScaled::activated(int iMsg)
{
Gui::Command* cmd = this;
auto worker = [cmd](std::string FeatName, std::vector<App::DocumentObject*> features) {
if (features.empty())
return;
doCommand(Doc,"App.activeDocument().%s.Factor = 2", FeatName.c_str());
doCommand(Doc,"App.activeDocument().%s.Occurrences = 2", FeatName.c_str());
finishTransformed(cmd, FeatName);
};
prepareTransformed(this, "Scaled", worker);
}
bool CmdPartDesignScaled::isActive(void)
{
return hasActiveDocument();
}
//===========================================================================
// PartDesign_MultiTransform
//===========================================================================
DEF_STD_CMD_A(CmdPartDesignMultiTransform);
CmdPartDesignMultiTransform::CmdPartDesignMultiTransform()
: Command("PartDesign_MultiTransform")
{
sAppModule = "PartDesign";
sGroup = QT_TR_NOOP("PartDesign");
sMenuText = QT_TR_NOOP("Create MultiTransform");
sToolTipText = QT_TR_NOOP("Create a multitransform feature");
sWhatsThis = "PartDesign_MultiTransform";
sStatusTip = sToolTipText;
sPixmap = "PartDesign_MultiTransform";
}
void CmdPartDesignMultiTransform::activated(int iMsg)
{
PartDesign::Body *pcActiveBody = PartDesignGui::getBody(/*messageIfNot = */false);
//if (!pcActiveBody) return;
std::vector<App::DocumentObject*> features;
// Check if a Transformed feature has been selected, convert it to MultiTransform
features = getSelection().getObjectsOfType(PartDesign::Transformed::getClassTypeId());
if (!features.empty()) {
// Throw out MultiTransform features, we don't want to nest them
for (std::vector<App::DocumentObject*>::iterator f = features.begin(); f != features.end(); ) {
if ((*f)->getTypeId().isDerivedFrom(PartDesign::MultiTransform::getClassTypeId()))
f = features.erase(f);
else
f++;
}
if (features.empty()) return;
// Note: If multiple Transformed features were selected, only the first one is used
PartDesign::Transformed* trFeat = static_cast<PartDesign::Transformed*>(features.front());
// Move the insert point back one feature
App::DocumentObject* oldTip = 0;
App::DocumentObject* prevFeature = 0;
if (pcActiveBody){
oldTip = pcActiveBody->Tip.getValue();
prevFeature = pcActiveBody->getPrevFeature(trFeat);
}
Gui::Selection().clearSelection();
if (prevFeature != NULL)
Gui::Selection().addSelection(prevFeature->getDocument()->getName(), prevFeature->getNameInDocument());
openCommand("Convert to MultiTransform feature");
doCommand(Gui, "FreeCADGui.runCommand('PartDesign_MoveTip')");
// Remove the Transformed feature from the Body
if(pcActiveBody)
doCommand(Doc, "App.activeDocument().%s.removeFeature(App.activeDocument().%s)",
pcActiveBody->getNameInDocument(), trFeat->getNameInDocument());
// Create a MultiTransform feature and move the Transformed feature inside it
std::string FeatName = getUniqueObjectName("MultiTransform");
doCommand(Doc, "App.activeDocument().addObject(\"PartDesign::MultiTransform\",\"%s\")", FeatName.c_str());
doCommand(Doc, "App.activeDocument().%s.Originals = App.activeDocument().%s.Originals", FeatName.c_str(), trFeat->getNameInDocument());
doCommand(Doc, "App.activeDocument().%s.Originals = []", trFeat->getNameInDocument());
doCommand(Doc, "App.activeDocument().%s.Transformations = [App.activeDocument().%s]", FeatName.c_str(), trFeat->getNameInDocument());
// Add the MultiTransform into the Body at the current insert point
finishFeature(this, FeatName);
// Restore the insert point
if (pcActiveBody && oldTip != trFeat) {
Gui::Selection().clearSelection();
Gui::Selection().addSelection(oldTip->getDocument()->getName(), oldTip->getNameInDocument());
Gui::Command::doCommand(Gui::Command::Gui,"FreeCADGui.runCommand('PartDesign_MoveTip')");
Gui::Selection().clearSelection();
} // otherwise the insert point remains at the new MultiTransform, which is fine
} else {
Gui::Command* cmd = this;
auto worker = [cmd, pcActiveBody](std::string FeatName, std::vector<App::DocumentObject*> features) {
if (features.empty())
return;
// Make sure the user isn't presented with an empty screen because no transformations are defined yet...
App::DocumentObject* prevSolid = 0;
if (pcActiveBody)
pcActiveBody->getPrevSolidFeature(NULL, true);
if (prevSolid != NULL) {
Part::Feature* feat = static_cast<Part::Feature*>(prevSolid);
doCommand(Doc,"App.activeDocument().%s.Shape = App.activeDocument().%s.Shape",
FeatName.c_str(), feat->getNameInDocument());
}
finishFeature(cmd, FeatName);
};
prepareTransformed(this, "MultiTransform", worker);
}
}
bool CmdPartDesignMultiTransform::isActive(void)
{
return hasActiveDocument();
}
//===========================================================================
// PartDesign_Boolean
//===========================================================================
/* Boolean commands =======================================================*/
DEF_STD_CMD_A(CmdPartDesignBoolean);
CmdPartDesignBoolean::CmdPartDesignBoolean()
:Command("PartDesign_Boolean")
{
sAppModule = "PartDesign";
sGroup = QT_TR_NOOP("PartDesign");
sMenuText = QT_TR_NOOP("Boolean operation");
sToolTipText = QT_TR_NOOP("Boolean operation with two or more bodies");
sWhatsThis = sToolTipText;
sStatusTip = sToolTipText;
sPixmap = "PartDesign_Boolean";
}
void CmdPartDesignBoolean::activated(int iMsg)
{
Gui::SelectionFilter BodyFilter("SELECT PartDesign::Body COUNT 1..");
PartDesign::Body* body;
std::string bodyString("");
if (BodyFilter.match()) {
body = static_cast<PartDesign::Body*>(BodyFilter.Result[0][0].getObject());
std::vector<App::DocumentObject*> bodies;
std::vector<std::vector<Gui::SelectionObject> >::iterator i = BodyFilter.Result.begin();
i++;
for (; i != BodyFilter.Result.end(); i++) {
for (std::vector<Gui::SelectionObject>::iterator j = i->begin(); j != i->end(); j++) {
bodies.push_back(j->getObject());
}
}
bodyString = PartDesignGui::getPythonStr(bodies);
} else {
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("No body selected"),
QObject::tr("Please select a body for the boolean operation"));
return;
}
openCommand("Create Boolean");
PartDesign::Body* activeBody = Gui::Application::Instance->activeView()->getActiveObject<PartDesign::Body*>(PDBODYKEY);
// Make sure we are working on the selected body
if (body != activeBody) {
Gui::Selection().clearSelection();
Gui::Selection().addSelection(body->getDocument()->getName(), body->Tip.getValue()->getNameInDocument());
Gui::Command::doCommand(Gui::Command::Gui,"FreeCADGui.runCommand('PartDesign_MoveTip')");
}
std::string FeatName = getUniqueObjectName("Boolean");
doCommand(Doc,"App.activeDocument().addObject('PartDesign::Boolean','%s')",FeatName.c_str());
if (!bodyString.empty())
doCommand(Doc,"App.activeDocument().%s.Bodies = %s",FeatName.c_str(),bodyString.c_str());
finishFeature(this, FeatName, nullptr, false);
}
bool CmdPartDesignBoolean::isActive(void)
{
if (getActiveGuiDocument())
return true;
else
return false;
}
//===========================================================================
// Initialization
//===========================================================================
void CreatePartDesignCommands(void)
{
Gui::CommandManager &rcCmdMgr = Gui::Application::Instance->commandManager();
rcCmdMgr.addCommand(new CmdPartDesignPart());
rcCmdMgr.addCommand(new CmdPartDesignBody());
rcCmdMgr.addCommand(new CmdPartDesignMoveTip());
rcCmdMgr.addCommand(new CmdPartDesignDuplicateSelection());
rcCmdMgr.addCommand(new CmdPartDesignMoveFeature());
rcCmdMgr.addCommand(new CmdPartDesignMoveFeatureInTree());
rcCmdMgr.addCommand(new CmdPartDesignPlane());
rcCmdMgr.addCommand(new CmdPartDesignLine());
rcCmdMgr.addCommand(new CmdPartDesignPoint());
rcCmdMgr.addCommand(new CmdPartDesignNewSketch());
rcCmdMgr.addCommand(new CmdPartDesignPad());
rcCmdMgr.addCommand(new CmdPartDesignPocket());
rcCmdMgr.addCommand(new CmdPartDesignRevolution());
rcCmdMgr.addCommand(new CmdPartDesignGroove());
rcCmdMgr.addCommand(new CmdPartDesignAdditivePipe);
rcCmdMgr.addCommand(new CmdPartDesignSubtractivePipe);
rcCmdMgr.addCommand(new CmdPartDesignAdditiveLoft);
rcCmdMgr.addCommand(new CmdPartDesignSubtractiveLoft);
rcCmdMgr.addCommand(new CmdPartDesignFillet());
rcCmdMgr.addCommand(new CmdPartDesignDraft());
rcCmdMgr.addCommand(new CmdPartDesignChamfer());
rcCmdMgr.addCommand(new CmdPartDesignThickness());
rcCmdMgr.addCommand(new CmdPartDesignMirrored());
rcCmdMgr.addCommand(new CmdPartDesignLinearPattern());
rcCmdMgr.addCommand(new CmdPartDesignPolarPattern());
//rcCmdMgr.addCommand(new CmdPartDesignScaled());
rcCmdMgr.addCommand(new CmdPartDesignMultiTransform());
rcCmdMgr.addCommand(new CmdPartDesignBoolean());
}