FreeCAD

Форк
0
/
TaskSweep.cpp 
505 строк · 17.6 Кб
1
/***************************************************************************
2
 *   Copyright (c) 2011 Werner Mayer <wmayer[at]users.sourceforge.net>     *
3
 *                                                                         *
4
 *   This file is part of the FreeCAD CAx development system.              *
5
 *                                                                         *
6
 *   This library is free software; you can redistribute it and/or         *
7
 *   modify it under the terms of the GNU Library General Public           *
8
 *   License as published by the Free Software Foundation; either          *
9
 *   version 2 of the License, or (at your option) any later version.      *
10
 *                                                                         *
11
 *   This library  is distributed in the hope that it will be useful,      *
12
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
13
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
14
 *   GNU Library General Public License for more details.                  *
15
 *                                                                         *
16
 *   You should have received a copy of the GNU Library General Public     *
17
 *   License along with this library; see the file COPYING.LIB. If not,    *
18
 *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
19
 *   Suite 330, Boston, MA  02111-1307, USA                                *
20
 *                                                                         *
21
 ***************************************************************************/
22

23
#include "PreCompiled.h"
24

25
#ifndef _PreComp_
26
# include <QApplication>
27
# include <QMessageBox>
28
# include <QTextStream>
29
# include <QTimer>
30
# include <QTreeWidget>
31
# include <BRepBuilderAPI_MakeWire.hxx>
32
# include <Precision.hxx>
33
# include <ShapeAnalysis_FreeBounds.hxx>
34
# include <TopExp_Explorer.hxx>
35
# include <TopoDS.hxx>
36
# include <TopoDS_Iterator.hxx>
37
# include <TopTools_HSequenceOfShape.hxx>
38
#endif
39

40
#include <App/Application.h>
41
#include <App/Document.h>
42
#include <App/DocumentObject.h>
43
#include <App/Link.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>
54

55
#include "TaskSweep.h"
56
#include "ui_TaskSweep.h"
57

58

59
using namespace PartGui;
60

61
class SweepWidget::Private
62
{
63
public:
64
    Ui_TaskSweep ui;
65
    QString buttonText;
66
    std::string document;
67
    Private() = default;
68
    ~Private() = default;
69

70
    class EdgeSelection : public Gui::SelectionFilterGate
71
    {
72
    public:
73
        EdgeSelection()
74
            : Gui::SelectionFilterGate(nullPointer())
75
        {
76
        }
77
        bool allow(App::Document* /*pDoc*/, App::DocumentObject*pObj, const char*sSubName) override
78
        {
79
            if (!sSubName || sSubName[0] == '\0') {
80
                // If selecting again the same edge the passed sub-element is empty. If the whole
81
                // shape is an edge or wire we can use it completely.
82
                Part::TopoShape topoShape = Part::Feature::getTopoShape(pObj);
83
                if (topoShape.isNull()) {
84
                    return false;
85
                }
86
                const TopoDS_Shape shape = topoShape.getShape();
87
                if (!shape.IsNull()) {
88
                    // a single edge
89
                    if (shape.ShapeType() == TopAbs_EDGE) {
90
                        return true;
91
                    }
92
                    // a single wire
93
                    if (shape.ShapeType() == TopAbs_WIRE) {
94
                        return true;
95
                    }
96
                    // a compound of only edges or wires
97
                    if (shape.ShapeType() == TopAbs_COMPOUND) {
98
                        TopoDS_Iterator it(shape);
99
                        for (; it.More(); it.Next()) {
100
                            if (it.Value().IsNull())
101
                                return false;
102
                            if ((it.Value().ShapeType() != TopAbs_EDGE) &&
103
                                    (it.Value().ShapeType() != TopAbs_WIRE))
104
                                return false;
105
                        }
106

107
                        return true;
108
                    }
109
                }
110
            }
111
            else {
112
                std::string element(sSubName);
113
                return element.substr(0,4) == "Edge";
114
            }
115

116
            return false;
117
        }
118
    };
119
};
120

121
/* TRANSLATOR PartGui::SweepWidget */
122

123
SweepWidget::SweepWidget(QWidget* parent)
124
  : d(new Private())
125
{
126
    Q_UNUSED(parent);
127
    Gui::Command::runCommand(Gui::Command::App, "from FreeCAD import Base");
128
    Gui::Command::runCommand(Gui::Command::App, "import Part");
129

130
    d->ui.setupUi(this);
131
    d->ui.selector->setAvailableLabel(tr("Available profiles"));
132
    d->ui.selector->setSelectedLabel(tr("Selected profiles"));
133
    d->ui.labelPath->clear();
134

135
    // clang-format off
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);
142
    // clang-format on
143

144
    findShapes();
145
}
146

147
SweepWidget::~SweepWidget()
148
{
149
    delete d;
150
    Gui::Selection().rmvSelectionGate();
151
}
152

153
void SweepWidget::findShapes()
154
{
155
    App::Document* activeDoc = App::GetApplication().getActiveDocument();
156
    Gui::Document* activeGui = Gui::Application::Instance->getDocument(activeDoc);
157
    if (!activeGui)
158
        return;
159
    d->document = activeDoc->getName();
160

161
    std::vector<App::DocumentObject*> objs = activeDoc->getObjectsOfType<App::DocumentObject>();
162

163
    for (auto obj : objs) {
164
        Part::TopoShape topoShape = Part::Feature::getTopoShape(obj);
165
        if (topoShape.isNull()) {
166
            continue;
167
        }
168
        TopoDS_Shape shape = topoShape.getShape();
169
        if (shape.IsNull()) continue;
170

171
        // also allow compounds with a single face, wire or vertex or
172
        // if there are only edges building one wire
173
        if (shape.ShapeType() == TopAbs_COMPOUND) {
174
            Handle(TopTools_HSequenceOfShape) hEdges = new TopTools_HSequenceOfShape();
175
            Handle(TopTools_HSequenceOfShape) hWires = new TopTools_HSequenceOfShape();
176

177
            TopoDS_Iterator it(shape);
178
            int numChilds=0;
179
            TopoDS_Shape child;
180
            for (; it.More(); it.Next(), numChilds++) {
181
                if (!it.Value().IsNull()) {
182
                    child = it.Value();
183
                    if (child.ShapeType() == TopAbs_EDGE) {
184
                        hEdges->Append(child);
185
                    }
186
                }
187
            }
188

189
            // a single child
190
            if (numChilds == 1) {
191
                shape = child;
192
            }
193
            // or all children are edges
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);
199
            }
200
        }
201

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());
208

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);
216
        }
217
    }
218
}
219

220
bool SweepWidget::isPathValid(const Gui::SelectionObject& sel) const
221
{
222
    const App::DocumentObject* path = sel.getObject();
223
    const std::vector<std::string>& sub = sel.getSubNames();
224

225
    TopoDS_Shape pathShape;
226
    const Part::TopoShape& shape = Part::Feature::getTopoShape(path);
227
    if (shape.isNull()){
228
        return false;
229
    }
230
    if (!sub.empty()) {
231
        try {
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));
236
            }
237
            pathShape = mkWire.Wire();
238
        }
239
        catch (...) {
240
            return false;
241
        }
242
    }
243
    else if (shape.getShape().ShapeType() == TopAbs_EDGE) {
244
        pathShape = shape.getShape();
245
    }
246
    else if (shape.getShape().ShapeType() == TopAbs_WIRE) {
247
        BRepBuilderAPI_MakeWire mkWire(TopoDS::Wire(shape.getShape()));
248
        pathShape = mkWire.Wire();
249
    }
250
    else if (shape.getShape().ShapeType() == TopAbs_COMPOUND) {
251
        try {
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)) {
256
                    return false;
257
                }
258
            }
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());
263

264
            ShapeAnalysis_FreeBounds::ConnectEdgesToWires(hEdges, Precision::Confusion(), Standard_True, hWires);
265
            int len = hWires->Length();
266
            if (len != 1)
267
                return false;
268
            pathShape = hWires->Value(1);
269
        }
270
        catch (...) {
271
            return false;
272
        }
273
    }
274

275
    return (!pathShape.IsNull());
276
}
277

278
bool SweepWidget::accept()
279
{
280
    if (d->ui.buttonPath->isChecked())
281
        return false;
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;
288

289
    bool ok = true;
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 /*need element*/));
300
            }
301
            for (const auto & it : subShapes) {
302
                TopoDS_Shape dsShape = it.getShape();
303
                if (dsShape.IsNull() || dsShape.ShapeType() != TopAbs_EDGE) { //only edge selection allowed
304
                    ok = false;
305
                }
306
            }
307
        } else { //could be not a part::feature or app:link to non-part::feature or app::part without a visible part::feature
308
            ok = false;
309
        }
310

311
    } else { //not just one object selected
312
        ok = false;
313
    }
314

315
    QString list, solid, frenet;
316
    if (d->ui.checkSolid->isChecked())
317
        solid = QString::fromLatin1("True");
318
    else
319
        solid = QString::fromLatin1("False");
320

321
    if (d->ui.checkFrenet->isChecked())
322
        frenet = QString::fromLatin1("True");
323
    else
324
        frenet = QString::fromLatin1("False");
325

326
    QTextStream str(&list);
327

328
    int count = d->ui.selector->selectedTreeWidget()->topLevelItemCount();
329
    if (count < 1) {
330
        QMessageBox::critical(this, tr("Too few elements"), tr("At least one edge or wire is required."));
331
        return false;
332
    }
333
    if (!ok) {
334
        QMessageBox::critical(this, tr("Invalid selection"), tr("Select one or more edges from a single object."));
335
        return false;
336
    }
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())));
343
            return false;
344
        }
345
        str << "App.getDocument('" << d->document.c_str() << "')." << name << ", ";
346
    }
347

348
    try {
349
        Gui::WaitCursor wc;
350
        QString cmd;
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"
357
            )
358
            .arg(list,
359
                 QLatin1String(selection.c_str()),
360
                 solid,
361
                 frenet,
362
                 QString::fromLatin1(d->document.c_str()));
363

364
        Gui::Document* doc = Gui::Application::Instance->getDocument(d->document.c_str());
365
        if (!doc)
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();
373
            doc->abortCommand();
374
            throw Base::RuntimeError(msg);
375
        }
376
        doc->commitCommand();
377
    }
378
    catch (const Base::Exception& e) {
379
        QMessageBox::warning(this, tr("Input error"), QCoreApplication::translate("Exception", e.what()));
380
        return false;
381
    }
382

383
    return true;
384
}
385

386
bool SweepWidget::reject()
387
{
388
    if (d->ui.buttonPath->isChecked())
389
        return false;
390
    return true;
391
}
392

393
void SweepWidget::onCurrentItemChanged(QTreeWidgetItem* current, QTreeWidgetItem* previous)
394
{
395
    if (previous) {
396
        Gui::Selection().rmvSelection(d->document.c_str(),
397
            (const char*)previous->data(0,Qt::UserRole).toByteArray());
398
    }
399
    if (current) {
400
        Gui::Selection().addSelection(d->document.c_str(),
401
            (const char*)current->data(0,Qt::UserRole).toByteArray());
402
    }
403
}
404

405
void SweepWidget::onButtonPathToggled(bool on)
406
{
407
    if (on) {
408
        QList<QWidget*> c = this->findChildren<QWidget*>();
409
        for (auto it : c)
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);
416

417
        Gui::Selection().clearSelection();
418
        Gui::Selection().addSelectionGate(new Private::EdgeSelection());
419
    }
420
    else {
421
        QList<QWidget*> c = this->findChildren<QWidget*>();
422
        for (auto it : c)
423
            it->setEnabled(true);
424
        d->ui.buttonPath->setText(d->buttonText);
425
        d->ui.labelPath->clear();
426
        Gui::Selection().rmvSelectionGate();
427

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();
432
        if (matchEdge) {
433
            // check if path is valid
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();
438
            }
439
        }
440
        else if (matchPart) {
441
            // check if path is valid
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();
446
            }
447
        }
448
    }
449
}
450

451
void SweepWidget::changeEvent(QEvent *e)
452
{
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"));
458
    }
459
}
460

461

462
/* TRANSLATOR PartGui::TaskSweep */
463

464
TaskSweep::TaskSweep() : label(nullptr)
465
{
466
    widget = new SweepWidget();
467
    addTaskBox(Gui::BitmapFactory().pixmap("Part_Sweep"), widget);
468
}
469

470
TaskSweep::~TaskSweep()
471
{
472
    delete label;
473
}
474

475
void TaskSweep::open()
476
{
477
}
478

479
void TaskSweep::clicked(int id)
480
{
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.");
485
        if (!label) {
486
            label = new Gui::StatusWidget(widget);
487
            label->setStatusText(help);
488
        }
489

490
        label->show();
491
        QTimer::singleShot(3000, label, &Gui::StatusWidget::hide);
492
    }
493
}
494

495
bool TaskSweep::accept()
496
{
497
    return widget->accept();
498
}
499

500
bool TaskSweep::reject()
501
{
502
    return widget->reject();
503
}
504

505
#include "moc_TaskSweep.cpp"
506

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.