23
#include "PreCompiled.h"
25
# include <BRepAdaptor_Curve.hxx>
26
# include <BRep_Tool.hxx>
27
# include <Precision.hxx>
28
# include <ShapeExtend_Explorer.hxx>
29
# include <TopExp_Explorer.hxx>
31
# include <TopTools_HSequenceOfShape.hxx>
33
# include <QMessageBox>
36
#include <App/Application.h>
37
#include <App/Document.h>
38
#include <App/DocumentObject.h>
41
#include <Base/UnitsApi.h>
42
#include <Gui/Application.h>
43
#include <Gui/BitmapFactory.h>
44
#include <Gui/Command.h>
45
#include <Gui/Document.h>
46
#include <Gui/Utilities.h>
47
#include <Gui/ViewProvider.h>
48
#include <Gui/WaitCursor.h>
50
#include "ui_DlgExtrusion.h"
51
#include "DlgExtrusion.h"
54
FC_LOG_LEVEL_INIT("Part",true,true)
56
using namespace PartGui;
58
class DlgExtrusion::EdgeSelection : public Gui::SelectionFilterGate
64
: Gui::SelectionFilterGate(nullPointer())
68
bool allow(App::Document* , App::DocumentObject* pObj, const char* sSubName) override
70
this->canSelect = false;
72
if (!sSubName || sSubName[0] == '\0')
74
std::string element(sSubName);
75
if (element.substr(0,4) != "Edge")
77
Part::TopoShape part = Part::Feature::getTopoShape(pObj);
82
TopoDS_Shape sub = Part::Feature::getTopoShape(pObj, sSubName, true ).getShape();
83
if (!sub.IsNull() && sub.ShapeType() == TopAbs_EDGE) {
84
const TopoDS_Edge& edge = TopoDS::Edge(sub);
85
BRepAdaptor_Curve adapt(edge);
86
if (adapt.GetType() == GeomAbs_Line) {
87
this->canSelect = true;
99
DlgExtrusion::DlgExtrusion(QWidget* parent, Qt::WindowFlags fl)
100
: QDialog(parent, fl), ui(new Ui_DlgExtrusion), filter(nullptr)
105
ui->statusLabel->clear();
106
ui->dirX->setDecimals(Base::UnitsApi::getDecimals());
107
ui->dirY->setDecimals(Base::UnitsApi::getDecimals());
108
ui->dirZ->setDecimals(Base::UnitsApi::getDecimals());
109
ui->spinLenFwd->setUnit(Base::Unit::Length);
110
ui->spinLenFwd->setValue(10.0);
111
ui->spinLenRev->setUnit(Base::Unit::Length);
112
ui->spinTaperAngle->setUnit(Base::Unit::Angle);
113
ui->spinTaperAngle->setUnit(Base::Unit::Angle);
116
Gui::ItemViewSelection sel(ui->treeWidget);
117
sel.applyFrom(Gui::Selection().getObjectsOfType(Part::Feature::getClassTypeId()));
118
sel.applyFrom(Gui::Selection().getObjectsOfType(App::Link::getClassTypeId()));
119
sel.applyFrom(Gui::Selection().getObjectsOfType(App::Part::getClassTypeId()));
121
this->onDirModeChanged();
122
ui->spinLenFwd->selectAll();
126
QMetaObject::invokeMethod(ui->spinLenFwd, "setFocus", Qt::QueuedConnection);
134
DlgExtrusion::~DlgExtrusion()
137
Gui::Selection().rmvSelectionGate();
144
void DlgExtrusion::setupConnections()
147
connect(ui->rbDirModeCustom, &QRadioButton::toggled,
148
this, &DlgExtrusion::onDirModeCustomToggled);
149
connect(ui->rbDirModeEdge, &QRadioButton::toggled,
150
this, &DlgExtrusion::onDirModeEdgeToggled);
151
connect(ui->rbDirModeNormal, &QRadioButton::toggled,
152
this, &DlgExtrusion::onDirModeNormalToggled);
153
connect(ui->btnSelectEdge, &QPushButton::clicked,
154
this, &DlgExtrusion::onSelectEdgeClicked);
155
connect(ui->btnX, &QPushButton::clicked,
156
this, &DlgExtrusion::onButtnoXClicked);
157
connect(ui->btnY, &QPushButton::clicked,
158
this, &DlgExtrusion::onButtonYClicked);
159
connect(ui->btnZ, &QPushButton::clicked,
160
this, &DlgExtrusion::onButtonZClicked);
161
connect(ui->chkSymmetric, &QCheckBox::toggled,
162
this, &DlgExtrusion::onCheckSymmetricToggled);
163
connect(ui->txtLink, &QLineEdit::textChanged,
164
this, &DlgExtrusion::onTextLinkTextChanged);
168
void DlgExtrusion::changeEvent(QEvent *e)
170
if (e->type() == QEvent::LanguageChange) {
171
ui->retranslateUi(this);
173
QDialog::changeEvent(e);
176
void DlgExtrusion::keyPressEvent(QKeyEvent* ke)
183
void DlgExtrusion::onDirModeCustomToggled(bool on)
186
this->onDirModeChanged();
189
void DlgExtrusion::onDirModeEdgeToggled(bool on)
192
this->onDirModeChanged();
195
void DlgExtrusion::onDirModeNormalToggled(bool on)
198
this->onDirModeChanged();
201
void DlgExtrusion::onSelectEdgeClicked()
204
filter = new EdgeSelection();
205
Gui::Selection().addSelectionGate(filter);
206
ui->btnSelectEdge->setText(tr("Selecting..."));
210
QString code = QString::fromLatin1(
212
"tv = Show.TempoVis(App.ActiveDocument, tag= 'PartGui::DlgExtrusion')\n"
215
std::vector<App::DocumentObject*>sources = getShapesToExtrude();
216
QString features_to_hide;
217
for (App::DocumentObject* obj: sources){
220
features_to_hide.append(QString::fromLatin1("App.ActiveDocument."));
221
features_to_hide.append(QString::fromLatin1(obj->getNameInDocument()));
222
features_to_hide.append(QString::fromLatin1(", \n"));
224
QByteArray code_2 = code.arg(features_to_hide).toLatin1();
225
Base::Interpreter().runString(code_2.constData());
226
} catch (Base::PyException &e){
230
Gui::Selection().rmvSelectionGate();
232
ui->btnSelectEdge->setText(tr("Select"));
236
Base::Interpreter().runString("del(tv)");
237
} catch (Base::PyException &e){
243
void DlgExtrusion::onButtnoXClicked()
245
Base::Vector3d axis(1.0, 0.0, 0.0);
246
if ((getDir() - axis).Length() < 1e-7)
248
setDirMode(Part::Extrusion::dmCustom);
252
void DlgExtrusion::onButtonYClicked()
254
Base::Vector3d axis(0.0, 1.0, 0.0);
255
if ((getDir() - axis).Length() < 1e-7)
257
setDirMode(Part::Extrusion::dmCustom);
261
void DlgExtrusion::onButtonZClicked()
263
Base::Vector3d axis(0.0, 0.0, 1.0);
264
if ((getDir() - axis).Length() < 1e-7)
266
setDirMode(Part::Extrusion::dmCustom);
270
void DlgExtrusion::onCheckSymmetricToggled(bool on)
272
ui->spinLenRev->setEnabled(!on);
275
void DlgExtrusion::onTextLinkTextChanged(QString)
280
void DlgExtrusion::onDirModeChanged()
282
Part::Extrusion::eDirMode dirMode = this->getDirMode();
283
ui->dirX->setEnabled(dirMode == Part::Extrusion::dmCustom);
284
ui->dirY->setEnabled(dirMode == Part::Extrusion::dmCustom);
285
ui->dirZ->setEnabled(dirMode == Part::Extrusion::dmCustom);
286
ui->txtLink->setEnabled(dirMode == Part::Extrusion::dmEdge);
290
void DlgExtrusion::onSelectionChanged(const Gui::SelectionChanges& msg)
292
if (msg.Type == Gui::SelectionChanges::AddSelection) {
293
if (filter && filter->canSelect) {
294
this->setAxisLink(msg.pObjectName, msg.pSubName);
295
this->setDirMode(Part::Extrusion::dmEdge);
300
App::DocumentObject& DlgExtrusion::getShapeToExtrude() const
302
std::vector<App::DocumentObject*> objs = this->getShapesToExtrude();
304
throw Base::ValueError("No shapes selected");
308
void DlgExtrusion::fetchDir()
310
bool lengths_are_at_defaults =
311
(fabs(ui->spinLenFwd->value().getValue() - 10.0) < 1e-7)
312
&& (fabs(ui->spinLenRev->value().getValue() - 0.0) < 1e-7);
313
bool lengths_are_zero =
314
(fabs(ui->spinLenFwd->value().getValue() - 0.0) < 1e-7)
315
&& (fabs(ui->spinLenRev->value().getValue() - 0.0) < 1e-7);
318
Base::Vector3d pos, dir;
319
bool fetched = false;
320
bool dir_has_valid_magnitude = false;
321
if(this->getDirMode() == Part::Extrusion::dmEdge){
322
App::PropertyLinkSub lnk; this->getAxisLink(lnk);
323
fetched = Part::Extrusion::fetchAxisLink(lnk, pos, dir);
324
dir_has_valid_magnitude = fetched;
325
} else if (this->getDirMode() == Part::Extrusion::dmNormal){
326
App::PropertyLink lnk;
327
lnk.setValue(&this->getShapeToExtrude());
328
dir = Part::Extrusion::calculateShapeNormal(lnk);
331
if (dir_has_valid_magnitude && lengths_are_at_defaults){
332
ui->spinLenFwd->setValue(0);
333
} else if (!dir_has_valid_magnitude && lengths_are_zero){
334
ui->spinLenFwd->setValue(1.0);
339
} catch (Base::Exception &){
346
void DlgExtrusion::autoSolid()
349
App::DocumentObject* dobj = &this->getShapeToExtrude();
350
Part::TopoShape shape = Part::Feature::getTopoShape(dobj);
351
if (shape.isNull()) {
354
TopoDS_Shape sh = shape.getShape();
357
ShapeExtend_Explorer xp;
358
Handle(TopTools_HSequenceOfShape) leaves = xp.SeqFromCompound(sh, Standard_True);
359
int cntClosedWires = 0;
360
for(int i = 0; i < leaves->Length(); i++){
361
const TopoDS_Shape &leaf = leaves->Value(i+1);
364
if (leaf.ShapeType() == TopAbs_WIRE || leaf.ShapeType() == TopAbs_EDGE){
365
if (BRep_Tool::IsClosed(leaf)){
370
ui->chkSolid->setChecked( cntClosedWires == leaves->Length() );
376
void DlgExtrusion::findShapes()
378
App::Document* activeDoc = App::GetApplication().getActiveDocument();
381
Gui::Document* activeGui = Gui::Application::Instance->getDocument(activeDoc);
382
this->document = activeDoc->getName();
383
this->label = activeDoc->Label.getValue();
385
std::vector<App::DocumentObject*> objs = activeDoc->getObjectsOfType<App::DocumentObject>();
387
for (auto obj : objs) {
388
Part::TopoShape topoShape = Part::Feature::getTopoShape(obj);
389
if (topoShape.isNull()) {
392
TopoDS_Shape shape = topoShape.getShape();
393
if (shape.IsNull()) continue;
394
if (canExtrude(shape)) {
395
QTreeWidgetItem* item = new QTreeWidgetItem(ui->treeWidget);
396
item->setText(0, QString::fromUtf8(obj->Label.getValue()));
397
item->setData(0, Qt::UserRole, QString::fromLatin1(obj->getNameInDocument()));
398
Gui::ViewProvider* vp = activeGui->getViewProvider(obj);
400
item->setIcon(0, vp->getIcon());
405
bool DlgExtrusion::canExtrude(const TopoDS_Shape& shape) const
409
TopAbs_ShapeEnum type = shape.ShapeType();
410
if (type == TopAbs_VERTEX || type == TopAbs_EDGE ||
411
type == TopAbs_WIRE || type == TopAbs_FACE ||
412
type == TopAbs_SHELL)
414
if (type == TopAbs_COMPOUND) {
416
xp.Init(shape,TopAbs_SOLID);
420
xp.Init(shape,TopAbs_COMPSOLID);
431
void DlgExtrusion::accept()
436
} catch (Base::AbortException&){
441
void DlgExtrusion::apply()
445
throw Base::AbortException();
448
this->onSelectEdgeClicked();
451
App::Document* activeDoc = App::GetApplication().getDocument(this->document.c_str());
453
QMessageBox::critical(this, windowTitle(),
454
tr("The document '%1' doesn't exist.").arg(QString::fromUtf8(this->label.c_str())));
457
activeDoc->openTransaction("Extrude");
459
Base::Reference<ParameterGrp> hGrp = App::GetApplication().GetUserParameter()
460
.GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod/Part");
461
bool addBaseName = hGrp->GetBool("AddBaseObjectName", false);
463
std::vector<App::DocumentObject*> objects = this->getShapesToExtrude();
464
for (App::DocumentObject* sourceObj: objects) {
467
if (Part::Feature::getTopoShape(sourceObj).isNull()){
468
FC_ERR("Object " << sourceObj->getFullName()
469
<< " is not Part object (has no OCC shape). Can't extrude it.");
474
name = sourceObj->getDocument()->getUniqueObjectName("Extrude").c_str();
481
FCMD_OBJ_DOC_CMD(sourceObj,"addObject('Part::Extrusion','" << name << "')");
482
auto newObj = sourceObj->getDocument()->getObject(name.c_str());
484
this->writeParametersToFeature(*newObj, sourceObj);
486
Gui::Command::copyVisual(newObj, "ShapeAppearance", sourceObj);
487
Gui::Command::copyVisual(newObj, "LineColor", sourceObj);
488
Gui::Command::copyVisual(newObj, "PointColor", sourceObj);
490
FCMD_OBJ_HIDE(sourceObj);
493
activeDoc->commitTransaction();
494
Gui::Command::updateActive();
496
catch (Base::AbortException&){
499
catch (Base::Exception &err){
500
QMessageBox::critical(this,
502
tr("Creating Extrusion failed.\n%1")
503
.arg(QCoreApplication::translate("Exception", err.what())));
507
QMessageBox::critical(this, windowTitle(),
508
tr("Creating Extrusion failed.\n%1").arg(QString::fromUtf8("Unknown error")));
513
void DlgExtrusion::reject()
516
this->onSelectEdgeClicked();
521
Base::Vector3d DlgExtrusion::getDir() const
523
return Base::Vector3d(
529
void DlgExtrusion::setDir(Base::Vector3d newDir)
531
ui->dirX->setValue(newDir.x);
532
ui->dirY->setValue(newDir.y);
533
ui->dirZ->setValue(newDir.z);
536
Part::Extrusion::eDirMode DlgExtrusion::getDirMode() const
538
if(ui->rbDirModeCustom->isChecked())
539
return Part::Extrusion::dmCustom;
540
if(ui->rbDirModeEdge->isChecked())
541
return Part::Extrusion::dmEdge;
542
if(ui->rbDirModeNormal->isChecked())
543
return Part::Extrusion::dmNormal;
546
return Part::Extrusion::dmCustom;
549
void DlgExtrusion::setDirMode(Part::Extrusion::eDirMode newMode)
551
ui->rbDirModeCustom->blockSignals(true);
552
ui->rbDirModeEdge->blockSignals(true);
553
ui->rbDirModeNormal->blockSignals(true);
555
ui->rbDirModeCustom->setChecked(newMode == Part::Extrusion::dmCustom);
556
ui->rbDirModeEdge->setChecked(newMode == Part::Extrusion::dmEdge);
557
ui->rbDirModeNormal->setChecked(newMode == Part::Extrusion::dmNormal);
559
ui->rbDirModeCustom->blockSignals(false);
560
ui->rbDirModeEdge->blockSignals(false);
561
ui->rbDirModeNormal->blockSignals(false);
562
this->onDirModeChanged();
565
void DlgExtrusion::getAxisLink(App::PropertyLinkSub& lnk) const
567
QString text = ui->txtLink->text();
569
if (text.length() == 0) {
570
lnk.setValue(nullptr);
572
QStringList parts = text.split(QChar::fromLatin1(':'));
573
App::DocumentObject* obj = App::GetApplication().getActiveDocument()->getObject(parts[0].toLatin1());
575
throw Base::ValueError(tr("Object not found: %1").arg(parts[0]).toUtf8().constData());
578
if (parts.size() == 1) {
580
} else if (parts.size() == 2) {
581
std::vector<std::string> subs;
582
subs.emplace_back(parts[1].toLatin1().constData());
583
lnk.setValue(obj,subs);
589
void DlgExtrusion::setAxisLink(const App::PropertyLinkSub& lnk)
591
if (!lnk.getValue()){
592
ui->txtLink->clear();
595
if (lnk.getSubValues().size() == 1){
596
this->setAxisLink(lnk.getValue()->getNameInDocument(), lnk.getSubValues()[0].c_str());
598
this->setAxisLink(lnk.getValue()->getNameInDocument(), "");
602
void DlgExtrusion::setAxisLink(const char* objname, const char* subname)
604
if(objname && strlen(objname) > 0){
605
QString txt = QString::fromLatin1(objname);
606
if (subname && strlen(subname) > 0){
607
txt = txt + QString::fromLatin1(":") + QString::fromLatin1(subname);
609
ui->txtLink->setText(txt);
611
ui->txtLink->clear();
615
std::vector<App::DocumentObject*> DlgExtrusion::getShapesToExtrude() const
617
QList<QTreeWidgetItem *> items = ui->treeWidget->selectedItems();
618
App::Document* doc = App::GetApplication().getDocument(this->document.c_str());
620
throw Base::RuntimeError("Document lost");
622
std::vector<App::DocumentObject*> objects;
623
for (auto item : items) {
624
App::DocumentObject* obj = doc->getObject(item->data(0, Qt::UserRole).toString().toLatin1());
626
throw Base::RuntimeError("Object not found");
627
objects.push_back(obj);
632
bool DlgExtrusion::validate()
635
if (ui->treeWidget->selectedItems().isEmpty()) {
636
QMessageBox::critical(this, windowTitle(),
637
tr("No shapes selected for extrusion. Select some, first."));
643
bool hasValidAxisLink = false;
645
App::PropertyLinkSub lnk;
646
this->getAxisLink(lnk);
647
Base::Vector3d dir, base;
648
hasValidAxisLink = Part::Extrusion::fetchAxisLink(lnk, base, dir);
649
} catch(Base::Exception &err) {
650
errmsg = QCoreApplication::translate("Exception", err.what());
651
} catch(Standard_Failure &err) {
652
errmsg = QString::fromLocal8Bit(err.GetMessageString());
654
errmsg = tr("Unknown error");
656
if (this->getDirMode() == Part::Extrusion::dmEdge && !hasValidAxisLink){
657
if (errmsg.length() > 0)
658
QMessageBox::critical(this, windowTitle(), tr("Extrusion direction link is invalid.\n\n%1").arg(errmsg));
660
QMessageBox::critical(this, windowTitle(), tr("Direction mode is to use an edge, but no edge is linked."));
661
ui->txtLink->setFocus();
663
} else if (this->getDirMode() != Part::Extrusion::dmEdge && !hasValidAxisLink){
665
ui->txtLink->clear();
669
if (this->getDirMode() == Part::Extrusion::dmNormal){
672
App::PropertyLink lnk;
673
lnk.setValue(&this->getShapeToExtrude());
674
Part::Extrusion::calculateShapeNormal(lnk);
675
} catch(Base::Exception &err) {
676
errmsg = QCoreApplication::translate("Exception", err.what());
677
} catch(Standard_Failure &err) {
678
errmsg = QString::fromLocal8Bit(err.GetMessageString());
680
errmsg = QString::fromUtf8("Unknown error");
682
if (errmsg.length() > 0){
683
QMessageBox::critical(this, windowTitle(), tr("Can't determine normal vector of shape to be extruded. Please use other mode. \n\n(%1)").arg(errmsg));
684
ui->rbDirModeNormal->setFocus();
690
if (this->getDirMode() == Part::Extrusion::dmCustom){
691
if(this->getDir().Length() < Precision::Confusion()){
692
QMessageBox::critical(this, windowTitle(),
693
tr("Extrusion direction vector is zero-length. It must be non-zero."));
694
ui->dirX->setFocus();
700
if (!ui->chkSymmetric->isChecked()
701
&& fabs(ui->spinLenFwd->value().getValue() + ui->spinLenRev->value().getValue()) < Precision::Confusion()
702
&& ! (fabs(ui->spinLenFwd->value().getValue() - ui->spinLenRev->value().getValue()) < Precision::Confusion())){
703
QMessageBox::critical(this, windowTitle(),
704
tr("Total extrusion length is zero (length1 == -length2). It must be nonzero."));
705
ui->spinLenFwd->setFocus();
712
void DlgExtrusion::writeParametersToFeature(App::DocumentObject &feature, App::DocumentObject* base) const
714
Gui::Command::doCommand(Gui::Command::Doc,"f = App.getDocument('%s').getObject('%s')", feature.getDocument()->getName(), feature.getNameInDocument());
717
Gui::Command::doCommand(Gui::Command::Doc,"f.Base = App.getDocument('%s').getObject('%s')", base->getDocument()->getName(), base->getNameInDocument());
719
Part::Extrusion::eDirMode dirMode = this->getDirMode();
720
const char* modestr = Part::Extrusion::eDirModeStrings[dirMode];
721
Gui::Command::doCommand(Gui::Command::Doc,"f.DirMode = \"%s\"", modestr);
723
if (dirMode == Part::Extrusion::dmCustom){
724
Base::Vector3d dir = this->getDir();
725
Gui::Command::doCommand(Gui::Command::Doc, "f.Dir = App.Vector(%.15f, %.15f, %.15f)", dir.x, dir.y, dir.z);
728
App::PropertyLinkSub lnk;
729
this->getAxisLink(lnk);
730
std::stringstream linkstr;
731
if (!lnk.getValue()) {
734
linkstr << "(App.getDocument(\"" << lnk.getValue()->getDocument()->getName() <<"\")." << lnk.getValue()->getNameInDocument();
736
for (const std::string &str: lnk.getSubValues()){
737
linkstr << "\"" << str << "\"";
741
Gui::Command::doCommand(Gui::Command::Doc,"f.DirLink = %s", linkstr.str().c_str());
743
Gui::Command::doCommand(Gui::Command::Doc,"f.LengthFwd = %.15f", ui->spinLenFwd->value().getValue());
744
Gui::Command::doCommand(Gui::Command::Doc,"f.LengthRev = %.15f", ui->spinLenRev->value().getValue());
746
Gui::Command::doCommand(Gui::Command::Doc,"f.Solid = %s", ui->chkSolid->isChecked() ? "True" : "False");
747
Gui::Command::doCommand(Gui::Command::Doc,"f.Reversed = %s", ui->chkReversed->isChecked() ? "True" : "False");
748
Gui::Command::doCommand(Gui::Command::Doc,"f.Symmetric = %s", ui->chkSymmetric->isChecked() ? "True" : "False");
749
Gui::Command::doCommand(Gui::Command::Doc,"f.TaperAngle = %.15f", ui->spinTaperAngle->value().getValue());
750
Gui::Command::doCommand(Gui::Command::Doc,"f.TaperAngleRev = %.15f", ui->spinTaperAngleRev->value().getValue());
756
TaskExtrusion::TaskExtrusion()
758
widget = new DlgExtrusion();
759
addTaskBox(Gui::BitmapFactory().pixmap("Part_Extrude"), widget);
762
bool TaskExtrusion::accept()
765
return (widget->result() == QDialog::Accepted);
768
bool TaskExtrusion::reject()
774
void TaskExtrusion::clicked(int id)
776
if (id == QDialogButtonBox::Apply) {
779
} catch (Base::AbortException&){
785
#include "moc_DlgExtrusion.cpp"