Developer Documentation
TopologyPlugin.cc
1/*===========================================================================*\
2* *
3* OpenFlipper *
4 * Copyright (c) 2001-2015, RWTH-Aachen University *
5 * Department of Computer Graphics and Multimedia *
6 * All rights reserved. *
7 * www.openflipper.org *
8 * *
9 *---------------------------------------------------------------------------*
10 * This file is part of OpenFlipper. *
11 *---------------------------------------------------------------------------*
12 * *
13 * Redistribution and use in source and binary forms, with or without *
14 * modification, are permitted provided that the following conditions *
15 * are met: *
16 * *
17 * 1. Redistributions of source code must retain the above copyright notice, *
18 * this list of conditions and the following disclaimer. *
19 * *
20 * 2. Redistributions in binary form must reproduce the above copyright *
21 * notice, this list of conditions and the following disclaimer in the *
22 * documentation and/or other materials provided with the distribution. *
23 * *
24 * 3. Neither the name of the copyright holder nor the names of its *
25 * contributors may be used to endorse or promote products derived from *
26 * this software without specific prior written permission. *
27 * *
28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *
29 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
30 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
31 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER *
32 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
33 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, *
34 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR *
35 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
36 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING *
37 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS *
38 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
39* *
40\*===========================================================================*/
41
42
43#include "TopologyPlugin.hh"
44#include <QActionGroup>
45
46#define EDGE_FLIP_POPUP "<B>Flip Edge</B><br>Rotate an edge"
47#define EDGE_COLLAPSE_POPUP "<B>Collapse Edge</B><br>Collapse an edge into one of its vertices."
48#define EDGE_SPLIT_POPUP "<B>Split Edge</B><br>Split an edge at the clicked point."
49#define FACE_ADD_POPUP "<B>Add Face</B><br>Insert a face between clicked vertices."
50#define FACE_SPLIT_POPUP "<B>Split Face</B><br>Split a face at a clicked point."
51#define FACE_DELETE_POPUP "<B>Delete Face</B><br>Remove a clicked face."
52
53
54
55//******************************************************************************
56
58 toolbar_(0),
59 edgeFlipAction_(0),
60 edgeSplitAction_(0),
61 edgeCollapseAction_(0),
62 faceAddAction_(0),
63 faceDeleteAction_(0),
64 faceSplitAction_(0)
65{
66
67}
68
69//******************************************************************************
70
75 emit addHiddenPickMode(EDGE_FLIP_POPUP);
76 emit addHiddenPickMode(EDGE_SPLIT_POPUP);
77 emit addHiddenPickMode(EDGE_COLLAPSE_POPUP);
78 emit addHiddenPickMode(FACE_ADD_POPUP);
79 emit addHiddenPickMode(FACE_SPLIT_POPUP);
80 emit addHiddenPickMode(FACE_DELETE_POPUP);
81
82
83 toolbar_ = new QToolBar("Topology");
84
85 QActionGroup* group = new QActionGroup(toolbar_);
86
87 QString iconPath = OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator();
88
89 const QString baseHelpURL = "<a href='qthelp://org.openflipper.plugin-topology/Plugin-Topology/index.html";
90 const QString clickText = tr("Click for more information</a>");
91
92
93 edgeFlipAction_ = toolbar_->addAction( QIcon(iconPath + "topology-edgeFlip.png"), EDGE_FLIP_POPUP );
94 edgeFlipAction_->setCheckable( true);
95 edgeFlipAction_->setActionGroup(group);
96 edgeFlipAction_->setWhatsThis(tr("Flip edge. ") + baseHelpURL+ "#flip_edge'>" + clickText);
97
98 edgeSplitAction_ = toolbar_->addAction( QIcon(iconPath + "topology-edgeSplit.png"), EDGE_SPLIT_POPUP );
99 edgeSplitAction_->setCheckable( true);
100 edgeSplitAction_->setActionGroup(group);
101 edgeSplitAction_->setWhatsThis(tr("Split edge. ") + baseHelpURL+ "#split_edge'>" + clickText);
102
103
104 edgeCollapseAction_ = toolbar_->addAction( QIcon(iconPath + "topology-edgeCollapse.png"), EDGE_COLLAPSE_POPUP );
105 edgeCollapseAction_->setCheckable( true);
106 edgeCollapseAction_->setActionGroup(group);
107 edgeCollapseAction_->setWhatsThis(tr("Collapse edge. ") + baseHelpURL+ "#collapse_edge'>" + clickText);
108
109
110 toolbar_->addSeparator();
111 faceAddAction_ = toolbar_->addAction( QIcon(iconPath + "topology-addFace.png"), FACE_ADD_POPUP );
112 faceAddAction_->setCheckable( true);
113 faceAddAction_->setActionGroup(group);
114 faceAddAction_->setWhatsThis(tr("Add face.") + baseHelpURL+ "#add_face'>" + clickText);
115
116
117 faceDeleteAction_ = toolbar_->addAction( QIcon(iconPath + "topology-deleteFace.png"), FACE_DELETE_POPUP );
118 faceDeleteAction_->setCheckable( true);
119 faceDeleteAction_->setActionGroup(group);
120 faceDeleteAction_->setWhatsThis(tr("Delete face. ") + baseHelpURL+ "#delete_face'>" + clickText);
121
122
123 faceSplitAction_ = toolbar_->addAction( QIcon(iconPath + "topology-splitFace.png"), FACE_SPLIT_POPUP );
124 faceSplitAction_->setCheckable( true);
125 faceSplitAction_->setActionGroup(group);
126 faceSplitAction_->setWhatsThis(tr("Split face. ") + baseHelpURL+ "#split_face'>" + clickText);
127
128 group->setExclusive(true);
129
130 connect( toolbar_, SIGNAL( actionTriggered(QAction*) ), this, SLOT( toolBarTriggered(QAction*) ));
131
132 emit addToolbar( toolbar_ );
133}
134
135
136//******************************************************************************
137
138
143void TopologyPlugin::toolBarTriggered(QAction* _action){
144 if ( _action->text() == EDGE_FLIP_POPUP)
145 PluginFunctions::pickMode(EDGE_FLIP_POPUP);
146 else if ( _action->text() == EDGE_SPLIT_POPUP)
147 PluginFunctions::pickMode(EDGE_SPLIT_POPUP);
148 else if ( _action->text() == EDGE_COLLAPSE_POPUP)
149 PluginFunctions::pickMode(EDGE_COLLAPSE_POPUP);
150 else if ( _action->text() == FACE_ADD_POPUP)
151 PluginFunctions::pickMode(FACE_ADD_POPUP);
152 else if ( _action->text() == FACE_SPLIT_POPUP)
153 PluginFunctions::pickMode(FACE_SPLIT_POPUP);
154 else if ( _action->text() == FACE_DELETE_POPUP)
155 PluginFunctions::pickMode(FACE_DELETE_POPUP);
156
157 PluginFunctions::actionMode(Viewer::PickingMode);
158}
159
160
161//******************************************************************************
162
167void TopologyPlugin::slotPickModeChanged( const std::string& _mode) {
168
169 edgeFlipAction_->setChecked( _mode == EDGE_FLIP_POPUP );
170 edgeSplitAction_->setChecked( _mode == EDGE_SPLIT_POPUP );
171 edgeCollapseAction_->setChecked( _mode == EDGE_COLLAPSE_POPUP );
172 faceAddAction_->setChecked( _mode == FACE_ADD_POPUP );
173 faceDeleteAction_->setChecked( _mode == FACE_DELETE_POPUP );
174 faceSplitAction_->setChecked( _mode == FACE_SPLIT_POPUP );
175}
176
177//******************************************************************************
178
183void TopologyPlugin::slotMouseEvent( QMouseEvent* _event ) {
184 if ( _event->buttons() == Qt::RightButton )
185 return;
186
187 if ( PluginFunctions::pickMode() == EDGE_FLIP_POPUP ) { flip_edge(_event); } else
188 if ( PluginFunctions::pickMode() == EDGE_COLLAPSE_POPUP ) { collapse_edge(_event); } else
189 if ( PluginFunctions::pickMode() == EDGE_SPLIT_POPUP ) { split_edge(_event); } else
190 if ( PluginFunctions::pickMode() == FACE_ADD_POPUP ) { add_face(_event); } else
191 if ( PluginFunctions::pickMode() == FACE_SPLIT_POPUP ) { split_face(_event); } else
192 if ( PluginFunctions::pickMode() == FACE_DELETE_POPUP ) { delete_face(_event); }
193}
194
195
196//******************************************************************************
197
202 for ( uint i = 0 ; i < addFaceVertices_.size() ; ++i ) {
203 BaseObjectData* object;
204 if ( ! PluginFunctions::getObject( addFaceVertices_[i].first , object ) )
205 continue;
206
207 if ( object->dataType(DATA_TRIANGLE_MESH) ) {
209 TriMesh::VertexHandle vh = m->vertex_handle( addFaceVertices_[i].second );
210 if ( vh.is_valid()) {
211 m->status(vh).set_selected(false);
212 }
213 }
214
215 if ( object->dataType(DATA_POLY_MESH) ) {
217 PolyMesh::VertexHandle vh = m->vertex_handle( addFaceVertices_[i].second );
218 if ( vh.is_valid()) {
219 m->status(vh).set_selected(false);
220 }
221 }
222
223 emit updatedObject(object->id(),UPDATE_SELECTION);
224 }
225
226 addFaceVertices_.clear();
227 emit updateView();
228}
229
230
231//******************************************************************************
232
237void TopologyPlugin::add_face(QMouseEvent* _event) {
238 if (( _event->type() != QEvent::MouseButtonPress) && (_event->type() != QEvent::MouseButtonDblClick))
239 return;
240
241 size_t node_idx, target_idx;
242 ACG::Vec3d hit_point;
243
244 if (PluginFunctions::scenegraphPick(ACG::SceneGraph::PICK_FACE, _event->pos(),node_idx, target_idx, &hit_point)) {
245
246 BaseObjectData* object;
247 if ( PluginFunctions::getPickedObject(node_idx, object) ) {
248
249 //--- Add Face for TriMesh
250 if ( object->picked(node_idx) && object->dataType(DATA_TRIANGLE_MESH) ) {
251 TriMesh& m = *PluginFunctions::triMesh(object);
252 TriMesh::FaceHandle fh = m.face_handle(target_idx);
253
254 TriMesh::FaceVertexIter fv_it(m,fh);
255 TriMesh::VertexHandle closest = *fv_it;
256 float shortest_distance = (m.point(closest) - hit_point).sqrnorm();
257
258 ++fv_it;
259 if ( (m.point(*fv_it) - hit_point).sqrnorm() < shortest_distance ) {
260 shortest_distance = (m.point(*fv_it) - hit_point).sqrnorm();
261 closest = *fv_it;
262 }
263
264 ++fv_it;
265 if ( (m.point(*fv_it) - hit_point).sqrnorm() < shortest_distance ) {
266 //shortest_distance = (m.point(*fv_it) - hit_point).sqrnorm(); Unnecessary. Not used anymore after this point
267 closest = *fv_it;
268 }
269
270
271 std::pair<int,int> newVertex(object->id(), closest.idx() );
272
273 for ( uint i = 0 ; i < addFaceVertices_.size() ; ++i ) {
274 if ( ( addFaceVertices_[i].first == newVertex.first ) &&
275 ( addFaceVertices_[i].second == newVertex.second ) ) {
276 addFaceVertices_.erase(addFaceVertices_.begin()+i);
277 m.status(closest).set_selected(false);
278 emit updatedObject(object->id(),UPDATE_SELECTION);
279 emit updateView();
280 return;
281 }
282 }
283
284 // New Vertex so add it to the list
285 addFaceVertices_.push_back( std::pair<int,int>(object->id(), closest.idx() ) );
286 m.status(closest).set_selected(true);
287
288 // We need 3 in the list to proceed
289 if ( addFaceVertices_.size() < 3 ) {
290 emit updatedObject(object->id(),UPDATE_SELECTION);
291 emit updateView();
292 return;
293 }
294
295 // check if the objects are of same type
296 DataType dt = object->dataType();
297 for ( uint i = 0 ; i < addFaceVertices_.size() ; ++i ) {
298 BaseObjectData* tmpObject;
299 if ( ! PluginFunctions::getObject( addFaceVertices_[i].first , tmpObject ) ) {
300 emit log(LOGERR,"Unable to get object for adding face");
302 return;
303 }
304
305 if ( tmpObject->dataType() != dt ) {
306 emit log(LOGERR,"Adding faces between different type of meshes is not supported!");
308 return;
309 }
310 }
311
312 // check if we add a face between multiple objects
314 int objectId = addFaceVertices_[0].first;
315 for ( uint i = 0 ; i < addFaceVertices_.size() ; ++i ) {
316 if ( addFaceVertices_[i].first != objectId ) {
317 emit log(LOGERR,"Adding faces between different objects!");
319 return;
320 }
321 }
322
323 TriMesh::VertexHandle vh0 = m.vertex_handle( addFaceVertices_[0].second );
324 TriMesh::VertexHandle vh1 = m.vertex_handle( addFaceVertices_[1].second );
325 TriMesh::VertexHandle vh2 = m.vertex_handle( addFaceVertices_[2].second );
326
327 // store state and disable output
328 bool errlog = omerr().is_enabled();
329 omerr().disable();
330
331 fh = m.add_face(vh0,vh1,vh2);
332 if ( !fh.is_valid() ) {
333 fh = m.add_face(vh2,vh1,vh0);
334 }
335
336 emit updatedObject(object->id(),UPDATE_ALL);
337 emit updateView();
338
339 // reenable output if it was enabled
340 if (errlog)
341 omerr().enable();
342
344
345 if ( fh.is_valid() )
346 emit createBackup(object->id(),"Add Face", UPDATE_TOPOLOGY);
347 else
348 emit log(LOGERR,"Unable to add face!");
349
350 }
351
352 //--- Add Face for PolyMesh
353 if ( object->picked(node_idx) && object->dataType(DATA_POLY_MESH) ) {
354
356 OpenMesh::SmartFaceHandle fh = make_smart(m.face_handle(target_idx), m);
357
358 //find the closest vertex in the picked face
360 float shortest_distance = FLT_MAX;
361
362 for (auto fv_it : fh.vertices()){
363 float distance = (m.point( fv_it ) - hit_point).sqrnorm();
364
365 if (distance < shortest_distance){
366 shortest_distance = distance;
367 closest = fv_it;
368 }
369 }
370
371 if (!closest.is_valid())
372 return;
373
374 if (_event->type() != QEvent::MouseButtonDblClick){
375
376 std::pair<int,int> newVertex(object->id(), closest.idx() );
377
378 for ( uint i = 0 ; i < addFaceVertices_.size() ; ++i ) {
379 if ( ( addFaceVertices_[i].first == newVertex.first ) &&
380 ( addFaceVertices_[i].second == newVertex.second ) ) {
381 addFaceVertices_.erase(addFaceVertices_.begin()+i);
382 m.status(closest).set_selected(false);
383 emit updatedObject(object->id(),UPDATE_SELECTION);
384 emit updateView();
385 return;
386 }
387 }
388
389 // New Vertex so add it to the list
390 addFaceVertices_.push_back( std::pair<int,int>(object->id(), closest.idx() ) );
391 m.status(closest).set_selected(true);
392 }
393
394 // We need at least 3 in the list to proceed
395 if ( (addFaceVertices_.size() < 3) || (_event->type() != QEvent::MouseButtonDblClick) ) {
396 emit updatedObject(object->id(),UPDATE_SELECTION);
397 emit updateView();
398 return;
399 }
400
401 // check if the objects are of same type
402 DataType dt = object->dataType();
403 for ( uint i = 0 ; i < addFaceVertices_.size() ; ++i ) {
404 BaseObjectData* tmpObject;
405 if ( ! PluginFunctions::getObject( addFaceVertices_[i].first , tmpObject ) ) {
406 emit log(LOGERR,"Unable to get object for adding face");
408 return;
409 }
410
411 if ( tmpObject->dataType() != dt ) {
412 emit log(LOGERR,"Adding faces between different type of meshes is not supported!");
414 return;
415 }
416 }
417
418 // check if we add a face between multiple objects
420 int objectId = addFaceVertices_[0].first;
421 for ( uint i = 0 ; i < addFaceVertices_.size() ; ++i ) {
422 if ( addFaceVertices_[i].first != objectId ) {
423 emit log(LOGERR,"Adding faces between different objects!");
425 return;
426 }
427 }
428
429 std::vector< PolyMesh::VertexHandle > vhs;
430 for ( uint i = 0 ; i < addFaceVertices_.size() ; ++i )
431 vhs.push_back( m.vertex_handle( addFaceVertices_[i].second ) );
432
433 // store state and disable output
434 bool errlog = omerr().is_enabled();
435 omerr().disable();
436
437 fh = m.add_face(vhs);
438
439 if (!fh.is_valid()){
440 std::vector< PolyMesh::VertexHandle > rvhs;
441 //reverse vector
442 while (!vhs.empty()){
443 rvhs.push_back( vhs.back() );
444 vhs.pop_back();
445 }
446
447 fh = m.add_face(rvhs);
448
449 }
450
451 emit updatedObject(object->id(),UPDATE_ALL);
452 emit updateView();
453
454 // reenable output if it was enabled
455 if (errlog)
456 omerr().enable();
457
459
460 if ( fh.is_valid() )
461 emit createBackup(object->id(),"Add Face", UPDATE_TOPOLOGY);
462 else
463 emit log(LOGERR,"Unable to add face!");
464
465 }
466 }
467 }
468}
469
470
471//******************************************************************************
472
477void TopologyPlugin::split_face(QMouseEvent* _event) {
478 if ( _event->type() != QEvent::MouseButtonPress )
479 return;
480
481 size_t target_idx;
482 ACG::Vec3d hit_point;
483
484 BaseObjectData* object = 0;
485 if (PluginFunctions::scenegraphPick(ACG::SceneGraph::PICK_FACE, _event->pos(),object, target_idx, true, &hit_point)) {
486
487 if ( object != 0 ) {
488 if ( object->dataType(DATA_TRIANGLE_MESH) ) {
489 TriMesh& m = *PluginFunctions::triMesh(object);
490 TriMesh::FaceHandle fh = m.face_handle(target_idx);
491
492 emit log(LOGOUT,"Picked Face " + QString::number(fh.idx()) + ", normal (" +
493 QString::number(m.normal(fh)[0]) + "," +
494 QString::number(m.normal(fh)[1]) + "," +
495 QString::number(m.normal(fh)[2]) + ") ") ;
496
497 TriMesh::VertexHandle vh = m.add_vertex(hit_point);
498 m.split(fh,vh);
499 m.garbage_collection();
500 m.update_normals();
501
502 emit updatedObject(object->id(), UPDATE_TOPOLOGY);
503 emit updateView();
504 emit createBackup(object->id(),"Split Face", UPDATE_TOPOLOGY);
505 }
506
507 if ( object->dataType(DATA_POLY_MESH) ) {
509 PolyMesh::FaceHandle fh = m.face_handle(target_idx);
510
511 emit log(LOGOUT,"Picked Face " + QString::number(fh.idx()) + ", normal (" +
512 QString::number(m.normal(fh)[0]) + "," +
513 QString::number(m.normal(fh)[1]) + "," +
514 QString::number(m.normal(fh)[2]) + ") ") ;
515
516 PolyMesh::VertexHandle vh = m.add_vertex(hit_point);
517 m.split(fh,vh);
518 m.garbage_collection();
519 m.update_normals();
520
521 emit updatedObject(object->id(), UPDATE_TOPOLOGY);
522 emit updateView();
523 emit createBackup(object->id(),"Split Face", UPDATE_TOPOLOGY);
524 }
525 } else return;
526 }
527}
528
529
530//******************************************************************************
531
536void TopologyPlugin::delete_face(QMouseEvent* _event) {
537 if ( _event->type() != QEvent::MouseButtonPress )
538 return;
539
540 size_t node_idx, target_idx;
541 ACG::Vec3d hit_point;
542
543 if (PluginFunctions::scenegraphPick(ACG::SceneGraph::PICK_FACE, _event->pos(),node_idx, target_idx, &hit_point)) {
544 BaseObjectData* object;
545
546 if ( PluginFunctions::getPickedObject(node_idx, object) ) {
547 if ( object->picked(node_idx) && object->dataType(DATA_TRIANGLE_MESH) ) {
548 TriMesh& m = *PluginFunctions::triMesh(object);
549 TriMesh::FaceHandle fh = m.face_handle(target_idx);
550 emit log(LOGOUT,"Picked Face " + QString::number(fh.idx()) + ", normal (" +
551 QString::number(m.normal(fh)[0]) + "," +
552 QString::number(m.normal(fh)[1]) + "," +
553 QString::number(m.normal(fh)[2]) + ") ") ;
554
555 m.delete_face(fh);
556 m.garbage_collection();
557 }
558
559 if ( object->picked(node_idx) && object->dataType(DATA_POLY_MESH) ) {
561 PolyMesh::FaceHandle fh = m.face_handle(target_idx);
562 emit log(LOGOUT,"Picked Face " + QString::number(fh.idx()) + ", normal (" +
563 QString::number(m.normal(fh)[0]) + "," +
564 QString::number(m.normal(fh)[1]) + "," +
565 QString::number(m.normal(fh)[2]) + ") ") ;
566
567 m.delete_face(fh);
568 m.garbage_collection();
569 }
570
571 emit updatedObject(object->id(), UPDATE_TOPOLOGY);
572 emit updateView();
573 emit createBackup(object->id(),"Delete Face", UPDATE_TOPOLOGY);
574 } else return;
575 }
576}
577
578
579//******************************************************************************
580
585void TopologyPlugin::flip_edge(QMouseEvent* _event) {
586 if ( _event->type() != QEvent::MouseButtonPress )
587 return;
588
589 size_t node_idx, target_idx;
590 ACG::Vec3d hit_point;
591
592 if (PluginFunctions::scenegraphPick(ACG::SceneGraph::PICK_FACE, _event->pos(),node_idx, target_idx, &hit_point)) {
593 BaseObjectData* object;
594 if ( PluginFunctions::getPickedObject(node_idx, object) ) {
595 if ( object->picked(node_idx) && object->dataType(DATA_TRIANGLE_MESH) ) {
596 TriMesh& m = *PluginFunctions::triMesh(object);
597 TriMesh::FaceHandle fh = m.face_handle(target_idx);
598
599 TriMesh::FaceEdgeIter fe_it(m,fh);
600 TriMesh::HalfedgeHandle e1 = m.halfedge_handle(*fe_it,0);
601
602 ++fe_it;
603 TriMesh::HalfedgeHandle e2 = m.halfedge_handle(*fe_it,0);
604
605 ++fe_it;
606 TriMesh::HalfedgeHandle e3 = m.halfedge_handle(*fe_it,0);
607
608 double min_dist = ACG::Geometry::distPointLineSquared(hit_point,m.point(m.to_vertex_handle( e1 )), m.point(m.from_vertex_handle( e1 )));
609 TriMesh::EdgeHandle closest_edge = m.edge_handle(e1);
610
611 double dist = ACG::Geometry::distPointLineSquared(hit_point,m.point(m.to_vertex_handle( e2 )), m.point(m.from_vertex_handle( e2 )));
612 if ( dist < min_dist ) {
613 min_dist = dist;
614 closest_edge = m.edge_handle(e2);
615 }
616
617 dist = ACG::Geometry::distPointLineSquared(hit_point,m.point(m.to_vertex_handle( e3 )),m.point(m.from_vertex_handle( e3 )));
618 if ( dist < min_dist) {
619 // min_dist = dist; Unnecessary. Not used after this point
620 closest_edge = m.edge_handle(e3);
621 }
622
623 if ( m.is_flip_ok(closest_edge) )
624 m.flip(closest_edge);
625 else
626 emit log(LOGERR,"Flip is not allowed here!");
627
628 emit log(LOGOUT,"Picked Edge " + QString::number(closest_edge.idx()));
629
630 emit updatedObject(object->id(), UPDATE_TOPOLOGY);
631 emit updateView();
632 emit createBackup(object->id(),"Edge Flip", UPDATE_TOPOLOGY);
633 }
634
635 if ( object->picked(node_idx) && object->dataType(DATA_POLY_MESH) ) {
636 emit log(LOGWARN,"Edge Flips not supported for Poly Meshes");
637 }
638
639 } else return;
640 }
641}
642
643
644//******************************************************************************
645
650void TopologyPlugin::collapse_edge(QMouseEvent* _event) {
651 if ( _event->type() != QEvent::MouseButtonPress )
652 return;
653
654 size_t node_idx, target_idx;
655 ACG::Vec3d hit_point;
656
657 if (PluginFunctions::scenegraphPick(ACG::SceneGraph::PICK_FACE, _event->pos(),node_idx, target_idx, &hit_point)) {
658 BaseObjectData* object;
659 if ( PluginFunctions::getPickedObject(node_idx, object) ) {
660 if ( object->picked(node_idx) && object->dataType(DATA_TRIANGLE_MESH) ) {
661 TriMesh& m = *PluginFunctions::triMesh(object);
662 TriMesh::FaceHandle fh = m.face_handle(target_idx);
663
664 TriMesh::FaceEdgeIter fe_it(m,fh);
665 TriMesh::HalfedgeHandle e1 = m.halfedge_handle(*fe_it,0);
666
667 ++fe_it;
668 TriMesh::HalfedgeHandle e2 = m.halfedge_handle(*fe_it,0);
669
670 ++fe_it;
671 TriMesh::HalfedgeHandle e3 = m.halfedge_handle(*fe_it,0);
672
673 double min_dist = ACG::Geometry::distPointLineSquared(hit_point,m.point(m.to_vertex_handle( e1 )), m.point(m.from_vertex_handle( e1 )));
674 TriMesh::HalfedgeHandle closest_halfedge = e1;
675
676 double dist = ACG::Geometry::distPointLineSquared(hit_point,m.point(m.to_vertex_handle( e2 )), m.point(m.from_vertex_handle( e2 )));
677 if ( dist < min_dist ) {
678 min_dist = dist;
679 closest_halfedge = e2;
680 }
681
682 dist = ACG::Geometry::distPointLineSquared(hit_point,m.point(m.to_vertex_handle( e3 )),m.point(m.from_vertex_handle( e3 )));
683 if ( dist < min_dist) {
684 //min_dist = dist; Unnecessary. Not used after this point
685 closest_halfedge = e3;
686 }
687
688 // collapse into to point which is closer to hitpoint
689 TriMesh::Point to = m.point( m.to_vertex_handle(closest_halfedge) );
690 TriMesh::Point from = m.point( m.from_vertex_handle(closest_halfedge) );
691
692 if ( (hit_point - to).sqrnorm() > (hit_point - from).sqrnorm() )
693 closest_halfedge = m.opposite_halfedge_handle(closest_halfedge);
694
695 if ( m.is_collapse_ok(closest_halfedge) ){
696 // m.point(m.to_vertex_handle(closest_edge)) = hit_point;
697 m.collapse(closest_halfedge );
698 m.garbage_collection();
699 } else
700 emit log(LOGERR,"Collapse is not allowed here!");
701
702 emit log(LOGOUT,"Picked Edge " + QString::number(closest_halfedge.idx()));
703
704 emit updatedObject(object->id(), UPDATE_TOPOLOGY);
705 emit updateView();
706 emit createBackup(object->id(),"Edge Collapse", UPDATE_TOPOLOGY);
707 }
708
709 // Poly Meshes
710 if ( object->picked(node_idx) && object->dataType(DATA_POLY_MESH) ) {
712 OpenMesh::SmartFaceHandle fh = make_smart(m.face_handle(target_idx), m);
713
714 // find closest edge
715 PolyMesh::HalfedgeHandle closest_edge(-1);
716 double min_dist = FLT_MAX;
717
718 for(auto fe_it : fh.edges())
719 {
720 OpenMesh::SmartHalfedgeHandle heh = fe_it.h0();
721 double dist = ACG::Geometry::distPointLineSquared(hit_point,m.point(heh.to()), m.point(heh.from()));
722
723 if( dist < min_dist)
724 {
725 min_dist = dist;
726 closest_edge = heh;
727 }
728 }
729
730 // collapse into to point which is closer to hitpoint
731 PolyMesh::Point to = m.point( m.to_vertex_handle(closest_edge) );
732 PolyMesh::Point from = m.point( m.from_vertex_handle(closest_edge) );
733
734 if ( (hit_point - to).sqrnorm() > (hit_point - from).sqrnorm() )
735 closest_edge = m.opposite_halfedge_handle(closest_edge);
736
737// if ( m.is_collapse_ok(closest_edge) ){
738 // m.point(m.to_vertex_handle(closest_edge)) = hit_point;
739 m.collapse(closest_edge );
740 m.garbage_collection();
741// } else
742// emit log(LOGERR,"Collapse is not allowed here!");
743
744 emit log(LOGOUT,"Picked Edge " + QString::number(closest_edge.idx()));
745
746 emit updatedObject(object->id(), UPDATE_TOPOLOGY);
747 emit updateView();
748 emit createBackup(object->id(),"Edge Collapse", UPDATE_TOPOLOGY);
749 }
750
751 } else return;
752 }
753}
754
755
756//******************************************************************************
757
762void TopologyPlugin::split_edge(QMouseEvent* _event) {
763 if ( _event->type() != QEvent::MouseButtonPress )
764 return;
765
766 size_t node_idx, target_idx;
767 ACG::Vec3d hit_point;
768
769 if (PluginFunctions::scenegraphPick(ACG::SceneGraph::PICK_FACE, _event->pos(),node_idx, target_idx, &hit_point)) {
770 BaseObjectData* object;
771 if ( PluginFunctions::getPickedObject(node_idx, object) ) {
772 if ( object->picked(node_idx) && object->dataType(DATA_TRIANGLE_MESH) ) {
773 TriMesh& m = *PluginFunctions::triMesh(object);
774 TriMesh::FaceHandle fh = m.face_handle(target_idx);
775
776 TriMesh::FaceEdgeIter fe_it(m,fh);
777 TriMesh::HalfedgeHandle e1 = m.halfedge_handle(*fe_it,0);
778
779 ++fe_it;
780 TriMesh::HalfedgeHandle e2 = m.halfedge_handle(*fe_it,0);
781
782 ++fe_it;
783 TriMesh::HalfedgeHandle e3 = m.halfedge_handle(*fe_it,0);
784
785 double min_dist = ACG::Geometry::distPointLineSquared(hit_point,m.point(m.to_vertex_handle( e1 )), m.point(m.from_vertex_handle( e1 )));
786 TriMesh::EdgeHandle closest_edge = m.edge_handle(e1);
787
789 hit_point,
790 m.point(m.to_vertex_handle(e2)),
791 m.point(m.from_vertex_handle(e2)));
792
793 if (dist < min_dist) {
794 min_dist = dist;
795 closest_edge = m.edge_handle(e2);
796 }
798 hit_point,
799 m.point(m.to_vertex_handle(e3)),
800 m.point(m.from_vertex_handle(e3)));
801
802 if (dist < min_dist) {
803 // min_dist = dist; Unnecessary. Not used after this point
804 closest_edge = m.edge_handle(e3);
805 }
806
807 m.split(closest_edge,hit_point);
808
809 emit log(LOGOUT,"Picked Edge " + QString::number(closest_edge.idx() ));
810
811 emit updatedObject(object->id(), UPDATE_TOPOLOGY);
812 emit updateView();
813 emit createBackup(object->id(),"Edge Split", UPDATE_TOPOLOGY);
814 }
815 if ( object->picked(node_idx) && object->dataType(DATA_POLY_MESH) ) {
817 OpenMesh::SmartFaceHandle fh = make_smart(m.face_handle(target_idx), m);
818
819 std::vector<OpenMesh::SmartHalfedgeHandle> halfEdgeHandles;
820 //get all edges which belongs to the picked face
821 for (auto fh_it : fh.halfedges())
822 {
823 halfEdgeHandles.push_back(fh_it);
824 }
825
826 //search for the nearest edge
827 PolyMesh::EdgeHandle closest_edge = halfEdgeHandles.begin()->edge();
828 double min_dist = ACG::Geometry::distPointLineSquared(hit_point, m.point( halfEdgeHandles.begin()->to() ), m.point( halfEdgeHandles.begin()->from() ));
829
830 for (auto iter : halfEdgeHandles)
831 {
832 double dist = ACG::Geometry::distPointLineSquared(hit_point, m.point( iter.to() ), m.point( iter.from() ));
833 if (dist < min_dist)
834 {
835 closest_edge = iter.edge();
836 min_dist = dist;
837 }
838 }
839
840 m.split(closest_edge,hit_point);
841
842 emit log(LOGOUT,"Picked Edge " + QString::number(closest_edge.idx()) );
843
844 emit updatedObject(object->id(), UPDATE_TOPOLOGY);
845 emit updateView();
846 emit createBackup(object->id(),"Edge Split", UPDATE_TOPOLOGY);
847 }
848
849 } else return;
850 }
851}
852
853
@ LOGERR
@ LOGWARN
@ LOGOUT
#define DATA_POLY_MESH
Definition: PolyMesh.hh:59
#define DATA_TRIANGLE_MESH
Definition: TriangleMesh.hh:60
virtual bool picked(uint _node_idx)
detect if the node has been picked
bool dataType(DataType _type) const
Definition: BaseObject.cc:219
int id() const
Definition: BaseObject.cc:188
Predefined datatypes.
Definition: DataTypes.hh:83
bool is_valid() const
The handle is valid iff the index is not negative.
Definition: Handles.hh:72
Kernel::FaceEdgeIter FaceEdgeIter
Circulator.
Definition: PolyMeshT.hh:169
Kernel::VertexHandle VertexHandle
Handle for referencing the corresponding item.
Definition: PolyMeshT.hh:136
Kernel::EdgeHandle EdgeHandle
Scalar type.
Definition: PolyMeshT.hh:138
Kernel::FaceVertexIter FaceVertexIter
Circulator.
Definition: PolyMeshT.hh:167
void update_normals()
Compute normals for all primitives.
SmartVertexHandle add_vertex(const Point _p)
Definition: PolyMeshT.hh:255
Kernel::FaceHandle FaceHandle
Scalar type.
Definition: PolyMeshT.hh:139
Kernel::HalfedgeHandle HalfedgeHandle
Scalar type.
Definition: PolyMeshT.hh:137
Kernel::Point Point
Coordinate type.
Definition: PolyMeshT.hh:112
void split(FaceHandle _fh, const Point &_p)
Face split (= 1-to-n split)
Definition: PolyMeshT.hh:606
SmartVertexHandle split(EdgeHandle _eh, const Point &_p)
Edge split (= 2-to-4 split)
Definition: TriMeshT.hh:275
void flip_edge(QMouseEvent *_event)
Flip edge.
void slotMouseEvent(QMouseEvent *_event)
this is called when a mouse event occurred
void collapse_edge(QMouseEvent *_event)
Collapse edge.
void split_edge(QMouseEvent *_event)
Split Edge.
TopologyPlugin()
Constructor.
void slotPickModeChanged(const std::string &_mode)
Toggle actions when the PickMode changes.
void split_face(QMouseEvent *_event)
Split a face at the current hit point.
void toolBarTriggered(QAction *_action)
called when an action on the toolbar was triggered
void clearAddFaceVertices()
clear the add face vector
void pluginsInitialized()
initialize the Plugin
void delete_face(QMouseEvent *_event)
Delete a face at the current hit point.
void add_face(QMouseEvent *_event)
Add a face.
const UpdateType UPDATE_TOPOLOGY(UpdateTypeSet(8))
Topology updated.
const UpdateType UPDATE_ALL(UpdateTypeSet(1))
Identifier for all updates.
const UpdateType UPDATE_SELECTION(UpdateTypeSet(16))
Selection updated.
Vec::value_type distPointLineSquared(const Vec &_p, const Vec &_v0, const Vec &_v1, Vec *_min_v)
squared distance from point _p to line segment (_v0,_v1)
Definition: Algorithms.cc:290
@ PICK_FACE
picks faces (should be implemented for all nodes)
Definition: PickTarget.hh:78
bool getObject(const int _identifier, BaseObject *&_object)
Get the object which has the given identifier.
TriMesh * triMesh(BaseObjectData *_object)
Get a triangle mesh from an object.
const std::string pickMode()
Get the current Picking mode.
PolyMesh * polyMesh(BaseObjectData *_object)
Get a poly mesh from an object.
bool getPickedObject(const size_t _node_idx, BaseObjectData *&_object)
Get the picked mesh.
bool scenegraphPick(ACG::SceneGraph::PickTarget _pickTarget, const QPoint &_mousePos, size_t &_nodeIdx, size_t &_targetIdx, ACG::Vec3d *_hitPointPtr=0)
Execute picking operation on scenegraph.
Viewer::ActionMode actionMode()
Get the current Action mode.
PolyConnectivity::ConstFaceEdgeRange edges() const
Returns a range of edges of the face (PolyConnectivity::fv_range())
PolyConnectivity::ConstFaceVertexRange vertices() const
Returns a range of vertices incident to the face (PolyConnectivity::fv_range())
PolyConnectivity::ConstFaceHalfedgeRange halfedges() const
Returns a range of halfedges of the face (PolyConnectivity::fh_range())