1
/***************************************************************************
2
* Copyright (c) 2010 Werner Mayer <wmayer[at]users.sourceforge.net> *
4
* This file is part of the FreeCAD CAx development system. *
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. *
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. *
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 *
21
***************************************************************************/
23
#include "PreCompiled.h"
31
#include <Inventor/SbLine.h>
32
#include <Inventor/SoPickedPoint.h>
33
#include <Inventor/details/SoFaceDetail.h>
34
#include <Inventor/events/SoKeyboardEvent.h>
35
#include <Inventor/events/SoLocation2Event.h>
36
#include <Inventor/events/SoMouseButtonEvent.h>
37
#include <Inventor/nodes/SoBaseColor.h>
38
#include <Inventor/nodes/SoCamera.h>
39
#include <Inventor/nodes/SoCoordinate3.h>
40
#include <Inventor/nodes/SoDirectionalLight.h>
41
#include <Inventor/nodes/SoDrawStyle.h>
42
#include <Inventor/nodes/SoFaceSet.h>
43
#include <Inventor/nodes/SoPickStyle.h>
44
#include <Inventor/nodes/SoPointSet.h>
45
#include <Inventor/nodes/SoSeparator.h>
46
#include <Inventor/nodes/SoShapeHints.h>
49
#include <App/Application.h>
50
#include <App/Document.h>
51
#include <Gui/View3DInventor.h>
52
#include <Gui/View3DInventorViewer.h>
53
#include <Gui/WaitCursor.h>
54
#include <Mod/Mesh/App/MeshFeature.h>
55
#include <Mod/Mesh/App/Core/Algorithm.h>
57
#include "MeshEditor.h"
58
#include "SoFCMeshObject.h"
62
using namespace MeshGui;
63
namespace sp = std::placeholders;
65
PROPERTY_SOURCE(MeshGui::ViewProviderFace, Gui::ViewProviderDocumentObject)
67
ViewProviderFace::ViewProviderFace()
70
pcCoords = new SoCoordinate3();
72
pcCoords->point.setNum(0);
73
pcFaces = new SoFaceSet;
75
pcMeshPick = new SoFCMeshPickNode();
80
ViewProviderFace::~ViewProviderFace()
87
void ViewProviderFace::attach(App::DocumentObject* obj)
89
ViewProviderDocumentObject::attach(obj);
91
pcMeshPick->mesh.setValue(static_cast<Mesh::Feature*>(obj)->Mesh.getValuePtr());
94
SoGroup* markers = new SoGroup();
95
SoDrawStyle* pointStyle = new SoDrawStyle();
96
pointStyle->style = SoDrawStyle::POINTS;
97
pointStyle->pointSize = 8.0F;
98
markers->addChild(pointStyle);
100
SoBaseColor* markcol = new SoBaseColor;
101
markcol->rgb.setValue(1.0F, 1.0F, 0.0F);
102
SoPointSet* marker = new SoPointSet();
103
markers->addChild(markcol);
104
markers->addChild(pcCoords);
105
markers->addChild(marker);
108
SoGroup* faces = new SoGroup();
109
SoDrawStyle* faceStyle = new SoDrawStyle();
110
faceStyle->style = SoDrawStyle::FILLED;
111
faces->addChild(faceStyle);
113
SoShapeHints* flathints = new SoShapeHints;
114
// flathints->vertexOrdering = SoShapeHints::COUNTERCLOCKWISE ;
115
// flathints->shapeType = SoShapeHints::UNKNOWN_SHAPE_TYPE;
116
faces->addChild(flathints);
118
SoBaseColor* basecol = new SoBaseColor;
120
App::Color col = mesh->ShapeColor.getValue();
121
basecol->rgb.setValue(col.r, col.g, col.b);
124
basecol->rgb.setValue(1.0F, 0.0F, 0.0F);
127
faces->addChild(basecol);
128
faces->addChild(pcCoords);
129
faces->addChild(pcFaces);
131
SoGroup* face_marker = new SoGroup();
132
face_marker->addChild(faces);
133
face_marker->addChild(markers);
135
addDisplayMaskMode(markers, "Marker");
136
addDisplayMaskMode(face_marker, "Face");
137
setDisplayMode("Marker");
140
void ViewProviderFace::setDisplayMode(const char* ModeName)
142
if (strcmp(ModeName, "Face") == 0) {
143
setDisplayMaskMode("Face");
145
else if (strcmp(ModeName, "Marker") == 0) {
146
setDisplayMaskMode("Marker");
148
ViewProviderDocumentObject::setDisplayMode(ModeName);
151
const char* ViewProviderFace::getDefaultDisplayMode() const
156
std::vector<std::string> ViewProviderFace::getDisplayModes() const
158
std::vector<std::string> modes;
159
modes.emplace_back("Marker");
160
modes.emplace_back("Face");
164
SoPickedPoint* ViewProviderFace::getPickedPoint(const SbVec2s& pos,
165
const Gui::View3DInventorViewer* viewer) const
167
SoSeparator* root = new SoSeparator;
169
root->addChild(viewer->getHeadlight());
170
root->addChild(viewer->getSoRenderManager()->getCamera());
171
root->addChild(this->pcMeshPick);
173
SoRayPickAction rp(viewer->getSoRenderManager()->getViewportRegion());
178
// returns a copy of the point
179
SoPickedPoint* pick = rp.getPickedPoint();
180
// return (pick ? pick->copy() : 0); // needs the same instance of CRT under MS Windows
181
return (pick ? new SoPickedPoint(*pick) : nullptr);
184
// ----------------------------------------------------------------------
186
/* TRANSLATOR MeshGui::MeshFaceAddition */
188
MeshFaceAddition::MeshFaceAddition(Gui::View3DInventor* parent)
190
, faceView(new MeshGui::ViewProviderFace())
193
MeshFaceAddition::~MeshFaceAddition()
198
void MeshFaceAddition::startEditing(MeshGui::ViewProviderMesh* vp)
200
Gui::View3DInventor* view = static_cast<Gui::View3DInventor*>(parent());
201
Gui::View3DInventorViewer* viewer = view->getViewer();
202
viewer->setEditing(true);
203
viewer->setSelectionEnabled(false);
204
viewer->setRedirectToSceneGraph(true);
205
viewer->setRedirectToSceneGraphEnabled(true);
208
faceView->attach(vp->getObject());
209
viewer->addViewProvider(faceView);
210
// faceView->mesh->startEditing();
211
viewer->addEventCallback(SoEvent::getClassTypeId(), MeshFaceAddition::addFacetCallback, this);
214
void MeshFaceAddition::finishEditing()
216
Gui::View3DInventor* view = static_cast<Gui::View3DInventor*>(parent());
217
Gui::View3DInventorViewer* viewer = view->getViewer();
218
viewer->setEditing(false);
219
viewer->setSelectionEnabled(true);
220
viewer->setRedirectToSceneGraph(false);
221
viewer->setRedirectToSceneGraphEnabled(false);
223
viewer->removeViewProvider(faceView);
224
// faceView->mesh->finishEditing();
225
viewer->removeEventCallback(SoEvent::getClassTypeId(),
226
MeshFaceAddition::addFacetCallback,
231
void MeshFaceAddition::addFace()
233
Mesh::Feature* mf = static_cast<Mesh::Feature*>(faceView->mesh->getObject());
234
App::Document* doc = mf->getDocument();
235
doc->openTransaction("Add triangle");
236
Mesh::MeshObject* mesh = mf->Mesh.startEditing();
237
MeshCore::MeshFacet f;
238
f._aulPoints[0] = faceView->index[0];
239
f._aulPoints[1] = faceView->index[1];
240
f._aulPoints[2] = faceView->index[2];
241
std::vector<MeshCore::MeshFacet> faces;
243
mesh->addFacets(faces, true);
244
mf->Mesh.finishEditing();
245
doc->commitTransaction();
250
void MeshFaceAddition::clearPoints()
252
faceView->index.clear();
253
faceView->current_index = -1;
254
faceView->pcCoords->point.setNum(0);
255
faceView->setDisplayMode("Marker");
258
void MeshFaceAddition::flipNormal()
260
if (faceView->index.size() < 3) {
263
std::swap(faceView->index[0], faceView->index[1]);
264
SbVec3f v1 = faceView->pcCoords->point[0];
265
SbVec3f v2 = faceView->pcCoords->point[1];
266
faceView->pcCoords->point.set1Value(0, v2);
267
faceView->pcCoords->point.set1Value(1, v1);
270
bool MeshFaceAddition::addMarkerPoint()
272
if (faceView->current_index < 0) {
275
if (faceView->index.size() >= 3) {
278
faceView->index.push_back(faceView->current_index);
279
faceView->current_index = -1;
280
if (faceView->index.size() == 3) {
281
faceView->setDisplayMode("Face");
286
void MeshFaceAddition::showMarker(SoPickedPoint* pp)
288
const SbVec3f& vec = pp->getPoint();
289
const SoDetail* detail = pp->getDetail();
291
if (detail->isOfType(SoFaceDetail::getClassTypeId())) {
292
const SoFaceDetail* fd = static_cast<const SoFaceDetail*>(detail);
293
Mesh::Feature* mf = static_cast<Mesh::Feature*>(faceView->mesh->getObject());
294
const MeshCore::MeshFacetArray& facets =
295
mf->Mesh.getValuePtr()->getKernel().GetFacets();
296
const MeshCore::MeshPointArray& points =
297
mf->Mesh.getValuePtr()->getKernel().GetPoints();
298
// is the face index valid?
299
int face_index = fd->getFaceIndex();
300
if (face_index >= (int)facets.size()) {
303
// is a border facet picked?
304
MeshCore::MeshFacet f = facets[face_index];
305
if (!f.HasOpenEdge()) {
306
// check if a neighbour facet is at the border
308
for (Mesh::FacetIndex nbIndex : f._aulNeighbours) {
309
if (facets[nbIndex].HasOpenEdge()) {
320
int point_index = -1;
321
float distance = FLT_MAX;
325
for (int i = 0; i < 3; i++) {
326
int index = (int)f._aulPoints[i];
327
if (std::find(faceView->index.begin(), faceView->index.end(), index)
328
!= faceView->index.end()) {
329
continue; // already inside
331
if (f._aulNeighbours[i] == MeshCore::FACET_INDEX_MAX
332
|| f._aulNeighbours[(i + 2) % 3] == MeshCore::FACET_INDEX_MAX) {
334
float len = Base::DistanceP2(pnt, Base::Vector3f(vec[0], vec[1], vec[2]));
335
if (len < distance) {
338
face_pnt.setValue(pnt.x, pnt.y, pnt.z);
343
if (point_index < 0) {
344
return; // picked point is rejected
347
int num = faceView->pcCoords->point.getNum();
348
if (faceView->current_index >= 0) {
349
num = std::max<int>(num - 1, 0);
351
faceView->current_index = point_index;
352
faceView->pcCoords->point.set1Value(num, face_pnt);
358
void MeshFaceAddition::addFacetCallback(void* ud, SoEventCallback* n)
360
MeshFaceAddition* that = static_cast<MeshFaceAddition*>(ud);
361
ViewProviderFace* face = that->faceView;
362
Gui::View3DInventorViewer* view = static_cast<Gui::View3DInventorViewer*>(n->getUserData());
364
const SoEvent* ev = n->getEvent();
365
// If we are in navigation mode then ignore all but key events
366
if (!view->isRedirectedToSceneGraph()) {
367
if (!ev->getTypeId().isDerivedFrom(SoKeyboardEvent::getClassTypeId())) {
371
if (ev->getTypeId() == SoLocation2Event::getClassTypeId()) {
373
if (face->index.size() < 3) {
374
SoPickedPoint* point = face->getPickedPoint(ev->getPosition(), view);
376
that->showMarker(point);
381
else if (ev->getTypeId() == SoMouseButtonEvent::getClassTypeId()) {
382
const SoMouseButtonEvent* mbe = static_cast<const SoMouseButtonEvent*>(ev);
383
if (mbe->getButton() == SoMouseButtonEvent::BUTTON1
384
|| mbe->getButton() == SoMouseButtonEvent::BUTTON2
385
|| mbe->getButton() == SoMouseButtonEvent::BUTTON3) {
388
if (mbe->getButton() == SoMouseButtonEvent::BUTTON1
389
&& mbe->getState() == SoButtonEvent::DOWN) {
390
that->addMarkerPoint();
392
else if (mbe->getButton() == SoMouseButtonEvent::BUTTON1
393
&& mbe->getState() == SoButtonEvent::UP) {
394
if (face->index.size() == 3) {
396
QAction* add = menu.addAction(MeshFaceAddition::tr("Add triangle"));
397
QAction* swp = menu.addAction(MeshFaceAddition::tr("Flip normal"));
398
QAction* clr = menu.addAction(MeshFaceAddition::tr("Clear"));
399
QAction* act = menu.exec(QCursor::pos());
401
QTimer::singleShot(300, that, &MeshFaceAddition::addFace);
403
else if (act == swp) {
404
QTimer::singleShot(300, that, &MeshFaceAddition::flipNormal);
406
else if (act == clr) {
407
QTimer::singleShot(300, that, &MeshFaceAddition::clearPoints);
411
else if (mbe->getButton() == SoMouseButtonEvent::BUTTON2
412
&& mbe->getState() == SoButtonEvent::UP) {
414
QAction* fin = menu.addAction(MeshFaceAddition::tr("Finish"));
415
QAction* act = menu.exec(QCursor::pos());
417
QTimer::singleShot(300, that, &MeshFaceAddition::finishEditing);
421
// toggle between edit and navigation mode
422
else if (ev->getTypeId().isDerivedFrom(SoKeyboardEvent::getClassTypeId())) {
423
const SoKeyboardEvent* const ke = static_cast<const SoKeyboardEvent*>(ev);
424
if (ke->getState() == SoButtonEvent::DOWN && ke->getKey() == SoKeyboardEvent::ESCAPE) {
425
SbBool toggle = view->isRedirectedToSceneGraph();
426
view->setRedirectToSceneGraph(!toggle);
432
// ----------------------------------------------------------------------
436
// for sorting of elements
437
struct NofFacetsCompare
439
bool operator()(const std::vector<Mesh::PointIndex>& rclC1,
440
const std::vector<Mesh::PointIndex>& rclC2)
442
return rclC1.size() < rclC2.size();
445
} // namespace MeshGui
447
/* TRANSLATOR MeshGui::MeshFillHole */
449
MeshFillHole::MeshFillHole(MeshHoleFiller& hf, Gui::View3DInventor* parent)
454
myBoundariesRoot = new SoSeparator;
455
myBoundariesRoot->ref();
456
myBoundaryRoot = new SoSeparator;
457
myBoundaryRoot->ref();
458
myBoundariesGroup = new SoSeparator();
459
myBoundariesGroup->ref();
460
myBridgeRoot = new SoSeparator;
463
SoDrawStyle* pointStyle = new SoDrawStyle();
464
pointStyle->style = SoDrawStyle::POINTS;
465
pointStyle->pointSize = 8.0f;
466
myBridgeRoot->addChild(pointStyle);
468
SoBaseColor* markcol = new SoBaseColor;
469
markcol->rgb.setValue(1.0f, 1.0f, 0.0f);
470
myBridgeRoot->addChild(markcol);
472
myVertex = new SoCoordinate3();
473
myBridgeRoot->addChild(myVertex);
474
myBridgeRoot->addChild(new SoPointSet);
478
MeshFillHole::~MeshFillHole()
480
myBoundariesRoot->unref();
481
myBoundariesGroup->unref();
482
myBoundaryRoot->unref();
483
myBridgeRoot->unref();
486
void MeshFillHole::startEditing(MeshGui::ViewProviderMesh* vp)
488
this->myMesh = static_cast<Mesh::Feature*>(vp->getObject());
490
Gui::View3DInventor* view = static_cast<Gui::View3DInventor*>(parent());
491
Gui::View3DInventorViewer* viewer = view->getViewer();
492
viewer->setEditing(true);
493
// viewer->setRedirectToSceneGraph(true);
494
viewer->addEventCallback(SoEvent::getClassTypeId(), MeshFillHole::fileHoleCallback, this);
496
myConnection = App::GetApplication().signalChangedObject.connect(
497
std::bind(&MeshFillHole::slotChangedObject, this, sp::_1, sp::_2));
500
Gui::coinRemoveAllChildren(myBoundariesRoot);
501
myBoundariesRoot->addChild(viewer->getHeadlight());
502
myBoundariesRoot->addChild(viewer->getSoRenderManager()->getCamera());
503
myBoundariesRoot->addChild(myBoundariesGroup);
504
Gui::coinRemoveAllChildren(myBoundaryRoot);
505
myBoundaryRoot->addChild(viewer->getHeadlight());
506
myBoundaryRoot->addChild(viewer->getSoRenderManager()->getCamera());
508
static_cast<SoGroup*>(viewer->getSceneGraph())->addChild(myBridgeRoot);
511
void MeshFillHole::finishEditing()
513
Gui::View3DInventor* view = static_cast<Gui::View3DInventor*>(parent());
514
Gui::View3DInventorViewer* viewer = view->getViewer();
515
viewer->setEditing(false);
516
// viewer->setRedirectToSceneGraph(false);
517
viewer->removeEventCallback(SoEvent::getClassTypeId(), MeshFillHole::fileHoleCallback, this);
518
myConnection.disconnect();
520
static_cast<SoGroup*>(viewer->getSceneGraph())->removeChild(myBridgeRoot);
523
void MeshFillHole::closeBridge()
525
// Do the hole-filling
527
auto it = std::find(myPolygon.begin(), myPolygon.end(), myVertex1);
528
auto jt = std::find(myPolygon.begin(), myPolygon.end(), myVertex2);
529
if (it != myPolygon.end() && jt != myPolygon.end()) {
530
// which iterator comes first
534
// split the boundary into two loops and take the shorter one
535
std::list<TBoundary> bounds;
538
loop1.insert(loop1.end(), myPolygon.begin(), it);
539
loop1.insert(loop1.end(), jt, myPolygon.end());
540
loop2.insert(loop2.end(), it, jt);
541
// this happens when myVertex1 == myVertex2
543
bounds.push_back(loop1);
545
else if (loop1.size() < loop2.size()) {
546
bounds.push_back(loop1);
549
bounds.push_back(loop2);
552
App::Document* doc = myMesh->getDocument();
553
doc->openTransaction("Bridge && Fill hole");
554
Mesh::MeshObject* pMesh = myMesh->Mesh.startEditing();
555
bool ok = myHoleFiller.fillHoles(*pMesh, bounds, myVertex1, myVertex2);
556
myMesh->Mesh.finishEditing();
558
doc->commitTransaction();
561
doc->abortTransaction();
566
void MeshFillHole::slotChangedObject(const App::DocumentObject& Obj, const App::Property& Prop)
568
if (&Obj == myMesh && strcmp(Prop.getName(), "Mesh") == 0) {
569
Gui::coinRemoveAllChildren(myBoundariesGroup);
570
myVertex->point.setNum(0);
578
void MeshFillHole::createPolygons()
583
SoPickStyle* pickStyle = new SoPickStyle();
584
pickStyle->style = SoPickStyle::BOUNDING_BOX;
585
myBoundariesGroup->addChild(pickStyle);
586
myBoundaryRoot->addChild(pickStyle);
589
const MeshCore::MeshKernel& rMesh = this->myMesh->Mesh.getValue().getKernel();
591
// get the mesh boundaries as an array of point indices
592
std::list<std::vector<Mesh::PointIndex>> borders;
593
MeshCore::MeshAlgorithm cAlgo(rMesh);
594
MeshCore::MeshPointIterator p_iter(rMesh);
595
cAlgo.GetMeshBorders(borders);
596
cAlgo.SplitBoundaryLoops(borders);
598
// sort the borders in ascending order of the number of edges
599
borders.sort(NofFacetsCompare());
602
for (auto& border : borders) {
603
if (border.front() == border.back()) {
606
count += border.size();
609
SoCoordinate3* coords = new SoCoordinate3();
610
myBoundariesGroup->addChild(coords);
611
myBoundaryRoot->addChild(coords);
613
coords->point.setNum(count);
615
for (const auto& border : borders) {
616
SoPolygon* polygon = new SoPolygon();
617
polygon->startIndex = index;
618
polygon->numVertices = border.size();
619
myBoundariesGroup->addChild(polygon);
620
myPolygons[polygon] = border;
621
for (Mesh::PointIndex jt : border) {
623
coords->point.set1Value(index++, p_iter->x, p_iter->y, p_iter->z);
628
SoNode* MeshFillHole::getPickedPolygon(
629
const SoRayPickAction& action /*SoNode* root, const SbVec2s& pos*/) const
631
SoPolygon* poly = nullptr;
632
const SoPickedPointList& points = action.getPickedPointList();
633
for (int i = 0; i < points.getLength(); i++) {
634
const SoPickedPoint* point = points[i];
636
&& point->getPath()->getTail()->getTypeId() == MeshGui::SoPolygon::getClassTypeId()) {
637
// we have something picked, now check if it was an SoPolygon node
638
SoPolygon* node = static_cast<SoPolygon*>(point->getPath()->getTail());
642
// check which polygon has less edges
643
else if (node->numVertices.getValue() < poly->numVertices.getValue()) {
652
float MeshFillHole::findClosestPoint(const SbLine& ray,
653
const TBoundary& polygon,
654
Mesh::PointIndex& vertex_index,
655
SbVec3f& closestPoint) const
657
// now check which vertex of the polygon is closest to the ray
658
float minDist = FLT_MAX;
659
vertex_index = MeshCore::POINT_INDEX_MAX;
661
const MeshCore::MeshKernel& rMesh = myMesh->Mesh.getValue().getKernel();
662
const MeshCore::MeshPointArray& pts = rMesh.GetPoints();
663
for (Mesh::PointIndex it : polygon) {
665
const Base::Vector3f& v = pts[it];
666
vertex.setValue(v.x, v.y, v.z);
667
SbVec3f point = ray.getClosestPoint(vertex);
668
float distance = (vertex - point).sqrLength();
669
if (distance < minDist) {
672
closestPoint = vertex;
679
void MeshFillHole::fileHoleCallback(void* ud, SoEventCallback* n)
681
MeshFillHole* self = static_cast<MeshFillHole*>(ud);
682
Gui::View3DInventorViewer* view = static_cast<Gui::View3DInventorViewer*>(n->getUserData());
684
const SoEvent* ev = n->getEvent();
685
if (ev->getTypeId() == SoLocation2Event::getClassTypeId()) {
687
SoRayPickAction rp(view->getSoRenderManager()->getViewportRegion());
688
rp.setPoint(ev->getPosition());
690
if (self->myNumPoints == 0) {
691
rp.apply(self->myBoundariesRoot);
694
rp.apply(self->myBoundaryRoot);
696
SoNode* node = self->getPickedPolygon(rp);
698
auto it = self->myPolygons.find(node);
699
if (it != self->myPolygons.end()) {
700
// now check which vertex of the polygon is closest to the ray
701
Mesh::PointIndex vertex_index {};
702
SbVec3f closestPoint;
704
self->findClosestPoint(rp.getLine(), it->second, vertex_index, closestPoint);
705
if (minDist < 1.0F) {
706
if (self->myNumPoints == 0) {
707
self->myVertex->point.set1Value(0, closestPoint);
710
self->myVertex->point.set1Value(1, closestPoint);
716
else if (ev->getTypeId() == SoMouseButtonEvent::getClassTypeId()) {
718
const SoMouseButtonEvent* mbe = static_cast<const SoMouseButtonEvent*>(ev);
719
if (mbe->getButton() == SoMouseButtonEvent::BUTTON1
720
&& mbe->getState() == SoButtonEvent::UP) {
721
if (self->myNumPoints > 1) {
724
SoRayPickAction rp(view->getSoRenderManager()->getViewportRegion());
725
rp.setPoint(ev->getPosition());
727
if (self->myNumPoints == 0) {
728
rp.apply(self->myBoundariesRoot);
731
rp.apply(self->myBoundaryRoot);
733
SoNode* node = self->getPickedPolygon(rp);
735
auto it = self->myPolygons.find(node);
736
if (it != self->myPolygons.end()) {
737
// now check which vertex of the polygon is closest to the ray
738
Mesh::PointIndex vertex_index {};
739
SbVec3f closestPoint;
740
float minDist = self->findClosestPoint(rp.getLine(),
744
if (minDist < 1.0F) {
745
if (self->myNumPoints == 0) {
746
self->myBoundaryRoot->addChild(node);
747
self->myVertex->point.set1Value(0, closestPoint);
748
self->myNumPoints = 1;
749
self->myVertex1 = vertex_index;
752
// myVertex2 can be equal to myVertex1 which does a full hole-filling
753
self->myBoundaryRoot->removeChild(node);
754
self->myVertex->point.set1Value(1, closestPoint);
755
self->myNumPoints = 2;
756
self->myVertex2 = vertex_index;
757
self->myPolygon = it->second;
758
QTimer::singleShot(300, self, &MeshFillHole::closeBridge);
764
else if (mbe->getButton() == SoMouseButtonEvent::BUTTON2
765
&& mbe->getState() == SoButtonEvent::UP) {
767
QAction* fin = menu.addAction(MeshFillHole::tr("Finish"));
768
QAction* act = menu.exec(QCursor::pos());
770
QTimer::singleShot(300, self, &MeshFillHole::finishEditing);
776
#include "moc_MeshEditor.cpp"