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