Commit 6caa5d02 authored by Matthias Möller's avatar Matthias Möller
Browse files

adds lasso and floodfill selectionmode for OVM

thanks to Vladimir Chalupecky

refs #1880

git-svn-id: http://www.openflipper.org/svnrepo/OpenFlipper/branches/Free@17848 383ad7c9-94d9-4d36-a494-682f7c89f535
parent ad41a2be
...@@ -126,10 +126,13 @@ void VolumeMeshSelectionPlugin::pluginsInitialized() { ...@@ -126,10 +126,13 @@ void VolumeMeshSelectionPlugin::pluginsInitialized() {
iconPath + SHEET_SELECTION, cellType_, sheetSelectionHandle_); iconPath + SHEET_SELECTION, cellType_, sheetSelectionHandle_);
allSupportedTypes_ = vertexType_ | edgeType_ | faceType_ | cellType_; allSupportedTypes_ = vertexType_ | edgeType_ | faceType_ | cellType_;
floodFillSupportedTypes_ = vertexType_ | edgeType_ | faceType_;
// Determine, which selection modes are requested // Determine, which selection modes are requested
emit emit showToggleSelectionMode(environmentHandle_, true, allSupportedTypes_);
showToggleSelectionMode(environmentHandle_, true, allSupportedTypes_);
emit showVolumeLassoSelectionMode(environmentHandle_, true, allSupportedTypes_);
emit showFloodFillSelectionMode(environmentHandle_, true, floodFillSupportedTypes_);
// Define vertex operations // Define vertex operations
QStringList vertexOperations; QStringList vertexOperations;
...@@ -220,22 +223,114 @@ bool VolumeMeshSelectionPlugin::cellTypeActive() { ...@@ -220,22 +223,114 @@ bool VolumeMeshSelectionPlugin::cellTypeActive() {
void VolumeMeshSelectionPlugin::slotSelectionOperation(QString _operation) { void VolumeMeshSelectionPlugin::slotSelectionOperation(QString _operation) {
SelectionInterface::PrimitiveType type = 0u; SelectionInterface::PrimitiveType type = 0u;
emit emit getActivePrimitiveType(type);
getActivePrimitiveType(type);
if((type & allSupportedTypes_) == 0) if((type & allSupportedTypes_) == 0)
return; return;
// Test if operation should be applied to target objects only // Test if operation should be applied to target objects only
bool targetsOnly = false; bool targetsOnly = false;
emit emit targetObjectsOnly(targetsOnly);
targetObjectsOnly(targetsOnly);
PluginFunctions::IteratorRestriction restriction = (targetsOnly ? PluginFunctions::TARGET_OBJECTS PluginFunctions::IteratorRestriction restriction = (targetsOnly ? PluginFunctions::TARGET_OBJECTS
: PluginFunctions::ALL_OBJECTS); : PluginFunctions::ALL_OBJECTS);
/* if (_operation == V_SELECT_ALL) {
* TODO: Complete this function for all supported types for (PluginFunctions::ObjectIterator o_it(restriction, DataType(DATA_POLYHEDRAL_MESH | DATA_HEXAHEDRAL_MESH));
*/ o_it != PluginFunctions::objectsEnd(); ++o_it) {
if (o_it->visible())
selectAllVertices(o_it->id());
}
} else if (_operation == V_DESELECT_ALL) {
for (PluginFunctions::ObjectIterator o_it(restriction, DataType(DATA_POLYHEDRAL_MESH | DATA_HEXAHEDRAL_MESH));
o_it != PluginFunctions::objectsEnd(); ++o_it) {
if (o_it->visible())
deselectAllVertices(o_it->id());
}
} else if (_operation == V_INVERT) {
for (PluginFunctions::ObjectIterator o_it(restriction, DataType(DATA_POLYHEDRAL_MESH | DATA_HEXAHEDRAL_MESH));
o_it != PluginFunctions::objectsEnd(); ++o_it) {
if (o_it->visible())
invertVertexSelection(o_it->id());
}
} else if (_operation == V_DELETE) {
for (PluginFunctions::ObjectIterator o_it(restriction, DataType(DATA_POLYHEDRAL_MESH | DATA_HEXAHEDRAL_MESH));
o_it != PluginFunctions::objectsEnd(); ++o_it) {
if (o_it->visible())
deleteSelectedVertices(o_it->id());
}
} else if (_operation == E_SELECT_ALL) {
for (PluginFunctions::ObjectIterator o_it(restriction, DataType(DATA_POLYHEDRAL_MESH | DATA_HEXAHEDRAL_MESH));
o_it != PluginFunctions::objectsEnd(); ++o_it) {
if (o_it->visible())
selectAllEdges(o_it->id());
}
} else if (_operation == E_DESELECT_ALL) {
for (PluginFunctions::ObjectIterator o_it(restriction, DataType(DATA_POLYHEDRAL_MESH | DATA_HEXAHEDRAL_MESH));
o_it != PluginFunctions::objectsEnd(); ++o_it) {
if (o_it->visible())
deselectAllEdges(o_it->id());
}
} else if (_operation == E_INVERT) {
for (PluginFunctions::ObjectIterator o_it(restriction, DataType(DATA_POLYHEDRAL_MESH | DATA_HEXAHEDRAL_MESH));
o_it != PluginFunctions::objectsEnd(); ++o_it) {
if (o_it->visible())
invertEdgeSelection(o_it->id());
}
} else if (_operation == E_DELETE) {
for (PluginFunctions::ObjectIterator o_it(restriction, DataType(DATA_POLYHEDRAL_MESH | DATA_HEXAHEDRAL_MESH));
o_it != PluginFunctions::objectsEnd(); ++o_it) {
if (o_it->visible())
deleteSelectedEdges(o_it->id());
}
} else if (_operation == F_SELECT_ALL) {
for (PluginFunctions::ObjectIterator o_it(restriction, DataType(DATA_POLYHEDRAL_MESH | DATA_HEXAHEDRAL_MESH));
o_it != PluginFunctions::objectsEnd(); ++o_it) {
if (o_it->visible())
selectAllFaces(o_it->id());
}
} else if (_operation == F_DESELECT_ALL) {
for (PluginFunctions::ObjectIterator o_it(restriction, DataType(DATA_POLYHEDRAL_MESH | DATA_HEXAHEDRAL_MESH));
o_it != PluginFunctions::objectsEnd(); ++o_it) {
if (o_it->visible())
deselectAllFaces(o_it->id());
}
} else if (_operation == F_INVERT) {
for (PluginFunctions::ObjectIterator o_it(restriction, DataType(DATA_POLYHEDRAL_MESH | DATA_HEXAHEDRAL_MESH));
o_it != PluginFunctions::objectsEnd(); ++o_it) {
if (o_it->visible())
invertFaceSelection(o_it->id());
}
} else if (_operation == F_DELETE) {
for (PluginFunctions::ObjectIterator o_it(restriction, DataType(DATA_POLYHEDRAL_MESH | DATA_HEXAHEDRAL_MESH));
o_it != PluginFunctions::objectsEnd(); ++o_it) {
if (o_it->visible())
deleteSelectedFaces(o_it->id());
}
} else if (_operation == C_SELECT_ALL) {
for (PluginFunctions::ObjectIterator o_it(restriction, DataType(DATA_POLYHEDRAL_MESH | DATA_HEXAHEDRAL_MESH));
o_it != PluginFunctions::objectsEnd(); ++o_it) {
if (o_it->visible())
selectAllCells(o_it->id());
}
} else if (_operation == C_DESELECT_ALL) {
for (PluginFunctions::ObjectIterator o_it(restriction, DataType(DATA_POLYHEDRAL_MESH | DATA_HEXAHEDRAL_MESH));
o_it != PluginFunctions::objectsEnd(); ++o_it) {
if (o_it->visible())
deselectAllCells(o_it->id());
}
} else if (_operation == C_INVERT) {
for (PluginFunctions::ObjectIterator o_it(restriction, DataType(DATA_POLYHEDRAL_MESH | DATA_HEXAHEDRAL_MESH));
o_it != PluginFunctions::objectsEnd(); ++o_it) {
if (o_it->visible())
invertCellSelection(o_it->id());
}
} else if (_operation == C_DELETE) {
for (PluginFunctions::ObjectIterator o_it(restriction, DataType(DATA_POLYHEDRAL_MESH | DATA_HEXAHEDRAL_MESH));
o_it != PluginFunctions::objectsEnd(); ++o_it) {
if (o_it->visible())
deleteSelectedCells(o_it->id());
}
}
} }
//============================================================================================== //==============================================================================================
...@@ -373,6 +468,122 @@ void VolumeMeshSelectionPlugin::slotToggleSelection(QMouseEvent* _event, ...@@ -373,6 +468,122 @@ void VolumeMeshSelectionPlugin::slotToggleSelection(QMouseEvent* _event,
//============================================================================================== //==============================================================================================
void VolumeMeshSelectionPlugin::slotVolumeLassoSelection(QMouseEvent* _event,
PrimitiveType _currentType, bool _deselect)
{
if ((_currentType & allSupportedTypes_) == 0) return;
if (_event->type() == QEvent::MouseButtonPress)
{
volumeLassoPoints_.append(_event->pos());
return;
}
else if (_event->type() == QEvent::MouseButtonDblClick)
{
ACG::GLState &state = PluginFunctions::viewerProperties().glState();
bool updateGL = state.updateGL();
state.set_updateGL (false);
QPolygon p(volumeLassoPoints_);
QRegion region = QRegion(p);
SelectVolumeAction action(region, this, _currentType, _deselect, state);
ACG::SceneGraph::traverse (PluginFunctions::getRootNode(), action);
state.set_updateGL(updateGL);
// Clear lasso points
volumeLassoPoints_.clear();
}
}
void VolumeMeshSelectionPlugin::slotFloodFillSelection(QMouseEvent* _event,
double _maxAngle, PrimitiveType _currentType, bool _deselect)
{
// Return if none of the currently active types is handled by this plugin
if ((_currentType & floodFillSupportedTypes_) == 0)
return;
unsigned int node_idx, target_idx;
ACG::Vec3d hit_point;
// pick Anything to find all possible objects
if (PluginFunctions::scenegraphPick(ACG::SceneGraph::PICK_ANYTHING,
_event->pos(), node_idx, target_idx, &hit_point))
{
BaseObjectData* object = 0;
if (PluginFunctions::getPickedObject(node_idx, object))
{
if (object->dataType() == DATA_POLYHEDRAL_MESH)
{
if (PluginFunctions::scenegraphPick(ACG::SceneGraph::PICK_FACE,
_event->pos(), node_idx, target_idx, &hit_point))
{
if (PluginFunctions::getPickedObject(node_idx, object))
{
if (object->dataType(DATA_POLYHEDRAL_MESH))
{
floodFillSelection(PluginFunctions::polyhedralMesh(object),
target_idx, _maxAngle, _currentType, _deselect);
emit updatedObject(object->id(), UPDATE_SELECTION);
}
}
}
}
else if(object->dataType() == DATA_HEXAHEDRAL_MESH)
{
if (PluginFunctions::scenegraphPick(ACG::SceneGraph::PICK_FACE,
_event->pos(), node_idx, target_idx, &hit_point))
{
if(PluginFunctions::getPickedObject(node_idx, object) )
{
if(object->dataType(DATA_HEXAHEDRAL_MESH))
{
floodFillSelection(PluginFunctions::hexahedralMesh(object),
target_idx, _maxAngle, _currentType, _deselect);
emit updatedObject(object->id(), UPDATE_SELECTION);
}
}
}
}
else
{
emit log(LOGERR, tr("floodFillSelection: Unsupported dataType"));
}
}
}
}
/// Traverse the scenegraph and call the selection function for all mesh nodes
bool SelectVolumeAction::operator()(BaseNode* _node)
{
BaseObjectData* object = 0;
if (PluginFunctions::getPickedObject(_node->id(), object))
{
bool selected = false;
if (object->dataType(DATA_POLYHEDRAL_MESH))
{
PolyhedralMesh* m = PluginFunctions::polyhedralMesh(object);
selected = plugin_->volumeSelection(m, state_, &region_, type_, deselection_);
} else if(object->dataType(DATA_HEXAHEDRAL_MESH)) {
HexahedralMesh* m = PluginFunctions::hexahedralMesh(object);
selected = plugin_->volumeSelection(m, state_, &region_, type_, deselection_);
}
if (selected){
emit plugin_->updatedObject(object->id(), UPDATE_SELECTION);
}
}
return true;
}
//==============================================================================================
void VolumeMeshSelectionPlugin::slotCustomSelection(QMouseEvent *_event, PrimitiveType _currentType, void VolumeMeshSelectionPlugin::slotCustomSelection(QMouseEvent *_event, PrimitiveType _currentType,
QString _customIdentifier, bool _deselect) { QString _customIdentifier, bool _deselect) {
......
...@@ -86,6 +86,8 @@ public: ...@@ -86,6 +86,8 @@ public:
/// Default destructor /// Default destructor
~VolumeMeshSelectionPlugin(); ~VolumeMeshSelectionPlugin();
friend class SelectVolumeAction;
signals: signals:
// BaseInterface // BaseInterface
...@@ -107,8 +109,11 @@ signals: ...@@ -107,8 +109,11 @@ signals:
void registerType(QString _handleName, DataType _type); void registerType(QString _handleName, DataType _type);
void addPrimitiveType(QString _handleName, QString _name, QString _icon, SelectionInterface::PrimitiveType& _typeHandle); void addPrimitiveType(QString _handleName, QString _name, QString _icon, SelectionInterface::PrimitiveType& _typeHandle);
void addSelectionOperations(QString _handleName, QStringList _operationsList, QString _category, SelectionInterface::PrimitiveType _type = 0u); void addSelectionOperations(QString _handleName, QStringList _operationsList, QString _category, SelectionInterface::PrimitiveType _type = 0u);
void showToggleSelectionMode(QString _handleName, bool _show, SelectionInterface::PrimitiveType _associatedTypes); void showToggleSelectionMode(QString _handleName, bool _show, SelectionInterface::PrimitiveType _associatedTypes);
void showVolumeLassoSelectionMode(QString _handleName, bool _show, SelectionInterface::PrimitiveType _associatedTypes); void showVolumeLassoSelectionMode(QString _handleName, bool _show, SelectionInterface::PrimitiveType _associatedTypes);
void showFloodFillSelectionMode(QString _handleName, bool _show, SelectionInterface::PrimitiveType _associatedTypes);
void addCustomSelectionMode(QString _handleName, QString _modeName, QString _description, QString _icon, void addCustomSelectionMode(QString _handleName, QString _modeName, QString _description, QString _icon,
SelectionInterface::PrimitiveType _associatedTypes, QString& _customIdentifier); SelectionInterface::PrimitiveType _associatedTypes, QString& _customIdentifier);
...@@ -132,6 +137,9 @@ private slots: ...@@ -132,6 +137,9 @@ private slots:
// SelectionInterface // SelectionInterface
void slotSelectionOperation(QString _operation); void slotSelectionOperation(QString _operation);
void slotToggleSelection(QMouseEvent* _event, SelectionInterface::PrimitiveType _currentType, bool _deselect); void slotToggleSelection(QMouseEvent* _event, SelectionInterface::PrimitiveType _currentType, bool _deselect);
void slotVolumeLassoSelection(QMouseEvent* _event, SelectionInterface::PrimitiveType _currentType, bool _deselect);
void slotFloodFillSelection(QMouseEvent* _event, double _maxAngle, SelectionInterface::PrimitiveType _currentType, bool _deselect);
void slotCustomSelection(QMouseEvent *_event, SelectionInterface::PrimitiveType _currentType, QString _customIdentifier, bool _deselect); void slotCustomSelection(QMouseEvent *_event, SelectionInterface::PrimitiveType _currentType, QString _customIdentifier, bool _deselect);
void slotLoadSelection(const INIFile& _file); void slotLoadSelection(const INIFile& _file);
...@@ -311,6 +319,15 @@ public slots: ...@@ -311,6 +319,15 @@ public slots:
//=========================================================================== //===========================================================================
private: private:
/// Surface volume selection tool
template<class MeshT>
bool volumeSelection(MeshT* _mesh, ACG::GLState& _state, QRegion *_region,
PrimitiveType _primitiveTypes, bool _deselection);
/// Select all entities that are connected (and do not exceed the maximum dihedral angle)
template<class MeshT>
void floodFillSelection(MeshT* _mesh, uint _fh, double _maxAngle,
PrimitiveType _primitiveTypes, bool _deselection);
// Get orthogonal orientation of neighboring cell // Get orthogonal orientation of neighboring cell
unsigned char getOrthogonalOrientationOfNeighborCell(const OpenVolumeMesh::CellHandle& _ch1, unsigned char getOrthogonalOrientationOfNeighborCell(const OpenVolumeMesh::CellHandle& _ch1,
...@@ -334,6 +351,7 @@ private: ...@@ -334,6 +351,7 @@ private:
PrimitiveType cellType_; PrimitiveType cellType_;
PrimitiveType allSupportedTypes_; PrimitiveType allSupportedTypes_;
PrimitiveType floodFillSupportedTypes_;
/// Keep volume lasso points /// Keep volume lasso points
QVector<QPoint> volumeLassoPoints_; QVector<QPoint> volumeLassoPoints_;
...@@ -347,6 +365,38 @@ private: ...@@ -347,6 +365,38 @@ private:
}; };
/// Traverse the scenegraph and call the selection function for all mesh nodes
class SelectVolumeAction
{
public:
SelectVolumeAction(QRegion&_region, VolumeMeshSelectionPlugin* _plugin,
unsigned int _type, bool _deselection, ACG::GLState& _state)
: state_(_state)
, region_(_region)
, plugin_(_plugin)
, type_(_type)
, deselection_(_deselection)
{
}
void enter(BaseNode* /*_node*/) {}
void leave(BaseNode* /*_node*/) {}
bool operator()(BaseNode* _node);
private:
ACG::GLState& state_;
QRegion& region_;
VolumeMeshSelectionPlugin* plugin_;
unsigned int type_;
bool deselection_;
};
//=============================================================================
#if defined(INCLUDE_TEMPLATES) && !defined(VOLUMEMESHSELECTIONPLUGINT_CC)
#define VOLUMEMESHSELECTIONPLUGINT_TEMPLATES
#include "VolumeMeshSelectionPluginT.cc"
#endif
//============================================================================= //=============================================================================
#endif // VOLUMEMESHSELECTIONPLUGIN_HH defined #endif // VOLUMEMESHSELECTIONPLUGIN_HH defined
//============================================================================= //=============================================================================
#include "VolumeMeshSelectionPlugin.hh"
#include <stack>
#include <vector>
#include <cmath>
//***********************************************************************************
/** \brief Select all primitives that are entirely projected to the given region
*
* @param _mesh a mesh
* @param _state current gl state
* @param _region region
* @param _primitiveType primitive types to be selected
* @param _deselection true, if entities should be deselected
* @return true, if something was selected
*/
template<class MeshT>
bool VolumeMeshSelectionPlugin::volumeSelection(MeshT* _mesh, ACG::GLState& _state, QRegion *_region,
PrimitiveType _primitiveType, bool _deselection) {
using namespace OpenVolumeMesh;
ACG::Vec3d proj;
bool rv = false;
//reset tagged status
StatusAttrib status(*_mesh);
VertexIter v_it(_mesh->vertices_begin()), v_end(_mesh->vertices_end());
for (; v_it != v_end; ++v_it)
status[*v_it].set_tagged(false);
//tag all vertices that are projected into region
for (v_it = _mesh->vertices_begin(); v_it != v_end; ++v_it) {
proj = _state.project(_mesh->vertex(*v_it));
if (_region->contains(QPoint((int)proj[0], _state.context_height()- (int)proj[1]))) {
status[*v_it].set_tagged(true);
if (_primitiveType & vertexType_) {
rv = true;
status[*v_it].set_selected(!_deselection);
}
}
}
if (_primitiveType & edgeType_) {
EdgeIter e_it(_mesh->edges_begin()), e_end(_mesh->edges_end());
for (; e_it != e_end; ++e_it)
{
VertexHandle v1 = _mesh->halfedge(_mesh->halfedge_handle(*e_it, 0)).to_vertex();
VertexHandle v2 = _mesh->halfedge(_mesh->halfedge_handle(*e_it, 1)).to_vertex();
// select only edges whose both vertices are within the selection region
if (status[v1].tagged() && status[v2].tagged()) {
rv = true;
status[*e_it].set_selected(!_deselection);
}
}
}
if (_primitiveType & faceType_) {
FaceIter f_it(_mesh->faces_begin()), f_end(_mesh->faces_end());
for (; f_it != f_end; ++f_it) {
const std::vector<HalfEdgeHandle>& halfedges = _mesh->face(*f_it).halfedges();
bool all_fv_tagged = true;
for (std::size_t i = 0; i < halfedges.size(); ++i) {
VertexHandle v = _mesh->halfedge(halfedges[i]).to_vertex();
all_fv_tagged &= status[v].tagged();
}
// select only faces that are completely within the selection region
if (all_fv_tagged) {
rv = true;
status[*f_it].set_selected(!_deselection);
}
}
}
if (_primitiveType & cellType_) {
CellIter c_it(_mesh->cells_begin()), c_end(_mesh->cells_end());
for (; c_it != c_end; ++c_it) {
bool all_cv_tagged = true;
for (CellVertexIter cv_it(_mesh->cv_iter(*c_it)); cv_it.valid(); ++cv_it) {
all_cv_tagged &= status[*cv_it].tagged();
}
if (all_cv_tagged) {
rv = true;
status[*c_it].set_selected(!_deselection);
}
}
}
return rv;
}
//***********************************************************************************
/** \brief Select all primitves of a planar region surrounding the faceHandle
*
* @param _mesh a mesh
* @param _fh handle of the face that was picked
* @param _maxAngle the maximum dihedral angle over which the flood fill selection passes
* @param _primitiveTypes the currently active primitive types
* @param _deselection true if primitives should be deselected
*/
template<class MeshT>
void VolumeMeshSelectionPlugin::floodFillSelection(MeshT* _mesh, uint _fh, double _maxAngle,
PrimitiveType _primitiveTypes, bool _deselection)
{
using namespace OpenVolumeMesh;
FaceHandle hitFace(_fh);
if (!_mesh->is_boundary(hitFace))
return;
HalfFaceHandle hitHalfFace = _mesh->halfface_handle(hitFace, 0);
if (!_mesh->is_boundary(hitHalfFace))
hitHalfFace = _mesh->halfface_handle(hitFace, 1);
StatusAttrib status(*_mesh);
// reset tagged status
HalfFaceIter hf_it(_mesh->halffaces_begin()), hf_end(_mesh->halffaces_end());
for (; hf_it != hf_end; ++hf_it)
{
status[*hf_it].set_tagged(false);
}
status[hitHalfFace].set_tagged(true);
std::stack<HalfFaceHandle> hf_handles;
hf_handles.push(hitHalfFace);
NormalAttrib<MeshT> normals(*_mesh);
typename MeshT::PointT n1 = normals[hitHalfFace];
double maxAngle = _maxAngle / 180.0 * M_PI;
// tag all half-faces whose normal does not deviate too much from the
// initial face
while (!hf_handles.empty())
{
HalfFaceHandle hf = hf_handles.top();
hf_handles.pop();
for (BoundaryHalfFaceHalfFaceIter bhfhf_it(hf, _mesh) ; bhfhf_it.valid() ; ++bhfhf_it)
{
if (status[*bhfhf_it].tagged())
continue;
typename MeshT::PointT n2 = normals[*bhfhf_it];
double angle = std::acos(n1 | n2);