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
45 #define EDGE_FLIP_POPUP "<B>Flip Edge</B><br>Rotate an edge"
46 #define EDGE_COLLAPSE_POPUP "<B>Collapse Edge</B><br>Collapse an edge into one of its vertices."
47 #define EDGE_SPLIT_POPUP "<B>Split Edge</B><br>Split an edge at the clicked point."
48 #define FACE_ADD_POPUP "<B>Add Face</B><br>Insert a face between clicked vertices."
49 #define FACE_SPLIT_POPUP "<B>Split Face</B><br>Split a face at a clicked point."
50 #define FACE_DELETE_POPUP "<B>Delete Face</B><br>Remove a clicked face."
51
52
53
54 //******************************************************************************
55
56 TopologyPlugin::TopologyPlugin() :
57 toolbar_(0),
58 edgeFlipAction_(0),
59 edgeSplitAction_(0),
60 edgeCollapseAction_(0),
61 faceAddAction_(0),
62 faceDeleteAction_(0),
63 faceSplitAction_(0)
64 {
65
66 }
67
68 //******************************************************************************
69
73 void TopologyPlugin::pluginsInitialized() {
74 emit addHiddenPickMode(EDGE_FLIP_POPUP);
75 emit addHiddenPickMode(EDGE_SPLIT_POPUP);
76 emit addHiddenPickMode(EDGE_COLLAPSE_POPUP);
77 emit addHiddenPickMode(FACE_ADD_POPUP);
78 emit addHiddenPickMode(FACE_SPLIT_POPUP);
79 emit addHiddenPickMode(FACE_DELETE_POPUP);
80
81
82 toolbar_ = new QToolBar("Topology");
83
84 QActionGroup* group = new QActionGroup(0);
85
86 QString iconPath = OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator();
87
88 const QString baseHelpURL = "<a href='qthelp://org.openflipper.plugin-topology/Plugin-Topology/index.html";
89 const QString clickText = tr("Click for more information</a>");
90
91
92 edgeFlipAction_ = toolbar_->addAction( QIcon(iconPath + "topology-edgeFlip.png"), EDGE_FLIP_POPUP );
93 edgeFlipAction_->setCheckable( true);
94 edgeFlipAction_->setActionGroup(group);
95 edgeFlipAction_->setWhatsThis(tr("Flip edge. ") + baseHelpURL+ "#flip_edge'>" + clickText);
96
97 edgeSplitAction_ = toolbar_->addAction( QIcon(iconPath + "topology-edgeSplit.png"), EDGE_SPLIT_POPUP );
98 edgeSplitAction_->setCheckable( true);
99 edgeSplitAction_->setActionGroup(group);
100 edgeSplitAction_->setWhatsThis(tr("Split edge. ") + baseHelpURL+ "#split_edge'>" + clickText);
101
102
103 edgeCollapseAction_ = toolbar_->addAction( QIcon(iconPath + "topology-edgeCollapse.png"), EDGE_COLLAPSE_POPUP );
104 edgeCollapseAction_->setCheckable( true);
105 edgeCollapseAction_->setActionGroup(group);
106 edgeCollapseAction_->setWhatsThis(tr("Collapse edge. ") + baseHelpURL+ "#collapse_edge'>" + clickText);
107
108
109 toolbar_->addSeparator();
110 faceAddAction_ = toolbar_->addAction( QIcon(iconPath + "topology-addFace.png"), FACE_ADD_POPUP );
111 faceAddAction_->setCheckable( true);
112 faceAddAction_->setActionGroup(group);
113 faceAddAction_->setWhatsThis(tr("Add face.") + baseHelpURL+ "#add_face'>" + clickText);
114
115
116 faceDeleteAction_ = toolbar_->addAction( QIcon(iconPath + "topology-deleteFace.png"), FACE_DELETE_POPUP );
117 faceDeleteAction_->setCheckable( true);
118 faceDeleteAction_->setActionGroup(group);
119 faceDeleteAction_->setWhatsThis(tr("Delete face. ") + baseHelpURL+ "#delete_face'>" + clickText);
120
121
122 faceSplitAction_ = toolbar_->addAction( QIcon(iconPath + "topology-splitFace.png"), FACE_SPLIT_POPUP );
123 faceSplitAction_->setCheckable( true);
124 faceSplitAction_->setActionGroup(group);
125 faceSplitAction_->setWhatsThis(tr("Split face. ") + baseHelpURL+ "#split_face'>" + clickText);
126
127 group->setExclusive(true);
128
129 connect( toolbar_, SIGNAL( actionTriggered(QAction*) ), this, SLOT( toolBarTriggered(QAction*) ));
130
131 emit addToolbar( toolbar_ );
132 }
133
134
135 //******************************************************************************
136
137
142 void TopologyPlugin::toolBarTriggered(QAction* _action){
143 if ( _action->text() == EDGE_FLIP_POPUP)
144 PluginFunctions::pickMode(EDGE_FLIP_POPUP);
145 else if ( _action->text() == EDGE_SPLIT_POPUP)
146 PluginFunctions::pickMode(EDGE_SPLIT_POPUP);
147 else if ( _action->text() == EDGE_COLLAPSE_POPUP)
148 PluginFunctions::pickMode(EDGE_COLLAPSE_POPUP);
149 else if ( _action->text() == FACE_ADD_POPUP)
150 PluginFunctions::pickMode(FACE_ADD_POPUP);
151 else if ( _action->text() == FACE_SPLIT_POPUP)
152 PluginFunctions::pickMode(FACE_SPLIT_POPUP);
153 else if ( _action->text() == FACE_DELETE_POPUP)
154 PluginFunctions::pickMode(FACE_DELETE_POPUP);
155
156 PluginFunctions::actionMode(Viewer::PickingMode);
157 }
158
159
160 //******************************************************************************
161
166 void TopologyPlugin::slotPickModeChanged( const std::string& _mode) {
167
168 edgeFlipAction_->setChecked( _mode == EDGE_FLIP_POPUP );
169 edgeSplitAction_->setChecked( _mode == EDGE_SPLIT_POPUP );
170 edgeCollapseAction_->setChecked( _mode == EDGE_COLLAPSE_POPUP );
171 faceAddAction_->setChecked( _mode == FACE_ADD_POPUP );
172 faceDeleteAction_->setChecked( _mode == FACE_DELETE_POPUP );
173 faceSplitAction_->setChecked( _mode == FACE_SPLIT_POPUP );
174 }
175
176 //******************************************************************************
177
182 void TopologyPlugin::slotMouseEvent( QMouseEvent* _event ) {
183 if ( _event->buttons() == Qt::RightButton )
184 return;
185
186 if ( PluginFunctions::pickMode() == EDGE_FLIP_POPUP ) { flip_edge(_event); } else
187 if ( PluginFunctions::pickMode() == EDGE_COLLAPSE_POPUP ) { collapse_edge(_event); } else
188 if ( PluginFunctions::pickMode() == EDGE_SPLIT_POPUP ) { split_edge(_event); } else
189 if ( PluginFunctions::pickMode() == FACE_ADD_POPUP ) { add_face(_event); } else
190 if ( PluginFunctions::pickMode() == FACE_SPLIT_POPUP ) { split_face(_event); } else
191 if ( PluginFunctions::pickMode() == FACE_DELETE_POPUP ) { delete_face(_event); }
192 }
193
194
195 //******************************************************************************
196
200 void TopologyPlugin::clearAddFaceVertices() {
201 for ( uint i = 0 ; i < addFaceVertices_.size() ; ++i ) {
202 BaseObjectData* object;
203 if ( ! PluginFunctions::getObject( addFaceVertices_[i].first , object ) )
204 continue;
205
206 if ( object->dataType(DATA_TRIANGLE_MESH) ) {
207 TriMesh* m = PluginFunctions::triMesh(object);
208 TriMesh::VertexHandle vh = m->vertex_handle( addFaceVertices_[i].second );
209 if ( vh.is_valid()) {
210 m->status(vh).set_selected(false);
211 }
212 }
213
214 if ( object->dataType(DATA_POLY_MESH) ) {
215 PolyMesh* m = PluginFunctions::polyMesh(object);
216 PolyMesh::VertexHandle vh = m->vertex_handle( addFaceVertices_[i].second );
217 if ( vh.is_valid()) {
218 m->status(vh).set_selected(false);
219 }
220 }
221
222 emit updatedObject(object->id(),UPDATE_SELECTION);
223 }
224
225 addFaceVertices_.clear();
226 emit updateView();
227 }
228
229
230 //******************************************************************************
231
236 void TopologyPlugin::add_face(QMouseEvent* _event) {
237 if (( _event->type() != QEvent::MouseButtonPress) && (_event->type() != QEvent::MouseButtonDblClick))
238 return;
239
240 size_t node_idx, target_idx;
241 ACG::Vec3d hit_point;
242
243 if (PluginFunctions::scenegraphPick(ACG::SceneGraph::PICK_FACE, _event->pos(),node_idx, target_idx, &hit_point)) {
244
245 BaseObjectData* object;
246 if ( PluginFunctions::getPickedObject(node_idx, object) ) {
247
248 //--- Add Face for TriMesh
249 if ( object->picked(node_idx) && object->dataType(DATA_TRIANGLE_MESH) ) {
250 TriMesh& m = *PluginFunctions::triMesh(object);
251 TriMesh::FaceHandle fh = m.face_handle(target_idx);
252
253 TriMesh::FaceVertexIter fv_it(m,fh);
254 TriMesh::VertexHandle closest = *fv_it;
255 float shortest_distance = (m.point(closest) - hit_point).sqrnorm();
256
257 ++fv_it;
258 if ( (m.point(*fv_it) - hit_point).sqrnorm() < shortest_distance ) {
259 shortest_distance = (m.point(*fv_it) - hit_point).sqrnorm();
260 closest = *fv_it;
261 }
262
263 ++fv_it;
264 if ( (m.point(*fv_it) - hit_point).sqrnorm() < shortest_distance ) {
265 //shortest_distance = (m.point(*fv_it) - hit_point).sqrnorm(); Unnecessary. Not used anymore after this point
266 closest = *fv_it;
267 }
268
269
270 std::pair<int,int> newVertex(object->id(), closest.idx() );
271
272 for ( uint i = 0 ; i < addFaceVertices_.size() ; ++i ) {
273 if ( ( addFaceVertices_[i].first == newVertex.first ) &&
274 ( addFaceVertices_[i].second == newVertex.second ) ) {
275 addFaceVertices_.erase(addFaceVertices_.begin()+i);
276 m.status(closest).set_selected(false);
277 emit updatedObject(object->id(),UPDATE_SELECTION);
278 emit updateView();
279 return;
280 }
281 }
282
283 // New Vertex so add it to the list
284 addFaceVertices_.push_back( std::pair<int,int>(object->id(), closest.idx() ) );
285 m.status(closest).set_selected(true);
286
287 // We need 3 in the list to proceed
288 if ( addFaceVertices_.size() < 3 ) {
289 emit updatedObject(object->id(),UPDATE_SELECTION);
290 emit updateView();
291 return;
292 }
293
294 // check if the objects are of same type
295 DataType dt = object->dataType();
296 for ( uint i = 0 ; i < addFaceVertices_.size() ; ++i ) {
297 BaseObjectData* tmpObject;
298 if ( ! PluginFunctions::getObject( addFaceVertices_[i].first , tmpObject ) ) {
299 emit log(LOGERR,"Unable to get object for adding face");
300 clearAddFaceVertices();
301 return;
302 }
303
304 if ( tmpObject->dataType() != dt ) {
305 emit log(LOGERR,"Adding faces between different type of meshes is not supported!");
306 clearAddFaceVertices();
307 return;
308 }
309 }
310
311 // check if we add a face between multiple objects
313 int objectId = addFaceVertices_[0].first;
314 for ( uint i = 0 ; i < addFaceVertices_.size() ; ++i ) {
315 if ( addFaceVertices_[i].first != objectId ) {
316 emit log(LOGERR,"Adding faces between different objects!");
317 clearAddFaceVertices();
318 return;
319 }
320 }
321
322 TriMesh::VertexHandle vh0 = m.vertex_handle( addFaceVertices_[0].second );
323 TriMesh::VertexHandle vh1 = m.vertex_handle( addFaceVertices_[1].second );
324 TriMesh::VertexHandle vh2 = m.vertex_handle( addFaceVertices_[2].second );
325
326 // store state and disable output
327 bool errlog = omerr().is_enabled();
328 omerr().disable();
329
330 fh = m.add_face(vh0,vh1,vh2);
331 if ( !fh.is_valid() ) {
332 fh = m.add_face(vh2,vh1,vh0);
333 }
334
335 emit updatedObject(object->id(),UPDATE_ALL);
336 emit updateView();
337
338 // reenable output if it was enabled
339 if (errlog)
340 omerr().enable();
341
342 clearAddFaceVertices();
343
344 if ( fh.is_valid() )
345 emit createBackup(object->id(),"Add Face", UPDATE_TOPOLOGY);
346 else
347 emit log(LOGERR,"Unable to add face!");
348
349 }
350
351 //--- Add Face for PolyMesh
352 if ( object->picked(node_idx) && object->dataType(DATA_POLY_MESH) ) {
353
354 PolyMesh& m = *PluginFunctions::polyMesh(object);
355 PolyMesh::FaceHandle fh = m.face_handle(target_idx);
356
357 //find the closest vertex in the picked face
358 PolyMesh::VertexHandle closest;
359 float shortest_distance = FLT_MAX;
360
361 for (PolyMesh::FaceVertexIter fv_it(m,fh); fv_it.is_valid(); ++fv_it){
362 float distance = (m.point( *fv_it ) - hit_point).sqrnorm();
363
364 if (distance < shortest_distance){
365 shortest_distance = distance;
366 closest = *fv_it;
367 }
368 }
369
370 if (!closest.is_valid())
371 return;
372
373 if (_event->type() != QEvent::MouseButtonDblClick){
374
375 std::pair<int,int> newVertex(object->id(), closest.idx() );
376
377 for ( uint i = 0 ; i < addFaceVertices_.size() ; ++i ) {
378 if ( ( addFaceVertices_[i].first == newVertex.first ) &&
379 ( addFaceVertices_[i].second == newVertex.second ) ) {
380 addFaceVertices_.erase(addFaceVertices_.begin()+i);
381 m.status(closest).set_selected(false);
382 emit updatedObject(object->id(),UPDATE_SELECTION);
383 emit updateView();
384 return;
385 }
386 }
387
388 // New Vertex so add it to the list
389 addFaceVertices_.push_back( std::pair<int,int>(object->id(), closest.idx() ) );
390 m.status(closest).set_selected(true);
391 }
392
393 // We need at least 3 in the list to proceed
394 if ( (addFaceVertices_.size() < 3) || (_event->type() != QEvent::MouseButtonDblClick) ) {
395 emit updatedObject(object->id(),UPDATE_SELECTION);
396 emit updateView();
397 return;
398 }
399
400 // check if the objects are of same type
401 DataType dt = object->dataType();
402 for ( uint i = 0 ; i < addFaceVertices_.size() ; ++i ) {
403 BaseObjectData* tmpObject;
404 if ( ! PluginFunctions::getObject( addFaceVertices_[i].first , tmpObject ) ) {
405 emit log(LOGERR,"Unable to get object for adding face");
406 clearAddFaceVertices();
407 return;
408 }
409
410 if ( tmpObject->dataType() != dt ) {
411 emit log(LOGERR,"Adding faces between different type of meshes is not supported!");
412 clearAddFaceVertices();
413 return;
414 }
415 }
416
417 // check if we add a face between multiple objects
419 int objectId = addFaceVertices_[0].first;
420 for ( uint i = 0 ; i < addFaceVertices_.size() ; ++i ) {
421 if ( addFaceVertices_[i].first != objectId ) {
422 emit log(LOGERR,"Adding faces between different objects!");
423 clearAddFaceVertices();
424 return;
425 }
426 }
427
428 std::vector< PolyMesh::VertexHandle > vhs;
429 for ( uint i = 0 ; i < addFaceVertices_.size() ; ++i )
430 vhs.push_back( m.vertex_handle( addFaceVertices_[i].second ) );
431
432 // store state and disable output
433 bool errlog = omerr().is_enabled();
434 omerr().disable();
435
436 fh = m.add_face(vhs);
437
438 if (!fh.is_valid()){
439 std::vector< PolyMesh::VertexHandle > rvhs;
440 //reverse vector
441 while (!vhs.empty()){
442 rvhs.push_back( vhs.back() );
443 vhs.pop_back();
444 }
445
446 fh = m.add_face(rvhs);
447
448 }
449
450 emit updatedObject(object->id(),UPDATE_ALL);
451 emit updateView();
452
453 // reenable output if it was enabled
454 if (errlog)
455 omerr().enable();
456
457 clearAddFaceVertices();
458
459 if ( fh.is_valid() )
460 emit createBackup(object->id(),"Add Face", UPDATE_TOPOLOGY);
461 else
462 emit log(LOGERR,"Unable to add face!");
463
464 }
465 }
466 }
467 }
468
469
470 //******************************************************************************
471
476 void TopologyPlugin::split_face(QMouseEvent* _event) {
477 if ( _event->type() != QEvent::MouseButtonPress )
478 return;
479
480 size_t target_idx;
481 ACG::Vec3d hit_point;
482
483 BaseObjectData* object = 0;
484 if (PluginFunctions::scenegraphPick(ACG::SceneGraph::PICK_FACE, _event->pos(),object, target_idx, true, &hit_point)) {
485
486 if ( object != 0 ) {
487 if ( object->dataType(DATA_TRIANGLE_MESH) ) {
488 TriMesh& m = *PluginFunctions::triMesh(object);
489 TriMesh::FaceHandle fh = m.face_handle(target_idx);
490
491 emit log(LOGOUT,"Picked Face " + QString::number(fh.idx()) + ", normal (" +
492 QString::number(m.normal(fh)[0]) + "," +
493 QString::number(m.normal(fh)[1]) + "," +
494 QString::number(m.normal(fh)[2]) + ") ") ;
495
496 TriMesh::VertexHandle vh = m.add_vertex(hit_point);
497 m.split(fh,vh);
498 m.garbage_collection();
499 m.update_normals();
500
501 emit updatedObject(object->id(), UPDATE_TOPOLOGY);
502 emit updateView();
503 emit createBackup(object->id(),"Split Face", UPDATE_TOPOLOGY);
504 }
505
506 if ( object->dataType(DATA_POLY_MESH) ) {
507 PolyMesh& m = *PluginFunctions::polyMesh(object);
508 PolyMesh::FaceHandle fh = m.face_handle(target_idx);
509
510 emit log(LOGOUT,"Picked Face " + QString::number(fh.idx()) + ", normal (" +
511 QString::number(m.normal(fh)[0]) + "," +
512 QString::number(m.normal(fh)[1]) + "," +
513 QString::number(m.normal(fh)[2]) + ") ") ;
514
515 PolyMesh::VertexHandle vh = m.add_vertex(hit_point);
516 m.split(fh,vh);
517 m.garbage_collection();
518 m.update_normals();
519
520 emit updatedObject(object->id(), UPDATE_TOPOLOGY);
521 emit updateView();
522 emit createBackup(object->id(),"Split Face", UPDATE_TOPOLOGY);
523 }
524 } else return;
525 }
526 }
527
528
529 //******************************************************************************
530
535 void TopologyPlugin::delete_face(QMouseEvent* _event) {
536 if ( _event->type() != QEvent::MouseButtonPress )
537 return;
538
539 size_t node_idx, target_idx;
540 ACG::Vec3d hit_point;
541
542 if (PluginFunctions::scenegraphPick(ACG::SceneGraph::PICK_FACE, _event->pos(),node_idx, target_idx, &hit_point)) {
543 BaseObjectData* object;
544
545 if ( PluginFunctions::getPickedObject(node_idx, object) ) {
546 if ( object->picked(node_idx) && object->dataType(DATA_TRIANGLE_MESH) ) {
547 TriMesh& m = *PluginFunctions::triMesh(object);
548 TriMesh::FaceHandle fh = m.face_handle(target_idx);
549 emit log(LOGOUT,"Picked Face " + QString::number(fh.idx()) + ", normal (" +
550 QString::number(m.normal(fh)[0]) + "," +
551 QString::number(m.normal(fh)[1]) + "," +
552 QString::number(m.normal(fh)[2]) + ") ") ;
553
554 m.delete_face(fh);
555 m.garbage_collection();
556 }
557
558 if ( object->picked(node_idx) && object->dataType(DATA_POLY_MESH) ) {
559 PolyMesh& m = *PluginFunctions::polyMesh(object);
560 PolyMesh::FaceHandle fh = m.face_handle(target_idx);
561 emit log(LOGOUT,"Picked Face " + QString::number(fh.idx()) + ", normal (" +
562 QString::number(m.normal(fh)[0]) + "," +
563 QString::number(m.normal(fh)[1]) + "," +
564 QString::number(m.normal(fh)[2]) + ") ") ;
565
566 m.delete_face(fh);
567 m.garbage_collection();
568 }
569
570 emit updatedObject(object->id(), UPDATE_TOPOLOGY);
571 emit updateView();
572 emit createBackup(object->id(),"Delete Face", UPDATE_TOPOLOGY);
573 } else return;
574 }
575 }
576
577
578 //******************************************************************************
579
584 void TopologyPlugin::flip_edge(QMouseEvent* _event) {
585 if ( _event->type() != QEvent::MouseButtonPress )
586 return;
587
588 size_t node_idx, target_idx;
589 ACG::Vec3d hit_point;
590
591 if (PluginFunctions::scenegraphPick(ACG::SceneGraph::PICK_FACE, _event->pos(),node_idx, target_idx, &hit_point)) {
592 BaseObjectData* object;
593 if ( PluginFunctions::getPickedObject(node_idx, object) ) {
594 if ( object->picked(node_idx) && object->dataType(DATA_TRIANGLE_MESH) ) {
595 TriMesh& m = *PluginFunctions::triMesh(object);
596 TriMesh::FaceHandle fh = m.face_handle(target_idx);
597
598 TriMesh::FaceEdgeIter fe_it(m,fh);
599 TriMesh::HalfedgeHandle e1 = m.halfedge_handle(*fe_it,0);
600
601 ++fe_it;
602 TriMesh::HalfedgeHandle e2 = m.halfedge_handle(*fe_it,0);
603
604 ++fe_it;
605 TriMesh::HalfedgeHandle e3 = m.halfedge_handle(*fe_it,0);
606
607 double min_dist = ACG::Geometry::distPointLineSquared(hit_point,m.point(m.to_vertex_handle( e1 )), m.point(m.from_vertex_handle( e1 )));
608 TriMesh::EdgeHandle closest_edge = m.edge_handle(e1);
609
610 double dist = ACG::Geometry::distPointLineSquared(hit_point,m.point(m.to_vertex_handle( e2 )), m.point(m.from_vertex_handle( e2 )));
611 if ( dist < min_dist ) {
612 min_dist = dist;
613 closest_edge = m.edge_handle(e2);
614 }
615
616 dist = ACG::Geometry::distPointLineSquared(hit_point,m.point(m.to_vertex_handle( e3 )),m.point(m.from_vertex_handle( e3 )));
617 if ( dist < min_dist) {
618 // min_dist = dist; Unnecessary. Not used after this point
619 closest_edge = m.edge_handle(e3);
620 }
621
622 if ( m.is_flip_ok(closest_edge) )
623 m.flip(closest_edge);
624 else
625 emit log(LOGERR,"Flip is not allowed here!");
626
627 emit log(LOGOUT,"Picked Edge " + QString::number(closest_edge.idx()));
628
629 emit updatedObject(object->id(), UPDATE_TOPOLOGY);
630 emit updateView();
631 emit createBackup(object->id(),"Edge Flip", UPDATE_TOPOLOGY);
632 }
633
634 if ( object->picked(node_idx) && object->dataType(DATA_POLY_MESH) ) {
635 emit log(LOGWARN,"Edge Flips not supported for Poly Meshes");
636 }
637
638 } else return;
639 }
640 }
641
642
643 //******************************************************************************
644
649 void TopologyPlugin::collapse_edge(QMouseEvent* _event) {
650 if ( _event->type() != QEvent::MouseButtonPress )
651 return;
652
653 size_t node_idx, target_idx;
654 ACG::Vec3d hit_point;
655
656 if (PluginFunctions::scenegraphPick(ACG::SceneGraph::PICK_FACE, _event->pos(),node_idx, target_idx, &hit_point)) {
657 BaseObjectData* object;
658 if ( PluginFunctions::getPickedObject(node_idx, object) ) {
659 if ( object->picked(node_idx) && object->dataType(DATA_TRIANGLE_MESH) ) {
660 TriMesh& m = *PluginFunctions::triMesh(object);
661 TriMesh::FaceHandle fh = m.face_handle(target_idx);
662
663 TriMesh::FaceEdgeIter fe_it(m,fh);
664 TriMesh::HalfedgeHandle e1 = m.halfedge_handle(*fe_it,0);
665
666 ++fe_it;
667 TriMesh::HalfedgeHandle e2 = m.halfedge_handle(*fe_it,0);
668
669 ++fe_it;
670 TriMesh::HalfedgeHandle e3 = m.halfedge_handle(*fe_it,0);
671
672 double min_dist = ACG::Geometry::distPointLineSquared(hit_point,m.point(m.to_vertex_handle( e1 )), m.point(m.from_vertex_handle( e1 )));
673 TriMesh::HalfedgeHandle closest_halfedge = e1;
674
675 double dist = ACG::Geometry::distPointLineSquared(hit_point,m.point(m.to_vertex_handle( e2 )), m.point(m.from_vertex_handle( e2 )));
676 if ( dist < min_dist ) {
677 min_dist = dist;
678 closest_halfedge = e2;
679 }
680
681 dist = ACG::Geometry::distPointLineSquared(hit_point,m.point(m.to_vertex_handle( e3 )),m.point(m.from_vertex_handle( e3 )));
682 if ( dist < min_dist) {
683 //min_dist = dist; Unnecessary. Not used after this point
684 closest_halfedge = e3;
685 }
686
687 // collapse into to point which is closer to hitpoint
688 TriMesh::Point to = m.point( m.to_vertex_handle(closest_halfedge) );
689 TriMesh::Point from = m.point( m.from_vertex_handle(closest_halfedge) );
690
691 if ( (hit_point - to).sqrnorm() > (hit_point - from).sqrnorm() )
692 closest_halfedge = m.opposite_halfedge_handle(closest_halfedge);
693
694 if ( m.is_collapse_ok(closest_halfedge) ){
695 // m.point(m.to_vertex_handle(closest_edge)) = hit_point;
696 m.collapse(closest_halfedge );
697 m.garbage_collection();
698 } else
699 emit log(LOGERR,"Collapse is not allowed here!");
700
701 emit log(LOGOUT,"Picked Edge " + QString::number(closest_halfedge.idx()));
702
703 emit updatedObject(object->id(), UPDATE_TOPOLOGY);
704 emit updateView();
705 emit createBackup(object->id(),"Edge Collapse", UPDATE_TOPOLOGY);
706 }
707
708 // Poly Meshes
709 if ( object->picked(node_idx) && object->dataType(DATA_POLY_MESH) ) {
710 PolyMesh& m = *PluginFunctions::polyMesh(object);
711 PolyMesh::FaceHandle fh = m.face_handle(target_idx);
712
713 // find closest edge
714 PolyMesh::HalfedgeHandle closest_edge(-1);
715 double min_dist = FLT_MAX;
716
717 PolyMesh::FaceEdgeIter fe_it(m,fh);
718 for(; fe_it.is_valid(); ++fe_it)
719 {
720 PolyMesh::HalfedgeHandle heh = m.halfedge_handle(*fe_it,0);
721 double dist = ACG::Geometry::distPointLineSquared(hit_point,m.point(m.to_vertex_handle( heh )), m.point(m.from_vertex_handle( heh )));
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
762 void 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
788 double dist = ACG::Geometry::distPointLineSquared(
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 }
797 dist = ACG::Geometry::distPointLineSquared(
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) ) {
816 PolyMesh& m = *PluginFunctions::polyMesh(object);
817 PolyMesh::FaceHandle fh = m.face_handle(target_idx);
818
819 PolyMesh::FaceHalfedgeIter fh_it(m,fh);
820
821 std::vector<PolyMesh::HalfedgeHandle> halfEdgeHandles;
822 //get all edges which belongs to the picked face
823 for (;fh_it.is_valid(); ++fh_it)
824 {
825 halfEdgeHandles.push_back(*fh_it);
826 }
827
828 //search for the nearest edge
829 PolyMesh::EdgeHandle closest_edge = m.edge_handle(*halfEdgeHandles.begin());
830 double min_dist = ACG::Geometry::distPointLineSquared(hit_point, m.point(m.to_vertex_handle( *halfEdgeHandles.begin() )), m.point(m.from_vertex_handle( *halfEdgeHandles.begin() )));
831
832 for (std::vector<PolyMesh::HalfedgeHandle>::iterator iter = halfEdgeHandles.begin(); iter != halfEdgeHandles.end(); ++iter)
833 {
834 double dist = ACG::Geometry::distPointLineSquared(hit_point, m.point(m.to_vertex_handle( *iter )), m.point(m.from_vertex_handle( *iter )));
835 if (dist < min_dist)
836 {
837 closest_edge = m.edge_handle(*iter);
838 min_dist = dist;
839 }
840 }
841
842 m.split(closest_edge,hit_point);
843
844 emit log(LOGOUT,"Picked Edge " + QString::number(closest_edge.idx()) );
845
846 emit updatedObject(object->id(), UPDATE_TOPOLOGY);
847 emit updateView();
848 emit createBackup(object->id(),"Edge Split", UPDATE_TOPOLOGY);
849 }
850
851 } else return;
852 }
853 }
854
855
OpenMesh::TriMesh_ArrayKernelT
Definition: PolyMesh_ArrayKernelT.hh:73
LOGWARN
Definition: LoggingInterface.hh:95
UPDATE_ALL
const UpdateType UPDATE_ALL(UpdateTypeSet(1))
Identifier for all updates.
TopologyPlugin::split_edge
void split_edge(QMouseEvent *_event)
Split Edge.
Definition: TopologyPlugin.cc:762
TopologyPlugin::collapse_edge
void collapse_edge(QMouseEvent *_event)
Collapse edge.
Definition: TopologyPlugin.cc:649
PluginFunctions::scenegraphPick
bool scenegraphPick(ACG::SceneGraph::PickTarget _pickTarget, const QPoint &_mousePos, size_t &_nodeIdx, size_t &_targetIdx, ACG::Vec3d *_hitPointPtr=0)
Execute picking operation on scenegraph.
Definition: PluginFunctions.cc:478
DATA_POLY_MESH
#define DATA_POLY_MESH
Definition: PolyMesh.hh:59
OpenMesh::PolyMeshT::Point
Kernel::Point Point
Coordinate type.
Definition: PolyMeshT.hh:112
OpenMesh::PolyMeshT::add_vertex
VertexHandle add_vertex(const Point &_p)
Alias for new_vertex(const Point&).
Definition: PolyMeshT.hh:235
BaseObject::id
int id() const
Definition: BaseObject.cc:190
PluginFunctions::getPickedObject
bool getPickedObject(const size_t _node_idx, BaseObjectData *&_object)
Get the picked mesh.
Definition: PluginFunctions.cc:259
TopologyPlugin::toolBarTriggered
void toolBarTriggered(QAction *_action)
called when an action on the toolbar was triggered
Definition: TopologyPlugin.cc:142
UPDATE_SELECTION
const UpdateType UPDATE_SELECTION(UpdateTypeSet(1)<< 4)
Selection updated.
TopologyPlugin::slotPickModeChanged
void slotPickModeChanged(const std::string &_mode)
Toggle actions when the PickMode changes.
Definition: TopologyPlugin.cc:166
OpenMesh::PolyMeshT::FaceVertexIter
Kernel::FaceVertexIter FaceVertexIter
Circulator.
Definition: PolyMeshT.hh:167
BaseObject::dataType
bool dataType(DataType _type) const
Definition: BaseObject.cc:221
OpenMesh::PolyMeshT::VertexHandle
Kernel::VertexHandle VertexHandle
Handle for referencing the corresponding item.
Definition: PolyMeshT.hh:136
PluginFunctions::triMesh
TriMesh * triMesh(BaseObjectData *_object)
Get a triangle mesh from an object.
Definition: PluginFunctionsTriangleMesh.cc:134
LOGERR
Definition: LoggingInterface.hh:96
ACG::Geometry::distPointLineSquared
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
OpenMesh::PolyMeshT::FaceEdgeIter
Kernel::FaceEdgeIter FaceEdgeIter
Circulator.
Definition: PolyMeshT.hh:169
PluginFunctions::getObject
bool getObject(const int _identifier, BaseObject *&_object)
Get the object which has the given identifier.
Definition: PluginFunctions.cc:294
TopologyPlugin::pluginsInitialized
void pluginsInitialized()
initialize the Plugin
Definition: TopologyPlugin.cc:73
OpenMesh::PolyMesh_ArrayKernelT
Definition: PolyMesh_ArrayKernelT.hh:94
TopologyPlugin::add_face
void add_face(QMouseEvent *_event)
Add a face.
Definition: TopologyPlugin.cc:236
TopologyPlugin::TopologyPlugin
TopologyPlugin()
Constructor.
Definition: TopologyPlugin.cc:56
TopologyPlugin::delete_face
void delete_face(QMouseEvent *_event)
Delete a face at the current hit point.
Definition: TopologyPlugin.cc:535
DataType
Predefined datatypes.
Definition: DataTypes.hh:83
OpenMesh::PolyMeshT::FaceHalfedgeIter
Kernel::FaceHalfedgeIter FaceHalfedgeIter
Circulator.
Definition: PolyMeshT.hh:168
OpenMesh::VectorT
Definition: Vector11T.hh:83
PluginFunctions::polyMesh
PolyMesh * polyMesh(BaseObjectData *_object)
Get a poly mesh from an object.
Definition: PluginFunctionsPolyMesh.cc:128
ACG::SceneGraph::PICK_FACE
picks faces (should be implemented for all nodes)
Definition: PickTarget.hh:78
UPDATE_TOPOLOGY
const UpdateType UPDATE_TOPOLOGY(UpdateTypeSet(1)<< 3)
Topology updated.
TopologyPlugin::split_face
void split_face(QMouseEvent *_event)
Split a face at the current hit point.
Definition: TopologyPlugin.cc:476
PluginFunctions::actionMode
Viewer::ActionMode actionMode()
Get the current Action mode.
Definition: PluginFunctions.cc:586
TopologyPlugin::flip_edge
void flip_edge(QMouseEvent *_event)
Flip edge.
Definition: TopologyPlugin.cc:584
BaseObjectData::picked
virtual bool picked(uint _node_idx)
detect if the node has been picked
Definition: BaseObjectData.cc:311
DATA_TRIANGLE_MESH
#define DATA_TRIANGLE_MESH
Definition: TriangleMesh.hh:60
BaseObjectData
Definition: BaseObjectData.hh:102
TopologyPlugin::clearAddFaceVertices
void clearAddFaceVertices()
clear the add face vector
Definition: TopologyPlugin.cc:200
OpenMesh::PolyMeshT::split
void split(FaceHandle _fh, const Point &_p)
Face split (= 1-to-n split)
Definition: PolyMeshT.hh:560
OpenMesh::PolyMeshT::update_normals
void update_normals()
Compute normals for all primitives.
Definition: PolyMeshT_impl.hh:259
TopologyPlugin::slotMouseEvent
void slotMouseEvent(QMouseEvent *_event)
this is called when a mouse event occurred
Definition: TopologyPlugin.cc:182
LOGOUT
Definition: LoggingInterface.hh:93
PluginFunctions::pickMode
const std::string pickMode()
Get the current Picking mode.
Definition: PluginFunctions.cc:575
OpenMesh::TriMeshT::split
VertexHandle split(EdgeHandle _eh, const Point &_p)
Edge split (= 2-to-4 split)
Definition: TriMeshT.hh:267