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 
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 
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 
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");
301  return;
302  }
303 
304  if ( tmpObject->dataType() != dt ) {
305  emit log(LOGERR,"Adding faces between different type of meshes is not supported!");
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!");
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 
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");
407  return;
408  }
409 
410  if ( tmpObject->dataType() != dt ) {
411  emit log(LOGERR,"Adding faces between different type of meshes is not supported!");
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!");
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 
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 
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) ) {
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 
const UpdateType UPDATE_ALL(UpdateTypeSet(1))
Identifier for all updates.
void split_edge(QMouseEvent *_event)
Split Edge.
void collapse_edge(QMouseEvent *_event)
Collapse edge.
bool scenegraphPick(ACG::SceneGraph::PickTarget _pickTarget, const QPoint &_mousePos, size_t &_nodeIdx, size_t &_targetIdx, ACG::Vec3d *_hitPointPtr=0)
Execute picking operation on scenegraph.
#define DATA_POLY_MESH
Definition: PolyMesh.hh:59
Kernel::Point Point
Coordinate type.
Definition: PolyMeshT.hh:112
VertexHandle add_vertex(const Point &_p)
Alias for new_vertex(const Point&).
Definition: PolyMeshT.hh:235
int id() const
Definition: BaseObject.cc:190
bool getPickedObject(const size_t _node_idx, BaseObjectData *&_object)
Get the picked mesh.
void toolBarTriggered(QAction *_action)
called when an action on the toolbar was triggered
const UpdateType UPDATE_SELECTION(UpdateTypeSet(1)<< 4)
Selection updated.
void slotPickModeChanged(const std::string &_mode)
Toggle actions when the PickMode changes.
Kernel::FaceVertexIter FaceVertexIter
Circulator.
Definition: PolyMeshT.hh:167
bool dataType(DataType _type) const
Definition: BaseObject.cc:221
Kernel::VertexHandle VertexHandle
Handle for referencing the corresponding item.
Definition: PolyMeshT.hh:136
TriMesh * triMesh(BaseObjectData *_object)
Get a triangle mesh from an object.
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
Kernel::FaceEdgeIter FaceEdgeIter
Circulator.
Definition: PolyMeshT.hh:169
bool getObject(const int _identifier, BaseObject *&_object)
Get the object which has the given identifier.
void pluginsInitialized()
initialize the Plugin
void add_face(QMouseEvent *_event)
Add a face.
TopologyPlugin()
Constructor.
void delete_face(QMouseEvent *_event)
Delete a face at the current hit point.
Predefined datatypes.
Definition: DataTypes.hh:83
Kernel::FaceHalfedgeIter FaceHalfedgeIter
Circulator.
Definition: PolyMeshT.hh:168
PolyMesh * polyMesh(BaseObjectData *_object)
Get a poly mesh from an object.
picks faces (should be implemented for all nodes)
Definition: PickTarget.hh:78
const UpdateType UPDATE_TOPOLOGY(UpdateTypeSet(1)<< 3)
Topology updated.
void split_face(QMouseEvent *_event)
Split a face at the current hit point.
Viewer::ActionMode actionMode()
Get the current Action mode.
void flip_edge(QMouseEvent *_event)
Flip edge.
virtual bool picked(uint _node_idx)
detect if the node has been picked
#define DATA_TRIANGLE_MESH
Definition: TriangleMesh.hh:60
void clearAddFaceVertices()
clear the add face vector
void split(FaceHandle _fh, const Point &_p)
Face split (= 1-to-n split)
Definition: PolyMeshT.hh:560
void update_normals()
Compute normals for all primitives.
void slotMouseEvent(QMouseEvent *_event)
this is called when a mouse event occurred
const std::string pickMode()
Get the current Picking mode.
VertexHandle split(EdgeHandle _eh, const Point &_p)
Edge split (= 2-to-4 split)
Definition: TriMeshT.hh:267