2076 lines
78 KiB
C++
2076 lines
78 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 <TopoDS_Face.hxx>
|
|
# include <TopoDS.hxx>
|
|
# include <BRepAdaptor_Surface.hxx>
|
|
# include <TopExp_Explorer.hxx>
|
|
# include <QMessageBox>
|
|
#endif
|
|
|
|
#include <sstream>
|
|
#include <algorithm>
|
|
|
|
#include <App/DocumentObjectGroup.h>
|
|
#include <App/Origin.h>
|
|
#include <App/OriginFeature.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/Document.h>
|
|
|
|
#include <Mod/Sketcher/App/SketchObject.h>
|
|
|
|
#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>
|
|
#include <Mod/PartDesign/App/DatumLine.h>
|
|
#include <Mod/PartDesign/App/DatumPlane.h>
|
|
#include <Mod/PartDesign/App/ShapeBinder.h>
|
|
|
|
#include "TaskFeaturePick.h"
|
|
#include "ReferenceSelection.h"
|
|
#include "Utils.h"
|
|
#include "WorkflowManager.h"
|
|
|
|
// TODO Remove this header after fixing code so it won;t be needed here (2015-10-20, Fat-Zer)
|
|
#include "ui_DlgReference.h"
|
|
|
|
using namespace std;
|
|
using namespace Attacher;
|
|
|
|
|
|
//===========================================================================
|
|
// 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;
|
|
}
|
|
|
|
PartDesign::Body *pcActiveBody = PartDesignGui::getBody(/*messageIfNot = */false);
|
|
|
|
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 if (pcActiveBody) {
|
|
|
|
// TODO Check how this will work outside of a body (2015-10-20, Fat-Zer)
|
|
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) {
|
|
Part::AttachExtension* pcDatum = cmd.getDocument()->getObject(FeatName.c_str())->getExtensionByType<Part::AttachExtension>();
|
|
pcDatum->attacher().references.Paste(support);
|
|
SuggestResult sugr;
|
|
pcDatum->attacher().suggestMapModes(sugr);
|
|
if (sugr.message == Attacher::SuggestResult::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::getModeName(sugr.bestFitMode).c_str());
|
|
} 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.addObject(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)
|
|
{
|
|
Q_UNUSED(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)
|
|
{
|
|
Q_UNUSED(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)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
UnifiedDatumCommand(*this, Base::Type::fromName("PartDesign::Point"),"DatumPoint");
|
|
}
|
|
|
|
bool CmdPartDesignPoint::isActive(void)
|
|
{
|
|
if (getActiveGuiDocument())
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
//===========================================================================
|
|
// PartDesign_ShapeBinder
|
|
//===========================================================================
|
|
|
|
DEF_STD_CMD_A(CmdPartDesignShapeBinder);
|
|
|
|
CmdPartDesignShapeBinder::CmdPartDesignShapeBinder()
|
|
:Command("PartDesign_ShapeBinder")
|
|
{
|
|
sAppModule = "PartDesign";
|
|
sGroup = QT_TR_NOOP("PartDesign");
|
|
sMenuText = QT_TR_NOOP("Create a shape binder");
|
|
sToolTipText = QT_TR_NOOP("Create a new shape binder");
|
|
sWhatsThis = sToolTipText;
|
|
sStatusTip = sToolTipText;
|
|
sPixmap = "PartDesign_ShapeBinder";
|
|
}
|
|
|
|
void CmdPartDesignShapeBinder::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
App::PropertyLinkSubList support;
|
|
getSelection().getAsPropertyLinkSubList(support);
|
|
|
|
bool bEditSelected = false;
|
|
if (support.getSize() == 1 && support.getValue() ){
|
|
if (support.getValue()->isDerivedFrom(PartDesign::ShapeBinder::getClassTypeId()))
|
|
bEditSelected = true;
|
|
}
|
|
|
|
if (bEditSelected) {
|
|
// TODO probably we not should handle edit here (2015-10-26, Fat-Zer)
|
|
std::string tmp = std::string("Edit ShapeBinder");
|
|
openCommand(tmp.c_str());
|
|
doCommand(Gui::Command::Gui,"Gui.activeDocument().setEdit('%s')",
|
|
support.getValue()->getNameInDocument());
|
|
} else {
|
|
PartDesign::Body *pcActiveBody = PartDesignGui::getBody(/*messageIfNot = */true);
|
|
if (pcActiveBody == 0)
|
|
return;
|
|
|
|
std::string FeatName = getUniqueObjectName("ShapeBinder");
|
|
std::string tmp = std::string("Create ShapeBinder");
|
|
|
|
openCommand(tmp.c_str());
|
|
|
|
doCommand(Gui::Command::Doc,"App.activeDocument().addObject('%s','%s')",
|
|
"PartDesign::ShapeBinder",FeatName.c_str());
|
|
|
|
//test if current selection fits a mode.
|
|
if (support.getSize() > 0) {
|
|
doCommand(Gui::Command::Doc,"App.activeDocument().%s.Support = %s",
|
|
FeatName.c_str(), support.getPyReprString().c_str());
|
|
}
|
|
doCommand(Gui::Command::Doc,"App.activeDocument().%s.addObject(App.activeDocument().%s)",
|
|
pcActiveBody->getNameInDocument(), FeatName.c_str());
|
|
doCommand(Gui::Command::Doc,"App.activeDocument().recompute()"); // recompute the feature based on its references
|
|
doCommand(Gui::Command::Gui,"Gui.activeDocument().setEdit('%s')",FeatName.c_str());
|
|
}
|
|
// TODO do a proper error processing (2015-09-11, Fat-Zer)
|
|
}
|
|
|
|
bool CmdPartDesignShapeBinder::isActive(void)
|
|
{
|
|
return hasActiveDocument ();
|
|
}
|
|
|
|
//===========================================================================
|
|
// 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)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
App::Document *doc = getDocument ();
|
|
PartDesign::Body *pcActiveBody = PartDesignGui::getBody(
|
|
/*messageIfNot = */ PartDesignGui::assureModernWorkflow ( doc ) );
|
|
|
|
// No PartDesign feature without Body past FreeCAD 0.13
|
|
if ( !pcActiveBody ) {
|
|
// Call normal sketch command for old workflow
|
|
if ( PartDesignGui::isLegacyWorkflow ( doc) ) {
|
|
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->hasObject(obj)) {
|
|
if ( !obj->isDerivedFrom ( App::Plane::getClassTypeId() ) ) {
|
|
// TODO check here if the plane associated with right part/body (2015-09-01, Fat-Zer)
|
|
|
|
auto pcActivePart = PartDesignGui::getPartFor(pcActiveBody, false);
|
|
|
|
//check the prerequisites for the selected objects
|
|
//the user has to decide which option we should take if external references are used
|
|
// TODO share this with UnifiedDatumCommand() (2015-10-20, Fat-Zer)
|
|
QDialog* dia = new QDialog;
|
|
Ui_Dialog dlg;
|
|
dlg.setupUi(dia);
|
|
dia->setModal(true);
|
|
int result = dia->exec();
|
|
if(result == QDialog::DialogCode::Rejected)
|
|
return;
|
|
else if(!dlg.radioXRef->isChecked()) {
|
|
|
|
std::string sub;
|
|
if(FaceFilter.match())
|
|
sub = FaceFilter.Result[0][0].getSubNames()[0];
|
|
auto copy = PartDesignGui::TaskFeaturePick::makeCopy(obj, sub, dlg.radioIndependent->isChecked());
|
|
|
|
if(pcActiveBody)
|
|
pcActiveBody->addObject(copy);
|
|
else if (pcActivePart)
|
|
pcActivePart->addObject(copy);
|
|
|
|
if(PlaneFilter.match())
|
|
supportString = std::string("(App.activeDocument().") + copy->getNameInDocument() + ", '')";
|
|
else
|
|
//it is ensured that only a single face is selected, hence it must always be Face1 of the shapebinder
|
|
supportString = std::string("(App.activeDocument().") + copy->getNameInDocument() + ", 'Face1')";
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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::getModeName(Attacher::mmFlatFace).c_str());
|
|
doCommand(Doc,"App.activeDocument().%s.addObject(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
|
|
unsigned validPlanes = 0;
|
|
|
|
auto group = App::GeoFeatureGroupExtension::getGroupOfObject ( pcActiveBody );
|
|
App::GeoFeatureGroupExtension* geoGroup = nullptr;
|
|
if(group)
|
|
geoGroup = group->getExtensionByType<App::GeoFeatureGroupExtension>();
|
|
|
|
std::vector<App::DocumentObject*> planes;
|
|
std::vector<PartDesignGui::TaskFeaturePick::featureStatus> status;
|
|
|
|
// Baseplanes are preaprooved
|
|
if ( pcActiveBody ) {
|
|
try {
|
|
for ( auto plane: pcActiveBody->getOrigin ()->planes() ) {
|
|
planes.push_back (plane);
|
|
status.push_back(PartDesignGui::TaskFeaturePick::basePlane);
|
|
validPlanes++;
|
|
}
|
|
} catch (const Base::Exception &ex) {
|
|
Base::Console().Error ("%s\n", ex.what() );
|
|
}
|
|
}
|
|
|
|
std::vector<App::DocumentObject*> datumPlanes =
|
|
getDocument()->getObjectsOfType(PartDesign::Plane::getClassTypeId());
|
|
|
|
for (auto plane: datumPlanes) {
|
|
planes.push_back ( plane );
|
|
// Check whether this plane belongs to the active body
|
|
if ( pcActiveBody && pcActiveBody->hasObject(plane) ) {
|
|
if ( !pcActiveBody->isAfterInsertPoint ( plane ) ) {
|
|
validPlanes++;
|
|
status.push_back(PartDesignGui::TaskFeaturePick::validFeature);
|
|
} else {
|
|
status.push_back(PartDesignGui::TaskFeaturePick::afterTip);
|
|
}
|
|
} else {
|
|
PartDesign::Body *planeBody = PartDesign::Body::findBodyOf (plane);
|
|
if ( planeBody ) {
|
|
if ( ( geoGroup && geoGroup->hasObject ( planeBody, true ) ) ||
|
|
!App::GeoFeatureGroupExtension::getGroupOfObject (planeBody) ) {
|
|
status.push_back ( PartDesignGui::TaskFeaturePick::otherBody );
|
|
} else {
|
|
status.push_back ( PartDesignGui::TaskFeaturePick::otherPart );
|
|
}
|
|
} else {
|
|
if ( ( geoGroup && geoGroup->hasObject ( plane, true ) ) ||
|
|
!App::GeoFeatureGroupExtension::getGroupOfObject ( plane ) ) {
|
|
status.push_back ( PartDesignGui::TaskFeaturePick::otherPart );
|
|
} else if (pcActiveBody) {
|
|
status.push_back ( PartDesignGui::TaskFeaturePick::notInBody );
|
|
} else { // if we are outside a body count it as valid
|
|
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::getModeName(Attacher::mmFlatFace).c_str());
|
|
Gui::Command::updateActive(); // Make sure the Support's Placement property is updated
|
|
Gui::Command::doCommand(Doc,"App.activeDocument().%s.addObject(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
|
|
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->Tip.getValue();
|
|
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.addObject(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.insertObject(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 ProfileBased features
|
|
//===========================================================================
|
|
|
|
// Take a list of Part2DObjects and classify them for creating a
|
|
// ProfileBased feature. FirstFreeSketch is the first free sketch in the same body
|
|
// or sketches.end() if non available. The returned number is the amount of free sketches
|
|
unsigned validateSketches(std::vector<App::DocumentObject*>& sketches,
|
|
std::vector<PartDesignGui::TaskFeaturePick::featureStatus>& status,
|
|
std::vector<App::DocumentObject*>::iterator& firstFreeSketch)
|
|
{
|
|
// TODO Review the function for non-part bodies (2015-09-04, Fat-Zer)
|
|
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 freeSketches = 0;
|
|
firstFreeSketch = 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);
|
|
continue;
|
|
}
|
|
} else if (!pcActiveBody->hasObject(*s)) {
|
|
// Check whether this plane belongs to a body of the same part
|
|
PartDesign::Body* b = PartDesign::Body::findBodyOf(*s);
|
|
if(!b)
|
|
status.push_back(PartDesignGui::TaskFeaturePick::notInBody);
|
|
else if(pcActivePart && pcActivePart->hasObject(b, true))
|
|
status.push_back(PartDesignGui::TaskFeaturePick::otherBody);
|
|
else
|
|
status.push_back(PartDesignGui::TaskFeaturePick::otherPart);
|
|
|
|
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->isAfterInsertPoint(*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 (firstFreeSketch == sketches.end())
|
|
firstFreeSketch = s;
|
|
freeSketches++;
|
|
status.push_back(PartDesignGui::TaskFeaturePick::validFeature);
|
|
}
|
|
|
|
return freeSketches;
|
|
}
|
|
|
|
void prepareProfileBased(Gui::Command* cmd, const std::string& which,
|
|
boost::function<void (Part::Feature*, std::string)> func)
|
|
{
|
|
auto base_worker = [which, cmd, func](App::DocumentObject* feature, std::string sub) {
|
|
|
|
if (!feature || !feature->isDerivedFrom(Part::Feature::getClassTypeId()))
|
|
return;
|
|
|
|
// Related to #0002760: when an operation can't be performed due to a broken
|
|
// profile then make sure that it is recomputed when cancelling the operation
|
|
// otherwise it might be impossible to see that it's broken.
|
|
if (feature->isTouched())
|
|
feature->recomputeFeature();
|
|
|
|
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());
|
|
|
|
if (feature->isDerivedFrom(Part::Part2DObject::getClassTypeId())) {
|
|
Gui::Command::doCommand(Gui::Command::Doc,"App.activeDocument().%s.Profile = App.activeDocument().%s",
|
|
FeatName.c_str(), feature->getNameInDocument());
|
|
}
|
|
else {
|
|
Gui::Command::doCommand(Gui::Command::Doc,"App.activeDocument().%s.Profile = (App.activeDocument().%s, [\"%s\"])",
|
|
FeatName.c_str(), feature->getNameInDocument(), sub.c_str());
|
|
}
|
|
|
|
func(static_cast<Part::Feature*>(feature), FeatName);
|
|
};
|
|
|
|
//if a profie is selected we can make our life easy and fast
|
|
auto selection = cmd->getSelection().getSelectionEx();
|
|
if (!selection.empty() && selection.front().hasSubNames()) {
|
|
base_worker(selection.front().getObject(), selection.front().getSubNames().front());
|
|
return;
|
|
}
|
|
|
|
//no face profile was selected, do he extended sketch logic
|
|
|
|
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.empty()) {//no sketches were selected. Let user pick an object from valid ones available in document
|
|
sketches = cmd->getDocument()->getObjectsOfType(Part::Part2DObject::getClassTypeId());
|
|
bNoSketchWasSelected = true;
|
|
}
|
|
|
|
if (sketches.empty()) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("No sketch to work on"),
|
|
QObject::tr("No sketch is available in the document"));
|
|
return;
|
|
}
|
|
|
|
std::vector<PartDesignGui::TaskFeaturePick::featureStatus> status;
|
|
std::vector<App::DocumentObject*>::iterator firstFreeSketch;
|
|
int freeSketches = validateSketches(sketches, status, firstFreeSketch);
|
|
|
|
auto accepter = [=](const std::vector<App::DocumentObject*>& features) -> bool {
|
|
|
|
if(features.empty())
|
|
return false;
|
|
|
|
return true;
|
|
};
|
|
|
|
auto sketch_worker = [&](std::vector<App::DocumentObject*> features) {
|
|
|
|
base_worker(features.front(), "");
|
|
};
|
|
|
|
//if there is a sketch selected which is from another 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();
|
|
|
|
// TODO Clean this up (2015-10-20, Fat-Zer)
|
|
auto* pcActiveBody = PartDesignGui::getBody(false);
|
|
if (pcActiveBody && !bNoSketchWasSelected && ext) {
|
|
|
|
auto* pcActivePart = PartDesignGui::getPartFor(pcActiveBody, false);
|
|
|
|
QDialog* dia = new QDialog;
|
|
Ui_Dialog dlg;
|
|
dlg.setupUi(dia);
|
|
dia->setModal(true);
|
|
int result = dia->exec();
|
|
if(result == QDialog::DialogCode::Rejected)
|
|
return;
|
|
else if(!dlg.radioXRef->isChecked()) {
|
|
|
|
auto copy = PartDesignGui::TaskFeaturePick::makeCopy(sketches[0], "", dlg.radioIndependent->isChecked());
|
|
auto oBody = PartDesignGui::getBodyFor(sketches[0], false);
|
|
if(oBody)
|
|
pcActiveBody->addObject(copy);
|
|
else
|
|
pcActivePart->addObject(copy);
|
|
|
|
sketches[0] = copy;
|
|
firstFreeSketch = sketches.begin();
|
|
}
|
|
}
|
|
|
|
// Show sketch choose dialog and let user pick sketch if no sketch was selected and no free one available or
|
|
// multiple free ones are available
|
|
if (bNoSketchWasSelected && (freeSketches != 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();
|
|
pickDlg = new PartDesignGui::TaskDlgFeaturePick(sketches, status, accepter, sketch_worker);
|
|
if (!bNoSketchWasSelected && ext)
|
|
pickDlg->showExternal(true);
|
|
|
|
Gui::Control().showDialog(pickDlg);
|
|
}
|
|
else {
|
|
std::vector<App::DocumentObject*> theSketch;
|
|
if (!bNoSketchWasSelected)
|
|
theSketch.push_back(sketches[0]);
|
|
else
|
|
theSketch.push_back(*firstFreeSketch);
|
|
|
|
sketch_worker(theSketch);
|
|
}
|
|
}
|
|
|
|
void finishProfileBased(const Gui::Command* cmd, const Part::Feature* sketch, const std::string& FeatName)
|
|
{
|
|
if(sketch && sketch->isDerivedFrom(Part::Part2DObject::getClassTypeId()))
|
|
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)
|
|
{
|
|
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 = [this, cmd](Part::Feature* profile, 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 = profile->getGroup();
|
|
if (grp) {
|
|
Gui::Command::doCommand(Doc,"App.activeDocument().%s.addObject(App.activeDocument().%s)"
|
|
,grp->getNameInDocument(),FeatName.c_str());
|
|
if(profile->isDerivedFrom(Part::Part2DObject::getClassTypeId()))
|
|
Gui::Command::doCommand(Doc,"App.activeDocument().%s.removeObject(App.activeDocument().%s)"
|
|
,grp->getNameInDocument(), profile->getNameInDocument());
|
|
}
|
|
Gui::Command::updateActive();
|
|
|
|
Part::Part2DObject* sketch = dynamic_cast<Part::Part2DObject*>(profile);
|
|
finishProfileBased(cmd, sketch, FeatName);
|
|
cmd->adjustCameraPosition();
|
|
};
|
|
|
|
prepareProfileBased(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 = sToolTipText;
|
|
sStatusTip = sToolTipText;
|
|
sPixmap = "PartDesign_Pocket";
|
|
}
|
|
|
|
void CmdPartDesignPocket::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 = [this, cmd](Part::Feature* sketch, std::string FeatName) {
|
|
|
|
if (FeatName.empty()) return;
|
|
|
|
Gui::Command::doCommand(Doc,"App.activeDocument().%s.Length = 5.0",FeatName.c_str());
|
|
finishProfileBased(cmd, sketch, FeatName);
|
|
cmd->adjustCameraPosition();
|
|
};
|
|
|
|
prepareProfileBased(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)
|
|
{
|
|
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 = [this, cmd](Part::Feature* 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());
|
|
|
|
finishProfileBased(cmd, sketch, FeatName);
|
|
cmd->adjustCameraPosition();
|
|
};
|
|
|
|
prepareProfileBased(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)
|
|
{
|
|
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 = [this, cmd](Part::Feature* 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());
|
|
|
|
finishProfileBased(cmd, sketch, FeatName);
|
|
cmd->adjustCameraPosition();
|
|
};
|
|
|
|
prepareProfileBased(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)
|
|
{
|
|
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 = [this, cmd](Part::Feature* sketch, std::string FeatName) {
|
|
|
|
if (FeatName.empty()) return;
|
|
|
|
// specific parameters for pipe
|
|
Gui::Command::updateActive();
|
|
|
|
finishProfileBased(cmd, sketch, FeatName);
|
|
cmd->adjustCameraPosition();
|
|
};
|
|
|
|
prepareProfileBased(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)
|
|
{
|
|
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 = [this, cmd](Part::Feature* sketch, std::string FeatName) {
|
|
|
|
if (FeatName.empty()) return;
|
|
|
|
// specific parameters for pipe
|
|
Gui::Command::updateActive();
|
|
|
|
finishProfileBased(cmd, sketch, FeatName);
|
|
cmd->adjustCameraPosition();
|
|
};
|
|
|
|
prepareProfileBased(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)
|
|
{
|
|
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 = [this, cmd](Part::Feature* sketch, std::string FeatName) {
|
|
|
|
if (FeatName.empty()) return;
|
|
|
|
// specific parameters for pipe
|
|
Gui::Command::updateActive();
|
|
|
|
finishProfileBased(cmd, sketch, FeatName);
|
|
cmd->adjustCameraPosition();
|
|
};
|
|
|
|
prepareProfileBased(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)
|
|
{
|
|
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 = [this, cmd](Part::Feature* sketch, std::string FeatName) {
|
|
|
|
if (FeatName.empty()) return;
|
|
|
|
// specific parameters for pipe
|
|
Gui::Command::updateActive();
|
|
|
|
finishProfileBased(cmd, sketch, FeatName);
|
|
cmd->adjustCameraPosition();
|
|
};
|
|
|
|
prepareProfileBased(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)
|
|
{
|
|
// No PartDesign feature without Body past FreeCAD 0.16
|
|
App::Document *doc = cmd->getDocument();
|
|
if (!PartDesignGui::assureModernWorkflow(doc))
|
|
return false;
|
|
|
|
PartDesign::Body *pcActiveBody = PartDesignGui::getBody(true);
|
|
|
|
if (!pcActiveBody)
|
|
return false;
|
|
|
|
std::vector<Gui::SelectionObject> 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;
|
|
}
|
|
else if (pcActiveBody != PartDesignGui::getBodyFor(selection[0].getObject(), false)) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Selection is not in Active Body"),
|
|
QObject::tr("Select an edge, face or body from an active 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.getShape().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());
|
|
cmd->doCommand(cmd->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 = sToolTipText;
|
|
sStatusTip = sToolTipText;
|
|
sPixmap = "PartDesign_Fillet";
|
|
}
|
|
|
|
void CmdPartDesignFillet::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(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 = sToolTipText;
|
|
sStatusTip = sToolTipText;
|
|
sPixmap = "PartDesign_Chamfer";
|
|
}
|
|
|
|
void CmdPartDesignChamfer::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(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)
|
|
{
|
|
Q_UNUSED(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)
|
|
{
|
|
Q_UNUSED(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());
|
|
// TODO Wjat that function supposed to do? (2015-08-05, Fat-Zer)
|
|
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 if (features.size() > 1) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Multiple Features Selected"),
|
|
QObject::tr("Please select only one subtractive or additive feature first."));
|
|
return;
|
|
}
|
|
else {
|
|
PartDesign::Body *pcActiveBody = PartDesignGui::getBody(true);
|
|
if (pcActiveBody != PartDesignGui::getBodyFor(features[0], false)) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Selection is not in Active Body"),
|
|
QObject::tr("Please select only one subtractive or additive feature in an active body."));
|
|
return;
|
|
}
|
|
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 = sToolTipText;
|
|
sStatusTip = sToolTipText;
|
|
sPixmap = "PartDesign_Mirrored";
|
|
}
|
|
|
|
void CmdPartDesignMirrored::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
// No PartDesign feature without Body past FreeCAD 0.16
|
|
App::Document *doc = getDocument();
|
|
if (!PartDesignGui::assureModernWorkflow(doc))
|
|
return;
|
|
|
|
PartDesign::Body *pcActiveBody = PartDesignGui::getBody(true);
|
|
|
|
if (!pcActiveBody)
|
|
return;
|
|
|
|
Gui::Command* cmd = this;
|
|
auto worker = [this, cmd](std::string FeatName, std::vector<App::DocumentObject*> features) {
|
|
|
|
if (features.empty())
|
|
return;
|
|
|
|
bool direction = false;
|
|
if(features.front()->isDerivedFrom(PartDesign::ProfileBased::getClassTypeId())) {
|
|
Part::Part2DObject *sketch = (static_cast<PartDesign::ProfileBased*>(features.front()))->getVerifiedSketch(/* silent =*/ true);
|
|
if (sketch) {
|
|
doCommand(Doc,"App.activeDocument().%s.MirrorPlane = (App.activeDocument().%s, [\"V_Axis\"])",
|
|
FeatName.c_str(), sketch->getNameInDocument());
|
|
direction = true;
|
|
}
|
|
}
|
|
if(!direction) {
|
|
auto body = static_cast<PartDesign::Body*>(Part::BodyBase::findBodyOf(features.front()));
|
|
if(body) {
|
|
doCommand(Doc,"App.activeDocument().%s.MirrorPlane = (App.activeDocument().%s, [\"\"])", FeatName.c_str(),
|
|
body->getOrigin()->getXY()->getNameInDocument());
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
// No PartDesign feature without Body past FreeCAD 0.16
|
|
App::Document *doc = getDocument();
|
|
if (!PartDesignGui::assureModernWorkflow(doc))
|
|
return;
|
|
|
|
PartDesign::Body *pcActiveBody = PartDesignGui::getBody(true);
|
|
|
|
if (!pcActiveBody)
|
|
return;
|
|
|
|
Gui::Command* cmd = this;
|
|
auto worker = [this, cmd](std::string FeatName, std::vector<App::DocumentObject*> features) {
|
|
|
|
if (features.empty())
|
|
return;
|
|
|
|
bool direction = false;
|
|
if(features.front()->isDerivedFrom(PartDesign::ProfileBased::getClassTypeId())) {
|
|
Part::Part2DObject *sketch = (static_cast<PartDesign::ProfileBased*>(features.front()))->getVerifiedSketch(/* silent =*/ true);
|
|
if (sketch) {
|
|
doCommand(Doc,"App.activeDocument().%s.Direction = (App.activeDocument().%s, [\"H_Axis\"])",
|
|
FeatName.c_str(), sketch->getNameInDocument());
|
|
direction = true;
|
|
}
|
|
}
|
|
if(!direction) {
|
|
auto body = static_cast<PartDesign::Body*>(Part::BodyBase::findBodyOf(features.front()));
|
|
if(body) {
|
|
doCommand(Doc,"App.activeDocument().%s.Direction = (App.activeDocument().%s, [\"\"])", FeatName.c_str(),
|
|
body->getOrigin()->getX()->getNameInDocument());
|
|
}
|
|
}
|
|
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)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
// No PartDesign feature without Body past FreeCAD 0.16
|
|
App::Document *doc = getDocument();
|
|
if (!PartDesignGui::assureModernWorkflow(doc))
|
|
return;
|
|
|
|
PartDesign::Body *pcActiveBody = PartDesignGui::getBody(true);
|
|
|
|
if (!pcActiveBody)
|
|
return;
|
|
|
|
Gui::Command* cmd = this;
|
|
auto worker = [this, cmd](std::string FeatName, std::vector<App::DocumentObject*> features) {
|
|
|
|
if (features.empty())
|
|
return;
|
|
|
|
bool direction = false;
|
|
if(features.front()->isDerivedFrom(PartDesign::ProfileBased::getClassTypeId())) {
|
|
Part::Part2DObject *sketch = (static_cast<PartDesign::ProfileBased*>(features.front()))->getVerifiedSketch(/* silent =*/ true);
|
|
if (sketch) {
|
|
doCommand(Doc,"App.activeDocument().%s.Axis = (App.activeDocument().%s, [\"N_Axis\"])",
|
|
FeatName.c_str(), sketch->getNameInDocument());
|
|
direction = true;
|
|
}
|
|
}
|
|
if(!direction) {
|
|
auto body = static_cast<PartDesign::Body*>(Part::BodyBase::findBodyOf(features.front()));
|
|
if(body) {
|
|
doCommand(Doc,"App.activeDocument().%s.Axis = (App.activeDocument().%s, [\"\"])", FeatName.c_str(),
|
|
body->getOrigin()->getZ()->getNameInDocument());
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
Gui::Command* cmd = this;
|
|
auto worker = [this, 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)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
App::Document *doc = getDocument();
|
|
PartDesign::Body *pcActiveBody = PartDesignGui::getBody(
|
|
/*messageIfNot = */ PartDesignGui::assureModernWorkflow(doc));
|
|
|
|
// No PartDesign feature without Body past FreeCAD 0.16
|
|
if (!pcActiveBody && PartDesignGui::isModernWorkflow(doc))
|
|
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());
|
|
// TODO Review this (2015-09-05, Fat-Zer)
|
|
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.removeObject(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 = [this, 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 = pcActiveBody->Tip.getValue();
|
|
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)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
PartDesign::Body *pcActiveBody = PartDesignGui::getBody(/*messageIfNot = */true);
|
|
if (!pcActiveBody) return;
|
|
|
|
Gui::SelectionFilter BodyFilter("SELECT PartDesign::Body COUNT 1..");
|
|
|
|
openCommand("Create Boolean");
|
|
std::string FeatName = getUniqueObjectName("Boolean");
|
|
doCommand(Doc,"App.activeDocument().addObject('PartDesign::Boolean','%s')",FeatName.c_str());
|
|
|
|
if (BodyFilter.match()) {
|
|
std::vector<App::DocumentObject*> bodies;
|
|
std::vector<std::vector<Gui::SelectionObject> >::iterator i = BodyFilter.Result.begin();
|
|
for (; i != BodyFilter.Result.end(); i++) {
|
|
for (std::vector<Gui::SelectionObject>::iterator j = i->begin(); j != i->end(); j++) {
|
|
if(j->getObject() != pcActiveBody)
|
|
bodies.push_back(j->getObject());
|
|
}
|
|
}
|
|
std::string bodyString = PartDesignGui::buildLinkListPythonStr(bodies);
|
|
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 CmdPartDesignShapeBinder());
|
|
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());
|
|
}
|