This adds isNullOrEmpty string helper that cheks if string is... well null or empty. It is done to improve readability of the code and better express intent.
789 lines
27 KiB
C++
789 lines
27 KiB
C++
/***************************************************************************
|
|
* Copyright (c) 2008 Werner Mayer <wmayer[at]users.sourceforge.net> *
|
|
* *
|
|
* 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 <BRepAdaptor_Curve.hxx>
|
|
# include <BRep_Tool.hxx>
|
|
# include <Precision.hxx>
|
|
# include <ShapeExtend_Explorer.hxx>
|
|
# include <TopExp_Explorer.hxx>
|
|
# include <TopoDS.hxx>
|
|
# include <TopTools_HSequenceOfShape.hxx>
|
|
# include <QKeyEvent>
|
|
# include <QMessageBox>
|
|
#endif
|
|
|
|
#include <App/Application.h>
|
|
#include <App/Document.h>
|
|
#include <App/DocumentObject.h>
|
|
#include <App/Link.h>
|
|
#include <App/Part.h>
|
|
#include <Base/UnitsApi.h>
|
|
#include <Base/Tools.h>
|
|
|
|
#include <Gui/Application.h>
|
|
#include <Gui/BitmapFactory.h>
|
|
#include <Gui/Command.h>
|
|
#include <Gui/Document.h>
|
|
#include <Gui/Utilities.h>
|
|
#include <Gui/ViewProvider.h>
|
|
#include <Gui/WaitCursor.h>
|
|
|
|
#include "ui_DlgExtrusion.h"
|
|
#include "DlgExtrusion.h"
|
|
|
|
|
|
|
|
FC_LOG_LEVEL_INIT("Part", true, true)
|
|
|
|
using namespace PartGui;
|
|
|
|
class DlgExtrusion::EdgeSelection : public Gui::SelectionFilterGate
|
|
{
|
|
public:
|
|
bool canSelect;
|
|
|
|
EdgeSelection()
|
|
: Gui::SelectionFilterGate(nullPointer())
|
|
{
|
|
canSelect = false;
|
|
}
|
|
bool allow(App::Document* /*pDoc*/, App::DocumentObject* pObj, const char* sSubName) override
|
|
{
|
|
this->canSelect = false;
|
|
|
|
if (Base::Tools::isNullOrEmpty(sSubName))
|
|
return false;
|
|
std::string element(sSubName);
|
|
if (element.substr(0,4) != "Edge")
|
|
return false;
|
|
Part::TopoShape part = Part::Feature::getTopoShape(pObj);
|
|
if (part.isNull()) {
|
|
return false;
|
|
}
|
|
try {
|
|
TopoDS_Shape sub = Part::Feature::getTopoShape(pObj, sSubName, true /*need element*/).getShape();
|
|
if (!sub.IsNull() && sub.ShapeType() == TopAbs_EDGE) {
|
|
const TopoDS_Edge& edge = TopoDS::Edge(sub);
|
|
BRepAdaptor_Curve adapt(edge);
|
|
if (adapt.GetType() == GeomAbs_Line) {
|
|
this->canSelect = true;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
catch (...) {
|
|
}
|
|
|
|
return false;
|
|
}
|
|
};
|
|
|
|
DlgExtrusion::DlgExtrusion(QWidget* parent, Qt::WindowFlags fl)
|
|
: QDialog(parent, fl), ui(new Ui_DlgExtrusion), filter(nullptr)
|
|
{
|
|
ui->setupUi(this);
|
|
setupConnections();
|
|
|
|
ui->statusLabel->clear();
|
|
ui->dirX->setDecimals(Base::UnitsApi::getDecimals());
|
|
ui->dirY->setDecimals(Base::UnitsApi::getDecimals());
|
|
ui->dirZ->setDecimals(Base::UnitsApi::getDecimals());
|
|
ui->spinLenFwd->setUnit(Base::Unit::Length);
|
|
ui->spinLenFwd->setValue(10.0);
|
|
ui->spinLenRev->setUnit(Base::Unit::Length);
|
|
ui->spinTaperAngle->setUnit(Base::Unit::Angle);
|
|
ui->spinTaperAngle->setUnit(Base::Unit::Angle);
|
|
findShapes();
|
|
|
|
Gui::ItemViewSelection sel(ui->treeWidget);
|
|
sel.applyFrom(Gui::Selection().getObjectsOfType(Part::Feature::getClassTypeId()));
|
|
sel.applyFrom(Gui::Selection().getObjectsOfType(App::Link::getClassTypeId()));
|
|
sel.applyFrom(Gui::Selection().getObjectsOfType(App::Part::getClassTypeId()));
|
|
|
|
this->onDirModeChanged();
|
|
ui->spinLenFwd->selectAll();
|
|
// Make sure that the spin box has the focus to get key events
|
|
// Calling setFocus() directly doesn't work because the spin box is not
|
|
// yet visible.
|
|
QMetaObject::invokeMethod(ui->spinLenFwd, "setFocus", Qt::QueuedConnection);
|
|
|
|
this->autoSolid();
|
|
}
|
|
|
|
/*
|
|
* Destroys the object and frees any allocated resources
|
|
*/
|
|
DlgExtrusion::~DlgExtrusion()
|
|
{
|
|
if (filter){
|
|
Gui::Selection().rmvSelectionGate();
|
|
filter = nullptr;
|
|
}
|
|
|
|
// no need to delete child widgets, Qt does it all for us
|
|
}
|
|
|
|
void DlgExtrusion::setupConnections()
|
|
{
|
|
// clang-format off
|
|
connect(ui->rbDirModeCustom, &QRadioButton::toggled,
|
|
this, &DlgExtrusion::onDirModeCustomToggled);
|
|
connect(ui->rbDirModeEdge, &QRadioButton::toggled,
|
|
this, &DlgExtrusion::onDirModeEdgeToggled);
|
|
connect(ui->rbDirModeNormal, &QRadioButton::toggled,
|
|
this, &DlgExtrusion::onDirModeNormalToggled);
|
|
connect(ui->btnSelectEdge, &QPushButton::clicked,
|
|
this, &DlgExtrusion::onSelectEdgeClicked);
|
|
connect(ui->btnX, &QPushButton::clicked,
|
|
this, &DlgExtrusion::onButtnoXClicked);
|
|
connect(ui->btnY, &QPushButton::clicked,
|
|
this, &DlgExtrusion::onButtonYClicked);
|
|
connect(ui->btnZ, &QPushButton::clicked,
|
|
this, &DlgExtrusion::onButtonZClicked);
|
|
connect(ui->chkSymmetric, &QCheckBox::toggled,
|
|
this, &DlgExtrusion::onCheckSymmetricToggled);
|
|
connect(ui->txtLink, &QLineEdit::textChanged,
|
|
this, &DlgExtrusion::onTextLinkTextChanged);
|
|
// clang-format on
|
|
}
|
|
|
|
void DlgExtrusion::changeEvent(QEvent *e)
|
|
{
|
|
if (e->type() == QEvent::LanguageChange) {
|
|
ui->retranslateUi(this);
|
|
}
|
|
QDialog::changeEvent(e);
|
|
}
|
|
|
|
void DlgExtrusion::keyPressEvent(QKeyEvent* ke)
|
|
{
|
|
// The extrusion dialog is embedded into a task panel
|
|
// which is a parent widget and will handle the event
|
|
ke->ignore();
|
|
}
|
|
|
|
void DlgExtrusion::onDirModeCustomToggled(bool on)
|
|
{
|
|
if(on) //this check prevents dual fire of dirmode changed - on radio buttons, one will come on, and other will come off, causing two events.
|
|
this->onDirModeChanged();
|
|
}
|
|
|
|
void DlgExtrusion::onDirModeEdgeToggled(bool on)
|
|
{
|
|
if(on)
|
|
this->onDirModeChanged();
|
|
}
|
|
|
|
void DlgExtrusion::onDirModeNormalToggled(bool on)
|
|
{
|
|
if(on)
|
|
this->onDirModeChanged();
|
|
}
|
|
|
|
void DlgExtrusion::onSelectEdgeClicked()
|
|
{
|
|
if (!filter) {
|
|
filter = new EdgeSelection();
|
|
Gui::Selection().addSelectionGate(filter);
|
|
ui->btnSelectEdge->setText(tr("Selecting..."));
|
|
|
|
//visibility automation
|
|
try{
|
|
QString code = QStringLiteral(
|
|
"import Show\n"
|
|
"tv = Show.TempoVis(App.ActiveDocument, tag= 'PartGui::DlgExtrusion')\n"
|
|
"tv.hide([%1])"
|
|
);
|
|
std::vector<App::DocumentObject*>sources = getShapesToExtrude();
|
|
QString features_to_hide;
|
|
for (App::DocumentObject* obj: sources){
|
|
if (!obj)
|
|
continue;
|
|
features_to_hide.append(QStringLiteral("App.ActiveDocument."));
|
|
features_to_hide.append(QString::fromLatin1(obj->getNameInDocument()));
|
|
features_to_hide.append(QStringLiteral(", \n"));
|
|
}
|
|
QByteArray code_2 = code.arg(features_to_hide).toLatin1();
|
|
Base::Interpreter().runString(code_2.constData());
|
|
} catch (Base::PyException &e){
|
|
e.ReportException();
|
|
}
|
|
} else {
|
|
Gui::Selection().rmvSelectionGate();
|
|
filter = nullptr;
|
|
ui->btnSelectEdge->setText(tr("Select"));
|
|
|
|
//visibility automation
|
|
try{
|
|
Base::Interpreter().runString("del(tv)");
|
|
} catch (Base::PyException &e){
|
|
e.ReportException();
|
|
}
|
|
}
|
|
}
|
|
|
|
void DlgExtrusion::onButtnoXClicked()
|
|
{
|
|
Base::Vector3d axis(1.0, 0.0, 0.0);
|
|
if ((getDir() - axis).Length() < 1e-7)
|
|
axis = axis * (-1);
|
|
setDirMode(Part::Extrusion::dmCustom);
|
|
setDir(axis);
|
|
}
|
|
|
|
void DlgExtrusion::onButtonYClicked()
|
|
{
|
|
Base::Vector3d axis(0.0, 1.0, 0.0);
|
|
if ((getDir() - axis).Length() < 1e-7)
|
|
axis = axis * (-1);
|
|
setDirMode(Part::Extrusion::dmCustom);
|
|
setDir(axis);
|
|
}
|
|
|
|
void DlgExtrusion::onButtonZClicked()
|
|
{
|
|
Base::Vector3d axis(0.0, 0.0, 1.0);
|
|
if ((getDir() - axis).Length() < 1e-7)
|
|
axis = axis * (-1);
|
|
setDirMode(Part::Extrusion::dmCustom);
|
|
setDir(axis);
|
|
}
|
|
|
|
void DlgExtrusion::onCheckSymmetricToggled(bool on)
|
|
{
|
|
ui->spinLenRev->setEnabled(!on);
|
|
}
|
|
|
|
void DlgExtrusion::onTextLinkTextChanged(QString)
|
|
{
|
|
this->fetchDir();
|
|
}
|
|
|
|
void DlgExtrusion::onDirModeChanged()
|
|
{
|
|
Part::Extrusion::eDirMode dirMode = this->getDirMode();
|
|
ui->dirX->setEnabled(dirMode == Part::Extrusion::dmCustom);
|
|
ui->dirY->setEnabled(dirMode == Part::Extrusion::dmCustom);
|
|
ui->dirZ->setEnabled(dirMode == Part::Extrusion::dmCustom);
|
|
ui->txtLink->setEnabled(dirMode == Part::Extrusion::dmEdge);
|
|
this->fetchDir();
|
|
}
|
|
|
|
void DlgExtrusion::onSelectionChanged(const Gui::SelectionChanges& msg)
|
|
{
|
|
if (msg.Type == Gui::SelectionChanges::AddSelection) {
|
|
if (filter && filter->canSelect) {
|
|
this->setAxisLink(msg.pObjectName, msg.pSubName);
|
|
this->setDirMode(Part::Extrusion::dmEdge);
|
|
}
|
|
}
|
|
}
|
|
|
|
App::DocumentObject& DlgExtrusion::getShapeToExtrude() const
|
|
{
|
|
std::vector<App::DocumentObject*> objs = this->getShapesToExtrude();
|
|
if (objs.empty())
|
|
throw Base::ValueError("No shapes selected");
|
|
return *(objs[0]);
|
|
}
|
|
|
|
void DlgExtrusion::fetchDir()
|
|
{
|
|
bool lengths_are_at_defaults =
|
|
(fabs(ui->spinLenFwd->value().getValue() - 10.0) < 1e-7)
|
|
&& (fabs(ui->spinLenRev->value().getValue() - 0.0) < 1e-7);
|
|
bool lengths_are_zero =
|
|
(fabs(ui->spinLenFwd->value().getValue() - 0.0) < 1e-7)
|
|
&& (fabs(ui->spinLenRev->value().getValue() - 0.0) < 1e-7);
|
|
|
|
try{
|
|
Base::Vector3d pos, dir;
|
|
bool fetched = false;
|
|
bool dir_has_valid_magnitude = false;
|
|
if(this->getDirMode() == Part::Extrusion::dmEdge){
|
|
App::PropertyLinkSub lnk; this->getAxisLink(lnk);
|
|
fetched = Part::Extrusion::fetchAxisLink(lnk, pos, dir);
|
|
dir_has_valid_magnitude = fetched;
|
|
} else if (this->getDirMode() == Part::Extrusion::dmNormal){
|
|
App::PropertyLink lnk;
|
|
lnk.setValue(&this->getShapeToExtrude());
|
|
dir = Part::Extrusion::calculateShapeNormal(lnk);
|
|
fetched = true;
|
|
}
|
|
if (dir_has_valid_magnitude && lengths_are_at_defaults){
|
|
ui->spinLenFwd->setValue(0);
|
|
} else if (!dir_has_valid_magnitude && lengths_are_zero){
|
|
ui->spinLenFwd->setValue(1.0);
|
|
}
|
|
if (fetched){
|
|
this->setDir(dir);
|
|
}
|
|
} catch (Base::Exception &){
|
|
|
|
} catch (...){
|
|
|
|
}
|
|
}
|
|
|
|
void DlgExtrusion::autoSolid()
|
|
{
|
|
try{
|
|
App::DocumentObject* dobj = &this->getShapeToExtrude();
|
|
Part::TopoShape shape = Part::Feature::getTopoShape(dobj);
|
|
if (shape.isNull()) {
|
|
return;
|
|
}
|
|
TopoDS_Shape sh = shape.getShape();
|
|
if (sh.IsNull())
|
|
return;
|
|
ShapeExtend_Explorer xp;
|
|
Handle(TopTools_HSequenceOfShape) leaves = xp.SeqFromCompound(sh, /*recursive= */Standard_True);
|
|
int cntClosedWires = 0;
|
|
for(int i = 0; i < leaves->Length(); i++){
|
|
const TopoDS_Shape &leaf = leaves->Value(i+1);
|
|
if (leaf.IsNull())
|
|
return;
|
|
if (leaf.ShapeType() == TopAbs_WIRE || leaf.ShapeType() == TopAbs_EDGE){
|
|
if (BRep_Tool::IsClosed(leaf)){
|
|
cntClosedWires++;
|
|
}
|
|
}
|
|
}
|
|
ui->chkSolid->setChecked( cntClosedWires == leaves->Length() );
|
|
} catch(...) {
|
|
|
|
}
|
|
}
|
|
|
|
void DlgExtrusion::findShapes()
|
|
{
|
|
App::Document* activeDoc = App::GetApplication().getActiveDocument();
|
|
if (!activeDoc)
|
|
return;
|
|
Gui::Document* activeGui = Gui::Application::Instance->getDocument(activeDoc);
|
|
this->document = activeDoc->getName();
|
|
this->label = activeDoc->Label.getValue();
|
|
|
|
std::vector<App::DocumentObject*> objs = activeDoc->getObjectsOfType<App::DocumentObject>();
|
|
|
|
for (auto obj : objs) {
|
|
Part::TopoShape topoShape = Part::Feature::getTopoShape(obj);
|
|
if (topoShape.isNull()) {
|
|
continue;
|
|
}
|
|
TopoDS_Shape shape = topoShape.getShape();
|
|
if (shape.IsNull()) continue;
|
|
if (canExtrude(shape)) {
|
|
QTreeWidgetItem* item = new QTreeWidgetItem(ui->treeWidget);
|
|
item->setText(0, QString::fromUtf8(obj->Label.getValue()));
|
|
item->setData(0, Qt::UserRole, QString::fromLatin1(obj->getNameInDocument()));
|
|
Gui::ViewProvider* vp = activeGui->getViewProvider(obj);
|
|
if (vp)
|
|
item->setIcon(0, vp->getIcon());
|
|
}
|
|
}
|
|
}
|
|
|
|
bool DlgExtrusion::canExtrude(const TopoDS_Shape& shape) const
|
|
{
|
|
if (shape.IsNull())
|
|
return false;
|
|
TopAbs_ShapeEnum type = shape.ShapeType();
|
|
if (type == TopAbs_VERTEX || type == TopAbs_EDGE ||
|
|
type == TopAbs_WIRE || type == TopAbs_FACE ||
|
|
type == TopAbs_SHELL)
|
|
return true;
|
|
if (type == TopAbs_COMPOUND) {
|
|
TopExp_Explorer xp;
|
|
xp.Init(shape,TopAbs_SOLID);
|
|
while (xp.More()) {
|
|
return false;
|
|
}
|
|
xp.Init(shape,TopAbs_COMPSOLID);
|
|
while (xp.More()) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void DlgExtrusion::accept()
|
|
{
|
|
try{
|
|
apply();
|
|
QDialog::accept();
|
|
} catch (Base::AbortException&){
|
|
|
|
};
|
|
}
|
|
|
|
void DlgExtrusion::apply()
|
|
{
|
|
try{
|
|
if (!validate())
|
|
throw Base::AbortException();
|
|
|
|
if (filter) //if still selecting edge - stop. This is important for visibility automation.
|
|
this->onSelectEdgeClicked();
|
|
|
|
Gui::WaitCursor wc;
|
|
App::Document* activeDoc = App::GetApplication().getDocument(this->document.c_str());
|
|
if (!activeDoc) {
|
|
QMessageBox::critical(this, windowTitle(),
|
|
tr("The document '%1' doesn't exist.").arg(QString::fromUtf8(this->label.c_str())));
|
|
return;
|
|
}
|
|
activeDoc->openTransaction("Extrude");
|
|
|
|
Base::Reference<ParameterGrp> hGrp = App::GetApplication().GetUserParameter()
|
|
.GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod/Part");
|
|
bool addBaseName = hGrp->GetBool("AddBaseObjectName", false);
|
|
|
|
std::vector<App::DocumentObject*> objects = this->getShapesToExtrude();
|
|
for (App::DocumentObject* sourceObj: objects) {
|
|
assert(sourceObj);
|
|
|
|
if (Part::Feature::getTopoShape(sourceObj).isNull()){
|
|
FC_ERR("Object " << sourceObj->getFullName()
|
|
<< " is not Part object (has no OCC shape). Can't extrude it.");
|
|
continue;
|
|
}
|
|
|
|
std::string name;
|
|
name = sourceObj->getDocument()->getUniqueObjectName("Extrude").c_str();
|
|
if (addBaseName) {
|
|
//FIXME: implement
|
|
//QString baseName = QStringLiteral("Extrude_%1").arg(sourceObjectName);
|
|
//label = QStringLiteral("%1_Extrude").arg((*it)->text(0));
|
|
}
|
|
|
|
FCMD_OBJ_DOC_CMD(sourceObj,"addObject('Part::Extrusion','" << name << "')");
|
|
auto newObj = sourceObj->getDocument()->getObject(name.c_str());
|
|
|
|
this->writeParametersToFeature(*newObj, sourceObj);
|
|
|
|
Gui::Command::copyVisual(newObj, "ShapeAppearance", sourceObj);
|
|
Gui::Command::copyVisual(newObj, "LineColor", sourceObj);
|
|
Gui::Command::copyVisual(newObj, "PointColor", sourceObj);
|
|
|
|
FCMD_OBJ_HIDE(sourceObj);
|
|
}
|
|
|
|
activeDoc->commitTransaction();
|
|
Gui::Command::updateActive();
|
|
}
|
|
catch (Base::AbortException&){
|
|
throw;
|
|
}
|
|
catch (Base::Exception &err){
|
|
QMessageBox::critical(this,
|
|
windowTitle(),
|
|
tr("Creating Extrusion failed.\n%1")
|
|
.arg(QCoreApplication::translate("Exception", err.what())));
|
|
return;
|
|
}
|
|
catch(...) {
|
|
QMessageBox::critical(this, windowTitle(),
|
|
tr("Creating Extrusion failed.\n%1").arg(QStringLiteral("Unknown error")));
|
|
return;
|
|
}
|
|
}
|
|
|
|
void DlgExtrusion::reject()
|
|
{
|
|
if (filter) //if still selecting edge - stop.
|
|
this->onSelectEdgeClicked();
|
|
|
|
QDialog::reject();
|
|
}
|
|
|
|
Base::Vector3d DlgExtrusion::getDir() const
|
|
{
|
|
return Base::Vector3d(
|
|
ui->dirX->value(),
|
|
ui->dirY->value(),
|
|
ui->dirZ->value());
|
|
}
|
|
|
|
void DlgExtrusion::setDir(Base::Vector3d newDir)
|
|
{
|
|
ui->dirX->setValue(newDir.x);
|
|
ui->dirY->setValue(newDir.y);
|
|
ui->dirZ->setValue(newDir.z);
|
|
}
|
|
|
|
Part::Extrusion::eDirMode DlgExtrusion::getDirMode() const
|
|
{
|
|
if(ui->rbDirModeCustom->isChecked())
|
|
return Part::Extrusion::dmCustom;
|
|
if(ui->rbDirModeEdge->isChecked())
|
|
return Part::Extrusion::dmEdge;
|
|
if(ui->rbDirModeNormal->isChecked())
|
|
return Part::Extrusion::dmNormal;
|
|
|
|
//we shouldn't get here...
|
|
return Part::Extrusion::dmCustom;
|
|
}
|
|
|
|
void DlgExtrusion::setDirMode(Part::Extrusion::eDirMode newMode)
|
|
{
|
|
ui->rbDirModeCustom->blockSignals(true);
|
|
ui->rbDirModeEdge->blockSignals(true);
|
|
ui->rbDirModeNormal->blockSignals(true);
|
|
|
|
ui->rbDirModeCustom->setChecked(newMode == Part::Extrusion::dmCustom);
|
|
ui->rbDirModeEdge->setChecked(newMode == Part::Extrusion::dmEdge);
|
|
ui->rbDirModeNormal->setChecked(newMode == Part::Extrusion::dmNormal);
|
|
|
|
ui->rbDirModeCustom->blockSignals(false);
|
|
ui->rbDirModeEdge->blockSignals(false);
|
|
ui->rbDirModeNormal->blockSignals(false);
|
|
this->onDirModeChanged();
|
|
}
|
|
|
|
void DlgExtrusion::getAxisLink(App::PropertyLinkSub& lnk) const
|
|
{
|
|
QString text = ui->txtLink->text();
|
|
|
|
if (text.length() == 0) {
|
|
lnk.setValue(nullptr);
|
|
} else {
|
|
QStringList parts = text.split(QChar::fromLatin1(':'));
|
|
App::DocumentObject* obj = App::GetApplication().getActiveDocument()->getObject(parts[0].toLatin1());
|
|
if(!obj){
|
|
throw Base::ValueError(tr("Object not found: %1").arg(parts[0]).toUtf8().constData());
|
|
}
|
|
lnk.setValue(obj);
|
|
if (parts.size() == 1) {
|
|
return;
|
|
} else if (parts.size() == 2) {
|
|
std::vector<std::string> subs;
|
|
subs.emplace_back(parts[1].toLatin1().constData());
|
|
lnk.setValue(obj,subs);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void DlgExtrusion::setAxisLink(const App::PropertyLinkSub& lnk)
|
|
{
|
|
if (!lnk.getValue()){
|
|
ui->txtLink->clear();
|
|
return;
|
|
}
|
|
if (lnk.getSubValues().size() == 1){
|
|
this->setAxisLink(lnk.getValue()->getNameInDocument(), lnk.getSubValues()[0].c_str());
|
|
} else {
|
|
this->setAxisLink(lnk.getValue()->getNameInDocument(), "");
|
|
}
|
|
}
|
|
|
|
void DlgExtrusion::setAxisLink(const char* objname, const char* subname)
|
|
{
|
|
if(objname && strlen(objname) > 0){
|
|
QString txt = QString::fromLatin1(objname);
|
|
if (subname && strlen(subname) > 0){
|
|
txt = txt + QStringLiteral(":") + QString::fromLatin1(subname);
|
|
}
|
|
ui->txtLink->setText(txt);
|
|
} else {
|
|
ui->txtLink->clear();
|
|
}
|
|
}
|
|
|
|
std::vector<App::DocumentObject*> DlgExtrusion::getShapesToExtrude() const
|
|
{
|
|
QList<QTreeWidgetItem *> items = ui->treeWidget->selectedItems();
|
|
App::Document* doc = App::GetApplication().getDocument(this->document.c_str());
|
|
if (!doc)
|
|
throw Base::RuntimeError("Document lost");
|
|
|
|
std::vector<App::DocumentObject*> objects;
|
|
for (auto item : items) {
|
|
App::DocumentObject* obj = doc->getObject(item->data(0, Qt::UserRole).toString().toLatin1());
|
|
if (!obj)
|
|
throw Base::RuntimeError("Object not found");
|
|
objects.push_back(obj);
|
|
}
|
|
return objects;
|
|
}
|
|
|
|
bool DlgExtrusion::validate()
|
|
{
|
|
//check source shapes
|
|
if (ui->treeWidget->selectedItems().isEmpty()) {
|
|
QMessageBox::critical(this, windowTitle(),
|
|
tr("No shapes selected for extrusion. Select some, first."));
|
|
return false;
|
|
}
|
|
|
|
//check axis link
|
|
QString errmsg;
|
|
bool hasValidAxisLink = false;
|
|
try{
|
|
App::PropertyLinkSub lnk;
|
|
this->getAxisLink(lnk);
|
|
Base::Vector3d dir, base;
|
|
hasValidAxisLink = Part::Extrusion::fetchAxisLink(lnk, base, dir);
|
|
} catch(Base::Exception &err) {
|
|
errmsg = QCoreApplication::translate("Exception", err.what());
|
|
} catch(Standard_Failure &err) {
|
|
errmsg = QString::fromLocal8Bit(err.GetMessageString());
|
|
} catch(...) {
|
|
errmsg = tr("Unknown error");
|
|
}
|
|
if (this->getDirMode() == Part::Extrusion::dmEdge && !hasValidAxisLink){
|
|
if (errmsg.length() > 0)
|
|
QMessageBox::critical(this, windowTitle(), tr("Extrusion direction link is invalid.\n\n%1").arg(errmsg));
|
|
else
|
|
QMessageBox::critical(this, windowTitle(), tr("Direction mode is to use an edge, but no edge is linked."));
|
|
ui->txtLink->setFocus();
|
|
return false;
|
|
} else if (this->getDirMode() != Part::Extrusion::dmEdge && !hasValidAxisLink){
|
|
//axis link is invalid, but it is not required by the mode. We shouldn't complain it's invalid then...
|
|
ui->txtLink->clear();
|
|
}
|
|
|
|
//check normal
|
|
if (this->getDirMode() == Part::Extrusion::dmNormal){
|
|
errmsg.clear();
|
|
try {
|
|
App::PropertyLink lnk;
|
|
lnk.setValue(&this->getShapeToExtrude()); //simplified - check only for the first shape.
|
|
Part::Extrusion::calculateShapeNormal(lnk);
|
|
} catch(Base::Exception &err) {
|
|
errmsg = QCoreApplication::translate("Exception", err.what());
|
|
} catch(Standard_Failure &err) {
|
|
errmsg = QString::fromLocal8Bit(err.GetMessageString());
|
|
} catch(...) {
|
|
errmsg = QStringLiteral("Unknown error");
|
|
}
|
|
if (errmsg.length() > 0){
|
|
QMessageBox::critical(this, windowTitle(), tr("Can't determine normal vector of shape to be extruded. Please use other mode. \n\n(%1)").arg(errmsg));
|
|
ui->rbDirModeNormal->setFocus();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//check axis dir
|
|
if (this->getDirMode() == Part::Extrusion::dmCustom){
|
|
if(this->getDir().Length() < Precision::Confusion()){
|
|
QMessageBox::critical(this, windowTitle(),
|
|
tr("Extrusion direction vector is zero-length. It must be non-zero."));
|
|
ui->dirX->setFocus();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//check lengths
|
|
if (!ui->chkSymmetric->isChecked()
|
|
&& fabs(ui->spinLenFwd->value().getValue() + ui->spinLenRev->value().getValue()) < Precision::Confusion()
|
|
&& ! (fabs(ui->spinLenFwd->value().getValue() - ui->spinLenRev->value().getValue()) < Precision::Confusion())){
|
|
QMessageBox::critical(this, windowTitle(),
|
|
tr("Total extrusion length is zero (length1 == -length2). It must be nonzero."));
|
|
ui->spinLenFwd->setFocus();
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void DlgExtrusion::writeParametersToFeature(App::DocumentObject &feature, App::DocumentObject* base) const
|
|
{
|
|
Gui::Command::doCommand(Gui::Command::Doc,"f = App.getDocument('%s').getObject('%s')", feature.getDocument()->getName(), feature.getNameInDocument());
|
|
|
|
if (base)
|
|
Gui::Command::doCommand(Gui::Command::Doc,"f.Base = App.getDocument('%s').getObject('%s')", base->getDocument()->getName(), base->getNameInDocument());
|
|
|
|
Part::Extrusion::eDirMode dirMode = this->getDirMode();
|
|
const char* modestr = Part::Extrusion::eDirModeStrings[dirMode];
|
|
Gui::Command::doCommand(Gui::Command::Doc,"f.DirMode = \"%s\"", modestr);
|
|
|
|
if (dirMode == Part::Extrusion::dmCustom){
|
|
Base::Vector3d dir = this->getDir();
|
|
Gui::Command::doCommand(Gui::Command::Doc, "f.Dir = App.Vector(%.15f, %.15f, %.15f)", dir.x, dir.y, dir.z);
|
|
}
|
|
|
|
App::PropertyLinkSub lnk;
|
|
this->getAxisLink(lnk);
|
|
std::stringstream linkstr;
|
|
if (!lnk.getValue()) {
|
|
linkstr << "None";
|
|
} else {
|
|
linkstr << "(App.getDocument(\"" << lnk.getValue()->getDocument()->getName() <<"\")." << lnk.getValue()->getNameInDocument();
|
|
linkstr << ", [";
|
|
for (const std::string &str: lnk.getSubValues()){
|
|
linkstr << "\"" << str << "\"";
|
|
}
|
|
linkstr << "])";
|
|
}
|
|
Gui::Command::doCommand(Gui::Command::Doc,"f.DirLink = %s", linkstr.str().c_str());
|
|
|
|
Gui::Command::doCommand(Gui::Command::Doc,"f.LengthFwd = %.15f", ui->spinLenFwd->value().getValue());
|
|
Gui::Command::doCommand(Gui::Command::Doc,"f.LengthRev = %.15f", ui->spinLenRev->value().getValue());
|
|
|
|
Gui::Command::doCommand(Gui::Command::Doc,"f.Solid = %s", ui->chkSolid->isChecked() ? "True" : "False");
|
|
Gui::Command::doCommand(Gui::Command::Doc,"f.Reversed = %s", ui->chkReversed->isChecked() ? "True" : "False");
|
|
Gui::Command::doCommand(Gui::Command::Doc,"f.Symmetric = %s", ui->chkSymmetric->isChecked() ? "True" : "False");
|
|
Gui::Command::doCommand(Gui::Command::Doc,"f.TaperAngle = %.15f", ui->spinTaperAngle->value().getValue());
|
|
Gui::Command::doCommand(Gui::Command::Doc,"f.TaperAngleRev = %.15f", ui->spinTaperAngleRev->value().getValue());
|
|
}
|
|
|
|
|
|
// ---------------------------------------
|
|
|
|
TaskExtrusion::TaskExtrusion()
|
|
{
|
|
widget = new DlgExtrusion();
|
|
addTaskBox(Gui::BitmapFactory().pixmap("Part_Extrude"), widget);
|
|
}
|
|
|
|
bool TaskExtrusion::accept()
|
|
{
|
|
widget->accept();
|
|
return (widget->result() == QDialog::Accepted);
|
|
}
|
|
|
|
bool TaskExtrusion::reject()
|
|
{
|
|
widget->reject();
|
|
return true;
|
|
}
|
|
|
|
void TaskExtrusion::clicked(int id)
|
|
{
|
|
if (id == QDialogButtonBox::Apply) {
|
|
try{
|
|
widget->apply();
|
|
} catch (Base::AbortException&){
|
|
|
|
};
|
|
}
|
|
}
|
|
|
|
#include "moc_DlgExtrusion.cpp"
|