43#include "TopologyPlugin.hh"
44#include <QActionGroup>
46#define EDGE_FLIP_POPUP "<B>Flip Edge</B><br>Rotate an edge"
47#define EDGE_COLLAPSE_POPUP "<B>Collapse Edge</B><br>Collapse an edge into one of its vertices."
48#define EDGE_SPLIT_POPUP "<B>Split Edge</B><br>Split an edge at the clicked point."
49#define FACE_ADD_POPUP "<B>Add Face</B><br>Insert a face between clicked vertices."
50#define FACE_SPLIT_POPUP "<B>Split Face</B><br>Split a face at a clicked point."
51#define FACE_DELETE_POPUP "<B>Delete Face</B><br>Remove a clicked face."
61 edgeCollapseAction_(0),
75 emit addHiddenPickMode(EDGE_FLIP_POPUP);
76 emit addHiddenPickMode(EDGE_SPLIT_POPUP);
77 emit addHiddenPickMode(EDGE_COLLAPSE_POPUP);
78 emit addHiddenPickMode(FACE_ADD_POPUP);
79 emit addHiddenPickMode(FACE_SPLIT_POPUP);
80 emit addHiddenPickMode(FACE_DELETE_POPUP);
83 toolbar_ =
new QToolBar(
"Topology");
85 QActionGroup* group =
new QActionGroup(toolbar_);
87 QString iconPath = OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator();
89 const QString baseHelpURL =
"<a href='qthelp://org.openflipper.plugin-topology/Plugin-Topology/index.html";
90 const QString clickText = tr(
"Click for more information</a>");
93 edgeFlipAction_ = toolbar_->addAction( QIcon(iconPath +
"topology-edgeFlip.png"), EDGE_FLIP_POPUP );
94 edgeFlipAction_->setCheckable(
true);
95 edgeFlipAction_->setActionGroup(group);
96 edgeFlipAction_->setWhatsThis(tr(
"Flip edge. ") + baseHelpURL+
"#flip_edge'>" + clickText);
98 edgeSplitAction_ = toolbar_->addAction( QIcon(iconPath +
"topology-edgeSplit.png"), EDGE_SPLIT_POPUP );
99 edgeSplitAction_->setCheckable(
true);
100 edgeSplitAction_->setActionGroup(group);
101 edgeSplitAction_->setWhatsThis(tr(
"Split edge. ") + baseHelpURL+
"#split_edge'>" + clickText);
104 edgeCollapseAction_ = toolbar_->addAction( QIcon(iconPath +
"topology-edgeCollapse.png"), EDGE_COLLAPSE_POPUP );
105 edgeCollapseAction_->setCheckable(
true);
106 edgeCollapseAction_->setActionGroup(group);
107 edgeCollapseAction_->setWhatsThis(tr(
"Collapse edge. ") + baseHelpURL+
"#collapse_edge'>" + clickText);
110 toolbar_->addSeparator();
111 faceAddAction_ = toolbar_->addAction( QIcon(iconPath +
"topology-addFace.png"), FACE_ADD_POPUP );
112 faceAddAction_->setCheckable(
true);
113 faceAddAction_->setActionGroup(group);
114 faceAddAction_->setWhatsThis(tr(
"Add face.") + baseHelpURL+
"#add_face'>" + clickText);
117 faceDeleteAction_ = toolbar_->addAction( QIcon(iconPath +
"topology-deleteFace.png"), FACE_DELETE_POPUP );
118 faceDeleteAction_->setCheckable(
true);
119 faceDeleteAction_->setActionGroup(group);
120 faceDeleteAction_->setWhatsThis(tr(
"Delete face. ") + baseHelpURL+
"#delete_face'>" + clickText);
123 faceSplitAction_ = toolbar_->addAction( QIcon(iconPath +
"topology-splitFace.png"), FACE_SPLIT_POPUP );
124 faceSplitAction_->setCheckable(
true);
125 faceSplitAction_->setActionGroup(group);
126 faceSplitAction_->setWhatsThis(tr(
"Split face. ") + baseHelpURL+
"#split_face'>" + clickText);
128 group->setExclusive(
true);
130 connect( toolbar_, SIGNAL( actionTriggered(QAction*) ),
this, SLOT(
toolBarTriggered(QAction*) ));
132 emit addToolbar( toolbar_ );
144 if ( _action->text() == EDGE_FLIP_POPUP)
146 else if ( _action->text() == EDGE_SPLIT_POPUP)
148 else if ( _action->text() == EDGE_COLLAPSE_POPUP)
150 else if ( _action->text() == FACE_ADD_POPUP)
152 else if ( _action->text() == FACE_SPLIT_POPUP)
154 else if ( _action->text() == FACE_DELETE_POPUP)
169 edgeFlipAction_->setChecked( _mode == EDGE_FLIP_POPUP );
170 edgeSplitAction_->setChecked( _mode == EDGE_SPLIT_POPUP );
171 edgeCollapseAction_->setChecked( _mode == EDGE_COLLAPSE_POPUP );
172 faceAddAction_->setChecked( _mode == FACE_ADD_POPUP );
173 faceDeleteAction_->setChecked( _mode == FACE_DELETE_POPUP );
174 faceSplitAction_->setChecked( _mode == FACE_SPLIT_POPUP );
184 if ( _event->buttons() == Qt::RightButton )
202 for ( uint i = 0 ; i < addFaceVertices_.size() ; ++i ) {
210 if ( vh.is_valid()) {
211 m->status(vh).set_selected(
false);
218 if ( vh.is_valid()) {
219 m->status(vh).set_selected(
false);
226 addFaceVertices_.clear();
238 if (( _event->type() != QEvent::MouseButtonPress) && (_event->type() != QEvent::MouseButtonDblClick))
241 size_t node_idx, target_idx;
256 float shortest_distance = (m.point(closest) - hit_point).sqrnorm();
259 if ( (m.point(*fv_it) - hit_point).sqrnorm() < shortest_distance ) {
260 shortest_distance = (m.point(*fv_it) - hit_point).sqrnorm();
265 if ( (m.point(*fv_it) - hit_point).sqrnorm() < shortest_distance ) {
271 std::pair<int,int> newVertex(object->
id(), closest.idx() );
273 for ( uint i = 0 ; i < addFaceVertices_.size() ; ++i ) {
274 if ( ( addFaceVertices_[i].first == newVertex.first ) &&
275 ( addFaceVertices_[i].second == newVertex.second ) ) {
276 addFaceVertices_.erase(addFaceVertices_.begin()+i);
277 m.status(closest).set_selected(
false);
285 addFaceVertices_.push_back( std::pair<int,int>(object->
id(), closest.idx() ) );
286 m.status(closest).set_selected(
true);
289 if ( addFaceVertices_.size() < 3 ) {
297 for ( uint i = 0 ; i < addFaceVertices_.size() ; ++i ) {
300 emit log(
LOGERR,
"Unable to get object for adding face");
305 if ( tmpObject->
dataType() != dt ) {
306 emit log(
LOGERR,
"Adding faces between different type of meshes is not supported!");
314 int objectId = addFaceVertices_[0].first;
315 for ( uint i = 0 ; i < addFaceVertices_.size() ; ++i ) {
316 if ( addFaceVertices_[i].first != objectId ) {
317 emit log(
LOGERR,
"Adding faces between different objects!");
328 bool errlog = omerr().is_enabled();
331 fh = m.add_face(vh0,vh1,vh2);
332 if ( !fh.is_valid() ) {
333 fh = m.add_face(vh2,vh1,vh0);
348 emit log(
LOGERR,
"Unable to add face!");
360 float shortest_distance = FLT_MAX;
363 float distance = (m.point( fv_it ) - hit_point).sqrnorm();
365 if (distance < shortest_distance){
366 shortest_distance = distance;
371 if (!closest.is_valid())
374 if (_event->type() != QEvent::MouseButtonDblClick){
376 std::pair<int,int> newVertex(object->
id(), closest.idx() );
378 for ( uint i = 0 ; i < addFaceVertices_.size() ; ++i ) {
379 if ( ( addFaceVertices_[i].first == newVertex.first ) &&
380 ( addFaceVertices_[i].second == newVertex.second ) ) {
381 addFaceVertices_.erase(addFaceVertices_.begin()+i);
382 m.status(closest).set_selected(
false);
390 addFaceVertices_.push_back( std::pair<int,int>(object->
id(), closest.idx() ) );
391 m.status(closest).set_selected(
true);
395 if ( (addFaceVertices_.size() < 3) || (_event->type() != QEvent::MouseButtonDblClick) ) {
403 for ( uint i = 0 ; i < addFaceVertices_.size() ; ++i ) {
406 emit log(
LOGERR,
"Unable to get object for adding face");
411 if ( tmpObject->
dataType() != dt ) {
412 emit log(
LOGERR,
"Adding faces between different type of meshes is not supported!");
420 int objectId = addFaceVertices_[0].first;
421 for ( uint i = 0 ; i < addFaceVertices_.size() ; ++i ) {
422 if ( addFaceVertices_[i].first != objectId ) {
423 emit log(
LOGERR,
"Adding faces between different objects!");
429 std::vector< PolyMesh::VertexHandle > vhs;
430 for ( uint i = 0 ; i < addFaceVertices_.size() ; ++i )
431 vhs.push_back( m.vertex_handle( addFaceVertices_[i].second ) );
434 bool errlog = omerr().is_enabled();
437 fh = m.add_face(vhs);
440 std::vector< PolyMesh::VertexHandle > rvhs;
442 while (!vhs.empty()){
443 rvhs.push_back( vhs.back() );
447 fh = m.add_face(rvhs);
463 emit log(
LOGERR,
"Unable to add face!");
478 if ( _event->type() != QEvent::MouseButtonPress )
492 emit log(
LOGOUT,
"Picked Face " + QString::number(fh.idx()) +
", normal (" +
493 QString::number(m.normal(fh)[0]) +
"," +
494 QString::number(m.normal(fh)[1]) +
"," +
495 QString::number(m.normal(fh)[2]) +
") ") ;
499 m.garbage_collection();
511 emit log(
LOGOUT,
"Picked Face " + QString::number(fh.idx()) +
", normal (" +
512 QString::number(m.normal(fh)[0]) +
"," +
513 QString::number(m.normal(fh)[1]) +
"," +
514 QString::number(m.normal(fh)[2]) +
") ") ;
518 m.garbage_collection();
537 if ( _event->type() != QEvent::MouseButtonPress )
540 size_t node_idx, target_idx;
550 emit log(
LOGOUT,
"Picked Face " + QString::number(fh.idx()) +
", normal (" +
551 QString::number(m.normal(fh)[0]) +
"," +
552 QString::number(m.normal(fh)[1]) +
"," +
553 QString::number(m.normal(fh)[2]) +
") ") ;
556 m.garbage_collection();
562 emit log(
LOGOUT,
"Picked Face " + QString::number(fh.idx()) +
", normal (" +
563 QString::number(m.normal(fh)[0]) +
"," +
564 QString::number(m.normal(fh)[1]) +
"," +
565 QString::number(m.normal(fh)[2]) +
") ") ;
568 m.garbage_collection();
586 if ( _event->type() != QEvent::MouseButtonPress )
589 size_t node_idx, target_idx;
612 if ( dist < min_dist ) {
614 closest_edge = m.edge_handle(e2);
618 if ( dist < min_dist) {
620 closest_edge = m.edge_handle(e3);
623 if ( m.is_flip_ok(closest_edge) )
624 m.flip(closest_edge);
626 emit log(
LOGERR,
"Flip is not allowed here!");
628 emit log(
LOGOUT,
"Picked Edge " + QString::number(closest_edge.idx()));
636 emit log(
LOGWARN,
"Edge Flips not supported for Poly Meshes");
651 if ( _event->type() != QEvent::MouseButtonPress )
654 size_t node_idx, target_idx;
677 if ( dist < min_dist ) {
679 closest_halfedge = e2;
683 if ( dist < min_dist) {
685 closest_halfedge = e3;
689 TriMesh::Point to = m.point( m.to_vertex_handle(closest_halfedge) );
690 TriMesh::Point from = m.point( m.from_vertex_handle(closest_halfedge) );
692 if ( (hit_point - to).sqrnorm() > (hit_point - from).sqrnorm() )
693 closest_halfedge = m.opposite_halfedge_handle(closest_halfedge);
695 if ( m.is_collapse_ok(closest_halfedge) ){
697 m.collapse(closest_halfedge );
698 m.garbage_collection();
700 emit log(
LOGERR,
"Collapse is not allowed here!");
702 emit log(
LOGOUT,
"Picked Edge " + QString::number(closest_halfedge.idx()));
716 double min_dist = FLT_MAX;
718 for(
auto fe_it : fh.
edges())
734 if ( (hit_point - to).sqrnorm() > (hit_point - from).sqrnorm() )
735 closest_edge = m.opposite_halfedge_handle(closest_edge);
739 m.collapse(closest_edge );
740 m.garbage_collection();
744 emit log(
LOGOUT,
"Picked Edge " + QString::number(closest_edge.idx()));
763 if ( _event->type() != QEvent::MouseButtonPress )
766 size_t node_idx, target_idx;
790 m.point(m.to_vertex_handle(e2)),
791 m.point(m.from_vertex_handle(e2)));
793 if (dist < min_dist) {
795 closest_edge = m.edge_handle(e2);
799 m.point(m.to_vertex_handle(e3)),
800 m.point(m.from_vertex_handle(e3)));
802 if (dist < min_dist) {
804 closest_edge = m.edge_handle(e3);
807 m.
split(closest_edge,hit_point);
809 emit log(
LOGOUT,
"Picked Edge " + QString::number(closest_edge.idx() ));
819 std::vector<OpenMesh::SmartHalfedgeHandle> halfEdgeHandles;
823 halfEdgeHandles.push_back(fh_it);
830 for (
auto iter : halfEdgeHandles)
835 closest_edge = iter.edge();
840 m.
split(closest_edge,hit_point);
842 emit log(
LOGOUT,
"Picked Edge " + QString::number(closest_edge.idx()) );
#define DATA_TRIANGLE_MESH
virtual bool picked(uint _node_idx)
detect if the node has been picked
bool dataType(DataType _type) const
bool is_valid() const
The handle is valid iff the index is not negative.
Kernel::FaceEdgeIter FaceEdgeIter
Circulator.
Kernel::VertexHandle VertexHandle
Handle for referencing the corresponding item.
Kernel::EdgeHandle EdgeHandle
Scalar type.
Kernel::FaceVertexIter FaceVertexIter
Circulator.
void update_normals()
Compute normals for all primitives.
SmartVertexHandle add_vertex(const Point _p)
Kernel::FaceHandle FaceHandle
Scalar type.
Kernel::HalfedgeHandle HalfedgeHandle
Scalar type.
Kernel::Point Point
Coordinate type.
void split(FaceHandle _fh, const Point &_p)
Face split (= 1-to-n split)
SmartVertexHandle split(EdgeHandle _eh, const Point &_p)
Edge split (= 2-to-4 split)
void flip_edge(QMouseEvent *_event)
Flip edge.
void slotMouseEvent(QMouseEvent *_event)
this is called when a mouse event occurred
void collapse_edge(QMouseEvent *_event)
Collapse edge.
void split_edge(QMouseEvent *_event)
Split Edge.
TopologyPlugin()
Constructor.
void slotPickModeChanged(const std::string &_mode)
Toggle actions when the PickMode changes.
void split_face(QMouseEvent *_event)
Split a face at the current hit point.
void toolBarTriggered(QAction *_action)
called when an action on the toolbar was triggered
void clearAddFaceVertices()
clear the add face vector
void pluginsInitialized()
initialize the Plugin
void delete_face(QMouseEvent *_event)
Delete a face at the current hit point.
void add_face(QMouseEvent *_event)
Add a face.
const UpdateType UPDATE_TOPOLOGY(UpdateTypeSet(8))
Topology updated.
const UpdateType UPDATE_ALL(UpdateTypeSet(1))
Identifier for all updates.
const UpdateType UPDATE_SELECTION(UpdateTypeSet(16))
Selection updated.
Vec::value_type distPointLineSquared(const Vec &_p, const Vec &_v0, const Vec &_v1, Vec *_min_v)
squared distance from point _p to line segment (_v0,_v1)
@ PICK_FACE
picks faces (should be implemented for all nodes)
bool getObject(const int _identifier, BaseObject *&_object)
Get the object which has the given identifier.
TriMesh * triMesh(BaseObjectData *_object)
Get a triangle mesh from an object.
const std::string pickMode()
Get the current Picking mode.
PolyMesh * polyMesh(BaseObjectData *_object)
Get a poly mesh from an object.
bool getPickedObject(const size_t _node_idx, BaseObjectData *&_object)
Get the picked mesh.
bool scenegraphPick(ACG::SceneGraph::PickTarget _pickTarget, const QPoint &_mousePos, size_t &_nodeIdx, size_t &_targetIdx, ACG::Vec3d *_hitPointPtr=0)
Execute picking operation on scenegraph.
Viewer::ActionMode actionMode()
Get the current Action mode.
PolyConnectivity::ConstFaceEdgeRange edges() const
Returns a range of edges of the face (PolyConnectivity::fv_range())
PolyConnectivity::ConstFaceVertexRange vertices() const
Returns a range of vertices incident to the face (PolyConnectivity::fv_range())
PolyConnectivity::ConstFaceHalfedgeRange halfedges() const
Returns a range of halfedges of the face (PolyConnectivity::fh_range())