Commit 4deb7ec4 authored by Mike Kremer's avatar Mike Kremer

Refactored PropertyVisualization plugin.

The code was written by Max Lyon.
New features are:

- Changed plugin's architecture so that it can now be extended
  to support generic object types (implementation for OpenVolumeMesh
  has been added).
- Read/write properties from/into files. Also reading props into existing
  properties is now supported.
- Multiple properties can be selected (multiple toolboxes appear)
  and combined.
- Documentation has been added.

git-svn-id: http://www.openflipper.org/svnrepo/OpenFlipper/branches/Free@15818 383ad7c9-94d9-4d36-a494-682f7c89f535
parent 648c7083
......@@ -4,4 +4,8 @@ if (EXISTS ${CMAKE_SOURCE_DIR}/ObjectTypes/BSplineCurve)
add_definitions (-DENABLE_SKELETON_SUPPORT)
endif()
openflipper_plugin (INSTALLDATA Icons)
if (EXISTS ${CMAKE_SOURCE_DIR}/ObjectTypes/PolyhedralMesh)
add_definitions (-DENABLE_OPENVOLUMEMESH_SUPPORT)
endif()
openflipper_plugin (INSTALLDATA Icons DIRS OpenMesh OpenVolumeMesh Widgets)
/*===========================================================================*\
* *
* OpenFlipper *
* Copyright (C) 2001-2011 by Computer Graphics Group, RWTH Aachen *
* www.openflipper.org *
* *
*--------------------------------------------------------------------------- *
* This file is part of OpenFlipper. *
* *
* OpenFlipper is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 3 of *
* the License, or (at your option) any later version with the *
* following exceptions: *
* *
* If other files instantiate templates or use macros *
* or inline functions from this file, or you compile this file and *
* link it with other files to produce an executable, this file does *
* not by itself cause the resulting executable to be covered by the *
* GNU Lesser General Public License. This exception does not however *
* invalidate any other reasons why the executable file might be *
* covered by the GNU Lesser General Public License. *
* *
* OpenFlipper is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU LesserGeneral Public *
* License along with OpenFlipper. If not, *
* see <http://www.gnu.org/licenses/>. *
* *
\*===========================================================================*/
#ifndef OM_PROPERTY_MODEL_H
#define OM_PROPERTY_MODEL_H
#include "../PropertyModel.hh"
#include "OMPropertyVisualizer.hh"
#include "Widgets/LoadSaveWidget.hh"
#include "Widgets/PickWidget.hh"
#include <OpenFlipper/common/ViewerProperties.hh>
class PropertyVisPlugin;
/*! \class OMPropertyModelSubclass
* \brief Added for signal/slot support.
*
* This class adds signal/slot support for OMPropertyModelSubclass. OMPropertyModel is
* then derived from this class to make use of those signals and slots. This needs
* to be done because "Sorry, Qt does not support templates that contain signals,
* slots or Q_OBJECT"
* http://doc.trolltech.com/qq/qq15-academic.html
*/
class OMPropertyModelSubclass: public PropertyModel
{
Q_OBJECT
public:
OMPropertyModelSubclass(QObject *parent = 0)
: PropertyModel(parent)
{
}
public slots:
virtual void slotCombine(){ combine(); }
virtual void slotSaveProperty(){ saveProperty(); }
virtual void slotLoadProperty(){ loadProperty(); }
virtual void slotPickProperty(){ pickProperty(); }
private:
virtual void combine(){}
virtual void saveProperty(){}
virtual void pickProperty(){}
};
template<typename MeshT>
class OMPropertyModel: public OMPropertyModelSubclass {
public:
OMPropertyModel(MeshT* mesh, int objectID, QObject *parent = 0);
virtual ~OMPropertyModel(){}
virtual void updateWidget(const QModelIndexList& selectedIndices);
protected:
virtual QString getLoadFilenameFilter();
virtual QString getSaveFilenameFilter(unsigned int propId);
private:
/// Combines two properties.
virtual void combine();
/// Checks if two properties are combinable.
bool combinable(PropertyVisualizer* propertyVisualizer1, PropertyVisualizer* propertyVisualizer2);
/// Saves the currently slected properties.
virtual void saveProperty();
virtual bool parseHeader(QString header, PropertyVisualizer*& propVis, unsigned int& n);
virtual void setPropertyFromFile(QTextStream*& file_stream_, unsigned int n, PropertyVisualizer *propVis);
/// Disables picking.
void resetPicking();
/// Toggle picking on and off.
virtual void pickProperty();
/// Handles changing of pick mode.
virtual void pickModeChanged(const std::string& _mode);
/// Handles mouse events for picking.
virtual void mouseEvent(QMouseEvent* _event);
bool isVectorType(const TypeInfoWrapper& typeInfo) const;
/// Searches for properties with a specific entity type and creates visualizers.
void gatherProperties( MeshT* mesh,
typename MeshT::prop_iterator props_first,
typename MeshT::prop_iterator props_last,
PropertyInfo::ENTITY_FILTER filter);
/// Searches for all properties and creates the visualizers.
void gatherProperties();
/// Checks if visualizing this property is supported.
bool isSupported(OpenMesh::BaseProperty* const baseProp) const;
/// Checks if visualizing this type of property is supported.
bool isSupported(QString friendlyName) const;
/// Checks if we already created a PropertyVisualizer for this property.
bool isNew(OpenMesh::BaseProperty* const baseProp, PropertyInfo::ENTITY_FILTER filter);
/// Returns the TypeInfoWrapper for the property if it is supported.
TypeInfoWrapper getSupportedTypeInfoWrapper(OpenMesh::BaseProperty* const baseProp);
/// Returns the TypeInfoWrapper for the type of property if it is supported.
TypeInfoWrapper getSupportedTypeInfoWrapper(QString friendlyName);
/// Adds a new PropertyVisualizer.
void addPropertyVisualizer(OpenMesh::BaseProperty* const baseProp, MeshT* mesh, PropertyInfo::ENTITY_FILTER filter);
/// Adds a new property to the mesh.
void addProperty(QString propName, QString friendlyTypeName, PropertyInfo::ENTITY_FILTER filter);
private:
MeshT* mesh_;
int objectID_;
static const TypeInfoWrapper proptype_bool;
static const TypeInfoWrapper proptype_int;
static const TypeInfoWrapper proptype_uint;
static const TypeInfoWrapper proptype_double;
static const TypeInfoWrapper proptype_Vec3d;
static const TypeInfoWrapper proptype_Vec3f;
#ifdef ENABLE_SKELETON_SUPPORT
static const TypeInfoWrapper proptype_SkinWeights;
#endif
typedef std::set<TypeInfoWrapper> TypeInfoWrapperSet;
static const TypeInfoWrapper propertyTypes[];
static const TypeInfoWrapperSet supportedPropertyTypes;
QPushButton bCombine;
const PropertyInfo* mCombineProperty1;
const PropertyInfo* mCombineProperty2;
LoadSaveWidget mLoadSaveWidget;
PickWidget mPickWidget;
bool pickModeActive;
std::string lastPickMode;
Viewer::ActionMode lastActionMode;
};
#if defined(INCLUDE_TEMPLATES) && !defined(OM_PROPERTY_MODEL_CC)
#include "OMPropertyModelT.cc"
#endif
#endif /* OM_PROPERTY_MODEL_H */
/*===========================================================================*\
* *
* OpenFlipper *
* Copyright (C) 2001-2011 by Computer Graphics Group, RWTH Aachen *
* www.openflipper.org *
* *
*--------------------------------------------------------------------------- *
* This file is part of OpenFlipper. *
* *
* OpenFlipper is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 3 of *
* the License, or (at your option) any later version with the *
* following exceptions: *
* *
* If other files instantiate templates or use macros *
* or inline functions from this file, or you compile this file and *
* link it with other files to produce an executable, this file does *
* not by itself cause the resulting executable to be covered by the *
* GNU Lesser General Public License. This exception does not however *
* invalidate any other reasons why the executable file might be *
* covered by the GNU Lesser General Public License. *
* *
* OpenFlipper is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU LesserGeneral Public *
* License along with OpenFlipper. If not, *
* see <http://www.gnu.org/licenses/>. *
* *
\*===========================================================================*/
#define OM_PROPERTY_MODEL_CC
#include "OMPropertyModel.hh"
#include "OMPropertyVisualizerBoolean.hh"
#include "OMPropertyVisualizerDouble.hh"
#include "OMPropertyVisualizerInteger.hh"
#include "OMPropertyVisualizerVector.hh"
#include "OMPropertyVisualizerVectorFieldDifference.hh"
#ifdef ENABLE_SKELETON_SUPPORT
#include "OMPropertyVisualizerSkinWeights.hh"
#endif
#include "../Utils.hh"
#include <vector>
#define PROP_VIS "PropertyVisualization"
template<typename MeshT>
OMPropertyModel<MeshT>::OMPropertyModel(MeshT* mesh, int objectID, QObject *parent)
: OMPropertyModelSubclass(parent),
mesh_(mesh),
objectID_(objectID),
mCombineProperty1(0),
mCombineProperty2(0),
pickModeActive(false)
{
gatherProperties();
bCombine.setText(tr("Combine"));
bCombine.hide();
connect(&bCombine, SIGNAL(clicked()),
this, SLOT(slotCombine()));
widgets->layout()->addWidget(&bCombine);
widgets->layout()->addWidget(&mLoadSaveWidget);
connect(mLoadSaveWidget.save_property , SIGNAL(clicked()),
this, SLOT(slotSaveProperty()));
connect(mLoadSaveWidget.load_property , SIGNAL(clicked()),
this, SLOT(slotLoadProperty()));
widgets->layout()->addWidget(&mPickWidget);
connect(mPickWidget.pickButton, SIGNAL(clicked()),
this, SLOT(slotPickProperty()));
mPickWidget.hide();
QString iconPath = OpenFlipper::Options::iconDirStr() + OpenFlipper::Options::dirSeparator();
mPickWidget.pickButton->setIcon( QIcon(iconPath + "color-picker.png") );
lastPickMode = PluginFunctions::pickMode();
lastActionMode = PluginFunctions::actionMode();
}
template<typename MeshT>
void OMPropertyModel<MeshT>::updateWidget(const QModelIndexList& selectedIndices)
{
PropertyModel::updateWidget(selectedIndices);
if (selectedIndices.size() == 2)
{
if (combinable(propertyVisualizers[selectedIndices[0].row()], propertyVisualizers[selectedIndices[1].row()]))
{
bCombine.show();
mCombineProperty1 = &propertyVisualizers[selectedIndices[0].row()]->getPropertyInfo();
mCombineProperty2 = &propertyVisualizers[selectedIndices[1].row()]->getPropertyInfo();
}
else
bCombine.hide();
}
else
bCombine.hide();
if (selectedIndices.size() == 1)
mPickWidget.show();
else
{
mPickWidget.hide();
resetPicking();
}
}
template<typename MeshT>
QString OMPropertyModel<MeshT>::getLoadFilenameFilter()
{
QString filter;
filter = tr("Vertex Property (*.vprop)");
filter += tr(";; HalfEdge Property (*.hprop)");
filter += tr(";; Edge Property (*.eprop)");
filter += tr(";; Face Property (*.fprop)");
filter += tr(";; All Files (*)");
return filter;
}
template<typename MeshT>
QString OMPropertyModel<MeshT>::getSaveFilenameFilter(unsigned int propId)
{
PropertyVisualizer* propViz = propertyVisualizers[propId];
QString filter;
if (propViz->getPropertyInfo().isVertexProp())
filter = tr("Vertex Property (*.vprop)");
else if (propViz->getPropertyInfo().isHalfedgeProp())
filter = tr("HalfEdge Property (*.hprop)");
else if (propViz->getPropertyInfo().isEdgeProp())
filter = tr("Edge Property (*.eprop)");
else if (propViz->getPropertyInfo().isFaceProp())
filter = tr("Face Property (*.fprop)");
filter += tr(";; All Files (*)");
return filter;
}
/**
* @brief Combines two properties.
*
* If the user selects two properties which are both of a vector type (Vec3f or Vec3d)
* they can be combined. This method will create a new PropertyVisualizer (an
* OMPropertyVisualizerVectorFieldDifference<MeshT> to be exact) but not an additional
* property.
*/
template<typename MeshT>
void OMPropertyModel<MeshT>::combine()
{
beginResetModel();
if (isVectorType(mCombineProperty1->typeinfo()))
propertyVisualizers.push_back(new OMPropertyVisualizerVectorFieldDifference<MeshT>(mesh_, *mCombineProperty1, *mCombineProperty2));
endResetModel();
}
/**
* @brief Checks if two properties are combinable.
*
* @param propertyVisualizer1 First PropertyVisualizer for combination.
* @param propertyVisualizer2 Second PropertyVisualizer for combination.
*
* @return True if the two properties are combinable, False if not.
*
* If the user selects two properties which are both of a vector type (Vec3f or Vec3d)
* they can be combined. This method will check if the two provided PropertyVisualizers can
* be comined. Currently PropertyVisualizers can only be combined if both visualize a
* property with a vector type and the same entity type.
*/
template<typename MeshT>
bool OMPropertyModel<MeshT>::combinable(PropertyVisualizer* propertyVisualizer1, PropertyVisualizer* propertyVisualizer2)
{
const PropertyInfo& propInfo1 = propertyVisualizer1->getPropertyInfo();
const PropertyInfo& propInfo2 = propertyVisualizer2->getPropertyInfo();
TypeInfoWrapper typeInfo1 = propInfo1.typeinfo();
TypeInfoWrapper typeInfo2 = propInfo2.typeinfo();
return (isVectorType(typeInfo1) && isVectorType(typeInfo2)) && (propInfo1.entityType() == propInfo2.entityType());
}
template<typename MeshT>
void OMPropertyModel<MeshT>::saveProperty()
{
for (QModelIndexList::const_iterator it = currentIndices.begin(), it_end = currentIndices.end();
it != it_end; ++it) {
PropertyModel::saveProperty(it->row());
}
}
template<typename MeshT>
bool OMPropertyModel<MeshT>::parseHeader(QString header, PropertyVisualizer*& propVis, unsigned int &n)
{
QStringList headerParts = header.split(tr(", "), QString::SkipEmptyParts );
int headerVersion = headerParts[0].toUInt();
if (headerVersion == 1)
{
n = headerParts[1].toUInt();
unsigned int nExpected = 0;
PropertyInfo::ENTITY_FILTER filter = (PropertyInfo::ENTITY_FILTER)headerParts[2].toInt();
switch (filter)
{
case PropertyInfo::EF_FACE:
nExpected = mesh_->n_faces();
break;
case PropertyInfo::EF_EDGE:
nExpected = mesh_->n_edges();
break;
case PropertyInfo::EF_HALFEDGE:
nExpected = mesh_->n_halfedges();
break;
case PropertyInfo::EF_VERTEX:
nExpected = mesh_->n_vertices();
break;
default:
nExpected = -1;
break;
}
if (n != nExpected)
{
emit log(LOGERR, "Could not load property: unexpected number of entities");
return false;
}
QString friendlyName = headerParts[3];
if (!isSupported(friendlyName))
{
emit log(LOGERR, tr("Could not load property: unsupported property type %1").arg(friendlyName));
return false;
}
TypeInfoWrapper typeInfo = getSupportedTypeInfoWrapper(friendlyName);
QString propName = QInputDialog::getText(0, "Property Name", "Please enter name.",QLineEdit::Normal,headerParts[4]);
if (propName == "") return false;
bool replace = false;
if (!(isPropertyFree(propName, filter, typeInfo) || replace))
{
NewNameMessageBox* msgBox = new NewNameMessageBox(propName);
msgBox->exec();
if (msgBox->rename)
propName = QInputDialog::getText(0, "New Property Name", "Please enter new name.");
else if (msgBox->cancel)
return false;
else if (msgBox->replace)
replace = true;
delete msgBox;
}
if (!replace)
{
addProperty(propName, friendlyName, filter);
gatherProperties();
}
propVis = getPropertyVisualizer(propName, filter, typeInfo);
return true;
}
else
{
emit log(LOGERR, "Could not load property: unsupported header format");
return false;
}
}
template<typename MeshT>
void OMPropertyModel<MeshT>::setPropertyFromFile(QTextStream*& file_stream_, unsigned int n, PropertyVisualizer* propVis)
{
#ifdef ENABLE_SKELETON_SUPPORT
if (propVis->getPropertyInfo().typeinfo() == proptype_SkinWeights)
{
for (unsigned int i = 0; i < n; ++i)
{
QString propertyText = "";
QString tmp;
while ((tmp = readLine(file_stream_)) != "")
propertyText = propertyText + tmp;
propVis->setPropertyFromText(i, propertyText);
}
}
else
#endif
{
PropertyModel::setPropertyFromFile(file_stream_, n, propVis);
}
}
template<typename MeshT>
void OMPropertyModel<MeshT>:: resetPicking()
{
PluginFunctions::pickMode(lastPickMode);
PluginFunctions::actionMode(lastActionMode);
}
/**
* @brief Toggle picking on and off.
*
* This method will start and stop the picking.
*/
template<typename MeshT>
void OMPropertyModel<MeshT>::pickProperty()
{
if ( mPickWidget.pickButton->isChecked() ){
lastPickMode = PluginFunctions::pickMode();
lastActionMode = PluginFunctions::actionMode();
PluginFunctions::pickMode(PROP_VIS);
PluginFunctions::actionMode(Viewer::PickingMode);
} else {
resetPicking();
}
}
template<typename MeshT>
void OMPropertyModel<MeshT>::pickModeChanged(const std::string& _mode)
{
pickModeActive = (_mode == PROP_VIS);
if (!pickModeActive)
{
lastPickMode = _mode;
}
mPickWidget.pickButton->setChecked(pickModeActive);
}
/**
* @brief Handles mouse events for picking.
*
* If picking mode is active this method will find the picked entity and display
* its handle and value in the PickWidget.
*/
template<typename MeshT>
void OMPropertyModel<MeshT>::mouseEvent(QMouseEvent* _event)
{
if (!pickModeActive) return;
if (_event->type() == QEvent::MouseButtonPress)
{
unsigned int node_idx, face_idx;
ACG::Vec3d hit_point;
if (PluginFunctions::scenegraphPick(ACG::SceneGraph::PICK_FACE, _event->pos(),node_idx, face_idx, &hit_point)) {
BaseObjectData* object;
PluginFunctions::getPickedObject(node_idx, object);
if (object->id() == objectID_)
{
OMPropertyVisualizer<MeshT>* viz = (OMPropertyVisualizer<MeshT>*) propertyVisualizers[currentIndices.first().row()];
unsigned int primitiveId = viz->getClosestPrimitiveId(face_idx, hit_point);
mPickWidget.pickedHandle->setText(tr("%1").arg(primitiveId));
mPickWidget.pickedValue->setText(viz->getPropertyText(primitiveId));
}
}
}
}
template<typename MeshT>
bool OMPropertyModel<MeshT>::isVectorType(const TypeInfoWrapper& typeInfo) const
{
return (typeInfo == proptype_Vec3f) || (typeInfo == proptype_Vec3d);
}
template<typename MeshT>
void OMPropertyModel<MeshT>::gatherProperties( MeshT* mesh,
typename MeshT::prop_iterator props_first,
typename MeshT::prop_iterator props_last,
PropertyInfo::ENTITY_FILTER filter)
{
for (typename MeshT::prop_iterator pit = props_first; pit != props_last; ++pit) {
OpenMesh::BaseProperty*const baseProp = *pit;
if (baseProp && isSupported(baseProp) && isNew(baseProp, filter))
addPropertyVisualizer(baseProp, mesh, filter);
}
}
template<typename MeshT>
void OMPropertyModel<MeshT>::gatherProperties()
{
beginResetModel();
if (mesh_) {
gatherProperties(mesh_, mesh_->fprops_begin(), mesh_->fprops_end(), PropertyInfo::EF_FACE);
gatherProperties(mesh_, mesh_->eprops_begin(), mesh_->eprops_end(), PropertyInfo::EF_EDGE);
gatherProperties(mesh_, mesh_->hprops_begin(), mesh_->hprops_end(), PropertyInfo::EF_HALFEDGE);
gatherProperties(mesh_, mesh_->vprops_begin(), mesh_->vprops_end(), PropertyInfo::EF_VERTEX);
}
endResetModel();
}
/**
* @brief Checks if visualizing this property is supported.
*
* @param baseProp A pointer to the property that we want to visualize.
*
* @return True if the property can be visualized, False if not.
*/
template<typename MeshT>
bool OMPropertyModel<MeshT>::isSupported(OpenMesh::BaseProperty* const baseProp) const
{
TypeInfoWrapper bp_type = typeid(*baseProp);
TypeInfoWrapperSet::const_iterator propIt = supportedPropertyTypes.find(bp_type);
return propIt != supportedPropertyTypes.end();
}
/**
* @brief Checks if visualizing this property is supported.
*
* @param friendlyName The type we want to visualize in text form.
*
* @return True if the property type can be visualized, False if not.
*
* Currently supported are the types "bool", "int", "unsigned int", "double", "Vec3d",
* "Vec3f" and "SkinWeights"
*/
template<typename MeshT>
bool OMPropertyModel<MeshT>::isSupported(QString friendlyName) const
{
for (TypeInfoWrapperSet::const_iterator it = supportedPropertyTypes.begin();
it != supportedPropertyTypes.end();
++it )
{
if (friendlyName.toStdString().compare(it->getName()) == 0)
return true;
}
return false;
}
/**
* @brief Checks if we already created a PropertyVisualizer for this property.
*
* @param baseProp A pointer to the property.
* @param filter The entity type.
*
* @return True if we do not have PropertyVisualizer yet for that property, False if we do
*/
template<typename MeshT>
bool OMPropertyModel<MeshT>::isNew(OpenMesh::BaseProperty* const baseProp, PropertyInfo::ENTITY_FILTER filter)
{
for (unsigned int i = 0; i < propertyVisualizers.size(); ++i)
{
const PropertyInfo& propInfo = propertyVisualizers[i]->getPropertyInfo();
if (propInfo == PropertyInfo(baseProp->name(), getSupportedTypeInfoWrapper(baseProp) , filter))
return false;
}
return true;
}
/**
* @brief Returns the TypeInfoWrapper for the property if it is supported.
*
* @param baseProp A pointer to the property.
*
* @return A TypeInfoWrapper containing all the type information for the provided property.
*/
template<typename MeshT>
TypeInfoWrapper OMPropertyModel<MeshT>::getSupportedTypeInfoWrapper(OpenMesh::BaseProperty* const baseProp)
{
TypeInfoWrapper bp_type = typeid(*baseProp);
TypeInfoWrapperSet::const_iterator propIt = supportedPropertyTypes.find(bp_type);
return *propIt;
}
/**
* @brief Returns the TypeInfoWrapper for the type of property if it is supported.
*
* @param friendlyName The type we want to visualize in text form.