23
#include "PreCompiled.h"
26
# include <QApplication>
27
# include <QMessageBox>
28
# include <QTextStream>
30
# include <QTreeWidget>
31
# include <BRepBuilderAPI_MakeWire.hxx>
32
# include <Precision.hxx>
33
# include <ShapeAnalysis_FreeBounds.hxx>
34
# include <TopExp_Explorer.hxx>
36
# include <TopoDS_Iterator.hxx>
37
# include <TopTools_HSequenceOfShape.hxx>
40
#include <App/Application.h>
41
#include <App/Document.h>
42
#include <App/DocumentObject.h>
44
#include <Gui/Application.h>
45
#include <Gui/BitmapFactory.h>
46
#include <Gui/Command.h>
47
#include <Gui/Document.h>
48
#include <Gui/Selection.h>
49
#include <Gui/SelectionFilter.h>
50
#include <Gui/SelectionObject.h>
51
#include <Gui/ViewProvider.h>
52
#include <Gui/WaitCursor.h>
53
#include <Mod/Part/App/PartFeature.h>
56
#include "ui_TaskSweep.h"
59
using namespace PartGui;
61
class SweepWidget::Private
70
class EdgeSelection : public Gui::SelectionFilterGate
74
: Gui::SelectionFilterGate(nullPointer())
77
bool allow(App::Document* , App::DocumentObject*pObj, const char*sSubName) override
79
if (!sSubName || sSubName[0] == '\0') {
82
Part::TopoShape topoShape = Part::Feature::getTopoShape(pObj);
83
if (topoShape.isNull()) {
86
const TopoDS_Shape shape = topoShape.getShape();
87
if (!shape.IsNull()) {
89
if (shape.ShapeType() == TopAbs_EDGE) {
93
if (shape.ShapeType() == TopAbs_WIRE) {
97
if (shape.ShapeType() == TopAbs_COMPOUND) {
98
TopoDS_Iterator it(shape);
99
for (; it.More(); it.Next()) {
100
if (it.Value().IsNull())
102
if ((it.Value().ShapeType() != TopAbs_EDGE) &&
103
(it.Value().ShapeType() != TopAbs_WIRE))
112
std::string element(sSubName);
113
return element.substr(0,4) == "Edge";
123
SweepWidget::SweepWidget(QWidget* parent)
127
Gui::Command::runCommand(Gui::Command::App, "from FreeCAD import Base");
128
Gui::Command::runCommand(Gui::Command::App, "import Part");
131
d->ui.selector->setAvailableLabel(tr("Available profiles"));
132
d->ui.selector->setSelectedLabel(tr("Selected profiles"));
133
d->ui.labelPath->clear();
136
connect(d->ui.buttonPath, &QPushButton::toggled,
137
this, &SweepWidget::onButtonPathToggled);
138
connect(d->ui.selector->availableTreeWidget(), &QTreeWidget::currentItemChanged,
139
this, &SweepWidget::onCurrentItemChanged);
140
connect(d->ui.selector->selectedTreeWidget(), &QTreeWidget::currentItemChanged,
141
this, &SweepWidget::onCurrentItemChanged);
147
SweepWidget::~SweepWidget()
150
Gui::Selection().rmvSelectionGate();
153
void SweepWidget::findShapes()
155
App::Document* activeDoc = App::GetApplication().getActiveDocument();
156
Gui::Document* activeGui = Gui::Application::Instance->getDocument(activeDoc);
159
d->document = activeDoc->getName();
161
std::vector<App::DocumentObject*> objs = activeDoc->getObjectsOfType<App::DocumentObject>();
163
for (auto obj : objs) {
164
Part::TopoShape topoShape = Part::Feature::getTopoShape(obj);
165
if (topoShape.isNull()) {
168
TopoDS_Shape shape = topoShape.getShape();
169
if (shape.IsNull()) continue;
173
if (shape.ShapeType() == TopAbs_COMPOUND) {
174
Handle(TopTools_HSequenceOfShape) hEdges = new TopTools_HSequenceOfShape();
175
Handle(TopTools_HSequenceOfShape) hWires = new TopTools_HSequenceOfShape();
177
TopoDS_Iterator it(shape);
180
for (; it.More(); it.Next(), numChilds++) {
181
if (!it.Value().IsNull()) {
183
if (child.ShapeType() == TopAbs_EDGE) {
184
hEdges->Append(child);
190
if (numChilds == 1) {
194
else if (hEdges->Length() == numChilds) {
195
ShapeAnalysis_FreeBounds::ConnectEdgesToWires(hEdges,
196
Precision::Confusion(), Standard_False, hWires);
197
if (hWires->Length() == 1)
198
shape = hWires->Value(1);
202
if (shape.ShapeType() == TopAbs_FACE ||
203
shape.ShapeType() == TopAbs_WIRE ||
204
shape.ShapeType() == TopAbs_EDGE ||
205
shape.ShapeType() == TopAbs_VERTEX) {
206
QString label = QString::fromUtf8(obj->Label.getValue());
207
QString name = QString::fromLatin1(obj->getNameInDocument());
209
QTreeWidgetItem* child = new QTreeWidgetItem();
210
child->setText(0, label);
211
child->setToolTip(0, label);
212
child->setData(0, Qt::UserRole, name);
213
Gui::ViewProvider* vp = activeGui->getViewProvider(obj);
214
if (vp) child->setIcon(0, vp->getIcon());
215
d->ui.selector->availableTreeWidget()->addTopLevelItem(child);
220
bool SweepWidget::isPathValid(const Gui::SelectionObject& sel) const
222
const App::DocumentObject* path = sel.getObject();
223
const std::vector<std::string>& sub = sel.getSubNames();
225
TopoDS_Shape pathShape;
226
const Part::TopoShape& shape = Part::Feature::getTopoShape(path);
232
BRepBuilderAPI_MakeWire mkWire;
233
for (const auto & it : sub) {
234
TopoDS_Shape subshape = shape.getSubShape(it.c_str());
235
mkWire.Add(TopoDS::Edge(subshape));
237
pathShape = mkWire.Wire();
243
else if (shape.getShape().ShapeType() == TopAbs_EDGE) {
244
pathShape = shape.getShape();
246
else if (shape.getShape().ShapeType() == TopAbs_WIRE) {
247
BRepBuilderAPI_MakeWire mkWire(TopoDS::Wire(shape.getShape()));
248
pathShape = mkWire.Wire();
250
else if (shape.getShape().ShapeType() == TopAbs_COMPOUND) {
252
TopoDS_Iterator it(shape.getShape());
253
for (; it.More(); it.Next()) {
254
if ((it.Value().ShapeType() != TopAbs_EDGE) &&
255
(it.Value().ShapeType() != TopAbs_WIRE)) {
259
Handle(TopTools_HSequenceOfShape) hEdges = new TopTools_HSequenceOfShape();
260
Handle(TopTools_HSequenceOfShape) hWires = new TopTools_HSequenceOfShape();
261
for (TopExp_Explorer xp(shape.getShape(), TopAbs_EDGE); xp.More(); xp.Next())
262
hEdges->Append(xp.Current());
264
ShapeAnalysis_FreeBounds::ConnectEdgesToWires(hEdges, Precision::Confusion(), Standard_True, hWires);
265
int len = hWires->Length();
268
pathShape = hWires->Value(1);
275
return (!pathShape.IsNull());
278
bool SweepWidget::accept()
280
if (d->ui.buttonPath->isChecked())
282
const App::DocumentObject* docobj = nullptr;
283
std::string selection;
284
const std::vector<Gui::SelectionObject> selobjs = Gui::Selection().getSelectionEx();
285
std::vector<Part::TopoShape> subShapes;
286
Part::TopoShape topoShape = Part::TopoShape();
287
std::string spineObject, spineLabel;
290
if (selobjs.size() == 1) {
291
selection = selobjs[0].getAsPropertyLinkSubString();
292
const std::vector<std::string>& subnames = selobjs[0].getSubNames();
293
docobj = selobjs[0].getObject();
294
spineObject = selobjs[0].getFeatName();
295
spineLabel = docobj->Label.getValue();
296
topoShape = Part::Feature::getTopoShape(docobj);
297
if (!topoShape.isNull()) {
298
for (std::vector<std::string>::const_iterator it = subnames.begin(); it != subnames.end(); ++it) {
299
subShapes.push_back(Part::Feature::getTopoShape(docobj, subnames[0].c_str(), true ));
301
for (const auto & it : subShapes) {
302
TopoDS_Shape dsShape = it.getShape();
303
if (dsShape.IsNull() || dsShape.ShapeType() != TopAbs_EDGE) {
315
QString list, solid, frenet;
316
if (d->ui.checkSolid->isChecked())
317
solid = QString::fromLatin1("True");
319
solid = QString::fromLatin1("False");
321
if (d->ui.checkFrenet->isChecked())
322
frenet = QString::fromLatin1("True");
324
frenet = QString::fromLatin1("False");
326
QTextStream str(&list);
328
int count = d->ui.selector->selectedTreeWidget()->topLevelItemCount();
330
QMessageBox::critical(this, tr("Too few elements"), tr("At least one edge or wire is required."));
334
QMessageBox::critical(this, tr("Invalid selection"), tr("Select one or more edges from a single object."));
337
for (int i=0; i<count; i++) {
338
QTreeWidgetItem* child = d->ui.selector->selectedTreeWidget()->topLevelItem(i);
339
QString name = child->data(0, Qt::UserRole).toString();
340
if (name == QLatin1String(spineObject.c_str())) {
341
QMessageBox::critical(this, tr("Wrong selection"), tr("'%1' cannot be used as profile and path.")
342
.arg(QString::fromUtf8(spineLabel.c_str())));
345
str << "App.getDocument('" << d->document.c_str() << "')." << name << ", ";
351
cmd = QString::fromLatin1(
352
"App.getDocument('%5').addObject('Part::Sweep','Sweep')\n"
353
"App.getDocument('%5').ActiveObject.Sections=[%1]\n"
354
"App.getDocument('%5').ActiveObject.Spine=%2\n"
355
"App.getDocument('%5').ActiveObject.Solid=%3\n"
356
"App.getDocument('%5').ActiveObject.Frenet=%4\n"
359
QLatin1String(selection.c_str()),
362
QString::fromLatin1(d->document.c_str()));
364
Gui::Document* doc = Gui::Application::Instance->getDocument(d->document.c_str());
366
throw Base::RuntimeError("Document doesn't exist anymore");
367
doc->openCommand(QT_TRANSLATE_NOOP("Command", "Sweep"));
368
Gui::Command::runCommand(Gui::Command::App, cmd.toLatin1());
369
doc->getDocument()->recompute();
370
App::DocumentObject* obj = doc->getDocument()->getActiveObject();
371
if (obj && !obj->isValid()) {
372
std::string msg = obj->getStatusString();
374
throw Base::RuntimeError(msg);
376
doc->commitCommand();
378
catch (const Base::Exception& e) {
379
QMessageBox::warning(this, tr("Input error"), QCoreApplication::translate("Exception", e.what()));
386
bool SweepWidget::reject()
388
if (d->ui.buttonPath->isChecked())
393
void SweepWidget::onCurrentItemChanged(QTreeWidgetItem* current, QTreeWidgetItem* previous)
396
Gui::Selection().rmvSelection(d->document.c_str(),
397
(const char*)previous->data(0,Qt::UserRole).toByteArray());
400
Gui::Selection().addSelection(d->document.c_str(),
401
(const char*)current->data(0,Qt::UserRole).toByteArray());
405
void SweepWidget::onButtonPathToggled(bool on)
408
QList<QWidget*> c = this->findChildren<QWidget*>();
410
it->setEnabled(false);
411
d->buttonText = d->ui.buttonPath->text();
412
d->ui.buttonPath->setText(tr("Done"));
413
d->ui.buttonPath->setEnabled(true);
414
d->ui.labelPath->setText(tr("Select one or more connected edges in the 3d view and press 'Done'"));
415
d->ui.labelPath->setEnabled(true);
417
Gui::Selection().clearSelection();
418
Gui::Selection().addSelectionGate(new Private::EdgeSelection());
421
QList<QWidget*> c = this->findChildren<QWidget*>();
423
it->setEnabled(true);
424
d->ui.buttonPath->setText(d->buttonText);
425
d->ui.labelPath->clear();
426
Gui::Selection().rmvSelectionGate();
428
Gui::SelectionFilter edgeFilter ("SELECT Part::Feature SUBELEMENT Edge COUNT 1..");
429
Gui::SelectionFilter partFilter ("SELECT Part::Feature COUNT 1");
430
bool matchEdge = edgeFilter.match();
431
bool matchPart = partFilter.match();
434
const std::vector<Gui::SelectionObject>& result = edgeFilter.Result[0];
435
if (!isPathValid(result.front())) {
436
QMessageBox::critical(this, tr("Sweep path"), tr("The selected sweep path is invalid."));
437
Gui::Selection().clearSelection();
440
else if (matchPart) {
442
const std::vector<Gui::SelectionObject>& result = partFilter.Result[0];
443
if (!isPathValid(result.front())) {
444
QMessageBox::critical(this, tr("Sweep path"), tr("The selected sweep path is invalid."));
445
Gui::Selection().clearSelection();
451
void SweepWidget::changeEvent(QEvent *e)
453
QWidget::changeEvent(e);
454
if (e->type() == QEvent::LanguageChange) {
455
d->ui.retranslateUi(this);
456
d->ui.selector->setAvailableLabel(tr("Vertex/Wire"));
457
d->ui.selector->setSelectedLabel(tr("Sweep"));
464
TaskSweep::TaskSweep() : label(nullptr)
466
widget = new SweepWidget();
467
addTaskBox(Gui::BitmapFactory().pixmap("Part_Sweep"), widget);
470
TaskSweep::~TaskSweep()
475
void TaskSweep::open()
479
void TaskSweep::clicked(int id)
481
if (id == QDialogButtonBox::Help) {
482
QString help = QApplication::translate("PartGui::TaskSweep",
483
"Select one or more profiles and select an edge or wire\n"
484
"in the 3D view for the sweep path.");
486
label = new Gui::StatusWidget(widget);
487
label->setStatusText(help);
491
QTimer::singleShot(3000, label, &Gui::StatusWidget::hide);
495
bool TaskSweep::accept()
497
return widget->accept();
500
bool TaskSweep::reject()
502
return widget->reject();
505
#include "moc_TaskSweep.cpp"