FreeCAD

Форк
0
/
TaskFaceAppearances.cpp 
511 строк · 17.7 Кб
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 <sstream>
27
# include <QFontMetrics>
28
# include <QPointer>
29
# include <QSet>
30
# include <BRep_Tool.hxx>
31
# include <BRepGProp.hxx>
32
# include <gp_Pnt.hxx>
33
# include <GProp_GProps.hxx>
34
# include <TopExp_Explorer.hxx>
35
# include <TopoDS.hxx>
36
# include <TopTools_IndexedMapOfShape.hxx>
37
# include <Inventor/SoPickedPoint.h>
38
# include <Inventor/actions/SoRayPickAction.h>
39
# include <Inventor/actions/SoSearchAction.h>
40
# include <Inventor/details/SoFaceDetail.h>
41
# include <Inventor/events/SoMouseButtonEvent.h>
42
# include <Inventor/nodes/SoCamera.h>
43
# include <Inventor/nodes/SoSeparator.h>
44
#endif
45

46
#include <App/Document.h>
47
#include <Gui/Application.h>
48
#include <Gui/Control.h>
49
#include <Gui/DlgMaterialPropertiesImp.h>
50
#include <Gui/Document.h>
51
#include <Gui/MainWindow.h>
52
#include <Gui/Selection.h>
53
#include <Gui/Tools.h>
54
#include <Gui/Utilities.h>
55
#include <Gui/View3DInventor.h>
56
#include <Gui/View3DInventorViewer.h>
57

58
#include <Mod/Material/Gui/MaterialTreeWidget.h>
59

60
#include "TaskFaceAppearances.h"
61
#include "ui_TaskFaceAppearances.h"
62
#include "SoBrepFaceSet.h"
63
#include "ViewProviderExt.h"
64

65

66
using namespace PartGui;
67
namespace sp = std::placeholders;
68

69
namespace PartGui {
70
    class FaceSelection : public Gui::SelectionFilterGate
71
    {
72
        const App::DocumentObject* object;
73
    public:
74
        explicit FaceSelection(const App::DocumentObject* obj)
75
            : Gui::SelectionFilterGate(), object(obj)
76
        {
77
        }
78
        bool allow(App::Document* /*pDoc*/, App::DocumentObject* pObj, const char* sSubName) override
79
        {
80
            if (pObj != this->object)
81
                return false;
82
            if (!sSubName || sSubName[0] == '\0')
83
                return false;
84
            std::string element(sSubName);
85
            return element.substr(0, 4) == "Face";
86
        }
87
    };
88
}
89

90
class FaceAppearances::Private
91
{
92
public:
93
    using Connection = boost::signals2::connection;
94
    Ui_TaskFaceAppearances* ui;
95
    QPointer<Gui::View3DInventorViewer> view;
96
    ViewProviderPartExt* vp;
97
    App::DocumentObject* obj;
98
    Gui::Document* doc;
99
    std::vector<App::Material> perface;
100
    QSet<int> index;
101
    bool boxSelection;
102
    Connection connectDelDoc;
103
    Connection connectDelObj;
104
    Connection connectUndoDoc;
105

106
    explicit Private(ViewProviderPartExt* vp) : ui(new Ui_TaskFaceAppearances()), view(nullptr), vp(vp)
107
    {
108
        obj = vp->getObject();
109
        doc = Gui::Application::Instance->getDocument(obj->getDocument());
110

111
        // build up map edge->face
112
        TopTools_IndexedMapOfShape mapOfShape;
113
        TopExp_Explorer xp(static_cast<Part::Feature*>(obj)->Shape.getValue(), TopAbs_FACE);
114
        while (xp.More()) {
115
            mapOfShape.Add(xp.Current());
116
            xp.Next();
117
        }
118

119
        std::vector<App::Material> current = vp->ShapeAppearance.getValues();
120
        perface = current;
121
        perface.resize(mapOfShape.Extent(), perface.front());
122

123
        boxSelection = false;
124
    }
125
    ~Private()
126
    {
127
        delete ui;
128
    }
129
    bool isVisibleFace(int faceIndex, const SbVec2f& pos, Gui::View3DInventorViewer* viewer)
130
    {
131
        SoSeparator* root = new SoSeparator;
132
        root->ref();
133
        root->addChild(viewer->getSoRenderManager()->getCamera());
134
        root->addChild(vp->getRoot());
135

136
        SoSearchAction searchAction;
137
        searchAction.setType(PartGui::SoBrepFaceSet::getClassTypeId());
138
        searchAction.setInterest(SoSearchAction::FIRST);
139
        searchAction.apply(root);
140
        SoPath* selectionPath = searchAction.getPath();
141

142
        SoRayPickAction rp(viewer->getSoRenderManager()->getViewportRegion());
143
        rp.setNormalizedPoint(pos);
144
        rp.apply(selectionPath);
145
        root->unref();
146

147
        SoPickedPoint* pick = rp.getPickedPoint();
148
        if (pick) {
149
            const SoDetail* detail = pick->getDetail();
150
            if (detail && detail->isOfType(SoFaceDetail::getClassTypeId())) {
151
                int index = static_cast<const SoFaceDetail*>(detail)->getPartIndex();
152
                if (faceIndex != index)
153
                    return false;
154
                SbVec3f dir = viewer->getViewDirection();
155
                const SbVec3f& nor = pick->getNormal();
156
                if (dir.dot(nor) > 0)
157
                    return false; // bottom side points to user
158
                return true;
159
            }
160
        }
161

162
        return false;
163
    }
164
    void addFacesToSelection(Gui::View3DInventorViewer* /*viewer*/,
165
                             const Gui::ViewVolumeProjection& proj,
166
                             const Base::Polygon2d& polygon,
167
                             const TopoDS_Shape& shape)
168
    {
169
        try {
170
            TopTools_IndexedMapOfShape M;
171

172
            TopExp_Explorer xp_face(shape, TopAbs_FACE);
173
            while (xp_face.More()) {
174
                M.Add(xp_face.Current());
175
                xp_face.Next();
176
            }
177

178
            App::Document* appdoc = doc->getDocument();
179
            for (Standard_Integer k = 1; k <= M.Extent(); k++) {
180
                const TopoDS_Shape& face = M(k);
181

182
                TopExp_Explorer xp_vertex(face, TopAbs_VERTEX);
183
                while (xp_vertex.More()) {
184
                    gp_Pnt p = BRep_Tool::Pnt(TopoDS::Vertex(xp_vertex.Current()));
185
                    Base::Vector3d pt2d;
186
                    pt2d = proj(Base::Vector3d(p.X(), p.Y(), p.Z()));
187
                    if (polygon.Contains(Base::Vector2d(pt2d.x, pt2d.y))) {
188
                        std::stringstream str;
189
                        str << "Face" << k;
190
                        Gui::Selection().addSelection(appdoc->getName(), obj->getNameInDocument(), str.str().c_str());
191
                        break;
192
                    }
193
                    xp_vertex.Next();
194
                }
195
            }
196
        }
197
        catch (...) {
198
        }
199
    }
200
    static void selectionCallback(void* ud, SoEventCallback* cb)
201
    {
202
        Gui::View3DInventorViewer* view = static_cast<Gui::View3DInventorViewer*>(cb->getUserData());
203
        view->removeEventCallback(SoMouseButtonEvent::getClassTypeId(), selectionCallback, ud);
204
        view->setSelectionEnabled(true);
205

206
        std::vector<SbVec2f> picked = view->getGLPolygon();
207
        SoCamera* cam = view->getSoRenderManager()->getCamera();
208
        SbViewVolume vv = cam->getViewVolume();
209
        Gui::ViewVolumeProjection proj(vv);
210
        Base::Polygon2d polygon;
211
        if (picked.size() == 2) {
212
            SbVec2f pt1 = picked[0];
213
            SbVec2f pt2 = picked[1];
214
            polygon.Add(Base::Vector2d(pt1[0], pt1[1]));
215
            polygon.Add(Base::Vector2d(pt1[0], pt2[1]));
216
            polygon.Add(Base::Vector2d(pt2[0], pt2[1]));
217
            polygon.Add(Base::Vector2d(pt2[0], pt1[1]));
218
        }
219
        else {
220
            for (const auto& it : picked)
221
                polygon.Add(Base::Vector2d(it[0], it[1]));
222
        }
223

224
        FaceAppearances* self = static_cast<FaceAppearances*>(ud);
225
        self->d->view = nullptr;
226
        if (self->d->obj && self->d->obj->isDerivedFrom<Part::Feature>()) {
227
            cb->setHandled();
228
            const TopoDS_Shape& shape = static_cast<Part::Feature*>(self->d->obj)->Shape.getValue();
229
            self->d->boxSelection = true;
230
            self->d->addFacesToSelection(view, proj, polygon, shape);
231
            self->d->boxSelection = false;
232
            self->d->ui->boxSelection->setChecked(false);
233
            self->updatePanel();
234
            view->redraw();
235
        }
236
    }
237
};
238

239
/* TRANSLATOR PartGui::TaskFaceAppearances */
240

241
FaceAppearances::FaceAppearances(ViewProviderPartExt* vp, QWidget* parent)
242
    : d(new Private(vp))
243
{
244
    Q_UNUSED(parent);
245
    d->ui->setupUi(this);
246
    setupConnections();
247

248
    d->ui->groupBox->setTitle(QString::fromUtf8(vp->getObject()->Label.getValue()));
249
    d->ui->buttonCustomAppearance->setDisabled(true);
250

251
    FaceSelection* gate = new FaceSelection(d->vp->getObject());
252
    Gui::Selection().addSelectionGate(gate);
253

254
    //NOLINTBEGIN
255
    d->connectDelDoc = Gui::Application::Instance->signalDeleteDocument.connect(std::bind
256
        (&FaceAppearances::slotDeleteDocument, this, sp::_1));
257
    d->connectDelObj = Gui::Application::Instance->signalDeletedObject.connect(std::bind
258
        (&FaceAppearances::slotDeleteObject, this, sp::_1));
259
    d->connectUndoDoc = d->doc->signalUndoDocument.connect(std::bind
260
        (&FaceAppearances::slotUndoDocument, this, sp::_1));
261
    //NOLINTEND
262
}
263

264
FaceAppearances::~FaceAppearances()
265
{
266
    if (d->view) {
267
        d->view->stopSelection();
268
        d->view->removeEventCallback(SoMouseButtonEvent::getClassTypeId(),
269
            Private::selectionCallback, this);
270
        d->view->setSelectionEnabled(true);
271
    }
272
    Gui::Selection().rmvSelectionGate();
273
    d->connectDelDoc.disconnect();
274
    d->connectDelObj.disconnect();
275
    d->connectUndoDoc.disconnect();
276
    delete d;
277
}
278

279
void FaceAppearances::setupConnections()
280
{
281
    // clang-format off
282
    connect(d->ui->defaultButton, &QPushButton::clicked,
283
            this, &FaceAppearances::onDefaultButtonClicked);
284
    connect(d->ui->boxSelection, &QPushButton::toggled,
285
            this, &FaceAppearances::onBoxSelectionToggled);
286
    connect(d->ui->widgetMaterial, &MatGui::MaterialTreeWidget::materialSelected,
287
            this, &FaceAppearances::onMaterialSelected);
288
    connect(d->ui->buttonCustomAppearance, &QPushButton::clicked,
289
            this, &FaceAppearances::onButtonCustomAppearanceClicked);
290
    // clang-format on
291
}
292

293
void FaceAppearances::slotUndoDocument(const Gui::Document& Doc)
294
{
295
    if (d->doc == &Doc) {
296
        d->doc->resetEdit();
297
        Gui::Control().closeDialog();
298
    }
299
}
300

301
void FaceAppearances::slotDeleteDocument(const Gui::Document& Doc)
302
{
303
    if (d->doc == &Doc)
304
        Gui::Control().closeDialog();
305
}
306

307
void FaceAppearances::slotDeleteObject(const Gui::ViewProvider& obj)
308
{
309
    if (d->vp == &obj)
310
        Gui::Control().closeDialog();
311
}
312

313
void FaceAppearances::onBoxSelectionToggled(bool checked)
314
{
315
    Gui::View3DInventor* view = qobject_cast<Gui::View3DInventor*>(Gui::getMainWindow()->activeWindow());
316
    // toggle the button state and feature
317
    d->boxSelection = checked;
318
    if (!checked) {
319
        // end box selection mode
320
        if (view)
321
            view->getViewer()->stopSelection();
322
    }
323

324
    if (view && checked) {
325
        Gui::View3DInventorViewer* viewer = view->getViewer();
326
        if (!viewer->isSelecting()) {
327
            viewer->startSelection(Gui::View3DInventorViewer::Rubberband);
328
            viewer->addEventCallback(SoMouseButtonEvent::getClassTypeId(), Private::selectionCallback, this);
329
            // avoid that the selection node handles the event otherwise the callback function won't be
330
            // called immediately
331
            viewer->setSelectionEnabled(false);
332
            d->view = viewer;
333
        }
334
    }
335
}
336

337
void FaceAppearances::onDefaultButtonClicked()
338
{
339
    std::fill(d->perface.begin(), d->perface.end(), d->vp->ShapeAppearance[0]);
340
    d->vp->ShapeAppearance.setValues(d->perface);
341
}
342

343
void FaceAppearances::onMaterialSelected(const std::shared_ptr<Materials::Material>& material)
344
{
345
    if (!d->index.isEmpty()) {
346
        for (int it : d->index) {
347
            d->perface[it] = material->getMaterialAppearance();
348
        }
349
        d->vp->ShapeAppearance.setValues(d->perface);
350
        // new color has been applied, unselect so that users can see this
351
        onSelectionChanged(Gui::SelectionChanges::ClrSelection);
352
        Gui::Selection().clearSelection();
353
    }
354
}
355

356
void FaceAppearances::onSelectionChanged(const Gui::SelectionChanges& msg)
357
{
358
    // no object selected in the combobox or no sub-element was selected
359
    if (!msg.pSubName)
360
        return;
361
    bool selection_changed = false;
362
    if (msg.Type == Gui::SelectionChanges::AddSelection) {
363
        // when adding a sub-element to the selection check
364
        // whether this is the currently handled object
365
        App::Document* doc = d->obj->getDocument();
366
        std::string docname = doc->getName();
367
        std::string objname = d->obj->getNameInDocument();
368
        if (docname == msg.pDocName && objname == msg.pObjectName) {
369
            int index = std::atoi(msg.pSubName + 4) - 1;
370
            d->index.insert(index);
371
            const App::Color& faceColor = d->perface[index].diffuseColor;
372
            QColor color;
373
            // alpha of App::Color is contrary to the one of QColor
374
            color.setRgbF(faceColor.r, faceColor.g, faceColor.b, (1.0 - faceColor.a));
375
            selection_changed = true;
376
        }
377
    }
378
    else if (msg.Type == Gui::SelectionChanges::RmvSelection) {
379
        App::Document* doc = d->obj->getDocument();
380
        std::string docname = doc->getName();
381
        std::string objname = d->obj->getNameInDocument();
382
        if (docname == msg.pDocName && objname == msg.pObjectName) {
383
            int index = std::atoi(msg.pSubName + 4) - 1;
384
            d->index.remove(index);
385
            selection_changed = true;
386
        }
387
    }
388
    else if (msg.Type == Gui::SelectionChanges::ClrSelection) {
389
        d->index.clear();
390
        selection_changed = true;
391
    }
392

393
    if (selection_changed && !d->boxSelection) {
394
        updatePanel();
395
    }
396
}
397

398
void FaceAppearances::updatePanel()
399
{
400
    QString faces = QString::fromLatin1("[");
401
    int size = d->index.size();
402
    for (int it : d->index) {
403
        faces += QString::number(it + 1);
404
        if (--size > 0)
405
            faces += QString::fromLatin1(",");
406
    }
407
    faces += QString::fromLatin1("]");
408

409
    int maxWidth = d->ui->labelElement->width();
410
    QFontMetrics fm(d->ui->labelElement->font());
411
    if (Gui::QtTools::horizontalAdvance(fm, faces) > maxWidth) {
412
        faces = fm.elidedText(faces, Qt::ElideMiddle, maxWidth);
413
    }
414

415
    d->ui->labelElement->setText(faces);
416
    d->ui->buttonCustomAppearance->setDisabled(d->index.isEmpty());
417
}
418

419
int FaceAppearances::getFirstIndex() const
420
{
421
    if (!d->index.isEmpty()) {
422
        return *(d->index.begin());
423
    }
424

425
    return 0;
426
}
427

428
/**
429
 * Opens a dialog that allows to modify the 'ShapeMaterial' property of all selected view providers.
430
 */
431
void FaceAppearances::onButtonCustomAppearanceClicked()
432
{
433
    Gui::Dialog::DlgMaterialPropertiesImp dlg(this);
434
    App::Material mat = d->perface[getFirstIndex()];
435
    dlg.setCustomMaterial(mat);
436
    dlg.setDefaultMaterial(mat);
437
    dlg.exec();
438

439
    // Set the face appearance
440
    if (!d->index.isEmpty()) {
441
        for (int it : d->index) {
442
            d->perface[it] = dlg.getCustomMaterial();
443
        }
444
        d->vp->ShapeAppearance.setValues(d->perface);
445
        // new color has been applied, unselect so that users can see this
446
        onSelectionChanged(Gui::SelectionChanges::ClrSelection);
447
        Gui::Selection().clearSelection();
448
    }
449
}
450

451
void FaceAppearances::open()
452
{
453
    Gui::Document* doc = Gui::Application::Instance->getDocument(d->vp->getObject()->getDocument());
454
    doc->openCommand(QT_TRANSLATE_NOOP("Command", "Change face colors"));
455
}
456

457
bool FaceAppearances::accept()
458
{
459
    Gui::Document* doc = Gui::Application::Instance->getDocument(d->vp->getObject()->getDocument());
460
    doc->commitCommand();
461
    doc->resetEdit();
462
    return true;
463
}
464

465
bool FaceAppearances::reject()
466
{
467
    Gui::Document* doc = Gui::Application::Instance->getDocument(d->vp->getObject()->getDocument());
468
    doc->abortCommand();
469
    doc->resetEdit();
470
    return true;
471
}
472

473
void FaceAppearances::changeEvent(QEvent* e)
474
{
475
    QWidget::changeEvent(e);
476
    if (e->type() == QEvent::LanguageChange) {
477
        d->ui->retranslateUi(this);
478
    }
479
}
480

481

482
/* TRANSLATOR PartGui::TaskFaceAppearances */
483

484
TaskFaceAppearances::TaskFaceAppearances(ViewProviderPartExt* vp)
485
{
486
    widget = new FaceAppearances(vp);
487
    addTaskBox(widget);
488
}
489

490
TaskFaceAppearances::~TaskFaceAppearances() = default;
491

492
void TaskFaceAppearances::open()
493
{
494
    widget->open();
495
}
496

497
void TaskFaceAppearances::clicked(int)
498
{
499
}
500

501
bool TaskFaceAppearances::accept()
502
{
503
    return widget->accept();
504
}
505

506
bool TaskFaceAppearances::reject()
507
{
508
    return widget->reject();
509
}
510

511
#include "moc_TaskFaceAppearances.cpp"
512

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

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

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

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