FreeCAD

Форк
0
/
ViewProvider.cpp 
2636 строк · 95.5 Кб
1
/***************************************************************************
2
 *   Copyright (c) 2004 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
#ifndef _PreComp_
25
#include <cstdlib>
26
#include <QAction>
27
#include <QMenu>
28

29
#include <Inventor/SbBox2s.h>
30
#include <Inventor/SbLine.h>
31
#include <Inventor/SbPlane.h>
32
#include <Inventor/SoPickedPoint.h>
33
#include <Inventor/actions/SoToVRML2Action.h>
34
#include <Inventor/details/SoFaceDetail.h>
35
#include <Inventor/events/SoMouseButtonEvent.h>
36
#include <Inventor/nodes/SoBaseColor.h>
37
#include <Inventor/nodes/SoCoordinate3.h>
38
#include <Inventor/nodes/SoDrawStyle.h>
39
#include <Inventor/nodes/SoLightModel.h>
40
#include <Inventor/nodes/SoIndexedFaceSet.h>
41
#include <Inventor/nodes/SoIndexedLineSet.h>
42
#include <Inventor/nodes/SoMaterial.h>
43
#include <Inventor/nodes/SoMaterialBinding.h>
44
#include <Inventor/nodes/SoOrthographicCamera.h>
45
#include <Inventor/nodes/SoPerspectiveCamera.h>
46
#include <Inventor/nodes/SoPolygonOffset.h>
47
#include <Inventor/nodes/SoShapeHints.h>
48
#include <Inventor/nodes/SoSeparator.h>
49
#include <Inventor/nodes/SoTransform.h>
50
#include <Inventor/VRMLnodes/SoVRMLGroup.h>
51
#endif
52

53
#include <QFuture>
54
#include <QFutureWatcher>
55
#include <QtConcurrentMap>
56

57
#include <App/Document.h>
58
#include <Base/Console.h>
59
#include <Base/Exception.h>
60
#include <Base/Sequencer.h>
61
#include <Base/Stream.h>
62
#include <Base/Tools.h>
63
#include <Base/ViewProj.h>
64
#include <Gui/ActionFunction.h>
65
#include <Gui/Application.h>
66
#include <Gui/BitmapFactory.h>
67
#include <Gui/Command.h>
68
#include <Gui/Document.h>
69
#include <Gui/Flag.h>
70
#include <Gui/Selection.h>
71
#include <Gui/SoFCDB.h>
72
#include <Gui/SoFCOffscreenRenderer.h>
73
#include <Gui/SoFCSelection.h>
74
#include <Gui/SoFCSelectionAction.h>
75
#include <Gui/Utilities.h>
76
#include <Gui/View3DInventorViewer.h>
77
#include <Gui/WaitCursor.h>
78
#include <Gui/Window.h>
79
#include <Mod/Mesh/App/Core/Algorithm.h>
80
#include <Mod/Mesh/App/Core/Grid.h>
81
#include <Mod/Mesh/App/Core/Iterator.h>
82
#include <Mod/Mesh/App/Core/MeshIO.h>
83
#include <Mod/Mesh/App/Core/Triangulation.h>
84
#include <Mod/Mesh/App/Core/Trim.h>
85
#include <Mod/Mesh/App/Core/Visitor.h>
86
#include <Mod/Mesh/App/MeshFeature.h>
87
#include <Mod/Mesh/Gui/ViewProviderMeshPy.h>
88
#include <zipios++/gzipoutputstream.h>
89

90
#include "SoFCIndexedFaceSet.h"
91
#include "SoFCMeshObject.h"
92
#include "ViewProvider.h"
93

94

95
using namespace MeshGui;
96
namespace sp = std::placeholders;
97

98
using Mesh::Feature;
99
using MeshCore::MeshFacet;
100
using MeshCore::MeshFacetIterator;
101
using MeshCore::MeshGeomFacet;
102
using MeshCore::MeshKernel;
103
using MeshCore::MeshPointIterator;
104

105
void ViewProviderMeshBuilder::buildNodes(const App::Property* prop,
106
                                         std::vector<SoNode*>& nodes) const
107
{
108
    SoCoordinate3* pcPointsCoord = nullptr;
109
    SoIndexedFaceSet* pcFaces = nullptr;
110

111
    if (nodes.empty()) {
112
        pcPointsCoord = new SoCoordinate3();
113
        nodes.push_back(pcPointsCoord);
114
        pcFaces = new SoIndexedFaceSet();
115
        nodes.push_back(pcFaces);
116
    }
117
    else if (nodes.size() == 2) {
118
        if (nodes[0]->getTypeId() == SoCoordinate3::getClassTypeId()) {
119
            pcPointsCoord = static_cast<SoCoordinate3*>(nodes[0]);
120
        }
121
        if (nodes[1]->getTypeId() == SoIndexedFaceSet::getClassTypeId()) {
122
            pcFaces = static_cast<SoIndexedFaceSet*>(nodes[1]);
123
        }
124
    }
125

126
    if (pcPointsCoord && pcFaces) {
127
        createMesh(prop, pcPointsCoord, pcFaces);
128
    }
129
}
130

131
void ViewProviderMeshBuilder::createMesh(const App::Property* prop,
132
                                         SoCoordinate3* coords,
133
                                         SoIndexedFaceSet* faces) const
134
{
135
    const Mesh::PropertyMeshKernel* mesh = static_cast<const Mesh::PropertyMeshKernel*>(prop);
136
    const MeshCore::MeshKernel& rcMesh = mesh->getValue().getKernel();
137
    createMesh(rcMesh, coords, faces);
138
}
139

140
void ViewProviderMeshBuilder::createMesh(const MeshCore::MeshKernel& kernel,
141
                                         SoCoordinate3* coords,
142
                                         SoIndexedFaceSet* faces) const
143
{
144

145
    // set the point coordinates
146
    const MeshCore::MeshPointArray& cP = kernel.GetPoints();
147
    coords->point.setNum(kernel.CountPoints());
148
    SbVec3f* verts = coords->point.startEditing();
149
    int i = 0;
150
    for (MeshCore::MeshPointArray::_TConstIterator it = cP.begin(); it != cP.end(); ++it, i++) {
151
        verts[i].setValue(it->x, it->y, it->z);
152
    }
153
    coords->point.finishEditing();
154

155
    // set the face indices
156
    int j = 0;
157
    const MeshCore::MeshFacetArray& cF = kernel.GetFacets();
158
    faces->coordIndex.setNum(4 * kernel.CountFacets());
159
    int32_t* indices = faces->coordIndex.startEditing();
160
    for (MeshCore::MeshFacetArray::_TConstIterator it = cF.begin(); it != cF.end(); ++it, j++) {
161
        for (int i = 0; i < 3; i++) {
162
            indices[4 * j + i] = it->_aulPoints[i];
163
        }
164
        indices[4 * j + 3] = SO_END_FACE_INDEX;
165
    }
166
    faces->coordIndex.finishEditing();
167
}
168

169
PROPERTY_SOURCE(MeshGui::ViewProviderExport, Gui::ViewProviderDocumentObject)
170

171
ViewProviderExport::ViewProviderExport() = default;
172

173
ViewProviderExport::~ViewProviderExport() = default;
174

175
std::vector<std::string> ViewProviderExport::getDisplayModes() const
176
{
177
    std::vector<std::string> mode;
178
    mode.emplace_back("");
179
    return mode;
180
}
181

182
const char* ViewProviderExport::getDefaultDisplayMode() const
183
{
184
    return "";
185
}
186

187
QIcon ViewProviderExport::getIcon() const
188
{
189
    // clang-format off
190
    const char * Mesh_Feature_xpm[] = {
191
        "22 22 6 1",
192
        ". c None",
193
        "# c #000000",
194
        "c c #ffff00",
195
        "a c #808080",
196
        "b c #c0c0c0",
197
        "f c #008000",
198
        ".............##.......",
199
        ".............###......",
200
        ".............#f##.....",
201
        ".#....####...#ff##....",
202
        ".##.##....#..#fff##...",
203
        ".###.........#ffff##..",
204
        ".####........#fffff##.",
205
        ".#####.......#ffffff##",
206
        ".............#########",
207
        ".####.................",
208
        "#abab##########.......",
209
        "#babababababab#.......",
210
        "#ababababababa#.......",
211
        "#babab################",
212
        "#abab##cccccccccccc##.",
213
        "#bab##cccccccccccc##..",
214
        "#ab##cccccccccccc##...",
215
        "#b##cccccccccccc##....",
216
        "###cccccccccccc##.....",
217
        "##cccccccccccc##......",
218
        "###############.......",
219
        "......................"};
220
    QPixmap px(Mesh_Feature_xpm);
221
    return px;
222
    // clang-format on
223
}
224

225
// ------------------------------------------------------
226

227
App::PropertyFloatConstraint::Constraints ViewProviderMesh::floatRange = {1.0F, 64.0F, 1.0F};
228
App::PropertyFloatConstraint::Constraints ViewProviderMesh::angleRange = {0.0F, 180.0F, 1.0F};
229
App::PropertyIntegerConstraint::Constraints ViewProviderMesh::intPercent = {0, 100, 5};
230
const char* ViewProviderMesh::LightingEnums[] = {"One side", "Two side", nullptr};
231

232
PROPERTY_SOURCE(MeshGui::ViewProviderMesh, Gui::ViewProviderGeometryObject)
233

234
ViewProviderMesh::ViewProviderMesh()
235
    : highlightMode {HighlighMode::None}
236
{
237
    // NOLINTBEGIN
238
    static const char* osgroup = "Object Style";
239

240
    ADD_PROPERTY_TYPE(LineTransparency, (0), osgroup, App::Prop_None, "Set line transparency.");
241
    LineTransparency.setConstraints(&intPercent);
242
    ADD_PROPERTY_TYPE(LineWidth, (1.0f), osgroup, App::Prop_None, "Set line width.");
243
    LineWidth.setConstraints(&floatRange);
244
    ADD_PROPERTY_TYPE(PointSize, (2.0f), osgroup, App::Prop_None, "Set point size.");
245
    PointSize.setConstraints(&floatRange);
246
    ADD_PROPERTY_TYPE(CreaseAngle, (0.0f), osgroup, App::Prop_None, "Set crease angle.");
247
    CreaseAngle.setConstraints(&angleRange);
248
    ADD_PROPERTY_TYPE(OpenEdges, (false), osgroup, App::Prop_None, "Set open edges.");
249
    ADD_PROPERTY_TYPE(Coloring, (false), osgroup, App::Prop_None, "Set coloring.");
250
    ADD_PROPERTY_TYPE(Lighting,
251
                      (1),
252
                      osgroup,
253
                      App::Prop_None,
254
                      "Set if the illumination comes from two sides\n or one side in the 3D view.");
255
    Lighting.setEnums(LightingEnums);
256
    ADD_PROPERTY_TYPE(LineColor, (0, 0, 0), osgroup, App::Prop_None, "Set line color.");
257

258
    // Create the selection node
259
    pcHighlight = Gui::ViewProviderBuilder::createSelection();
260
    pcHighlight->ref();
261
    if (pcHighlight->selectionMode.getValue() == Gui::SoFCSelection::SEL_OFF) {
262
        Selectable.setValue(false);
263
    }
264

265
    pcShapeGroup = new SoGroup();
266
    pcShapeGroup->ref();
267
    pcHighlight->addChild(pcShapeGroup);
268

269
    pOpenColor = new SoBaseColor();
270
    setOpenEdgeColorFrom(ShapeColor.getValue());
271
    pOpenColor->ref();
272

273
    pcLineStyle = new SoDrawStyle();
274
    pcLineStyle->ref();
275
    pcLineStyle->style = SoDrawStyle::LINES;
276
    pcLineStyle->lineWidth = LineWidth.getValue();
277

278
    pcPointStyle = new SoDrawStyle();
279
    pcPointStyle->ref();
280
    pcPointStyle->style = SoDrawStyle::POINTS;
281
    pcPointStyle->pointSize = PointSize.getValue();
282

283
    pShapeHints = new SoShapeHints;
284
    pShapeHints->shapeType = SoShapeHints::UNKNOWN_SHAPE_TYPE;
285
    pShapeHints->ref();
286

287
    pcMatBinding = new SoMaterialBinding;
288
    pcMatBinding->value = SoMaterialBinding::OVERALL;
289
    pcMatBinding->ref();
290

291
    pLineColor = new SoMaterial;
292
    pLineColor->ref();
293
    LineColor.touch();
294

295
    // read the correct shape color from the preferences
296
    Base::Reference<ParameterGrp> hGrp =
297
        Gui::WindowParameter::getDefaultParameter()->GetGroup("Mod/Mesh");
298

299
    // Mesh color
300
    App::Color color = ShapeColor.getValue();
301
    unsigned long current = color.getPackedValue();
302
    unsigned long setting = hGrp->GetUnsigned("MeshColor", current);
303
    if (current != setting) {
304
        color.setPackedValue((uint32_t)setting);
305
        ShapeColor.setValue(color);
306
    }
307
    Transparency.setValue(hGrp->GetInt("MeshTransparency", 0));
308

309
    // Line color
310
    color = LineColor.getValue();
311
    current = color.getPackedValue();
312
    setting = hGrp->GetUnsigned("LineColor", current);
313
    if (current != setting) {
314
        color.setPackedValue((uint32_t)setting);
315
        LineColor.setValue(color);
316
    }
317
    LineTransparency.setValue(hGrp->GetInt("LineTransparency", 0));
318

319
    bool twoside = hGrp->GetBool("TwoSideRendering", false);
320
    if (twoside) {
321
        Lighting.setValue(1);
322
    }
323
    else {
324
        Lighting.setValue((long)0);
325
    }
326

327
    bool normal_per_vertex = hGrp->GetBool("VertexPerNormals", false);
328
    if (normal_per_vertex) {
329
        double angle = hGrp->GetFloat("CreaseAngle", 0.0);
330
        CreaseAngle.setValue(angle);
331
    }
332

333
    if (hGrp->GetBool("ShowBoundingBox", false)) {
334
        SelectionStyle.setValue(1);
335
    }
336

337
    Coloring.setStatus(App::Property::Hidden, true);
338
    // NOLINTEND
339
}
340

341
ViewProviderMesh::~ViewProviderMesh()
342
{
343
    pcHighlight->unref();
344
    pcShapeGroup->unref();
345
    pOpenColor->unref();
346
    pcLineStyle->unref();
347
    pcPointStyle->unref();
348
    pShapeHints->unref();
349
    pcMatBinding->unref();
350
    pLineColor->unref();
351
}
352

353
void ViewProviderMesh::onChanged(const App::Property* prop)
354
{
355
    // we're going to change the number of colors to one
356
    if (prop == &ShapeColor || prop == &ShapeMaterial) {
357
        pcMatBinding->value = SoMaterialBinding::OVERALL;
358
    }
359
    if (prop == &LineTransparency) {
360
        float trans = LineTransparency.getValue() / 100.0F;
361
        pLineColor->transparency = trans;
362
    }
363
    else if (prop == &LineWidth) {
364
        pcLineStyle->lineWidth = LineWidth.getValue();
365
    }
366
    else if (prop == &PointSize) {
367
        pcPointStyle->pointSize = PointSize.getValue();
368
    }
369
    else if (prop == &CreaseAngle) {
370
        pShapeHints->creaseAngle = Base::toRadians<float>(CreaseAngle.getValue());
371
    }
372
    else if (prop == &OpenEdges) {
373
        showOpenEdges(OpenEdges.getValue());
374
    }
375
    else if (prop == &Lighting) {
376
        if (Lighting.getValue() == 0) {
377
            pShapeHints->vertexOrdering = SoShapeHints::UNKNOWN_ORDERING;
378
        }
379
        else {
380
            pShapeHints->vertexOrdering = SoShapeHints::COUNTERCLOCKWISE;
381
        }
382
    }
383
    else if (prop == &LineColor) {
384
        const App::Color& c = LineColor.getValue();
385
        pLineColor->diffuseColor.setValue(c.r, c.g, c.b);
386
    }
387
    else if (prop == &Coloring) {
388
        tryColorPerVertexOrFace(Coloring.getValue());
389
    }
390
    else if (prop == &SelectionStyle) {
391
        pcHighlight->style =
392
            SelectionStyle.getValue() ? Gui::SoFCSelection::BOX : Gui::SoFCSelection::EMISSIVE;
393
    }
394
    else {
395
        // Set the inverse color for open edges
396
        if (prop == &ShapeColor) {
397
            setOpenEdgeColorFrom(ShapeColor.getValue());
398
        }
399
        else if (prop == &ShapeMaterial) {
400
            setOpenEdgeColorFrom(ShapeMaterial.getValue().diffuseColor);
401
        }
402
    }
403

404
    ViewProviderGeometryObject::onChanged(prop);
405
}
406

407
void ViewProviderMesh::setOpenEdgeColorFrom(const App::Color& c)
408
{
409
    float r = 1.0F - c.r;
410
    r = r < 0.5F ? 0.0F : 1.0F;
411
    float g = 1.0F - c.g;
412
    g = g < 0.5F ? 0.0F : 1.0F;
413
    float b = 1.0F - c.b;
414
    b = b < 0.5F ? 0.0F : 1.0F;
415
    pOpenColor->rgb.setValue(r, g, b);
416
}
417

418
SoShape* ViewProviderMesh::getShapeNode() const
419
{
420
    return nullptr;
421
}
422

423
SoNode* ViewProviderMesh::getCoordNode() const
424
{
425
    return nullptr;
426
}
427

428
/**
429
 * Extracts the mesh data from the feature \a pcFeature and creates
430
 * an Inventor node \a SoNode with these data.
431
 */
432
void ViewProviderMesh::attach(App::DocumentObject* pcFeat)
433
{
434
    ViewProviderGeometryObject::attach(pcFeat);
435

436
    pcHighlight->objectName = pcFeat->getNameInDocument();
437
    pcHighlight->documentName = pcFeat->getDocument()->getName();
438
    pcHighlight->subElementName = "Main";
439

440
    // Note: Since for mesh data the SoFCSelection node has no SoSeparator but
441
    // an SoGroup as parent the EMISSIVE style if set has fundamentally no effect.
442
    // This behaviour is given due to the fact that SoFCSelection inherits from
443
    // SoGroup (formerly SoSeparator). If we wanted to enable emissive overlay for
444
    // highlighting or selection we would need an SoSeparator as parent node below.
445

446
    // faces
447
    SoGroup* pcFlatRoot = new SoGroup();
448
    pcFlatRoot->addChild(pShapeHints);
449
    pcFlatRoot->addChild(pcShapeMaterial);
450
    pcFlatRoot->addChild(pcMatBinding);
451
    pcFlatRoot->addChild(pcHighlight);
452
    addDisplayMaskMode(pcFlatRoot, "Shaded");
453

454
    // points
455
    SoGroup* pcPointRoot = new SoGroup();
456
    pcPointRoot->addChild(pcPointStyle);
457
    pcPointRoot->addChild(pShapeHints);
458
    pcPointRoot->addChild(pcShapeMaterial);
459
    pcPointRoot->addChild(pcMatBinding);
460
    pcPointRoot->addChild(pcHighlight);
461
    addDisplayMaskMode(pcPointRoot, "Point");
462

463
    // wires
464
    SoLightModel* pcLightModel = new SoLightModel();
465
    pcLightModel->model = SoLightModel::BASE_COLOR;
466
    SoGroup* pcWireRoot = new SoGroup();
467
    pcWireRoot->addChild(pcLineStyle);
468
    pcWireRoot->addChild(pcLightModel);
469
    SoMaterialBinding* binding = new SoMaterialBinding;
470
    binding->value = SoMaterialBinding::OVERALL;  // doesn't set several colors
471
    pcWireRoot->addChild(binding);
472
    pcWireRoot->addChild(pLineColor);
473
    pcWireRoot->addChild(pcHighlight);
474
    addDisplayMaskMode(pcWireRoot, "Wireframe");
475

476
    // faces+wires
477
    // Avoid any Z-buffer artifacts, so that the lines always
478
    // appear on top of the faces
479
    SoPolygonOffset* offset = new SoPolygonOffset();
480
    offset->styles = SoPolygonOffset::FILLED;
481
    offset->factor = 1.0F;
482
    offset->units = 1.0F;
483

484
    SoSeparator* pcWireSep = new SoSeparator();
485
    pcWireSep->addChild(pcLineStyle);
486
    pcWireSep->addChild(pcLightModel);
487
    pcWireSep->addChild(binding);
488
    pcWireSep->addChild(pLineColor);
489
    pcWireSep->addChild(pcHighlight);
490

491
    SoGroup* pcFlatWireRoot = new SoGroup();
492
    pcFlatWireRoot->addChild(pcWireSep);
493
    pcFlatWireRoot->addChild(offset);
494
    pcFlatWireRoot->addChild(pShapeHints);
495
    pcFlatWireRoot->addChild(pcShapeMaterial);
496
    pcFlatWireRoot->addChild(pcMatBinding);
497
    pcFlatWireRoot->addChild(pcShapeGroup);
498
    addDisplayMaskMode(pcFlatWireRoot, "Flat Lines");
499

500
    if (getColorProperty() || getMaterialProperty()) {
501
        Coloring.setStatus(App::Property::Hidden, false);
502
    }
503
}
504

505
void ViewProviderMesh::updateData(const App::Property* prop)
506
{
507
    Gui::ViewProviderGeometryObject::updateData(prop);
508

509
    if (prop->is<App::PropertyColorList>()) {
510
        Coloring.setStatus(App::Property::Hidden, false);
511
    }
512
    else if (prop->is<Mesh::PropertyMaterial>()) {
513
        Coloring.setStatus(App::Property::Hidden, false);
514
    }
515
}
516

517
QIcon ViewProviderMesh::getIcon() const
518
{
519
    static QIcon icon = Gui::BitmapFactory().pixmap("Mesh_Tree");
520
    return icon;
521
}
522

523
App::PropertyColorList* ViewProviderMesh::getColorProperty() const
524
{
525
    if (pcObject) {
526
        std::map<std::string, App::Property*> Map;
527
        pcObject->getPropertyMap(Map);
528
        for (const auto& it : Map) {
529
            Base::Type type = it.second->getTypeId();
530
            if (type == App::PropertyColorList::getClassTypeId()) {
531
                App::PropertyColorList* colors = static_cast<App::PropertyColorList*>(it.second);
532
                return colors;
533
            }
534
        }
535
    }
536
    return nullptr;  // no such property found
537
}
538

539
void ViewProviderMesh::tryColorPerVertexOrFace(bool on)
540
{
541
    if (on) {
542
        const Mesh::PropertyMeshKernel& meshProp = static_cast<Mesh::Feature*>(pcObject)->Mesh;
543
        const Mesh::MeshObject& mesh = meshProp.getValue();
544
        int numPoints = static_cast<int>(mesh.countPoints());
545
        int numFacets = static_cast<int>(mesh.countFacets());
546

547
        if (App::PropertyColorList* colors = getColorProperty()) {
548
            if (colors->getSize() == numPoints) {
549
                setColorPerVertex(colors);
550
            }
551
            else if (colors->getSize() == numFacets) {
552
                setColorPerFace(colors);
553
            }
554
        }
555
        else if (Mesh::PropertyMaterial* material = getMaterialProperty()) {
556
            auto bind = material->getBinding();
557
            if (bind == MeshCore::MeshIO::Binding::OVERALL) {
558
                pcMatBinding->value = SoMaterialBinding::OVERALL;
559

560
                if (!material->getDiffuseColor().empty()) {
561
                    auto c = material->getDiffuseColor()[0];
562
                    pcShapeMaterial->diffuseColor.setValue(c.r, c.g, c.b);
563
                }
564
                if (!material->getTransparency().empty()) {
565
                    pcShapeMaterial->transparency.setValue(material->getTransparency()[0]);
566
                }
567
            }
568
            else if (bind == MeshCore::MeshIO::Binding::PER_VERTEX) {
569
                if (material->getDiffuseColor().size() == std::size_t(numPoints)) {
570
                    pcMatBinding->value = SoMaterialBinding::PER_VERTEX_INDEXED;
571
                    setDiffuseColor(material->getDiffuseColor());
572
                }
573
            }
574
            else if (bind == MeshCore::MeshIO::Binding::PER_FACE) {
575
                if (material->getAmbientColor().size() == std::size_t(numFacets)) {
576
                    pcMatBinding->value = SoMaterialBinding::PER_FACE;
577
                    setAmbientColor(material->getAmbientColor());
578
                }
579
                if (material->getDiffuseColor().size() == std::size_t(numFacets)) {
580
                    pcMatBinding->value = SoMaterialBinding::PER_FACE;
581
                    setDiffuseColor(material->getDiffuseColor());
582
                }
583
                if (material->getEmissiveColor().size() == std::size_t(numFacets)) {
584
                    pcMatBinding->value = SoMaterialBinding::PER_FACE;
585
                    setEmissiveColor(material->getEmissiveColor());
586
                }
587
                if (material->getSpecularColor().size() == std::size_t(numFacets)) {
588
                    pcMatBinding->value = SoMaterialBinding::PER_FACE;
589
                    setSpecularColor(material->getSpecularColor());
590
                }
591
                if (material->getTransparency().size() == std::size_t(numFacets)) {
592
                    pcMatBinding->value = SoMaterialBinding::PER_FACE;
593
                    setFacetTransparency(material->getTransparency());
594
                }
595
            }
596
        }
597
    }
598
    else {
599
        pcMatBinding->value = SoMaterialBinding::OVERALL;
600
        const App::Color& c = ShapeColor.getValue();
601
        pcShapeMaterial->diffuseColor.setValue(c.r, c.g, c.b);
602
        pcShapeMaterial->transparency.setValue(Transparency.getValue() / 100.0f);
603
    }
604
}
605

606
void ViewProviderMesh::setColorPerVertex(const App::PropertyColorList* prop)
607
{
608
    pcMatBinding->value = SoMaterialBinding::PER_VERTEX_INDEXED;
609
    setDiffuseColor(prop->getValues());
610
}
611

612
void ViewProviderMesh::setColorPerFace(const App::PropertyColorList* prop)
613
{
614
    pcMatBinding->value = SoMaterialBinding::PER_FACE;
615
    setDiffuseColor(prop->getValues());
616
}
617

618
void ViewProviderMesh::setColorField(const std::vector<App::Color>& val, SoMFColor& field)
619
{
620
    field.setNum(val.size());
621
    SbColor* col = field.startEditing();
622

623
    std::size_t i = 0;
624
    for (auto it : val) {
625
        col[i++].setValue(it.r, it.g, it.b);
626
    }
627

628
    field.finishEditing();
629
}
630

631
void ViewProviderMesh::setAmbientColor(const std::vector<App::Color>& val)
632
{
633
    setColorField(val, pcShapeMaterial->ambientColor);
634
}
635

636
void ViewProviderMesh::setDiffuseColor(const std::vector<App::Color>& val)
637
{
638
    setColorField(val, pcShapeMaterial->diffuseColor);
639
}
640

641
void ViewProviderMesh::setSpecularColor(const std::vector<App::Color>& val)
642
{
643
    setColorField(val, pcShapeMaterial->specularColor);
644
}
645

646
void ViewProviderMesh::setEmissiveColor(const std::vector<App::Color>& val)
647
{
648
    setColorField(val, pcShapeMaterial->emissiveColor);
649
}
650

651
Mesh::PropertyMaterial* ViewProviderMesh::getMaterialProperty() const
652
{
653
    if (pcObject) {
654
        std::map<std::string, App::Property*> Map;
655
        pcObject->getPropertyMap(Map);
656
        for (const auto& it : Map) {
657
            Base::Type type = it.second->getTypeId();
658
            if (type == Mesh::PropertyMaterial::getClassTypeId()) {
659
                Mesh::PropertyMaterial* material = static_cast<Mesh::PropertyMaterial*>(it.second);
660
                return material;
661
            }
662
        }
663
    }
664

665
    return nullptr;  // no such property found
666
}
667

668
void ViewProviderMesh::setDisplayMode(const char* ModeName)
669
{
670
    if (strcmp("Shaded", ModeName) == 0) {
671
        setDisplayMaskMode("Shaded");
672
    }
673
    else if (strcmp("Points", ModeName) == 0) {
674
        setDisplayMaskMode("Point");
675
    }
676
    else if (strcmp("Flat Lines", ModeName) == 0) {
677
        setDisplayMaskMode("Flat Lines");
678
    }
679
    else if (strcmp("Wireframe", ModeName) == 0) {
680
        setDisplayMaskMode("Wireframe");
681
    }
682

683
    ViewProviderGeometryObject::setDisplayMode(ModeName);
684
}
685

686
std::vector<std::string> ViewProviderMesh::getDisplayModes() const
687
{
688
    std::vector<std::string> StrList;
689

690
    // add your own modes
691
    StrList.emplace_back("Shaded");
692
    StrList.emplace_back("Wireframe");
693
    StrList.emplace_back("Flat Lines");
694
    StrList.emplace_back("Points");
695

696
    return StrList;
697
}
698

699
bool ViewProviderMesh::exportToVrml(const char* filename,
700
                                    const MeshCore::Material& mat,
701
                                    bool binary) const
702
{
703
    SoCoordinate3* coords = new SoCoordinate3();
704
    SoIndexedFaceSet* faces = new SoIndexedFaceSet();
705
    ViewProviderMeshBuilder builder;
706
    builder.createMesh(&static_cast<Mesh::Feature*>(pcObject)->Mesh, coords, faces);
707

708
    SoMaterialBinding* binding = new SoMaterialBinding;
709
    SoMaterial* material = new SoMaterial;
710

711
    if (static_cast<int>(mat.diffuseColor.size()) == coords->point.getNum()) {
712
        binding->value = SoMaterialBinding::PER_VERTEX_INDEXED;
713
    }
714
    else if (static_cast<int>(mat.diffuseColor.size()) == faces->coordIndex.getNum() / 4) {
715
        binding->value = SoMaterialBinding::PER_FACE_INDEXED;
716
    }
717

718
    if (mat.diffuseColor.size() > 1) {
719
        material->diffuseColor.setNum(mat.diffuseColor.size());
720
        SbColor* colors = material->diffuseColor.startEditing();
721
        for (unsigned int i = 0; i < mat.diffuseColor.size(); i++) {
722
            colors[i].setValue(mat.diffuseColor[i].r, mat.diffuseColor[i].g, mat.diffuseColor[i].b);
723
        }
724
        material->diffuseColor.finishEditing();
725
    }
726

727
    SoGroup* group = new SoGroup();
728
    group->addChild(material);
729
    group->addChild(binding);
730
    group->addChild(new SoTransform());
731
    group->addChild(coords);
732
    group->addChild(faces);
733

734
    SoToVRML2Action tovrml2;
735
    group->ref();
736
    tovrml2.apply(group);
737
    group->unref();
738
    SoVRMLGroup* vrmlRoot = tovrml2.getVRML2SceneGraph();
739
    vrmlRoot->ref();
740
    std::string buffer = Gui::SoFCDB::writeNodesToString(vrmlRoot);
741
    vrmlRoot->unref();  // release the memory as soon as possible
742

743
    Base::FileInfo fi(filename);
744
    if (binary) {
745
        Base::ofstream str(fi, std::ios::out | std::ios::binary);
746
        zipios::GZIPOutputStream gzip(str);
747
        if (gzip) {
748
            gzip << buffer;
749
            gzip.close();
750
            return true;
751
        }
752
    }
753
    else {
754
        Base::ofstream str(fi, std::ios::out);
755
        if (str) {
756
            str << buffer;
757
            str.close();
758
            return true;
759
        }
760
    }
761

762
    return false;
763
}
764

765
void ViewProviderMesh::exportMesh(const char* filename, const char* fmt) const
766
{
767
    MeshCore::MeshIO::Format format = MeshCore::MeshIO::Undefined;
768
    if (fmt) {
769
        std::string dummy = "meshfile.";
770
        dummy += fmt;
771
        format = MeshCore::MeshOutput::GetFormat(dummy.c_str());
772
    }
773

774
    MeshCore::Material mat;
775
    int numColors = pcShapeMaterial->diffuseColor.getNum();
776
    const SbColor* colors = pcShapeMaterial->diffuseColor.getValues(0);
777
    mat.diffuseColor.reserve(numColors);
778
    for (int i = 0; i < numColors; i++) {
779
        const SbColor& c = colors[i];
780
        mat.diffuseColor.emplace_back(c[0], c[1], c[2]);
781
    }
782

783
    Mesh::MeshObject mesh = static_cast<Mesh::Feature*>(getObject())->Mesh.getValue();
784
    mesh.setPlacement(static_cast<Mesh::Feature*>(getObject())->globalPlacement());
785
    if (mat.diffuseColor.size() == mesh.countPoints()) {
786
        mat.binding = MeshCore::MeshIO::PER_VERTEX;
787
    }
788
    else if (mat.diffuseColor.size() == mesh.countFacets()) {
789
        mat.binding = MeshCore::MeshIO::PER_FACE;
790
    }
791
    else {
792
        mat.binding = MeshCore::MeshIO::OVERALL;
793
    }
794

795
    mesh.save(filename, format, &mat, getObject()->Label.getValue());
796
}
797

798
void ViewProviderMesh::setupContextMenu(QMenu* menu, QObject* receiver, const char* member)
799
{
800
    ViewProviderGeometryObject::setupContextMenu(menu, receiver, member);
801

802
    // toggle command to display components
803
    Gui::ActionFunction* func = new Gui::ActionFunction(menu);
804
    QAction* act = menu->addAction(QObject::tr("Display components"));
805
    act->setCheckable(true);
806
    act->setChecked(pcMatBinding->value.getValue() == SoMaterialBinding::PER_FACE
807
                    && highlightMode == HighlighMode::Component);
808
    func->toggle(act, [this](bool on) {
809
        this->setHighlightedComponents(on);
810
    });
811

812
    QAction* seg = menu->addAction(QObject::tr("Display segments"));
813
    seg->setCheckable(true);
814
    seg->setChecked(pcMatBinding->value.getValue() == SoMaterialBinding::PER_FACE
815
                    && highlightMode == HighlighMode::Segment);
816
    func->toggle(seg, [this](bool on) {
817
        this->setHighlightedSegments(on);
818
    });
819

820
    QAction* col = menu->addAction(QObject::tr("Display colors"));
821
    col->setVisible(canHighlightColors());
822
    col->setCheckable(true);
823
    col->setChecked(highlightMode == HighlighMode::Color);
824
    func->toggle(col, [this](bool on) {
825
        this->setHighlightedColors(on);
826
    });
827
}
828

829
bool ViewProviderMesh::setEdit(int ModNum)
830
{
831
    if (ModNum == ViewProvider::Transform) {
832
        return ViewProviderGeometryObject::setEdit(ModNum);
833
    }
834
    else if (ModNum == ViewProvider::Color) {
835
        highlightComponents();
836
    }
837
    return true;
838
}
839

840
void ViewProviderMesh::unsetEdit(int ModNum)
841
{
842
    if (ModNum == ViewProvider::Transform) {
843
        ViewProviderGeometryObject::unsetEdit(ModNum);
844
    }
845
    else if (ModNum == ViewProvider::Color) {
846
        unhighlightSelection();
847
    }
848
}
849

850
bool ViewProviderMesh::createToolMesh(const std::vector<SbVec2f>& rclPoly,
851
                                      const SbViewVolume& vol,
852
                                      const Base::Vector3f& rcNormal,
853
                                      std::vector<MeshCore::MeshGeomFacet>& aFaces)
854
{
855
    float fX {}, fY {}, fZ {};
856
    SbVec3f pt1, pt2, pt3, pt4;
857
    MeshGeomFacet face;
858
    std::vector<Base::Vector3f> top, bottom, polygon;
859

860
    for (std::vector<SbVec2f>::const_iterator it = rclPoly.begin(); it != rclPoly.end(); ++it) {
861
        // the following element
862
        std::vector<SbVec2f>::const_iterator nt = it + 1;
863
        if (nt == rclPoly.end()) {
864
            nt = rclPoly.begin();
865
        }
866
        else if (*it == *nt) {
867
            continue;  // two adjacent vertices are equal
868
        }
869

870
        vol.projectPointToLine(*it, pt1, pt2);
871
        vol.projectPointToLine(*nt, pt3, pt4);
872

873
        // 1st facet
874
        pt1.getValue(fX, fY, fZ);
875
        face._aclPoints[0].Set(fX, fY, fZ);
876
        pt4.getValue(fX, fY, fZ);
877
        face._aclPoints[1].Set(fX, fY, fZ);
878
        pt3.getValue(fX, fY, fZ);
879
        face._aclPoints[2].Set(fX, fY, fZ);
880
        if (face.Area() > 0) {
881
            aFaces.push_back(face);
882
        }
883

884
        // 2nd facet
885
        pt1.getValue(fX, fY, fZ);
886
        face._aclPoints[0].Set(fX, fY, fZ);
887
        pt2.getValue(fX, fY, fZ);
888
        face._aclPoints[1].Set(fX, fY, fZ);
889
        pt4.getValue(fX, fY, fZ);
890
        face._aclPoints[2].Set(fX, fY, fZ);
891
        if (face.Area() > 0) {
892
            aFaces.push_back(face);
893
        }
894

895
        if (it + 1 < rclPoly.end()) {
896
            pt1.getValue(fX, fY, fZ);
897
            top.emplace_back(fX, fY, fZ);
898
            pt2.getValue(fX, fY, fZ);
899
            bottom.emplace_back(fX, fY, fZ);
900
            // polygon we need to triangulate (in x,y-plane)
901
            it->getValue(fX, fY);
902
            polygon.emplace_back(fX, fY, 0.0f);
903
        }
904
    }
905

906
    // now create the lids
907
    std::vector<MeshGeomFacet> aLid;
908
    MeshCore::EarClippingTriangulator cTria;
909
    cTria.SetPolygon(polygon);
910
    bool ok = cTria.TriangulatePolygon();
911

912
    std::vector<MeshFacet> faces = cTria.GetFacets();
913
    for (const auto& face : faces) {
914
        MeshGeomFacet topFacet;
915
        topFacet._aclPoints[0] = top[face._aulPoints[0]];
916
        topFacet._aclPoints[1] = top[face._aulPoints[1]];
917
        topFacet._aclPoints[2] = top[face._aulPoints[2]];
918
        if (topFacet.GetNormal() * rcNormal < 0) {
919
            std::swap(topFacet._aclPoints[1], topFacet._aclPoints[2]);
920
            topFacet.CalcNormal();
921
        }
922
        aFaces.push_back(topFacet);
923

924
        MeshGeomFacet botFacet;
925
        botFacet._aclPoints[0] = bottom[face._aulPoints[0]];
926
        botFacet._aclPoints[1] = bottom[face._aulPoints[1]];
927
        botFacet._aclPoints[2] = bottom[face._aulPoints[2]];
928
        if (botFacet.GetNormal() * rcNormal > 0) {
929
            std::swap(botFacet._aclPoints[1], botFacet._aclPoints[2]);
930
            botFacet.CalcNormal();
931
        }
932
        aFaces.push_back(botFacet);
933
    }
934

935
    return ok;
936
}
937

938
void ViewProviderMesh::showOpenEdges(bool show)
939
{
940
    (void)show;
941
}
942

943
namespace MeshGui
944
{
945
class MeshSplit
946
{
947
public:
948
    MeshSplit(ViewProviderMesh* mesh,
949
              std::vector<SbVec2f> poly,
950
              const Gui::ViewVolumeProjection& proj)
951
        : mesh(mesh)
952
        , poly(std::move(poly))
953
        , proj(proj)
954
    {}
955
    void cutMesh()
956
    {
957
        Gui::Document* gui = mesh->getDocument();
958
        gui->openCommand(QT_TRANSLATE_NOOP("Command", "Cut"));
959
        ViewProviderMesh* copy = makeCopy();
960
        mesh->cutMesh(poly, proj, false);
961
        copy->cutMesh(poly, proj, true);
962
        gui->commitCommand();
963
        delete this;
964
    }
965
    void trimMesh()
966
    {
967
        Gui::Document* gui = mesh->getDocument();
968
        gui->openCommand(QT_TRANSLATE_NOOP("Command", "Trim"));
969
        ViewProviderMesh* copy = makeCopy();
970
        mesh->trimMesh(poly, proj, false);
971
        copy->trimMesh(poly, proj, true);
972
        gui->commitCommand();
973
        delete this;
974
    }
975
    ViewProviderMesh* makeCopy() const
976
    {
977
        Gui::Document* gui = mesh->getDocument();
978
        App::Document* doc = gui->getDocument();
979

980
        Mesh::Feature* cpy = static_cast<Mesh::Feature*>(doc->addObject("Mesh::Feature"));
981
        Mesh::Feature* org = static_cast<Mesh::Feature*>(mesh->getObject());
982
        cpy->Label.setValue(org->Label.getValue());
983
        cpy->Mesh.setValue(org->Mesh.getValue());
984

985
        return static_cast<ViewProviderMesh*>(gui->getViewProvider(cpy));
986
    }
987

988
private:
989
    ViewProviderMesh* mesh;
990
    std::vector<SbVec2f> poly;
991
    Gui::ViewVolumeProjection proj;
992
};
993
}  // namespace MeshGui
994

995
void ViewProviderMesh::clipMeshCallback(void* ud, SoEventCallback* n)
996
{
997
    // show the wait cursor because this could take quite some time
998
    Gui::WaitCursor wc;
999

1000
    // When this callback function is invoked we must in either case leave the edit mode
1001
    Gui::View3DInventorViewer* view = static_cast<Gui::View3DInventorViewer*>(n->getUserData());
1002
    view->setEditing(false);
1003
    view->removeEventCallback(SoMouseButtonEvent::getClassTypeId(), clipMeshCallback, ud);
1004
    n->setHandled();
1005

1006
    Gui::SelectionRole role {};
1007
    std::vector<SbVec2f> clPoly = view->getGLPolygon(&role);
1008
    if (clPoly.size() < 3) {
1009
        return;
1010
    }
1011
    if (clPoly.front() != clPoly.back()) {
1012
        clPoly.push_back(clPoly.front());
1013
    }
1014

1015
    std::vector<Gui::ViewProvider*> views =
1016
        view->getViewProvidersOfType(ViewProviderMesh::getClassTypeId());
1017
    if (!views.empty()) {
1018
        Gui::Application::Instance->activeDocument()->openCommand(
1019
            QT_TRANSLATE_NOOP("Command", "Cut"));
1020
        bool commitCommand = false;
1021
        for (auto it : views) {
1022
            ViewProviderMesh* self = static_cast<ViewProviderMesh*>(it);
1023
            if (self->getEditingMode() > -1) {
1024
                self->finishEditing();
1025
                SoCamera* cam = view->getSoRenderManager()->getCamera();
1026
                SbViewVolume vv = cam->getViewVolume();
1027
                Gui::ViewVolumeProjection proj(vv);
1028
                proj.setTransform(static_cast<Mesh::Feature*>(self->getObject())
1029
                                      ->Placement.getValue()
1030
                                      .toMatrix());
1031
                if (role == Gui::SelectionRole::Inner) {
1032
                    self->cutMesh(clPoly, proj, true);
1033
                    commitCommand = true;
1034
                }
1035
                else if (role == Gui::SelectionRole::Outer) {
1036
                    self->cutMesh(clPoly, proj, false);
1037
                    commitCommand = true;
1038
                }
1039
                else if (role == Gui::SelectionRole::Split) {
1040
                    // We must delay the split because it adds a new
1041
                    // node to the scenegraph which cannot be done while
1042
                    // traversing it
1043
                    Gui::TimerFunction* func = new Gui::TimerFunction();
1044
                    func->setAutoDelete(true);
1045
                    MeshSplit* split = new MeshSplit(self, clPoly, proj);
1046
                    func->setFunction([split]() {
1047
                        split->cutMesh();
1048
                    });
1049
                    func->singleShot(0);
1050
                }
1051
            }
1052
        }
1053

1054
        if (commitCommand) {
1055
            Gui::Application::Instance->activeDocument()->commitCommand();
1056
        }
1057
        else {
1058
            Gui::Application::Instance->activeDocument()->abortCommand();
1059
        }
1060

1061
        view->redraw();
1062
    }
1063
}
1064

1065
void ViewProviderMesh::trimMeshCallback(void* ud, SoEventCallback* n)
1066
{
1067
    // show the wait cursor because this could take quite some time
1068
    Gui::WaitCursor wc;
1069

1070
    // When this callback function is invoked we must in either case leave the edit mode
1071
    Gui::View3DInventorViewer* view = static_cast<Gui::View3DInventorViewer*>(n->getUserData());
1072
    view->setEditing(false);
1073
    view->removeEventCallback(SoMouseButtonEvent::getClassTypeId(), trimMeshCallback, ud);
1074
    n->setHandled();
1075

1076
    Gui::SelectionRole role {};
1077
    std::vector<SbVec2f> clPoly = view->getGLPolygon(&role);
1078
    if (clPoly.size() < 3) {
1079
        return;
1080
    }
1081
    if (clPoly.front() != clPoly.back()) {
1082
        clPoly.push_back(clPoly.front());
1083
    }
1084

1085
    std::vector<Gui::ViewProvider*> views =
1086
        view->getViewProvidersOfType(ViewProviderMesh::getClassTypeId());
1087
    if (!views.empty()) {
1088
        Gui::Application::Instance->activeDocument()->openCommand(
1089
            QT_TRANSLATE_NOOP("Command", "Trim"));
1090
        bool commitCommand = false;
1091
        for (auto it : views) {
1092
            ViewProviderMesh* self = static_cast<ViewProviderMesh*>(it);
1093
            if (self->getEditingMode() > -1) {
1094
                self->finishEditing();
1095
                SoCamera* cam = view->getSoRenderManager()->getCamera();
1096
                SbViewVolume vv = cam->getViewVolume();
1097
                Gui::ViewVolumeProjection proj(vv);
1098
                proj.setTransform(static_cast<Mesh::Feature*>(self->getObject())
1099
                                      ->Placement.getValue()
1100
                                      .toMatrix());
1101
                if (role == Gui::SelectionRole::Inner) {
1102
                    self->trimMesh(clPoly, proj, true);
1103
                    commitCommand = true;
1104
                }
1105
                else if (role == Gui::SelectionRole::Outer) {
1106
                    self->trimMesh(clPoly, proj, false);
1107
                    commitCommand = true;
1108
                }
1109
                else if (role == Gui::SelectionRole::Split) {
1110
                    // We must delay the split because it adds a new
1111
                    // node to the scenegraph which cannot be done while
1112
                    // traversing it
1113
                    Gui::TimerFunction* func = new Gui::TimerFunction();
1114
                    func->setAutoDelete(true);
1115
                    MeshSplit* split = new MeshSplit(self, clPoly, proj);
1116
                    func->setFunction([split]() {
1117
                        split->trimMesh();
1118
                    });
1119
                    func->singleShot(0);
1120
                }
1121
            }
1122
        }
1123

1124
        if (commitCommand) {
1125
            Gui::Application::Instance->activeDocument()->commitCommand();
1126
        }
1127
        else {
1128
            Gui::Application::Instance->activeDocument()->abortCommand();
1129
        }
1130

1131
        view->redraw();
1132
    }
1133
}
1134

1135
void ViewProviderMesh::partMeshCallback(void* ud, SoEventCallback* cb)
1136
{
1137
    // show the wait cursor because this could take quite some time
1138
    Gui::WaitCursor wc;
1139

1140
    // When this callback function is invoked we must in either case leave the edit mode
1141
    Gui::View3DInventorViewer* view = static_cast<Gui::View3DInventorViewer*>(cb->getUserData());
1142
    view->setEditing(false);
1143
    view->removeEventCallback(SoMouseButtonEvent::getClassTypeId(), partMeshCallback, ud);
1144
    cb->setHandled();
1145

1146
    Gui::SelectionRole role {};
1147
    std::vector<SbVec2f> clPoly = view->getGLPolygon(&role);
1148
    if (clPoly.size() < 3) {
1149
        return;
1150
    }
1151
    if (clPoly.front() != clPoly.back()) {
1152
        clPoly.push_back(clPoly.front());
1153
    }
1154

1155
    // get the normal of the front clipping plane
1156
    SbVec3f b, n;
1157
    view->getNearPlane(b, n);
1158
    Base::Vector3f cNormal(n[0], n[1], n[2]);
1159
    SoCamera* pCam = view->getSoRenderManager()->getCamera();
1160
    SbViewVolume vol = pCam->getViewVolume();
1161

1162
    // create a tool shape from these points
1163
    std::vector<MeshCore::MeshGeomFacet> aFaces;
1164
    if (!ViewProviderMesh::createToolMesh(clPoly, vol, cNormal, aFaces)) {
1165
        Base::Console().Message("The picked polygon seems to have self-overlappings. This could "
1166
                                "lead to strange results.");
1167
    }
1168

1169
    MeshCore::MeshKernel toolMesh;
1170
    bool locked = Base::Sequencer().setLocked(true);
1171
    toolMesh = aFaces;
1172
    Base::Sequencer().setLocked(locked);
1173

1174
    // Open a transaction object for the undo/redo stuff
1175
    Gui::Application::Instance->activeDocument()->openCommand(
1176
        QT_TRANSLATE_NOOP("Command", "Split"));
1177

1178
    try {
1179
        std::vector<Gui::ViewProvider*> views =
1180
            view->getViewProvidersOfType(ViewProviderMesh::getClassTypeId());
1181
        for (auto view : views) {
1182
            ViewProviderMesh* that = static_cast<ViewProviderMesh*>(view);
1183
            if (that->getEditingMode() > -1) {
1184
                that->finishEditing();
1185
                Base::Placement plm =
1186
                    static_cast<Mesh::Feature*>(that->getObject())->Placement.getValue();
1187
                plm.invert();
1188
                MeshCore::MeshKernel copyToolMesh(toolMesh);
1189
                copyToolMesh.Transform(plm.toMatrix());
1190
                if (role == Gui::SelectionRole::Inner) {
1191
                    that->splitMesh(copyToolMesh, cNormal, true);
1192
                }
1193
                else {
1194
                    that->splitMesh(copyToolMesh, cNormal, false);
1195
                }
1196
            }
1197
        }
1198
    }
1199
    catch (...) {
1200
        // Don't rethrow any exception
1201
    }
1202

1203
    // Close the transaction
1204
    Gui::Application::Instance->activeDocument()->commitCommand();
1205
    view->redraw();
1206
}
1207

1208
void ViewProviderMesh::segmMeshCallback(void* ud, SoEventCallback* cb)
1209
{
1210
    // show the wait cursor because this could take quite some time
1211
    Gui::WaitCursor wc;
1212

1213
    // When this callback function is invoked we must in either case leave the edit mode
1214
    Gui::View3DInventorViewer* view = static_cast<Gui::View3DInventorViewer*>(cb->getUserData());
1215
    view->setEditing(false);
1216
    view->removeEventCallback(SoMouseButtonEvent::getClassTypeId(), segmMeshCallback, ud);
1217
    cb->setHandled();
1218

1219
    Gui::SelectionRole role {};
1220
    std::vector<SbVec2f> clPoly = view->getGLPolygon(&role);
1221
    if (clPoly.size() < 3) {
1222
        return;
1223
    }
1224
    if (clPoly.front() != clPoly.back()) {
1225
        clPoly.push_back(clPoly.front());
1226
    }
1227

1228
    // get the normal of the front clipping plane
1229
    SbVec3f b, n;
1230
    view->getNearPlane(b, n);
1231
    Base::Vector3f cNormal(n[0], n[1], n[2]);
1232
    SoCamera* pCam = view->getSoRenderManager()->getCamera();
1233
    SbViewVolume vol = pCam->getViewVolume();
1234

1235
    // create a tool shape from these points
1236
    std::vector<MeshCore::MeshGeomFacet> aFaces;
1237
    if (!ViewProviderMesh::createToolMesh(clPoly, vol, cNormal, aFaces)) {
1238
        Base::Console().Message("The picked polygon seems to have self-overlappings. This could "
1239
                                "lead to strange results.");
1240
    }
1241

1242
    MeshCore::MeshKernel toolMesh;
1243
    bool locked = Base::Sequencer().setLocked(true);
1244
    toolMesh = aFaces;
1245
    Base::Sequencer().setLocked(locked);
1246

1247
    // Open a transaction object for the undo/redo stuff
1248
    Gui::Application::Instance->activeDocument()->openCommand(
1249
        QT_TRANSLATE_NOOP("Command", "Segment"));
1250

1251
    try {
1252
        std::vector<Gui::ViewProvider*> views =
1253
            view->getViewProvidersOfType(ViewProviderMesh::getClassTypeId());
1254
        for (auto view : views) {
1255
            ViewProviderMesh* that = static_cast<ViewProviderMesh*>(view);
1256
            if (that->getEditingMode() > -1) {
1257
                that->finishEditing();
1258
                Base::Placement plm =
1259
                    static_cast<Mesh::Feature*>(that->getObject())->Placement.getValue();
1260
                plm.invert();
1261
                MeshCore::MeshKernel copyToolMesh(toolMesh);
1262
                copyToolMesh.Transform(plm.toMatrix());
1263
                if (role == Gui::SelectionRole::Inner) {
1264
                    that->segmentMesh(copyToolMesh, cNormal, true);
1265
                }
1266
                else {
1267
                    that->segmentMesh(copyToolMesh, cNormal, false);
1268
                }
1269
            }
1270
        }
1271
    }
1272
    catch (...) {
1273
        // Don't rethrow any exception
1274
    }
1275

1276
    // Close the transaction
1277
    Gui::Application::Instance->activeDocument()->commitCommand();
1278
    view->redraw();
1279
}
1280

1281
void ViewProviderMesh::selectGLCallback(void* ud, SoEventCallback* n)
1282
{
1283
    // When this callback function is invoked we must in either case leave the edit mode
1284
    Gui::View3DInventorViewer* view = static_cast<Gui::View3DInventorViewer*>(n->getUserData());
1285
    view->setEditing(false);
1286
    view->removeEventCallback(SoMouseButtonEvent::getClassTypeId(), selectGLCallback, ud);
1287
    n->setHandled();
1288

1289
    std::vector<SbVec2f> clPoly = view->getGLPolygon();
1290
    if (clPoly.size() != 2) {
1291
        return;
1292
    }
1293
    const SoEvent* ev = n->getEvent();
1294

1295
    SbVec2f pos = clPoly[0];
1296
    float pX {}, pY {};
1297
    pos.getValue(pX, pY);
1298
    const SbVec2s& sz = view->getSoRenderManager()->getViewportRegion().getViewportSizePixels();
1299
    float fRatio = view->getSoRenderManager()->getViewportRegion().getViewportAspectRatio();
1300
    if (fRatio > 1.0F) {
1301
        pX = (pX - 0.5F) / fRatio + 0.5F;
1302
        pos.setValue(pX, pY);
1303
    }
1304
    else if (fRatio < 1.0F) {
1305
        pY = (pY - 0.5F) * fRatio + 0.5F;
1306
        pos.setValue(pX, pY);
1307
    }
1308

1309
    short x1 = (short)(pX * sz[0] + 0.5F);
1310
    short y1 = (short)(pY * sz[1] + 0.5F);
1311
    SbVec2s loc = ev->getPosition();
1312
    short x2 = loc[0];
1313
    short y2 = loc[1];
1314

1315
    short x = (x1 + x2) / 2;
1316
    short y = (y1 + y2) / 2;
1317
    short w = (x2 - x1);
1318
    short h = (y2 - y1);
1319
    if (w < 0) {
1320
        w = -w;
1321
    }
1322
    if (h < 0) {
1323
        h = -h;
1324
    }
1325

1326
    std::vector<Gui::ViewProvider*> views;
1327
    views = view->getViewProvidersOfType(ViewProviderMesh::getClassTypeId());
1328
    for (auto it : views) {
1329
        ViewProviderMesh* that = static_cast<ViewProviderMesh*>(it);
1330
        if (that->getEditingMode() > -1) {
1331
            that->finishEditing();
1332
            that->selectArea(x,
1333
                             y,
1334
                             w,
1335
                             h,
1336
                             view->getSoRenderManager()->getViewportRegion(),
1337
                             view->getSoRenderManager()->getCamera());
1338
        }
1339
    }
1340

1341
    view->redraw();
1342
}
1343

1344
void ViewProviderMesh::getFacetsFromPolygon(const std::vector<SbVec2f>& picked,
1345
                                            const Base::ViewProjMethod& proj,
1346
                                            SbBool inner,
1347
                                            std::vector<Mesh::FacetIndex>& indices) const
1348
{
1349
    const bool ok = true;
1350
    Base::Polygon2d polygon;
1351
    for (auto it : picked) {
1352
        polygon.Add(Base::Vector2d(it[0], it[1]));
1353
    }
1354

1355
    // Get the attached mesh property
1356
    Mesh::PropertyMeshKernel& meshProp = static_cast<Mesh::Feature*>(pcObject)->Mesh;
1357
    MeshCore::MeshAlgorithm cAlg(meshProp.getValue().getKernel());
1358
    cAlg.CheckFacets(&proj, polygon, true, indices);
1359

1360
    if (!inner) {
1361
        // get the indices that are completely outside
1362
        std::vector<Mesh::FacetIndex> complete(meshProp.getValue().countFacets());
1363
        std::generate(complete.begin(), complete.end(), Base::iotaGen<Mesh::FacetIndex>(0));
1364
        std::sort(indices.begin(), indices.end());
1365
        std::vector<Mesh::FacetIndex> complementary;
1366
        std::back_insert_iterator<std::vector<Mesh::FacetIndex>> biit(complementary);
1367
        std::set_difference(complete.begin(), complete.end(), indices.begin(), indices.end(), biit);
1368
        indices = complementary;
1369
    }
1370

1371
    if (!ok) {  // note: the mouse grabbing needs to be released
1372
        Base::Console().Message("The picked polygon seems to have self-overlappings. This could "
1373
                                "lead to strange results.");
1374
    }
1375
}
1376

1377
std::vector<Mesh::FacetIndex> ViewProviderMesh::getFacetsOfRegion(const SbViewportRegion& select,
1378
                                                                  const SbViewportRegion& region,
1379
                                                                  SoCamera* camera) const
1380
{
1381
    SoSeparator* root = new SoSeparator();
1382
    root->ref();
1383
    root->addChild(camera);
1384
    root->addChild(this->getCoordNode());
1385
    root->addChild(this->getShapeNode());
1386
    Gui::SoGLSelectAction gl(region, select);
1387
    gl.apply(root);
1388
    root->unref();
1389

1390
    std::vector<Mesh::FacetIndex> faces;
1391
    faces.insert(faces.end(), gl.indices.begin(), gl.indices.end());
1392
    return faces;
1393
}
1394

1395
void ViewProviderMesh::panCamera(SoCamera* cam,
1396
                                 float aspectratio,
1397
                                 const SbPlane& panplane,
1398
                                 const SbVec2f& currpos,
1399
                                 const SbVec2f& prevpos)
1400
{
1401
    if (!cam) {  // can happen for empty scenegraph
1402
        return;
1403
    }
1404
    if (currpos == prevpos) {  // useless invocation
1405
        return;
1406
    }
1407

1408

1409
    // Find projection points for the last and current mouse coordinates.
1410
    SbViewVolume vv = cam->getViewVolume(aspectratio);
1411
    SbLine line;
1412
    vv.projectPointToLine(currpos, line);
1413
    SbVec3f current_planept;
1414
    panplane.intersect(line, current_planept);
1415
    vv.projectPointToLine(prevpos, line);
1416
    SbVec3f old_planept;
1417
    panplane.intersect(line, old_planept);
1418

1419
    // Reposition camera according to the vector difference between the
1420
    // projected points.
1421
    cam->position = cam->position.getValue() - (current_planept - old_planept);
1422
}
1423

1424
void ViewProviderMesh::boxZoom(const SbBox2s& box, const SbViewportRegion& vp, SoCamera* cam)
1425
{
1426
    SbViewVolume vv = cam->getViewVolume(vp.getViewportAspectRatio());
1427

1428
    short sizeX {}, sizeY {};
1429
    box.getSize(sizeX, sizeY);
1430
    SbVec2s size = vp.getViewportSizePixels();
1431

1432
    // The bbox must not be empty i.e. width and length is zero, but it is possible that
1433
    // either width or length is zero
1434
    if (sizeX == 0 && sizeY == 0) {
1435
        return;
1436
    }
1437

1438
    // Get the new center in normalized pixel coordinates
1439
    short xmin {};
1440
    short xmax {};
1441
    short ymin {};
1442
    short ymax {};
1443
    box.getBounds(xmin, ymin, xmax, ymax);
1444
    const SbVec2f center((float)((xmin + xmax) / 2) / (float)std::max((int)(size[0] - 1), 1),
1445
                         (float)(size[1] - (ymin + ymax) / 2)
1446
                             / (float)std::max((int)(size[1] - 1), 1));
1447

1448
    SbPlane plane = vv.getPlane(cam->focalDistance.getValue());
1449
    panCamera(cam, vp.getViewportAspectRatio(), plane, SbVec2f(0.5, 0.5), center);
1450

1451
    // Set height or height angle of the camera
1452
    float scaleX = (float)sizeX / (float)size[0];
1453
    float scaleY = (float)sizeY / (float)size[1];
1454
    float scale = std::max<float>(scaleX, scaleY);
1455
    if (cam->getTypeId() == SoOrthographicCamera::getClassTypeId()) {
1456
        float height = static_cast<SoOrthographicCamera*>(cam)->height.getValue() * scale;
1457
        static_cast<SoOrthographicCamera*>(cam)->height = height;
1458
    }
1459
    else if (cam->getTypeId() == SoPerspectiveCamera::getClassTypeId()) {
1460
        float height = static_cast<SoPerspectiveCamera*>(cam)->heightAngle.getValue() / 2.0F;
1461
        height = 2.0F * atan(tan(height) * scale);
1462
        static_cast<SoPerspectiveCamera*>(cam)->heightAngle = height;
1463
    }
1464
}
1465

1466
std::vector<Mesh::FacetIndex>
1467
ViewProviderMesh::getVisibleFacetsAfterZoom(const SbBox2s& rect,
1468
                                            const SbViewportRegion& vp,
1469
                                            SoCamera* camera) const
1470
{
1471
    // camera copy will be deleted inside getVisibleFacets()
1472
    // because the ref counter reaches 0
1473
    camera = static_cast<SoCamera*>(camera->copy());
1474
    boxZoom(rect, vp, camera);
1475
    return getVisibleFacets(vp, camera);
1476
}
1477

1478
void ViewProviderMesh::renderGLCallback(void* ud, SoAction* action)
1479
{
1480
    if (action->isOfType(SoGLRenderAction::getClassTypeId())) {
1481
        ViewProviderMesh* mesh = static_cast<ViewProviderMesh*>(ud);
1482
        Gui::SoVisibleFaceAction fa;
1483
        fa.apply(mesh->getRoot());
1484
    }
1485
}
1486

1487
namespace MeshGui
1488
{
1489

1490
class Vertex
1491
{
1492
public:
1493
    Vertex(const MeshCore::MeshKernel& kernel,
1494
           const MeshCore::MeshFacetGrid& grid,
1495
           const Base::Vector3f& pos)
1496
        : kernel(kernel)
1497
        , grid(grid)
1498
        , pos(pos)
1499
    {}
1500
    bool visible(const Base::Vector3f& base) const
1501
    {
1502
        MeshCore::MeshAlgorithm meshAlg(kernel);
1503
        bool ok = meshAlg.IsVertexVisible(base, pos, grid);
1504
        return ok;
1505
    }
1506

1507
private:
1508
    const MeshCore::MeshKernel& kernel;
1509
    const MeshCore::MeshFacetGrid& grid;
1510
    Base::Vector3f pos;
1511
};
1512

1513
}  // namespace MeshGui
1514

1515
std::vector<Mesh::FacetIndex> ViewProviderMesh::getVisibleFacets(const SbViewportRegion& vp,
1516
                                                                 SoCamera* camera) const
1517
{
1518
    const Mesh::PropertyMeshKernel& meshProp = static_cast<Mesh::Feature*>(pcObject)->Mesh;
1519
    const Mesh::MeshObject& mesh = meshProp.getValue();
1520
    uint32_t count = (uint32_t)mesh.countFacets();
1521

1522
    SoSeparator* root = new SoSeparator;
1523
    root->ref();
1524
    root->addChild(camera);
1525

1526
    SoLightModel* lm = new SoLightModel();
1527
    lm->model = SoLightModel::BASE_COLOR;
1528
    root->addChild(lm);
1529
    SoMaterial* mat = new SoMaterial();
1530
    mat->diffuseColor.setNum(count);
1531
    SbColor* diffcol = mat->diffuseColor.startEditing();
1532
    for (uint32_t i = 0; i < count; i++) {
1533
        float t {};
1534
        diffcol[i].setPackedValue(i << 8, t);
1535
    }
1536

1537
    mat->diffuseColor.finishEditing();
1538

1539
    SoMaterialBinding* bind = new SoMaterialBinding();
1540
    bind->value = SoMaterialBinding::PER_FACE;
1541

1542
    root->addChild(mat);
1543
    root->addChild(bind);
1544
    root->addChild(this->getCoordNode());
1545
    root->addChild(this->getShapeNode());
1546

1547
    // Coin3d's off-screen renderer doesn't work out-of-the-box any more on most recent Linux
1548
    // systems. So, use FreeCAD's offscreen renderer now.
1549
    Gui::SoQtOffscreenRenderer renderer(vp);
1550
    renderer.setBackgroundColor(SbColor4f(0.0F, 0.0F, 0.0F));
1551

1552
    QImage img;
1553
    renderer.render(root);
1554
    renderer.writeToImage(img);
1555
    root->unref();
1556

1557
    int width = img.width();
1558
    int height = img.height();
1559
    QRgb color = 0;
1560
    std::vector<Mesh::FacetIndex> faces;
1561
    for (int y = 0; y < height; y++) {
1562
        for (int x = 0; x < width; x++) {
1563
            QRgb rgb = img.pixel(x, y);
1564
            rgb = rgb - (0xff << 24);
1565
            if (rgb != 0 && rgb != color) {
1566
                color = rgb;
1567
                faces.push_back((Mesh::FacetIndex)rgb);
1568
            }
1569
        }
1570
    }
1571

1572
    std::sort(faces.begin(), faces.end());
1573
    faces.erase(std::unique(faces.begin(), faces.end()), faces.end());
1574

1575
    return faces;
1576
}
1577

1578
void ViewProviderMesh::cutMesh(const std::vector<SbVec2f>& picked,
1579
                               const Base::ViewProjMethod& proj,
1580
                               SbBool inner)
1581
{
1582
    // Get the facet indices inside the tool mesh
1583
    std::vector<Mesh::FacetIndex> indices;
1584
    getFacetsFromPolygon(picked, proj, inner, indices);
1585
    removeFacets(indices);
1586
}
1587

1588
void ViewProviderMesh::trimMesh(const std::vector<SbVec2f>& polygon,
1589
                                const Base::ViewProjMethod& proj,
1590
                                SbBool inner)
1591
{
1592
    Mesh::MeshObject* mesh = static_cast<Mesh::Feature*>(pcObject)->Mesh.startEditing();
1593

1594
    Base::Polygon2d polygon2d;
1595
    for (auto it : polygon) {
1596
        polygon2d.Add(Base::Vector2d(it[0], it[1]));
1597
    }
1598

1599
    Mesh::MeshObject::CutType type = inner ? Mesh::MeshObject::INNER : Mesh::MeshObject::OUTER;
1600
    mesh->trim(polygon2d, proj, type);
1601
    static_cast<Mesh::Feature*>(pcObject)->Mesh.finishEditing();
1602
    pcObject->purgeTouched();
1603
}
1604

1605
void ViewProviderMesh::splitMesh(const MeshCore::MeshKernel& toolMesh,
1606
                                 const Base::Vector3f& normal,
1607
                                 SbBool clip_inner)
1608
{
1609
    // Get the attached mesh property
1610
    Mesh::PropertyMeshKernel& meshProp = static_cast<Mesh::Feature*>(pcObject)->Mesh;
1611
    const MeshCore::MeshKernel& meshPropKernel = meshProp.getValue().getKernel();
1612

1613
    // Get the facet indices inside the tool mesh
1614
    std::vector<Mesh::FacetIndex> indices;
1615
    MeshCore::MeshFacetGrid cGrid(meshPropKernel);
1616
    MeshCore::MeshAlgorithm cAlg(meshPropKernel);
1617
    cAlg.GetFacetsFromToolMesh(toolMesh, normal, cGrid, indices);
1618
    if (!clip_inner) {
1619
        // get the indices that are completely outside
1620
        std::vector<Mesh::FacetIndex> complete(meshPropKernel.CountFacets());
1621
        std::generate(complete.begin(), complete.end(), Base::iotaGen<Mesh::FacetIndex>(0));
1622
        std::sort(indices.begin(), indices.end());
1623
        std::vector<Mesh::FacetIndex> complementary;
1624
        std::back_insert_iterator<std::vector<Mesh::FacetIndex>> biit(complementary);
1625
        std::set_difference(complete.begin(), complete.end(), indices.begin(), indices.end(), biit);
1626
        indices = complementary;
1627
    }
1628

1629
    // Remove the facets from the mesh and create a new one
1630
    Mesh::MeshObject* kernel = meshProp.getValue().meshFromSegment(indices);
1631
    removeFacets(indices);
1632
    Mesh::Feature* splitMesh = static_cast<Mesh::Feature*>(
1633
        App::GetApplication().getActiveDocument()->addObject("Mesh::Feature",
1634
                                                             pcObject->getNameInDocument()));
1635
    // Note: deletes also kernel
1636
    splitMesh->Mesh.setValuePtr(kernel);
1637
    static_cast<Mesh::Feature*>(pcObject)->purgeTouched();
1638
}
1639

1640
void ViewProviderMesh::segmentMesh(const MeshCore::MeshKernel& toolMesh,
1641
                                   const Base::Vector3f& normal,
1642
                                   SbBool clip_inner)
1643
{
1644
    // Get the attached mesh property
1645
    Mesh::PropertyMeshKernel& meshProp = static_cast<Mesh::Feature*>(pcObject)->Mesh;
1646
    const MeshCore::MeshKernel& meshPropKernel = meshProp.getValue().getKernel();
1647

1648
    // Get the facet indices inside the tool mesh
1649
    std::vector<Mesh::FacetIndex> indices;
1650
    MeshCore::MeshFacetGrid cGrid(meshPropKernel);
1651
    MeshCore::MeshAlgorithm cAlg(meshPropKernel);
1652
    cAlg.GetFacetsFromToolMesh(toolMesh, normal, cGrid, indices);
1653
    if (!clip_inner) {
1654
        // get the indices that are completely outside
1655
        std::vector<Mesh::FacetIndex> complete(meshPropKernel.CountFacets());
1656
        std::generate(complete.begin(), complete.end(), Base::iotaGen<Mesh::FacetIndex>(0));
1657
        std::sort(indices.begin(), indices.end());
1658
        std::vector<Mesh::FacetIndex> complementary;
1659
        std::back_insert_iterator<std::vector<Mesh::FacetIndex>> biit(complementary);
1660
        std::set_difference(complete.begin(), complete.end(), indices.begin(), indices.end(), biit);
1661
        indices = complementary;
1662
    }
1663

1664
    Mesh::MeshObject* kernel = meshProp.startEditing();
1665
    kernel->addSegment(indices);
1666
    meshProp.finishEditing();
1667
    static_cast<Mesh::Feature*>(pcObject)->purgeTouched();
1668
}
1669

1670
void ViewProviderMesh::faceInfoCallback(void* ud, SoEventCallback* n)
1671
{
1672
    const SoMouseButtonEvent* mbe = static_cast<const SoMouseButtonEvent*>(n->getEvent());
1673
    Gui::View3DInventorViewer* view = static_cast<Gui::View3DInventorViewer*>(n->getUserData());
1674

1675
    // Mark all incoming mouse button events as handled, especially, to deactivate the selection
1676
    // node
1677
    n->getAction()->setHandled();
1678
    if (mbe->getButton() == SoMouseButtonEvent::BUTTON2 && mbe->getState() == SoButtonEvent::UP) {
1679
        n->setHandled();
1680
        // context-menu
1681
        QMenu menu;
1682
        QAction* cl = menu.addAction(QObject::tr("Leave info mode"));
1683
        QAction* id = menu.exec(QCursor::pos());
1684
        if (cl == id) {
1685
            view->setEditing(false);
1686
            view->getWidget()->setCursor(QCursor(Qt::ArrowCursor));
1687
            view->removeEventCallback(SoMouseButtonEvent::getClassTypeId(), faceInfoCallback, ud);
1688
            std::list<Gui::GLGraphicsItem*> glItems =
1689
                view->getGraphicsItemsOfType(Gui::GLFlagWindow::getClassTypeId());
1690
            for (auto glItem : glItems) {
1691
                view->removeGraphicsItem(glItem);
1692
                delete glItem;
1693
            }
1694

1695
            // See comment below
1696
            ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath(
1697
                "User parameter:BaseApp/Preferences/View");
1698
            hGrp->SetBool("ShowNaviCube", hGrp->GetBool("ShowNaviCube", true));
1699
        }
1700
    }
1701
    else if (mbe->getButton() == SoMouseButtonEvent::BUTTON1
1702
             && mbe->getState() == SoButtonEvent::DOWN) {
1703
        const SoPickedPoint* point = n->getPickedPoint();
1704
        if (!point) {
1705
            Base::Console().Message("No facet picked.\n");
1706
            return;
1707
        }
1708

1709
        n->setHandled();
1710

1711
        // By specifying the indexed mesh node 'pcFaceSet' we make sure that the picked point is
1712
        // really from the mesh we render and not from any other geometry
1713
        Gui::ViewProvider* vp = view->getViewProviderByPathFromTail(point->getPath());
1714
        if (!vp || !vp->isDerivedFrom<ViewProviderMesh>()) {
1715
            return;
1716
        }
1717

1718
        // FIXME: The Flag class doesn't work well (flickering) when the NaviCube is enabled.
1719
        // To avoid this the NaviCube is disabled for the time the flags are shown.
1720
        // When leaving this mode the NaviCube can be displayed again.
1721
        // For a proper solution it's best to move the Flag class to the QGraphicsView API.
1722
        view->setEnabledNaviCube(false);
1723

1724
        ViewProviderMesh* that = static_cast<ViewProviderMesh*>(vp);
1725
        const SoDetail* detail = point->getDetail(that->getShapeNode());
1726
        if (detail && detail->getTypeId() == SoFaceDetail::getClassTypeId()) {
1727
            // get the boundary to the picked facet
1728
            const SoFaceDetail* faceDetail = static_cast<const SoFaceDetail*>(detail);
1729
            Mesh::FacetIndex uFacet = faceDetail->getFaceIndex();
1730
            that->faceInfo(uFacet);
1731
            Gui::GLFlagWindow* flags = nullptr;
1732
            std::list<Gui::GLGraphicsItem*> glItems =
1733
                view->getGraphicsItemsOfType(Gui::GLFlagWindow::getClassTypeId());
1734
            if (glItems.empty()) {
1735
                flags = new Gui::GLFlagWindow(view);
1736
                view->addGraphicsItem(flags);
1737
            }
1738
            else {
1739
                flags = static_cast<Gui::GLFlagWindow*>(glItems.front());
1740
            }
1741

1742
            int point1 =
1743
                static_cast<const SoPointDetail*>(faceDetail->getPoint(0))->getCoordinateIndex();
1744
            int point2 =
1745
                static_cast<const SoPointDetail*>(faceDetail->getPoint(1))->getCoordinateIndex();
1746
            int point3 =
1747
                static_cast<const SoPointDetail*>(faceDetail->getPoint(2))->getCoordinateIndex();
1748
            Gui::Flag* flag = new Gui::Flag;
1749
            flag->setText(QObject::tr("Index: %1").arg(uFacet));
1750
            QString toolTip = QString::fromLatin1("Facet index: %1\n"
1751
                                                  "Points: <%2, %3, %4>")
1752
                                  .arg(uFacet)
1753
                                  .arg(point1)
1754
                                  .arg(point2)
1755
                                  .arg(point3);
1756
            flag->setToolTip(toolTip);
1757
            flag->setOrigin(point->getPoint());
1758
            flags->addFlag(flag, Gui::FlagLayout::TopRight);
1759
        }
1760
    }
1761
}
1762

1763
void ViewProviderMesh::fillHoleCallback(void* ud, SoEventCallback* n)
1764
{
1765
    const SoMouseButtonEvent* mbe = static_cast<const SoMouseButtonEvent*>(n->getEvent());
1766
    Gui::View3DInventorViewer* view = static_cast<Gui::View3DInventorViewer*>(n->getUserData());
1767

1768
    // Mark all incoming mouse button events as handled, especially, to deactivate the selection
1769
    // node
1770
    n->getAction()->setHandled();
1771
    if (mbe->getButton() == SoMouseButtonEvent::BUTTON2 && mbe->getState() == SoButtonEvent::UP) {
1772
        n->setHandled();
1773
        // context-menu
1774
        QMenu menu;
1775
        QAction* cl = menu.addAction(QObject::tr("Leave hole-filling mode"));
1776
        QAction* id = menu.exec(QCursor::pos());
1777
        if (cl == id) {
1778
            view->setEditing(false);
1779
            view->setSelectionEnabled(true);
1780
            view->getWidget()->setCursor(QCursor(Qt::ArrowCursor));
1781
            view->removeEventCallback(SoMouseButtonEvent::getClassTypeId(), fillHoleCallback, ud);
1782
        }
1783
    }
1784
    else if (mbe->getButton() == SoMouseButtonEvent::BUTTON1
1785
             && mbe->getState() == SoButtonEvent::DOWN) {
1786
        const SoPickedPoint* point = n->getPickedPoint();
1787
        if (!point) {
1788
            Base::Console().Message("No facet picked.\n");
1789
            return;
1790
        }
1791

1792
        n->setHandled();
1793

1794
        // By specifying the indexed mesh node 'pcFaceSet' we make sure that the picked point is
1795
        // really from the mesh we render and not from any other geometry
1796
        Gui::ViewProvider* vp = view->getViewProviderByPathFromTail(point->getPath());
1797
        if (!vp || !vp->isDerivedFrom<ViewProviderMesh>()) {
1798
            return;
1799
        }
1800
        ViewProviderMesh* that = static_cast<ViewProviderMesh*>(vp);
1801
        const SoDetail* detail = point->getDetail(that->getShapeNode());
1802
        if (detail && detail->getTypeId() == SoFaceDetail::getClassTypeId()) {
1803
            // get the boundary to the picked facet
1804
            Mesh::FacetIndex uFacet = ((SoFaceDetail*)detail)->getFaceIndex();
1805
            that->fillHole(uFacet);
1806
        }
1807
    }
1808
}
1809

1810
void ViewProviderMesh::markPartCallback(void* ud, SoEventCallback* n)
1811
{
1812
    // handle only mouse button events
1813
    if (n->getEvent()->isOfType(SoMouseButtonEvent::getClassTypeId())) {
1814
        const SoMouseButtonEvent* mbe = static_cast<const SoMouseButtonEvent*>(n->getEvent());
1815
        Gui::View3DInventorViewer* view = static_cast<Gui::View3DInventorViewer*>(n->getUserData());
1816

1817
        // Mark all incoming mouse button events as handled, especially, to deactivate the selection
1818
        // node
1819
        n->getAction()->setHandled();
1820
        if (mbe->getButton() == SoMouseButtonEvent::BUTTON2
1821
            && mbe->getState() == SoButtonEvent::UP) {
1822
            n->setHandled();
1823
            // context-menu
1824
            QMenu menu;
1825
            QAction* cl = menu.addAction(QObject::tr("Leave removal mode"));
1826
            QAction* rm = menu.addAction(QObject::tr("Delete selected faces"));
1827
            QAction* cf = menu.addAction(QObject::tr("Clear selected faces"));
1828
            QAction* id = menu.exec(QCursor::pos());
1829
            if (cl == id) {
1830
                view->setEditing(false);
1831
                view->setSelectionEnabled(true);
1832
                view->removeEventCallback(SoMouseButtonEvent::getClassTypeId(),
1833
                                          markPartCallback,
1834
                                          ud);
1835

1836
                std::vector<ViewProvider*> views =
1837
                    view->getViewProvidersOfType(ViewProviderMesh::getClassTypeId());
1838
                for (auto view : views) {
1839
                    static_cast<ViewProviderMesh*>(view)->clearSelection();
1840
                }
1841
            }
1842
            else if (cf == id) {
1843
                std::vector<ViewProvider*> views =
1844
                    view->getViewProvidersOfType(ViewProviderMesh::getClassTypeId());
1845
                for (auto view : views) {
1846
                    static_cast<ViewProviderMesh*>(view)->clearSelection();
1847
                }
1848
            }
1849
            else if (rm == id) {
1850
                Gui::Application::Instance->activeDocument()->openCommand(
1851
                    QT_TRANSLATE_NOOP("Command", "Delete"));
1852
                std::vector<ViewProvider*> views =
1853
                    view->getViewProvidersOfType(ViewProviderMesh::getClassTypeId());
1854
                for (auto view : views) {
1855
                    static_cast<ViewProviderMesh*>(view)->deleteSelection();
1856
                }
1857
                view->redraw();
1858
                Gui::Application::Instance->activeDocument()->commitCommand();
1859
            }
1860
        }
1861
        else if (mbe->getButton() == SoMouseButtonEvent::BUTTON1
1862
                 && mbe->getState() == SoButtonEvent::DOWN) {
1863
            const SoPickedPoint* point = n->getPickedPoint();
1864
            if (!point) {
1865
                Base::Console().Message("No facet picked.\n");
1866
                return;
1867
            }
1868

1869
            n->setHandled();
1870

1871
            // By specifying the indexed mesh node 'pcFaceSet' we make sure that the picked point is
1872
            // really from the mesh we render and not from any other geometry
1873
            Gui::ViewProvider* vp = view->getViewProviderByPathFromTail(point->getPath());
1874
            if (!vp || !vp->isDerivedFrom<ViewProviderMesh>()) {
1875
                return;
1876
            }
1877
            ViewProviderMesh* that = static_cast<ViewProviderMesh*>(vp);
1878
            const SoDetail* detail = point->getDetail(that->getShapeNode());
1879
            if (detail && detail->getTypeId() == SoFaceDetail::getClassTypeId()) {
1880
                // get the boundary to the picked facet
1881
                Mesh::FacetIndex uFacet = static_cast<const SoFaceDetail*>(detail)->getFaceIndex();
1882
                that->selectComponent(uFacet);
1883
            }
1884
        }
1885
    }
1886
}
1887

1888
void ViewProviderMesh::faceInfo(Mesh::FacetIndex uFacet)
1889
{
1890
    Mesh::Feature* fea = static_cast<Mesh::Feature*>(this->getObject());
1891
    const MeshCore::MeshKernel& rKernel = fea->Mesh.getValue().getKernel();
1892
    const MeshCore::MeshFacetArray& facets = rKernel.GetFacets();
1893
    if (uFacet < facets.size()) {
1894
        MeshCore::MeshFacet face = facets[uFacet];
1895
        MeshCore::MeshGeomFacet tria = rKernel.GetFacet(face);
1896
        Base::Console().Message(
1897
            "Mesh: %s Facet %lu: Points: <%lu, %lu, %lu>, Neighbours: <%lu, %lu, %lu>\n"
1898
            "Triangle: <[%.6f, %.6f, %.6f], [%.6f, %.6f, %.6f], [%.6f, %.6f, %.6f]>\n",
1899
            fea->getNameInDocument(),
1900
            uFacet,
1901
            face._aulPoints[0],
1902
            face._aulPoints[1],
1903
            face._aulPoints[2],
1904
            face._aulNeighbours[0],
1905
            face._aulNeighbours[1],
1906
            face._aulNeighbours[2],
1907
            tria._aclPoints[0].x,
1908
            tria._aclPoints[0].y,
1909
            tria._aclPoints[0].z,
1910
            tria._aclPoints[1].x,
1911
            tria._aclPoints[1].y,
1912
            tria._aclPoints[1].z,
1913
            tria._aclPoints[2].x,
1914
            tria._aclPoints[2].y,
1915
            tria._aclPoints[2].z);
1916
    }
1917
}
1918

1919
void ViewProviderMesh::fillHole(Mesh::FacetIndex uFacet)
1920
{
1921
    // get parameter from user settings
1922
    Base::Reference<ParameterGrp> hGrp =
1923
        Gui::WindowParameter::getDefaultParameter()->GetGroup("Mod/Mesh");
1924
    int level = (int)hGrp->GetInt("FillHoleLevel", 2);
1925

1926
    // get the boundary to the picked facet
1927
    std::list<Mesh::PointIndex> aBorder;
1928
    Mesh::Feature* fea = static_cast<Mesh::Feature*>(this->getObject());
1929
    const MeshCore::MeshKernel& rKernel = fea->Mesh.getValue().getKernel();
1930
    MeshCore::MeshRefPointToFacets cPt2Fac(rKernel);
1931
    MeshCore::MeshAlgorithm meshAlg(rKernel);
1932
    meshAlg.GetFacetBorder(uFacet, aBorder);
1933
    std::vector<Mesh::PointIndex> boundary(aBorder.begin(), aBorder.end());
1934
    std::list<std::vector<Mesh::PointIndex>> boundaries;
1935
    boundaries.push_back(boundary);
1936
    meshAlg.SplitBoundaryLoops(boundaries);
1937

1938
    std::vector<MeshCore::MeshFacet> newFacets;
1939
    std::vector<Base::Vector3f> newPoints;
1940
    unsigned long numberOfOldPoints = rKernel.CountPoints();
1941
    for (const auto& it : boundaries) {
1942
        if (it.size() < 3 /* || it->size() > 200*/) {
1943
            continue;
1944
        }
1945
        boundary = it;
1946
        MeshCore::MeshFacetArray faces;
1947
        MeshCore::MeshPointArray points;
1948
        MeshCore::QuasiDelaunayTriangulator cTria /*(0.05f)*/;
1949
        cTria.SetVerifier(new MeshCore::TriangulationVerifierV2);
1950
        if (meshAlg.FillupHole(boundary, cTria, faces, points, level, &cPt2Fac)) {
1951
            if (boundary.front() == boundary.back()) {
1952
                boundary.pop_back();
1953
            }
1954
            // the triangulation may produce additional points which we must take into account when
1955
            // appending to the mesh
1956
            unsigned long countBoundaryPoints = boundary.size();
1957
            unsigned long countDifference = points.size() - countBoundaryPoints;
1958
            if (countDifference > 0) {
1959
                MeshCore::MeshPointArray::_TIterator pt = points.begin() + countBoundaryPoints;
1960
                for (unsigned long i = 0; i < countDifference; i++, pt++) {
1961
                    boundary.push_back(numberOfOldPoints++);
1962
                    newPoints.push_back(*pt);
1963
                }
1964
            }
1965
            for (auto& face : faces) {
1966
                face._aulPoints[0] = boundary[face._aulPoints[0]];
1967
                face._aulPoints[1] = boundary[face._aulPoints[1]];
1968
                face._aulPoints[2] = boundary[face._aulPoints[2]];
1969
                newFacets.push_back(face);
1970
            }
1971
        }
1972
    }
1973

1974
    if (newFacets.empty()) {
1975
        return;  // nothing to do
1976
    }
1977

1978
    // add the facets to the mesh and open a transaction object for the undo/redo stuff
1979
    Gui::Application::Instance->activeDocument()->openCommand(
1980
        QT_TRANSLATE_NOOP("Command", "Fill hole"));
1981
    Mesh::MeshObject* kernel = fea->Mesh.startEditing();
1982
    kernel->addFacets(newFacets, newPoints, true);
1983
    fea->Mesh.finishEditing();
1984
    Gui::Application::Instance->activeDocument()->commitCommand();
1985
}
1986

1987
void ViewProviderMesh::setFacetTransparency(const std::vector<float>& facetTransparency)
1988
{
1989
    if (pcShapeMaterial->diffuseColor.getNum() != int(facetTransparency.size())) {
1990
        App::Color c = ShapeColor.getValue();
1991
        pcShapeMaterial->diffuseColor.setNum(facetTransparency.size());
1992
        SbColor* cols = pcShapeMaterial->diffuseColor.startEditing();
1993
        for (std::size_t index = 0; index < facetTransparency.size(); ++index) {
1994
            cols[index].setValue(c.r, c.g, c.b);
1995
        }
1996
        pcShapeMaterial->diffuseColor.finishEditing();
1997
    }
1998

1999
    pcShapeMaterial->transparency.setNum(facetTransparency.size());
2000
    float* tran = pcShapeMaterial->transparency.startEditing();
2001
    for (std::size_t index = 0; index < facetTransparency.size(); ++index) {
2002
        tran[index] = facetTransparency[index];
2003
    }
2004

2005
    pcShapeMaterial->transparency.finishEditing();
2006
    pcMatBinding->value = SoMaterialBinding::PER_FACE;
2007
}
2008

2009
void ViewProviderMesh::resetFacetTransparency()
2010
{
2011
    pcMatBinding->value = SoMaterialBinding::OVERALL;
2012
    App::Color c = ShapeColor.getValue();
2013
    pcShapeMaterial->diffuseColor.setValue(c.r, c.g, c.b);
2014
    pcShapeMaterial->transparency.setValue(0);
2015
}
2016

2017
/*! The triangles with the passed indices are already added to the mesh. */
2018
void ViewProviderMesh::appendFacets(const std::vector<Mesh::FacetIndex>&)
2019
{}
2020

2021
void ViewProviderMesh::removeFacets(const std::vector<Mesh::FacetIndex>& facets)
2022
{
2023
    // Get the attached mesh property
2024
    Mesh::PropertyMeshKernel& meshProp = static_cast<Mesh::Feature*>(pcObject)->Mesh;
2025
    Mesh::MeshObject* kernel = meshProp.startEditing();
2026

2027
    // get the colour property if there
2028
    App::PropertyColorList* prop = getColorProperty();
2029
    bool ok = Coloring.getValue();
2030

2031
    if (prop && prop->getSize() == static_cast<int>(kernel->countPoints())) {
2032
        std::vector<Mesh::PointIndex> pointDegree;
2033
        unsigned long invalid = kernel->getPointDegree(facets, pointDegree);
2034
        if (invalid > 0) {
2035
            // switch off coloring mode
2036
            Coloring.setValue(false);
2037

2038
            const std::vector<App::Color>& colors = prop->getValues();
2039
            std::vector<App::Color> valid_colors;
2040
            valid_colors.reserve(kernel->countPoints() - invalid);
2041
            std::size_t numPoints = pointDegree.size();
2042
            for (std::size_t index = 0; index < numPoints; index++) {
2043
                if (pointDegree[index] > 0) {
2044
                    valid_colors.push_back(colors[index]);
2045
                }
2046
            }
2047

2048
            prop->setValues(valid_colors);
2049
        }
2050
    }
2051
    else if (prop && prop->getSize() == static_cast<int>(kernel->countFacets())) {
2052
        // switch off coloring mode
2053
        Coloring.setValue(false);
2054

2055
        std::vector<bool> validFacets(kernel->countFacets(), true);
2056
        for (auto it : facets) {
2057
            validFacets[it] = false;
2058
        }
2059

2060
        const std::vector<App::Color>& colors = prop->getValues();
2061
        std::vector<App::Color> valid_colors;
2062
        valid_colors.reserve(colors.size());
2063
        std::size_t numColors = colors.size();
2064
        for (std::size_t index = 0; index < numColors; index++) {
2065
            if (validFacets[index]) {
2066
                valid_colors.push_back(colors[index]);
2067
            }
2068
        }
2069

2070
        prop->setValues(valid_colors);
2071
    }
2072

2073
    // Remove the facets from the mesh and open a transaction object for the undo/redo stuff
2074
    kernel->deleteFacets(facets);
2075
    meshProp.finishEditing();
2076
    pcObject->purgeTouched();
2077

2078
    Coloring.setValue(ok);
2079
}
2080

2081
void ViewProviderMesh::selectFacet(Mesh::FacetIndex facet)
2082
{
2083
    std::vector<Mesh::FacetIndex> selection;
2084
    selection.push_back(facet);
2085

2086
    const Mesh::MeshObject& rMesh = static_cast<Mesh::Feature*>(pcObject)->Mesh.getValue();
2087
    rMesh.addFacetsToSelection(selection);
2088

2089
    // Colorize the selection
2090
    pcMatBinding->value = SoMaterialBinding::PER_FACE;
2091
    int uCtFacets = (int)rMesh.countFacets();
2092

2093
    if (uCtFacets != pcShapeMaterial->diffuseColor.getNum()) {
2094
        highlightSelection();
2095
    }
2096
    else {
2097
        pcShapeMaterial->diffuseColor.set1Value(facet, 1.0F, 0.0F, 0.0F);
2098
    }
2099
}
2100

2101
void ViewProviderMesh::deselectFacet(Mesh::FacetIndex facet)
2102
{
2103
    std::vector<Mesh::FacetIndex> selection;
2104
    selection.push_back(facet);
2105

2106
    const Mesh::MeshObject& rMesh = static_cast<Mesh::Feature*>(pcObject)->Mesh.getValue();
2107
    rMesh.removeFacetsFromSelection(selection);
2108

2109
    // Colorize the selection
2110
    pcMatBinding->value = SoMaterialBinding::PER_FACE;
2111
    int uCtFacets = (int)rMesh.countFacets();
2112

2113
    if (rMesh.hasSelectedFacets()) {
2114
        if (uCtFacets != pcShapeMaterial->diffuseColor.getNum()) {
2115
            highlightSelection();
2116
        }
2117
        else {
2118
            App::Color c = ShapeColor.getValue();
2119
            pcShapeMaterial->diffuseColor.set1Value(facet, c.r, c.g, c.b);
2120
        }
2121
    }
2122
    else {
2123
        unhighlightSelection();
2124
    }
2125
}
2126

2127
bool ViewProviderMesh::isFacetSelected(Mesh::FacetIndex facet)
2128
{
2129
    const Mesh::MeshObject& rMesh = static_cast<Mesh::Feature*>(pcObject)->Mesh.getValue();
2130
    const MeshCore::MeshFacetArray& faces = rMesh.getKernel().GetFacets();
2131
    return faces[facet].IsFlag(MeshCore::MeshFacet::SELECTED);
2132
}
2133

2134
void ViewProviderMesh::selectComponent(Mesh::FacetIndex uFacet)
2135
{
2136
    std::vector<Mesh::FacetIndex> selection;
2137
    selection.push_back(uFacet);
2138

2139
    MeshCore::MeshTopFacetVisitor clVisitor(selection);
2140
    const Mesh::MeshObject& rMesh = static_cast<Mesh::Feature*>(pcObject)->Mesh.getValue();
2141
    const MeshCore::MeshKernel& rKernel = rMesh.getKernel();
2142
    MeshCore::MeshAlgorithm(rKernel).ResetFacetFlag(MeshCore::MeshFacet::VISIT);
2143
    rKernel.VisitNeighbourFacets(clVisitor, uFacet);
2144
    rMesh.addFacetsToSelection(selection);
2145

2146
    // Colorize the selection
2147
    highlightSelection();
2148
}
2149

2150
void ViewProviderMesh::deselectComponent(Mesh::FacetIndex uFacet)
2151
{
2152
    std::vector<Mesh::FacetIndex> selection;
2153
    selection.push_back(uFacet);
2154

2155
    MeshCore::MeshTopFacetVisitor clVisitor(selection);
2156
    const Mesh::MeshObject& rMesh = static_cast<Mesh::Feature*>(pcObject)->Mesh.getValue();
2157
    const MeshCore::MeshKernel& rKernel = rMesh.getKernel();
2158
    MeshCore::MeshAlgorithm(rKernel).ResetFacetFlag(MeshCore::MeshFacet::VISIT);
2159
    rKernel.VisitNeighbourFacets(clVisitor, uFacet);
2160
    rMesh.removeFacetsFromSelection(selection);
2161

2162
    // Colorize the selection
2163
    if (rMesh.hasSelectedFacets()) {
2164
        highlightSelection();
2165
    }
2166
    else {
2167
        unhighlightSelection();
2168
    }
2169
}
2170

2171
void ViewProviderMesh::setSelection(const std::vector<Mesh::FacetIndex>& indices)
2172
{
2173
    const Mesh::MeshObject& rMesh = static_cast<Mesh::Feature*>(pcObject)->Mesh.getValue();
2174
    rMesh.clearFacetSelection();
2175
    rMesh.addFacetsToSelection(indices);
2176

2177
    // Colorize the selection
2178
    if (indices.empty()) {
2179
        unhighlightSelection();
2180
    }
2181
    else {
2182
        highlightSelection();
2183
    }
2184
}
2185

2186
void ViewProviderMesh::addSelection(const std::vector<Mesh::FacetIndex>& indices)
2187
{
2188
    const Mesh::MeshObject& rMesh = static_cast<Mesh::Feature*>(pcObject)->Mesh.getValue();
2189
    rMesh.addFacetsToSelection(indices);
2190

2191
    // Colorize the selection
2192
    highlightSelection();
2193
}
2194

2195
void ViewProviderMesh::removeSelection(const std::vector<Mesh::FacetIndex>& indices)
2196
{
2197
    const Mesh::MeshObject& rMesh = static_cast<Mesh::Feature*>(pcObject)->Mesh.getValue();
2198
    rMesh.removeFacetsFromSelection(indices);
2199

2200
    // Colorize the selection
2201
    if (rMesh.hasSelectedFacets()) {
2202
        highlightSelection();
2203
    }
2204
    else {
2205
        unhighlightSelection();
2206
    }
2207
}
2208

2209
void ViewProviderMesh::invertSelection()
2210
{
2211
    const Mesh::MeshObject& rMesh = static_cast<Mesh::Feature*>(pcObject)->Mesh.getValue();
2212
    const MeshCore::MeshFacetArray& faces = rMesh.getKernel().GetFacets();
2213
    MeshCore::MeshIsNotFlag<MeshCore::MeshFacet> flag;
2214
    unsigned long num_notsel =
2215
        std::count_if(faces.begin(), faces.end(), [flag](const MeshCore::MeshFacet& f) {
2216
            return flag(f, MeshCore::MeshFacet::SELECTED);
2217
        });
2218
    std::vector<Mesh::FacetIndex> notselect;
2219
    notselect.reserve(num_notsel);
2220
    MeshCore::MeshFacetArray::_TConstIterator beg = faces.begin();
2221
    MeshCore::MeshFacetArray::_TConstIterator end = faces.end();
2222
    for (MeshCore::MeshFacetArray::_TConstIterator jt = beg; jt != end; ++jt) {
2223
        if (!jt->IsFlag(MeshCore::MeshFacet::SELECTED)) {
2224
            notselect.push_back(jt - beg);
2225
        }
2226
    }
2227
    setSelection(notselect);
2228
}
2229

2230
void ViewProviderMesh::clearSelection()
2231
{
2232
    const Mesh::MeshObject& rMesh = static_cast<Mesh::Feature*>(pcObject)->Mesh.getValue();
2233
    rMesh.clearFacetSelection();
2234
    unhighlightSelection();
2235
}
2236

2237
void ViewProviderMesh::deleteSelection()
2238
{
2239
    std::vector<Mesh::FacetIndex> indices;
2240
    Mesh::PropertyMeshKernel& meshProp = static_cast<Mesh::Feature*>(pcObject)->Mesh;
2241
    const Mesh::MeshObject& rMesh = meshProp.getValue();
2242
    rMesh.getFacetsFromSelection(indices);
2243
    if (!indices.empty()) {
2244
        rMesh.clearFacetSelection();
2245
        unhighlightSelection();
2246
        removeFacets(indices);
2247
    }
2248
}
2249

2250
bool ViewProviderMesh::hasSelection() const
2251
{
2252
    Mesh::PropertyMeshKernel& meshProp = static_cast<Mesh::Feature*>(pcObject)->Mesh;
2253
    const Mesh::MeshObject& rMesh = meshProp.getValue();
2254
    return rMesh.hasSelectedFacets();
2255
}
2256

2257
void ViewProviderMesh::selectArea(short x,
2258
                                  short y,
2259
                                  short w,
2260
                                  short h,
2261
                                  const SbViewportRegion& region,
2262
                                  SoCamera* camera)
2263
{
2264
    SbViewportRegion vp;
2265
    vp.setViewportPixels(x, y, w, h);
2266
    std::vector<Mesh::FacetIndex> faces = getFacetsOfRegion(vp, region, camera);
2267

2268
    const Mesh::MeshObject& rMesh = static_cast<Mesh::Feature*>(pcObject)->Mesh.getValue();
2269
    rMesh.addFacetsToSelection(faces);
2270

2271
    // Colorize the selected part
2272
    highlightSelection();
2273
}
2274

2275
void ViewProviderMesh::highlightSelection()
2276
{
2277
    std::vector<Mesh::FacetIndex> selection;
2278
    const Mesh::MeshObject& rMesh = static_cast<Mesh::Feature*>(pcObject)->Mesh.getValue();
2279
    rMesh.getFacetsFromSelection(selection);
2280
    if (selection.empty()) {
2281
        // If no faces are selected then simply return even
2282
        // without calling unhighlightSelection()
2283
        return;
2284
    }
2285

2286
    // Colorize the selection
2287
    pcMatBinding->value = SoMaterialBinding::PER_FACE;
2288
    App::Color c = ShapeColor.getValue();
2289
    int uCtFacets = (int)rMesh.countFacets();
2290
    pcShapeMaterial->diffuseColor.setNum(uCtFacets);
2291

2292
    SbColor* cols = pcShapeMaterial->diffuseColor.startEditing();
2293
    for (int i = 0; i < uCtFacets; i++) {
2294
        cols[i].setValue(c.r, c.g, c.b);
2295
    }
2296
    for (Mesh::FacetIndex it : selection) {
2297
        cols[it].setValue(1.0F, 0.0F, 0.0F);
2298
    }
2299
    pcShapeMaterial->diffuseColor.finishEditing();
2300
}
2301

2302
void ViewProviderMesh::unhighlightSelection()
2303
{
2304
    App::Color c = ShapeColor.getValue();
2305
    pcMatBinding->value = SoMaterialBinding::OVERALL;
2306
    pcShapeMaterial->diffuseColor.setNum(1);
2307
    pcShapeMaterial->diffuseColor.setValue(c.r, c.g, c.b);
2308
}
2309

2310
void ViewProviderMesh::setHighlightedComponents(bool on)
2311
{
2312
    if (on) {
2313
        highlightMode = HighlighMode::Component;
2314
        highlightComponents();
2315
    }
2316
    else {
2317
        highlightMode = HighlighMode::None;
2318
        unhighlightSelection();
2319
    }
2320
}
2321

2322
void ViewProviderMesh::highlightComponents()
2323
{
2324
    const Mesh::MeshObject& rMesh = static_cast<Mesh::Feature*>(pcObject)->Mesh.getValue();
2325
    std::vector<std::vector<Mesh::FacetIndex>> comps = rMesh.getComponents();
2326

2327
    // Colorize the components
2328
    pcMatBinding->value = SoMaterialBinding::PER_FACE;
2329
    int uCtFacets = (int)rMesh.countFacets();
2330
    pcShapeMaterial->diffuseColor.setNum(uCtFacets);
2331

2332
    SbColor* cols = pcShapeMaterial->diffuseColor.startEditing();
2333
    for (const auto& comp : comps) {
2334
        float fMax = (float)RAND_MAX;
2335
        float fRed = (float)rand() / fMax;
2336
        float fGrn = (float)rand() / fMax;
2337
        float fBlu = (float)rand() / fMax;
2338
        for (Mesh::FacetIndex jt : comp) {
2339
            cols[jt].setValue(fRed, fGrn, fBlu);
2340
        }
2341
    }
2342
    pcShapeMaterial->diffuseColor.finishEditing();
2343
}
2344

2345
void ViewProviderMesh::setHighlightedSegments(bool on)
2346
{
2347
    if (on) {
2348
        highlightMode = HighlighMode::Segment;
2349
        highlightSegments();
2350
    }
2351
    else {
2352
        highlightMode = HighlighMode::None;
2353
        unhighlightSelection();
2354
    }
2355
}
2356

2357
void ViewProviderMesh::highlightSegments()
2358
{
2359
    std::vector<App::Color> colors;
2360
    const Mesh::MeshObject& rMesh = static_cast<Mesh::Feature*>(pcObject)->Mesh.getValue();
2361
    unsigned long numSegm = rMesh.countSegments();
2362
    colors.resize(numSegm, this->ShapeColor.getValue());
2363

2364
    for (unsigned long i = 0; i < numSegm; i++) {
2365
        App::Color col;
2366
        if (col.fromHexString(rMesh.getSegment(i).getColor())) {
2367
            colors[i] = col;
2368
        }
2369
    }
2370

2371
    highlightSegments(colors);
2372
}
2373

2374
void ViewProviderMesh::highlightSegments(const std::vector<App::Color>& colors)
2375
{
2376
    const Mesh::MeshObject& rMesh = static_cast<Mesh::Feature*>(pcObject)->Mesh.getValue();
2377
    unsigned long numSegm = rMesh.countSegments();
2378
    if (numSegm > 0 && numSegm == colors.size()) {
2379
        // Colorize the components
2380
        pcMatBinding->value = SoMaterialBinding::PER_FACE;
2381
        int uCtFacets = (int)rMesh.countFacets();
2382

2383
        pcShapeMaterial->diffuseColor.setNum(uCtFacets);
2384
        SbColor* cols = pcShapeMaterial->diffuseColor.startEditing();
2385
        for (unsigned long i = 0; i < numSegm; i++) {
2386
            std::vector<Mesh::FacetIndex> segm = rMesh.getSegment(i).getIndices();
2387
            float fRed = colors[i].r;
2388
            float fGrn = colors[i].g;
2389
            float fBlu = colors[i].b;
2390
            for (Mesh::FacetIndex it : segm) {
2391
                cols[it].setValue(fRed, fGrn, fBlu);
2392
            }
2393
        }
2394
        pcShapeMaterial->diffuseColor.finishEditing();
2395
    }
2396
    else if (colors.size() == 1) {
2397
        pcMatBinding->value = SoMaterialBinding::OVERALL;
2398
        float fRed = colors[0].r;
2399
        float fGrn = colors[0].g;
2400
        float fBlu = colors[0].b;
2401
        pcShapeMaterial->diffuseColor.setValue(fRed, fGrn, fBlu);
2402
    }
2403
}
2404

2405
void ViewProviderMesh::setHighlightedColors(bool on)
2406
{
2407
    if (on) {
2408
        highlightMode = HighlighMode::Color;
2409
        highlightColors();
2410
    }
2411
    else {
2412
        highlightMode = HighlighMode::None;
2413
        unhighlightSelection();
2414
    }
2415
}
2416

2417
void ViewProviderMesh::highlightColors()
2418
{
2419
    const Mesh::MeshObject& rMesh = static_cast<Mesh::Feature*>(pcObject)->Mesh.getValue();
2420
    {
2421
        App::PropertyColorList* prop = Base::freecad_dynamic_cast<App::PropertyColorList>(
2422
            pcObject->getPropertyByName("FaceColors"));
2423
        if (prop && prop->getSize() == int(rMesh.countFacets())) {
2424
            setColorPerFace(prop);
2425
        }
2426
    }
2427
    {
2428
        App::PropertyColorList* prop = Base::freecad_dynamic_cast<App::PropertyColorList>(
2429
            pcObject->getPropertyByName("VertexColors"));
2430
        if (prop && prop->getSize() == int(rMesh.countPoints())) {
2431
            setColorPerVertex(prop);
2432
        }
2433
    }
2434
}
2435

2436
bool ViewProviderMesh::canHighlightColors() const
2437
{
2438
    const Mesh::MeshObject& rMesh = static_cast<Mesh::Feature*>(pcObject)->Mesh.getValue();
2439
    {
2440
        App::PropertyColorList* prop = Base::freecad_dynamic_cast<App::PropertyColorList>(
2441
            pcObject->getPropertyByName("FaceColors"));
2442
        if (prop && prop->getSize() == int(rMesh.countFacets())) {
2443
            return true;
2444
        }
2445
    }
2446
    {
2447
        App::PropertyColorList* prop = Base::freecad_dynamic_cast<App::PropertyColorList>(
2448
            pcObject->getPropertyByName("VertexColors"));
2449
        if (prop && prop->getSize() == int(rMesh.countPoints())) {
2450
            return true;
2451
        }
2452
    }
2453

2454
    return false;
2455
}
2456

2457
PyObject* ViewProviderMesh::getPyObject()
2458
{
2459
    if (!pyViewObject) {
2460
        pyViewObject = new ViewProviderMeshPy(this);
2461
    }
2462
    pyViewObject->IncRef();
2463
    return pyViewObject;
2464
}
2465

2466
// ------------------------------------------------------
2467

2468
PROPERTY_SOURCE(MeshGui::ViewProviderIndexedFaceSet, MeshGui::ViewProviderMesh)
2469

2470
ViewProviderIndexedFaceSet::ViewProviderIndexedFaceSet()
2471
{
2472
    // NOLINTBEGIN
2473
    pcMeshCoord = nullptr;
2474
    pcMeshFaces = nullptr;
2475
    // NOLINTEND
2476
}
2477

2478
ViewProviderIndexedFaceSet::~ViewProviderIndexedFaceSet() = default;
2479

2480
/**
2481
 * Extracts the mesh data from the feature \a pcFeature and creates
2482
 * an Inventor node \a SoNode with these data.
2483
 */
2484
void ViewProviderIndexedFaceSet::attach(App::DocumentObject* pcFeat)
2485
{
2486
    ViewProviderMesh::attach(pcFeat);
2487

2488
    pcMeshCoord = new SoCoordinate3;
2489
    pcHighlight->addChild(pcMeshCoord);
2490

2491
    pcMeshFaces = new SoFCIndexedFaceSet;
2492
    pcHighlight->addChild(pcMeshFaces);
2493

2494
    // read the threshold from the preferences
2495
    Base::Reference<ParameterGrp> hGrp =
2496
        Gui::WindowParameter::getDefaultParameter()->GetGroup("Mod/Mesh");
2497
    int size = hGrp->GetInt("RenderTriangleLimit", -1);
2498
    if (size > 0) {
2499
        static_cast<SoFCIndexedFaceSet*>(pcMeshFaces)->renderTriangleLimit =
2500
            (unsigned int)(pow(10.0F, size));
2501
    }
2502
}
2503

2504
void ViewProviderIndexedFaceSet::updateData(const App::Property* prop)
2505
{
2506
    ViewProviderMesh::updateData(prop);
2507
    if (prop->is<Mesh::PropertyMeshKernel>()) {
2508
        ViewProviderMeshBuilder builder;
2509
        builder.createMesh(prop, pcMeshCoord, pcMeshFaces);
2510
        showOpenEdges(OpenEdges.getValue());
2511
        highlightSelection();
2512
    }
2513
}
2514

2515
void ViewProviderIndexedFaceSet::showOpenEdges(bool show)
2516
{
2517
    if (pcOpenEdge) {
2518
        // remove the node and destroy the data
2519
        pcRoot->removeChild(pcOpenEdge);
2520
        pcOpenEdge = nullptr;
2521
    }
2522

2523
    if (show) {
2524
        pcOpenEdge = new SoSeparator();
2525
        pcOpenEdge->addChild(pcLineStyle);
2526
        pcOpenEdge->addChild(pOpenColor);
2527

2528
        pcOpenEdge->addChild(pcMeshCoord);
2529
        SoIndexedLineSet* lines = new SoIndexedLineSet;
2530
        pcOpenEdge->addChild(lines);
2531

2532
        // add to the highlight node
2533
        pcRoot->addChild(pcOpenEdge);
2534

2535
        // Build up the lines with indices to the list of vertices 'pcMeshCoord'
2536
        int index = 0;
2537
        const MeshCore::MeshKernel& rMesh =
2538
            static_cast<Mesh::Feature*>(pcObject)->Mesh.getValue().getKernel();
2539
        const MeshCore::MeshFacetArray& rFaces = rMesh.GetFacets();
2540
        for (const auto& rFace : rFaces) {
2541
            for (int i = 0; i < 3; i++) {
2542
                if (rFace._aulNeighbours[i] == MeshCore::FACET_INDEX_MAX) {
2543
                    lines->coordIndex.set1Value(index++, rFace._aulPoints[i]);
2544
                    lines->coordIndex.set1Value(index++, rFace._aulPoints[(i + 1) % 3]);
2545
                    lines->coordIndex.set1Value(index++, SO_END_LINE_INDEX);
2546
                }
2547
            }
2548
        }
2549
    }
2550
}
2551

2552
SoShape* ViewProviderIndexedFaceSet::getShapeNode() const
2553
{
2554
    return this->pcMeshFaces;
2555
}
2556

2557
SoNode* ViewProviderIndexedFaceSet::getCoordNode() const
2558
{
2559
    return this->pcMeshCoord;
2560
}
2561

2562
// ------------------------------------------------------
2563

2564
PROPERTY_SOURCE(MeshGui::ViewProviderMeshObject, MeshGui::ViewProviderMesh)
2565

2566
ViewProviderMeshObject::ViewProviderMeshObject()
2567
{
2568
    // NOLINTBEGIN
2569
    pcMeshNode = nullptr;
2570
    pcMeshShape = nullptr;
2571
    // NOLINTEND
2572
}
2573

2574
ViewProviderMeshObject::~ViewProviderMeshObject() = default;
2575

2576
void ViewProviderMeshObject::attach(App::DocumentObject* pcFeat)
2577
{
2578
    ViewProviderMesh::attach(pcFeat);
2579

2580
    pcMeshNode = new SoFCMeshObjectNode;
2581
    pcHighlight->addChild(pcMeshNode);
2582

2583
    pcMeshShape = new SoFCMeshObjectShape;
2584
    pcHighlight->addChild(pcMeshShape);
2585

2586
    // read the threshold from the preferences
2587
    Base::Reference<ParameterGrp> hGrp =
2588
        Gui::WindowParameter::getDefaultParameter()->GetGroup("Mod/Mesh");
2589
    int size = hGrp->GetInt("RenderTriangleLimit", -1);
2590
    if (size > 0) {
2591
        pcMeshShape->renderTriangleLimit = (unsigned int)(pow(10.0F, size));
2592
    }
2593
}
2594

2595
void ViewProviderMeshObject::updateData(const App::Property* prop)
2596
{
2597
    ViewProviderMesh::updateData(prop);
2598
    if (prop->is<Mesh::PropertyMeshKernel>()) {
2599
        const Mesh::PropertyMeshKernel* mesh = static_cast<const Mesh::PropertyMeshKernel*>(prop);
2600
        this->pcMeshNode->mesh.setValue(
2601
            Base::Reference<const Mesh::MeshObject>(mesh->getValuePtr()));
2602
        // Needs to update internal bounding box caches
2603
        this->pcMeshShape->touch();
2604
    }
2605
}
2606

2607
void ViewProviderMeshObject::showOpenEdges(bool show)
2608
{
2609
    if (pcOpenEdge) {
2610
        // remove the node and destroy the data
2611
        pcRoot->removeChild(pcOpenEdge);
2612
        pcOpenEdge = nullptr;
2613
    }
2614

2615
    if (show) {
2616
        pcOpenEdge = new SoSeparator();
2617
        pcOpenEdge->addChild(pcLineStyle);
2618
        pcOpenEdge->addChild(pOpenColor);
2619

2620
        pcOpenEdge->addChild(pcMeshNode);
2621
        pcOpenEdge->addChild(new SoFCMeshObjectBoundary);
2622

2623
        // add to the highlight node
2624
        pcRoot->addChild(pcOpenEdge);
2625
    }
2626
}
2627

2628
SoShape* ViewProviderMeshObject::getShapeNode() const
2629
{
2630
    return this->pcMeshShape;
2631
}
2632

2633
SoNode* ViewProviderMeshObject::getCoordNode() const
2634
{
2635
    return this->pcMeshNode;
2636
}
2637

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

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

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

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