Commit 5174e778 authored by Jan Möbius's avatar Jan Möbius

Plugin-Align added



git-svn-id: http://www.openflipper.org/svnrepo/OpenFlipper/branches/Free-Staging@19402 383ad7c9-94d9-4d36-a494-682f7c89f535
parent a9efaa32
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
/**
* @author Vladimir Chalupecky (vladimir.chalupecky@gmail.com)
*/
#include <ObjectTypes/PolyhedralMesh/PolyhedralMesh.hh>
#include <ObjectTypes/TriangleMesh/TriangleMesh.hh>
#include <OpenFlipper/BasePlugin/PluginFunctions.hh>
#include <Eigen/Dense>
#include "AlignPlugin.hh"
AlignPlugin::AlignPlugin()
: sourceMeshCombo_()
, targetMeshCombo_()
, doScaleCheckBox_()
, alignMeshesButton_()
, sourceId_(-1)
, targetId_(-1)
{
}
void AlignPlugin::slotAlignMeshes()
{
using namespace Eigen;
BaseObjectData *sourceObject = NULL;
if (!PluginFunctions::getObject(sourceId_, sourceObject)) {
emit log(LOGERR, tr("Cannot get source mesh object %1").arg(sourceId_));
return;
}
BaseObjectData *targetObject = NULL;
if (!PluginFunctions::getObject(targetId_, targetObject)) {
emit log(LOGERR, tr("Cannot get target mesh object %1").arg(targetId_));
return;
}
Vector3d sc;
Matrix3d spd;
if (!computePCA(getPoints(sourceObject), sc, spd)) {
emit log(LOGERR, "PCA of source object failed");
return;
}
Vector3d tc;
Matrix3d tpd;
Point_container targetPoints = getPoints(targetObject);
if (!computePCA(targetPoints, tc, tpd)) {
emit log(LOGERR, "PCA of target object failed");
return;
}
Affine3d t = spd.transpose() * Translation3d(-sc);
transformObject(sourceObject, t);
t = tpd.transpose() * Translation3d(-tc);
transformPoints(targetPoints, t);
t = t.inverse();
if (doScaleCheckBox_->isChecked()) {
BBox sbbox = computeBoundingBox(getPoints(sourceObject));
BBox tbbox = computeBoundingBox(targetPoints);
t = t * Scaling((tbbox.second - tbbox.first).cwiseQuotient(sbbox.second - sbbox.first));
}
transformObject(sourceObject, t);
emit updatedObject(sourceId_, UPDATE_GEOMETRY);
emit createBackup(sourceId_, "Align", UPDATE_GEOMETRY);
}
AlignPlugin::Point_container AlignPlugin::getPoints(BaseObjectData *object)
{
using namespace Eigen;
Point_container points;
if (object->dataType(DATA_HEXAHEDRAL_MESH)) {
HexahedralMesh *mesh = PluginFunctions::hexahedralMesh(object);
for (OpenVolumeMesh::VertexIter it = mesh->vertices_begin(); it != mesh->vertices_end(); ++it) {
if (mesh->is_boundary(*it)) {
Vector3d p = Map<const Vector3d>(mesh->vertex(*it).data());
points.push_back(p);
}
}
} else if (object->dataType(DATA_POLYHEDRAL_MESH)) {
PolyhedralMesh *mesh = PluginFunctions::polyhedralMesh(object);
for (OpenVolumeMesh::VertexIter it = mesh->vertices_begin(); it != mesh->vertices_end(); ++it) {
if (mesh->is_boundary(*it)) {
Vector3d p = Map<const Vector3d>(mesh->vertex(*it).data());
points.push_back(p);
}
}
} else { // DATA_TRIANGLE_MESH
TriMesh *mesh = PluginFunctions::triMesh(object);
points.reserve(mesh->n_vertices());
for (TriMesh::VertexIter it = mesh->vertices_begin(); it != mesh->vertices_end(); ++it) {
Vector3d p = Map<const Vector3d>(mesh->point(*it).data());
points.push_back(p);
}
}
return points;
}
void AlignPlugin::transformObject(BaseObjectData *object, Eigen::Affine3d const& t)
{
using namespace Eigen;
if (object->dataType(DATA_HEXAHEDRAL_MESH)) {
HexahedralMesh *mesh = PluginFunctions::hexahedralMesh(object);
for (OpenVolumeMesh::VertexIter it = mesh->vertices_begin(); it != mesh->vertices_end(); ++it) {
ACG::Vec3d const& p = mesh->vertex(*it);
Vector3d ep(p[0], p[1], p[2]);
Vector3d tep = t * ep;
mesh->set_vertex(*it, ACG::Vec3d(tep[0], tep[1], tep[2]));
}
} else if (object->dataType(DATA_POLYHEDRAL_MESH)) {
PolyhedralMesh *mesh = PluginFunctions::polyhedralMesh(object);
for (OpenVolumeMesh::VertexIter it = mesh->vertices_begin(); it != mesh->vertices_end(); ++it) {
ACG::Vec3d const& p = mesh->vertex(*it);
Vector3d ep(p[0], p[1], p[2]);
Vector3d tep = t * ep;
mesh->set_vertex(*it, ACG::Vec3d(tep[0], tep[1], tep[2]));
}
} else { // DATA_TRIANGLE_MESH
TriMesh *mesh = PluginFunctions::triMesh(object);
for (TriMesh::VertexIter it = mesh->vertices_begin(); it != mesh->vertices_end(); ++it) {
ACG::Vec3d const& p = mesh->point(*it);
Vector3d ep(p[0], p[1], p[2]);
Vector3d tep = t * ep;
mesh->set_point(*it, ACG::Vec3d(tep[0], tep[1], tep[2]));
}
mesh->update_normals();
}
}
void AlignPlugin::transformPoints(Point_container& points, Eigen::Affine3d const& t)
{
for (unsigned int i = 0; i < points.size(); ++i) {
points[i] = t * points[i];
}
}
AlignPlugin::BBox AlignPlugin::computeBoundingBox(Point_container const& points)
{
using namespace Eigen;
double inf = std::numeric_limits<double>::infinity();
Vector3d minp = Vector3d::Constant(inf);
Vector3d maxp = Vector3d::Constant(-inf);
for (unsigned int i = 0; i < points.size(); ++i) {
minp = minp.cwiseMin(points[i]);
maxp = maxp.cwiseMax(points[i]);
}
return BBox(minp, maxp);
}
Eigen::Vector3d AlignPlugin::computeCentroid(Point_container const& points)
{
using namespace Eigen;
Vector3d centroid = Vector3d::Zero();
for (unsigned int i = 0; i < points.size(); ++i) {
centroid += points[i];
}
if (points.size() > 0) {
centroid /= points.size();
}
return centroid;
}
bool AlignPlugin::computePCA(Point_container const& points, Eigen::Vector3d& centroid, Eigen::Matrix3d& principal_directions)
{
using namespace Eigen;
centroid = computeCentroid(points);
Matrix3d cov = Matrix3d::Zero();
for (unsigned int i = 0; i < points.size(); ++i) {
cov.noalias() += (points[i] - centroid) * (points[i] - centroid).transpose();
}
cov /= points.size() - 1;
SelfAdjointEigenSolver<Matrix3d> eigensolver(cov);
principal_directions = eigensolver.eigenvectors();
return eigensolver.info() == Success;
}
#if QT_VERSION < 0x050000
Q_EXPORT_PLUGIN2(alignplugin, AlignPlugin)
#endif
#pragma once
/**
* @author Vladimir Chalupecky (vladimir.chalupecky@gmail.com)
*/
#include <set>
#include <OpenFlipper/BasePlugin/BackupInterface.hh>
#include <OpenFlipper/BasePlugin/BaseInterface.hh>
#include <OpenFlipper/BasePlugin/LoadSaveInterface.hh>
#include <OpenFlipper/BasePlugin/LoggingInterface.hh>
#include <OpenFlipper/BasePlugin/ToolboxInterface.hh>
#include <OpenFlipper/common/Types.hh>
#include <Eigen/Geometry>
#include "ObjectInfoListModel.hh"
class AlignPlugin : public QObject,
BackupInterface,
BaseInterface,
LoadSaveInterface,
LoggingInterface,
ToolboxInterface
{
Q_OBJECT
Q_INTERFACES(BackupInterface)
Q_INTERFACES(BaseInterface)
Q_INTERFACES(LoadSaveInterface)
Q_INTERFACES(LoggingInterface)
Q_INTERFACES(ToolboxInterface)
#if QT_VERSION >= 0x050000
Q_PLUGIN_METADATA(IID "org.OpenFlipper.Plugins.Plugin-Align")
#endif
public:
AlignPlugin();
signals:
// BackupInterface
void createBackup(int objectId, QString name, UpdateType type = UPDATE_ALL);
// BaseInterface
void updateView();
void updatedObject(int objectId, UpdateType const& type);
// LoggingInterface
void log(Logtype type, QString message);
void log(QString message);
// ToolboxInterface
void addToolbox(QString name, QWidget *widget, QIcon *icon);
private slots:
// BaseInterface
void initializePlugin();
void pluginsInitialized();
void slotObjectUpdated(int, UpdateType const&);
void slotObjectSelectionChanged(int);
// LoadSaveInterface
void objectDeleted(int objectId);
private slots:
void slotAlignMeshes();
void slotSourceMeshChanged(int objectId);
void slotTargetMeshChanged(int objectId);
private:
void updateGui();
QComboBox *sourceMeshCombo_;
ObjectInfoListModel<Is_source_mesh> sourceMeshList_;
QComboBox *targetMeshCombo_;
ObjectInfoListModel<Is_target_mesh> targetMeshList_;
QCheckBox *doScaleCheckBox_;
QPushButton *alignMeshesButton_;
private:
typedef std::pair<Eigen::Vector3d, Eigen::Vector3d> BBox;
typedef std::vector<Eigen::Vector3d> Point_container;
Point_container getPoints(BaseObjectData *object);
void transformObject(BaseObjectData *object, Eigen::Affine3d const& t);
void transformPoints(Point_container& points, Eigen::Affine3d const& t);
BBox computeBoundingBox(Point_container const& points);
Eigen::Vector3d computeCentroid(Point_container const& points);
bool computePCA(Point_container const& points, Eigen::Vector3d& centroid, Eigen::Matrix3d& principal_directions);
int sourceId_;
int targetId_;
public:
// BaseInterface
QString name()
{
return QString("Mesh alignment plugin");
}
QString description()
{
return QString("Align two meshes by using PCA and scaling");
}
public slots:
QString version()
{
return QString("1.0");
}
};
include (plugin)
set(CURRENT_PLUGIN_DEPS "")
if (EXISTS ${CMAKE_SOURCE_DIR}/ObjectTypes/PolyhedralMesh)
add_definitions(-DENABLE_OPENVOLUMEMESH_SUPPORT)
add_definitions(-DENABLE_OPENVOLUMEMESH_POLYHEDRAL_SUPPORT)
list(APPEND CURRENT_PLUGIN_DEPS OpenVolumeMesh)
endif()
if (EXISTS ${CMAKE_SOURCE_DIR}/ObjectTypes/HexahedralMesh)
add_definitions(-DENABLE_OPENVOLUMEMESH_SUPPORT)
add_definitions(-DENABLE_OPENVOLUMEMESH_HEXAHEDRAL_SUPPORT)
list(APPEND CURRENT_PLUGIN_DEPS OpenVolumeMesh)
endif()
list(REMOVE_DUPLICATES CURRENT_PLUGIN_DEPS)
openflipper_plugin (
DEPS ${CURRENT_PLUGIN_DEPS} EIGEN3
)
The MIT License (MIT)
Copyright (c) 2014 Vladimír Chalupecký
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
#pragma once
#include <algorithm>
#include <utility>
#include <QAbstractItemModel>
#include <ObjectTypes/HexahedralMesh/HexahedralMesh.hh>
#include <ObjectTypes/PolyhedralMesh/PolyhedralMesh.hh>
#include <ObjectTypes/TriangleMesh/TriangleMesh.hh>
#include <OpenFlipper/BasePlugin/PluginFunctions.hh>
#include <OpenFlipper/common/DataTypes.hh>
struct Is_source_mesh {
bool operator()(BaseObjectData *object) const
{
if (object->source()) {
if (object->dataType(DATA_HEXAHEDRAL_MESH) ||
object->dataType(DATA_POLYHEDRAL_MESH) ||
object->dataType(DATA_TRIANGLE_MESH)) {
return true;
}
}
return false;
}
};
struct Is_target_mesh {
bool operator()(BaseObjectData *object) const
{
if (object->target()) {
if (object->dataType(DATA_HEXAHEDRAL_MESH) ||
object->dataType(DATA_POLYHEDRAL_MESH) ||
object->dataType(DATA_TRIANGLE_MESH)) {
return true;
}
}
return false;
}
};
template< typename TPredicate >
class ObjectInfoListModel : public QAbstractListModel
{
typedef std::vector<std::pair<QString, int> > Object_container;
public:
int rowCount(QModelIndex const&) const
{
return objects_.size() + 1;
}
QVariant data(QModelIndex const& index, int role = Qt::DisplayRole) const
{
switch (role) {
case Qt::DisplayRole:
if (index.row() <= 0) {
return tr("<none>");
}
return objects_[index.row() - 1].first;
case Qt::UserRole:
if (index.row() <= 0) {
return QVariant::fromValue<int>(-1);
}
return QVariant::fromValue(objects_[index.row() - 1].second);
default:
return QVariant::Invalid;
}
}
void removeObject(int object_id)
{
bool found = false;
for (unsigned int i = 0; i < objects_.size(); ++i) {
if (objects_[i].second == object_id) {
found = true;
break;
}
}
if (!found) {
return;
}
beginResetModel();
for (int i = objects_.size() - 1; i >= 0; --i) {
if (objects_[i].second == object_id) {
objects_.erase(objects_.begin() + i);
}
}
endResetModel();
}
void refresh()
{
Object_container objects;
for (PluginFunctions::ObjectIterator o_it; o_it != PluginFunctions::objectsEnd(); ++o_it) {
if (predicate_(*o_it)) {
objects.push_back(std::make_pair(o_it->name(), o_it->id()));
}
}
std::sort(objects.begin(), objects.end());
if (objects != objects_) {
beginResetModel();
objects.swap(objects_);
endResetModel();
}
}
private:
Object_container objects_;
TPredicate predicate_;
};
Plugin-Align
============
OpenFlipper plugin for aligning two meshes using PCA
## Installation
This plugin depends on the [Eigen3](http://eigen.tuxfamily.org) library.
```sh
$ cd ~/src/openflipper
$ git clone https://github.com/vladimir-ch/Plugin-Align.git
$ cd ~/build/openflipper
$ cmake ~/src/openflipper
$ make
```
/**
* @author Vladimir Chalupecky (vladimir.chalupecky@gmail.com)
*/
#include "AlignPlugin.hh"
void AlignPlugin::initializePlugin()
{
sourceMeshCombo_ = new QComboBox;
sourceMeshCombo_->setModel(&sourceMeshList_);
connect(sourceMeshCombo_, SIGNAL(currentIndexChanged(int)),
this, SLOT(slotSourceMeshChanged(int)));
targetMeshCombo_ = new QComboBox;
targetMeshCombo_->setModel(&targetMeshList_);
connect(targetMeshCombo_, SIGNAL(currentIndexChanged(int)),
this, SLOT(slotTargetMeshChanged(int)));
doScaleCheckBox_ = new QCheckBox(tr("Scale to target's bounding box"));
doScaleCheckBox_->setChecked(true);
alignMeshesButton_ = new QPushButton(tr("Align meshes"));
connect(alignMeshesButton_, SIGNAL(clicked()),
this, SLOT(slotAlignMeshes()));
QFormLayout *layout = new QFormLayout;
layout->addRow(new QLabel("Source mesh"), sourceMeshCombo_);
layout->addRow(new QLabel("Target mesh"), targetMeshCombo_);
layout->addRow(doScaleCheckBox_);
layout->addRow(alignMeshesButton_);
QWidget *toolbox = new QWidget;
toolbox->setLayout(layout);
updateGui();
emit addToolbox(tr("Align"), toolbox, NULL);
}
void AlignPlugin::pluginsInitialized()
{
}
void AlignPlugin::slotSourceMeshChanged(int objectId)
{
sourceId_ = sourceMeshCombo_->itemData(sourceMeshCombo_->currentIndex()).toInt();
updateGui();
}
void AlignPlugin::slotTargetMeshChanged(int objectId)
{
targetId_ = targetMeshCombo_->itemData(targetMeshCombo_->currentIndex()).toInt();
updateGui();
}
void AlignPlugin::slotObjectSelectionChanged(int)
{
sourceMeshList_.refresh();
targetMeshList_.refresh();
updateGui();
}
void AlignPlugin::slotObjectUpdated(int, UpdateType const&)
{
sourceMeshList_.refresh();
targetMeshList_.refresh();
updateGui();
}
void AlignPlugin::objectDeleted(int objectId)
{
sourceMeshList_.removeObject(objectId);
targetMeshList_.removeObject(objectId);
if (objectId == sourceId_) {
sourceId_ = -1;
sourceMeshCombo_->setCurrentIndex(-1);
}
if (objectId == targetId_) {
targetId_ = -1;
targetMeshCombo_->setCurrentIndex(-1);
}
updateGui();
}
void AlignPlugin::updateGui()
{
alignMeshesButton_->setEnabled(sourceId_ != -1 && targetId_ != -1);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment