cgal/Lab/demo/Lab/Plugins/Surface_mesh/Parameterization_plugin.cpp

1050 lines
35 KiB
C++

#include <QApplication>
#include <QAction>
#include <QMainWindow>
#include <QStringList>
#include "Scene_surface_mesh_item.h"
#include "Scene_textured_surface_mesh_item.h"
#include "Scene_polyhedron_selection_item.h"
#include "SMesh_type.h"
#include <CGAL/Three/CGAL_Lab_plugin_helper.h>
#include <CGAL/Three/CGAL_Lab_plugin_interface.h>
#include <CGAL/Three/Three.h>
#include "Scene.h"
#include <QElapsedTimer>
#include <QGraphicsScene>
#include <QGraphicsItem>
#include <QPen>
#include <QDockWidget>
#include <Messages_interface.h>
#include <CGAL/Polygon_mesh_processing/border.h>
#include <CGAL/Polygon_mesh_processing/measure.h>
#include <CGAL/property_map.h>
#include <CGAL/boost/graph/Seam_mesh.h>
#include <CGAL/boost/graph/graph_traits_Seam_mesh.h>
#include <CGAL/Surface_mesh_parameterization/ARAP_parameterizer_3.h>
#include <CGAL/Surface_mesh_parameterization/Barycentric_mapping_parameterizer_3.h>
#include <CGAL/Surface_mesh_parameterization/Discrete_authalic_parameterizer_3.h>
#include <CGAL/Surface_mesh_parameterization/Discrete_conformal_map_parameterizer_3.h>
#include <CGAL/Surface_mesh_parameterization/Iterative_authalic_parameterizer_3.h>
#include <CGAL/Surface_mesh_parameterization/Error_code.h>
#include <CGAL/Surface_mesh_parameterization/LSCM_parameterizer_3.h>
#include <CGAL/Surface_mesh_parameterization/Two_vertices_parameterizer_3.h>
#include <CGAL/Surface_mesh_parameterization/internal/orbifold_cone_helper.h>
#include <CGAL/Surface_mesh_parameterization/Orbifold_Tutte_parameterizer_3.h>
#include <CGAL/Surface_mesh_parameterization/parameterize.h>
#include <boost/container/flat_map.hpp>
#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include <CGAL/boost/graph/properties.h>
#include <CGAL/Qt/GraphicsViewNavigation.h>
#include "ui_Parameterization_widget.h"
#include "ui_OTE_dialog.h"
typedef Scene_surface_mesh_item Scene_facegraph_item;
typedef Scene_textured_surface_mesh_item Scene_textured_facegraph_item;
typedef SMesh Textured_face_graph;
typedef SMesh Base_face_graph;
typedef SMesh Face_graph;
typedef EPICK Traits;
namespace SMP = CGAL::Surface_mesh_parameterization;
typedef std::unordered_set<boost::graph_traits<Base_face_graph>::face_descriptor> Component;
typedef std::vector<Component> Components;
struct Is_selected_property_map{
typedef boost::graph_traits<Base_face_graph>::edge_descriptor edge_descriptor;
typedef boost::property_map<Base_face_graph, boost::halfedge_index_t>::type HIndexMap;
std::vector<bool>* is_selected_ptr;
Base_face_graph* graph;
HIndexMap idmap;
Is_selected_property_map()
: is_selected_ptr(nullptr), graph(nullptr) {}
Is_selected_property_map(std::vector<bool>& is_selected,
Base_face_graph* graph)
: is_selected_ptr( &is_selected), graph(graph)
{
idmap = get(boost::halfedge_index, *graph);
}
std::size_t id(edge_descriptor ed) { return get(idmap, halfedge(ed, *graph))/2; }
friend bool get(Is_selected_property_map map, edge_descriptor ed)
{
CGAL_assertion(map.is_selected_ptr!=NULL);
return (*map.is_selected_ptr)[map.id(ed)];
}
friend void put(Is_selected_property_map map, edge_descriptor ed, bool b)
{
CGAL_assertion(map.is_selected_ptr!=NULL);
(*map.is_selected_ptr)[map.id(ed)]=b;
}
};
class Navigation : public CGAL::Qt::GraphicsViewNavigation
{
public:
Navigation()
:CGAL::Qt::GraphicsViewNavigation(),
prev_pos(QPoint(0,0))
{ }
protected:
bool eventFilter(QObject *obj, QEvent *ev)
{
QGraphicsView* v = qobject_cast<QGraphicsView*>(obj);
if(v == nullptr) {
QWidget* viewport = qobject_cast<QWidget*>(obj);
if(viewport == nullptr) {
return false;
}
v = qobject_cast<QGraphicsView*>(viewport->parent());
if(v == nullptr) {
return false;
}
}
switch(ev->type())
{
case QEvent::MouseMove: {
QMouseEvent* me = static_cast<QMouseEvent*>(ev);
if(is_dragging)
{
qreal dir[2] = {v->mapToScene(me->pos()).x() - prev_pos.x(),
v->mapToScene(me->pos()).y() - prev_pos.y()};
v->translate(dir[0],dir[1]);
v->update();
}
prev_pos = v->mapToScene(me->pos());
break;
}
case QEvent::MouseButtonPress: {
is_dragging = true;
break;
}
case QEvent::MouseButtonRelease: {
is_dragging = false;
break;
}
case QEvent::Wheel: {
QWheelEvent* event = static_cast<QWheelEvent*>(ev);
QPointF pos = event->position();
QPointF old_pos = v->mapToScene(pos.x(), pos.y());
if(event->angleDelta().y() <0)
v->scale(1.2, 1.2);
else
v->scale(0.8, 0.8);
QPointF new_pos = v->mapToScene(pos.x(), pos.y());
QPointF delta = new_pos - old_pos;
v->translate(delta.x(), delta.y());
v->update();
break;
}
case QEvent::MouseButtonDblClick: {
v->fitInView(v->scene()->itemsBoundingRect(), Qt::KeepAspectRatio);
break;
}
default:
CGAL::Qt::GraphicsViewNavigation::eventFilter(obj, ev);
}
return false;
}
private:
bool is_dragging;
QPointF prev_pos;
};
namespace SMP = CGAL::Surface_mesh_parameterization;
typedef Traits::FT FT;
typedef boost::graph_traits<Face_graph>::vertex_descriptor P_vertex_descriptor;
typedef Traits::Point_2 Point_2;
typedef boost::graph_traits<Face_graph>::edge_descriptor P_edge_descriptor;
typedef boost::graph_traits<Face_graph>::halfedge_descriptor P_halfedge_descriptor;
// Textured polyhedron
typedef boost::graph_traits<Base_face_graph>::
edge_descriptor T_edge_descriptor;
typedef boost::graph_traits<Base_face_graph>::
halfedge_descriptor T_halfedge_descriptor;
typedef boost::graph_traits<Base_face_graph>::
vertex_descriptor T_vertex_descriptor;
// Seam
typedef CGAL::Unique_hash_map<T_halfedge_descriptor,Point_2> UV_uhm;
typedef CGAL::Unique_hash_map<T_edge_descriptor,bool> Seam_edge_uhm;
typedef CGAL::Unique_hash_map<T_vertex_descriptor,bool> Seam_vertex_uhm;
typedef boost::associative_property_map<UV_uhm> UV_pmap;
typedef boost::associative_property_map<Seam_edge_uhm> Seam_edge_pmap;
typedef boost::associative_property_map<Seam_vertex_uhm> Seam_vertex_pmap;
typedef CGAL::Seam_mesh<Base_face_graph,
Seam_edge_pmap, Seam_vertex_pmap> Seam_mesh;
typedef boost::graph_traits<Seam_mesh>::vertex_descriptor s_vertex_descriptor;
typedef boost::graph_traits<Seam_mesh>::halfedge_descriptor s_halfedge_descriptor;
typedef boost::graph_traits<Seam_mesh>::face_descriptor s_face_descriptor;
typedef boost::graph_traits<Seam_mesh>::edges_size_type s_edges_size_type;
typedef std::unordered_set<boost::graph_traits<Base_face_graph>::
face_descriptor> Component;
typedef std::vector<Component> Components;
typedef std::unordered_set<s_face_descriptor> SComponent;
typedef std::vector<SComponent> SComponents;
class UVItem : public QGraphicsItem
{
public :
UVItem(Components* components,
Base_face_graph* graph,
std::vector<std::vector<float> >uv_borders,
QRectF brect)
:
QGraphicsItem(),
bounding_rect(brect),
components(components),
graph(graph),
m_borders(uv_borders),
m_concatenated_borders(),
m_current_component(0)
{
std::size_t total_border_size = 0;
for(std::size_t i=0; i<m_borders.size(); ++i)
total_border_size += m_borders[i].size();
m_concatenated_borders.resize(total_border_size);
for(std::size_t i=0; i<m_borders.size(); ++i)
{
const std::vector<float>& ith_border = m_borders[i];
m_concatenated_borders.insert(m_concatenated_borders.end(),
ith_border.begin(), ith_border.end());
}
}
~UVItem()
{
delete components;
}
const std::vector<std::vector<float> >& borders() const { return m_borders; }
const std::vector<float>& concatenated_borders() const { return m_concatenated_borders; }
QRectF boundingRect() const
{
return bounding_rect;
}
QString item_name()const{ return texMesh_name; }
void set_item_name(QString s){ texMesh_name = s;}
void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)
{
QPen pen;
QBrush brush;
brush.setColor(QColor(100, 100, 255));
brush.setStyle(Qt::SolidPattern);
pen.setColor(Qt::black);
pen.setWidth(0);
painter->setPen(pen);
painter->setBrush(brush);
SMesh::Property_map<halfedge_descriptor,float> u,v;
u = graph->add_property_map<halfedge_descriptor,float>("h:u", 0.0f).first;
v = graph->add_property_map<halfedge_descriptor,float>("h:v", 0.0f).first;
for( Component::iterator
fi = components->at(m_current_component).begin();
fi != components->at(m_current_component).end();
++fi)
{
boost::graph_traits<Base_face_graph>::face_descriptor f(*fi);
QPointF points[3];
boost::graph_traits<Base_face_graph>::halfedge_descriptor h = halfedge(f, *graph);;
points[0] = QPointF(get(u, h), get(v, h));
h = next(halfedge(f, *graph), *graph);
points[1] = QPointF(get(u, h), get(v, h));
h = next(next(halfedge(f, *graph), *graph), *graph);
points[2] = QPointF(get(u, h), get(v, h));
painter->drawPolygon(points,3);
}
}
int number_of_components()const{return static_cast<int>(components->size());}
int current_component()const{return m_current_component;}
void set_current_component(int n){m_current_component = n;}
private:
QString texMesh_name;
QRectF bounding_rect;
Components* components;
Base_face_graph* graph;
std::vector<std::vector<float> > m_borders;
std::vector<float> m_concatenated_borders;
int m_current_component;
};
using namespace CGAL::Three;
class CGAL_Lab_parameterization_plugin :
public QObject,
public CGAL_Lab_plugin_helper
{
Q_OBJECT
Q_INTERFACES(CGAL::Three::CGAL_Lab_plugin_interface)
Q_PLUGIN_METADATA(IID "com.geometryfactory.CGALLab.PluginInterface/1.0")
public:
// used by CGAL_Lab_plugin_helper
QList<QAction*> actions() const
{
return _actions;
}
~CGAL_Lab_parameterization_plugin()
{
delete navigation;
}
void init(QMainWindow* mainWindow,
Scene_interface* scene_interface,
Messages_interface* msg)
{
mw = mainWindow;
scene = scene_interface;
messages = msg;
Scene* true_scene = static_cast<Scene*>(scene);
connect(true_scene, SIGNAL(itemAboutToBeDestroyed(CGAL::Three::Scene_item*)),
this, SLOT(destroyPolyline(CGAL::Three::Scene_item*)));
QAction* actionMVC = new QAction("Mean Value Coordinates", mw);
QAction* actionDCP = new QAction ("Discrete Conformal Map", mw);
QAction* actionLSC = new QAction("Least Square Conformal Map", mw);
QAction* actionDAP = new QAction("Discrete Authalic", mw);
QAction* actionIAP = new QAction("Iterative Authalic", mw);
QAction* actionARAP = new QAction("As Rigid As Possible", mw);
QAction* actionOTE = new QAction("Orbifold Tutte Embedding", mw);
QAction* actionBTP = new QAction("Tutte Barycentric", mw);
actionMVC->setObjectName("actionMVC");
actionDCP->setObjectName("actionDCP");
actionLSC->setObjectName("actionLSC");
actionDAP->setObjectName("actionDAP");
actionIAP->setObjectName("actionIAP");
actionARAP->setObjectName("actionARAP");
actionOTE->setObjectName("actionOTE");
actionBTP->setObjectName("actionBTP");
_actions << actionARAP
<< actionBTP
<< actionDAP
<< actionIAP
<< actionDCP
<< actionLSC
<< actionMVC
<< actionOTE;
autoConnectActions();
for(QAction *action : _actions)
action->setProperty("subMenuName",
"Triangulated Surface Mesh Parameterization"
);
dock_widget = new QDockWidget(
"UVMapping "
, mw);
ui_widget.setupUi(dock_widget);
dock_widget->setWindowTitle(tr(
"UVMapping "
));
graphics_scene = new QGraphicsScene(dock_widget);
ui_widget.graphicsView->setScene(graphics_scene);
ui_widget.graphicsView->setRenderHints(QPainter::Antialiasing);
navigation = new Navigation();
ui_widget.graphicsView->installEventFilter(navigation);
ui_widget.graphicsView->viewport()->installEventFilter(navigation);
ui_widget.component_numberLabel->setText("Component : 1");
connect(ui_widget.prevButton, &QPushButton::clicked, this, &CGAL_Lab_parameterization_plugin::on_prevButton_pressed);
connect(ui_widget.nextButton, &QPushButton::clicked, this, &CGAL_Lab_parameterization_plugin::on_nextButton_pressed);
addDockWidget(dock_widget);
dock_widget->setVisible(false);
current_uv_item = nullptr;
}
bool applicable(QAction*) const
{
if (scene->selectionIndices().size() == 1)
{
return qobject_cast<Scene_facegraph_item*>(scene->item(scene->mainSelectionIndex()))
|| qobject_cast<Scene_polyhedron_selection_item*>(scene->item(scene->mainSelectionIndex()));
}
for(CGAL::Three::Scene_interface::Item_id id : scene->selectionIndices())
{
//if one facegraph is found in the selection, it's fine
if (qobject_cast<Scene_facegraph_item*>(scene->item(id)))
return true;
}
return false;
}
void closure()
{
dock_widget->hide();
}
public Q_SLOTS:
void on_actionMVC_triggered();
void on_actionDCP_triggered();
void on_actionLSC_triggered();
void on_actionDAP_triggered();
void on_actionIAP_triggered();
void on_actionARAP_triggered();
void on_actionOTE_triggered();
void on_actionBTP_triggered();
void on_prevButton_pressed();
void on_nextButton_pressed();
void replacePolyline()
{
if(current_uv_item){
Scene_textured_facegraph_item* t_item =
qobject_cast<Scene_textured_facegraph_item*>(projections.key(current_uv_item));
t_item->add_border_edges(std::vector<float>(0));
}
int id = scene->mainSelectionIndex();
for(UVItem* pl : projections)
{
if(pl==nullptr || pl != projections[scene->item(id)])
continue;
current_uv_item = pl;
break;
}
if(!current_uv_item)
{
dock_widget->setWindowTitle(tr("UVMapping"));
ui_widget.component_numberLabel->setText(QString("Component :"));
}
else
{
if(!graphics_scene->items().empty())
graphics_scene->removeItem(graphics_scene->items().first());
graphics_scene->addItem(current_uv_item);
ui_widget.graphicsView->fitInView(current_uv_item->boundingRect(),
Qt::KeepAspectRatio);
ui_widget.component_numberLabel->setText(
QString("Component : %1/%2").arg(current_uv_item->current_component()+1)
.arg(current_uv_item->number_of_components()));
dock_widget->setWindowTitle(tr("UVMapping for %1")
.arg(current_uv_item->item_name()));
Scene_textured_facegraph_item* t_item =
qobject_cast<Scene_textured_facegraph_item*>(projections.key(current_uv_item));
t_item->add_border_edges(
current_uv_item->concatenated_borders());
}
}
void destroyPolyline(CGAL::Three::Scene_item* item)
{
for(UVItem* pli : projections)
{
if(projections.key(pli) != item)
continue;
graphics_scene->removeItem(pli);
delete pli;
projections.remove(item);
break;
}
if(projections.empty() || projections.first() == NULL)
{
current_uv_item = nullptr;
dock_widget->setWindowTitle(tr("UVMapping"));
ui_widget.component_numberLabel->setText(QString("Component :"));
}
else
current_uv_item = projections.first();
}
protected:
enum Parameterization_method { PARAM_MVC, PARAM_DCP, PARAM_LSC, PARAM_DAP,
PARAM_IAP, PARAM_ARAP, PARAM_OTE, PARAM_BTP};
void parameterize(Parameterization_method method);
private:
Messages_interface *messages = nullptr;
QList<QAction*> _actions;
QDockWidget* dock_widget = nullptr;
Ui::Parameterization ui_widget;
QGraphicsScene *graphics_scene;
Navigation* navigation = nullptr;
QMap<Scene_item*, UVItem*> projections;
UVItem* current_uv_item = nullptr;
}; // end CGAL_Lab_parameterization_plugin
void CGAL_Lab_parameterization_plugin::on_prevButton_pressed()
{
int id = scene->mainSelectionIndex();
for(UVItem* pl : projections)
{
if(pl==nullptr
|| pl != projections[scene->item(id)])
continue;
current_uv_item = pl;
break;
}
if(current_uv_item == nullptr)
return;
current_uv_item->set_current_component((std::max)(0,current_uv_item->current_component()-1));
replacePolyline();
}
void CGAL_Lab_parameterization_plugin::on_nextButton_pressed()
{
int id = scene->mainSelectionIndex();
for(UVItem* pl : projections)
{
if(pl==nullptr
|| pl != projections[scene->item(id)])
continue;
current_uv_item = pl;
break;
}
if(current_uv_item == nullptr)
return;
current_uv_item->set_current_component((std::min)(current_uv_item->number_of_components()-1,current_uv_item->current_component()+1));
ui_widget.component_numberLabel->setText(QString("Component : %1/%2").arg(current_uv_item->current_component()+1).arg(current_uv_item->number_of_components()));
replacePolyline();
}
void CGAL_Lab_parameterization_plugin::parameterize(const Parameterization_method method)
{
// get active polyhedron
Scene_facegraph_item* poly_item = nullptr;
CGAL::Three::Scene_interface::Item_id index = scene->mainSelectionIndex();
for(CGAL::Three::Scene_interface::Item_id id : scene->selectionIndices())
{
poly_item = qobject_cast<Scene_facegraph_item*>(scene->item(id));
if(!poly_item)
{
continue;
}
else
{
index = id;
break;
}
}
if(!poly_item)
{
CGAL::Three::Three::error("Selected item is not of the right type.");
return;
}
Face_graph* pMesh = poly_item->face_graph();
if(!pMesh)
{
CGAL::Three::Three::error("Selected item has no valid polyhedron.");
return;
}
Scene_polyhedron_selection_item* sel_item = nullptr;
bool is_seamed = false;
for(CGAL::Three::Scene_interface::Item_id id : scene->selectionIndices())
{
sel_item = qobject_cast<Scene_polyhedron_selection_item*>(scene->item(id));
if(!sel_item)
continue;
if(sel_item->selected_edges.empty())
continue;
if(method == PARAM_OTE && sel_item->selected_vertices.empty())
continue;
is_seamed = true;
}
if(method == PARAM_OTE &&
(sel_item == nullptr || sel_item->selected_vertices.empty())) {
std::cerr << "\nError: no cones/seam selected; Aborting parameterization." << std::endl;
return;
}
// Two property maps to store the seam edges and vertices
Seam_edge_uhm seam_edge_uhm(false);
Seam_edge_pmap seam_edge_pm(seam_edge_uhm);
Seam_vertex_uhm seam_vertex_uhm(false);
Seam_vertex_pmap seam_vertex_pm(seam_vertex_uhm);
if(!is_seamed && is_closed(*pMesh))
{
CGAL::Three::Three::error("The selected mesh has no (real or virtual) border.");
return;
}
QApplication::setOverrideCursor(Qt::WaitCursor);
///////////////////////////////////
////////// PARAMETERIZE ///////////
///////////////////////////////////
QElapsedTimer time;
time.start();
// add textured polyhedon to the scene
// \todo for surface_mesh
Base_face_graph tMesh = *pMesh;
std::vector<bool> mark(num_halfedges(tMesh)/2,false);
std::vector<T_edge_descriptor> seam_edges;
typedef boost::property_map<Base_face_graph, boost::vertex_index_t>::type VIDMap;
VIDMap vidmap = get(boost::vertex_index, tMesh);
if(is_seamed)
{
//create a textured_polyhedron edges selection from the ids of the corresponding vertices
typedef boost::property_map<Base_face_graph, boost::halfedge_index_t>::type HIDMap;
HIDMap hidmap = get(boost::halfedge_index, tMesh);
for(P_edge_descriptor ed : sel_item->selected_edges)
{
boost::graph_traits<Face_graph>::vertex_descriptor a(source(ed, *pMesh)), b(target(ed, *pMesh));
for(boost::graph_traits<Textured_face_graph>::edge_iterator it =
edges(tMesh).begin(); it != edges(tMesh).end();
++it)
{
boost::graph_traits<Textured_face_graph>::vertex_descriptor ta(source(*it, tMesh)), tb(target(*it, tMesh));
if((get(vidmap, ta) == get(vidmap, a) && get(vidmap,tb) == get(vidmap,b))
||
(get(vidmap,ta) == get(vidmap,b) && get(vidmap,tb) == get(vidmap,a)))
{
T_edge_descriptor ted(*it);
seam_edges.push_back(ted);
break;
}
}
}
qDebug() << sel_item->selected_edges.size() << ", " << seam_edges.size();
//fill seam mesh pmaps
for(T_edge_descriptor ed : seam_edges)
{
T_halfedge_descriptor hd = halfedge(ed, tMesh);
T_vertex_descriptor svd(source(hd, tMesh)), tvd(target(hd, tMesh));
if(!is_border(ed, tMesh))
{
put(seam_edge_pm, ed, true);
put(seam_vertex_pm, svd, true);
put(seam_vertex_pm, tvd, true);
mark[get(hidmap, hd)/2] = true;
}
}
}
// map the cones from the selection plugin to the textured polyhedron
std::unordered_set<T_vertex_descriptor> unordered_cones;
if(method == PARAM_OTE) {
for(P_vertex_descriptor vd : sel_item->selected_vertices) {
boost::graph_traits<Face_graph>::vertex_descriptor pvd(vd);
boost::graph_traits<Textured_face_graph>::vertex_iterator it = vertices(tMesh).begin(),
end = vertices(tMesh).end();
for(; it!=end; ++it) {
boost::graph_traits<Textured_face_graph>::vertex_descriptor tvd(*it);
if(get(vidmap, *it) == get(vidmap, pvd)) {
unordered_cones.insert(tvd);
}
}
}
}
Seam_mesh sMesh(tMesh, seam_edge_pm, seam_vertex_pm);
sMesh.set_seam_edges_number(static_cast<s_edges_size_type>(seam_edges.size()));
// The parameterized values
UV_uhm uv_uhm;
UV_pmap uv_pm(uv_uhm);
QString new_item_name;
//determine the different connected_components
boost::container::flat_map<boost::graph_traits<Base_face_graph>::face_descriptor, int> face_component_map;
boost::associative_property_map< boost::container::flat_map<s_face_descriptor, int> >
fccmap(face_component_map);
Is_selected_property_map edge_pmap(mark, &tMesh);
int number_of_components =
CGAL::Polygon_mesh_processing::connected_components(
tMesh,
fccmap,
CGAL::parameters::edge_is_constrained_map(
edge_pmap));
// Next is the gathering of the border halfedges of the connected component.
// It is wrong to pass the underlying mesh tMesh: a sphere split in half does
// not have any border if border_halfedges() is run with tMesh.
//
// The proper way would be to completely redesign the plugin to use Seam meshes
// everywhere. But that's not worth it. Instead, we abuse the fact that faces
// are the same in tMesh and sMesh.
// the SEAM MESH faces of each connected component
SComponents s_components(number_of_components);
for(boost::graph_traits<Base_face_graph>::face_iterator fit = faces(tMesh).begin();
fit != faces(tMesh).end(); ++fit) {
s_components.at(fccmap[*fit]).insert(s_face_descriptor(*fit));
}
// once per component
std::vector<std::vector<float> >uv_borders;
uv_borders.resize(number_of_components);
// to track whether the components are successfully parameterized
SMP::Error_code status = SMP::OK;
for(int current_component=0; current_component<number_of_components; ++current_component)
{
std::vector<s_halfedge_descriptor> border;
PMP::border_halfedges(s_components.at(current_component),
sMesh, std::back_inserter(border));
std::cout << sMesh.number_of_seam_edges() << " seams" << std::endl;
std::cout << (s_components.at(current_component)).size() << " faces" << std::endl;
std::cout << border.size() << " border halfedges" << std::endl;
// find longest border in the connected component
s_halfedge_descriptor bhd; // a halfedge on the (possibly virtual) border
std::unordered_set<s_halfedge_descriptor> visited;
FT result_len = 0;
for(s_halfedge_descriptor hd : border)
{
assert(is_border(hd, sMesh));
if(visited.find(hd) == visited.end())
{
FT len = 0;
for(s_halfedge_descriptor haf : halfedges_around_face(hd, sMesh))
{
len += PMP::edge_length(haf, sMesh);
visited.insert(haf);
}
if(result_len < len)
{
result_len = len;
bhd = hd;
}
}
}
CGAL_postcondition(bhd != s_halfedge_descriptor());
CGAL_postcondition(is_border(bhd, sMesh));
typedef boost::property_map<Base_face_graph, boost::vertex_point_t>::type VPMap;
VPMap vpmap =get(boost::vertex_point, tMesh);
// collect the border edges for that connected component
for(s_halfedge_descriptor haf : halfedges_around_face(bhd, sMesh))
{
uv_borders[current_component].push_back(get(vpmap, source(haf, tMesh)).x());
uv_borders[current_component].push_back(get(vpmap, source(haf, tMesh)).y());
uv_borders[current_component].push_back(get(vpmap, source(haf, tMesh)).z());
uv_borders[current_component].push_back(get(vpmap, target(haf, tMesh)).x());
uv_borders[current_component].push_back(get(vpmap, target(haf, tMesh)).y());
uv_borders[current_component].push_back(get(vpmap, target(haf, tMesh)).z());
}
switch(method)
{
case PARAM_MVC:
{
std::cout << "Parameterize (MVC)..." << std::endl;
new_item_name = tr("%1 (parameterized (MVC))").arg(poly_item->name());
typedef SMP::Mean_value_coordinates_parameterizer_3<Seam_mesh> Parameterizer;
status = SMP::parameterize(sMesh, Parameterizer(), bhd, uv_pm);
break;
}
case PARAM_DCP:
{
new_item_name = tr("%1 (parameterized (DCP))").arg(poly_item->name());
std::cout << "Parameterize (DCP)..." << std::endl;
typedef SMP::Discrete_conformal_map_parameterizer_3<Seam_mesh> Parameterizer;
status = SMP::parameterize(sMesh, Parameterizer(), bhd, uv_pm);
break;
}
case PARAM_LSC:
{
new_item_name = tr("%1 (parameterized (LSC))").arg(poly_item->name());
std::cout << "Parameterize (LSC)..." << std::endl;
typedef SMP::LSCM_parameterizer_3<Seam_mesh> Parameterizer;
status = SMP::parameterize(sMesh, Parameterizer(), bhd, uv_pm);
break;
}
case PARAM_DAP:
{
new_item_name = tr("%1 (parameterized (DAP))").arg(poly_item->name());
std::cout << "Parameterize (DAP)..." << std::endl;
typedef SMP::Discrete_authalic_parameterizer_3<Seam_mesh> Parameterizer;
status = SMP::parameterize(sMesh, Parameterizer(), bhd, uv_pm);
break;
}
case PARAM_IAP:
{
new_item_name = tr("%1 (parameterized (IAP))").arg(poly_item->name());
std::cout << "Parameterize (IAP)..." << std::endl;
typedef SMP::Iterative_authalic_parameterizer_3<Seam_mesh> Parameterizer;
Parameterizer parameterizer;
status = parameterizer.parameterize(sMesh, bhd, uv_pm, 15 /*iterations*/);
break;
}
case PARAM_ARAP:
{
new_item_name = tr("%1 (parameterized (ARAP))").arg(poly_item->name());
std::cout << "Parameterize (ARAP)..." << std::endl;
FT lambda = 10000; // a big value to ensure the parameterization is ARAP (and not ASAP)
typedef SMP::ARAP_parameterizer_3<Seam_mesh> Parameterizer;
status = SMP::parameterize(sMesh, Parameterizer(lambda), bhd, uv_pm);
break;
}
case PARAM_OTE:
{
new_item_name = tr("%1 (parameterized (OTE))").arg(poly_item->name());
std::cout << "Parameterize (OTE)..." << std::endl;
// OTE cannot handle multiple connected components right now
// @todo (need to remove the assertions such as cones.size() == 4
// and check where and when cones are used (passed by ID, for ex.?))
if(number_of_components != 1) {
std::cerr << "Orbifold Tutte Embedding can only handle one connected component" << std::endl;
status = SMP::ERROR_NO_TOPOLOGICAL_BALL;
break;
}
typedef SMP::Orbifold_Tutte_parameterizer_3<Seam_mesh> Parameterizer;
// Get orbifold type
QDialog dialog(mw);
Ui::OTE_dialog ui;
ui.setupUi(&dialog);
connect(ui.buttonBox, SIGNAL(accepted()), &dialog, SLOT(accept()));
connect(ui.buttonBox, SIGNAL(rejected()), &dialog, SLOT(reject()));
QApplication::restoreOverrideCursor();
int i = dialog.exec();
if (i == QDialog::Rejected)
{
std::cout << "Aborting parameterization" << std::endl;
QApplication::restoreOverrideCursor();
return;
}
SMP::Orbifold_type orb = static_cast<SMP::Orbifold_type>(ui.OrbComboBox->currentIndex());
std::cout << "Selected orbifold type: " << ui.OrbComboBox->currentText().toStdString() << std::endl;
if((unordered_cones.size() != 3 && unordered_cones.size() != 4) ||
(unordered_cones.size() == 3 && orb == SMP::Parallelogram ) ||
(unordered_cones.size() == 4 && orb != SMP::Parallelogram)) {
std::cerr << "Error: incompatible orbifold type and number of cones" << std::endl;
std::cerr << "Types I, II & III require 3 selected vertices" << std::endl;
std::cerr << "Type IV requires 4 selected vertices" << std::endl;
QApplication::restoreOverrideCursor();
return;
}
// Now, parameterize
Parameterizer parameterizer(orb);
// Mark cones in the seam mesh
std::unordered_map<s_vertex_descriptor, SMP::Cone_type> cmap;
if(!SMP::locate_unordered_cones(sMesh, unordered_cones.begin(), unordered_cones.end(), cmap))
{
std::cerr << "Error: invalid cone or seam selection" << std::endl;
QApplication::restoreOverrideCursor();
return;
}
QApplication::setOverrideCursor(Qt::WaitCursor);
// Fill the index property map
typedef std::unordered_map<s_vertex_descriptor, int> Indices;
Indices indices;
CGAL::Polygon_mesh_processing::connected_component(
face(opposite(bhd, sMesh), sMesh),
sMesh,
boost::make_function_output_iterator(
SMP::internal::Index_map_filler<Seam_mesh, Indices>(sMesh, indices)));
boost::associative_property_map<Indices> vimap(indices);
// Call to parameterizer
status = parameterizer.parameterize(sMesh, bhd, cmap, uv_pm, vimap);
break;
}
case PARAM_BTP:
{
std::cout << "Parameterize (BTP)..." << std::endl;
new_item_name = tr("%1 (parameterized (BTP))").arg(poly_item->name());
typedef SMP::Barycentric_mapping_parameterizer_3<Seam_mesh> Parameterizer;
status = SMP::parameterize(sMesh, Parameterizer(), bhd, uv_pm);
break;
}
} //end switch
std::cout << "Connected component " << current_component << ": ";
if(status == SMP::OK) {
std::cout << "success (in " << time.elapsed() << " ms)" << std::endl;
} else {
std::cerr << "failure: " << SMP::get_error_message(status) << std::endl;
QApplication::restoreOverrideCursor();
return;
}
if(status != SMP::OK)
break;
} //end for each component
QApplication::restoreOverrideCursor();
QPointF pmin(FLT_MAX, FLT_MAX), pmax(-FLT_MAX, -FLT_MAX);
SMesh::Property_map<halfedge_descriptor, float> umap;
SMesh::Property_map<halfedge_descriptor, float> vmap;
umap = tMesh.add_property_map<halfedge_descriptor, float>("h:u", 0.0f).first;
vmap = tMesh.add_property_map<halfedge_descriptor, float>("h:v", 0.0f).first;
tMesh.property_stats(std::cerr);
Base_face_graph::Halfedge_iterator it;
for(it = tMesh.halfedges_begin();
it != tMesh.halfedges_end();
++it)
{
Seam_mesh::halfedge_descriptor hd(*it);
FT u = uv_pm[target(hd, sMesh)].x();
FT v = uv_pm[target(hd, sMesh)].y();
put(umap, *it, static_cast<float>(u));
put(vmap, *it, static_cast<float>(v));
if(u<pmin.x())
pmin.setX(u);
if(u>pmax.x())
pmax.setX(u);
if(v<pmin.y())
pmin.setY(v);
if(v>pmax.y())
pmax.setY(v);
}
Components* components = new Components(0);
components->resize(number_of_components);
boost::graph_traits<Base_face_graph>::face_iterator bfit;
for(bfit = faces(tMesh).begin();
bfit != faces(tMesh).end();
++bfit)
{
components->at(fccmap[*bfit]).insert(*bfit);
}
Scene_textured_facegraph_item* new_item = new Scene_textured_facegraph_item(tMesh);
UVItem *projection = new UVItem(components,new_item->textured_face_graph(), uv_borders, QRectF(pmin, pmax));
projection->set_item_name(new_item_name);
new_item->setName(new_item_name);
new_item->setColor(Qt::white);
new_item->setRenderingMode(poly_item->renderingMode());
connect( new_item, SIGNAL(selectionChanged()),
this, SLOT(replacePolyline()) );
poly_item->setVisible(false);
scene->itemChanged(index);
scene->addItem(new_item);
if(!graphics_scene->items().empty())
graphics_scene->removeItem(graphics_scene->items().first());
graphics_scene->addItem(projection);
projections[new_item] = projection;
if(current_uv_item){
Scene_textured_facegraph_item* t_item =
qobject_cast<Scene_textured_facegraph_item*>(projections.key(current_uv_item));
t_item->add_border_edges(std::vector<float>(0));
}
current_uv_item = projection;
Scene_textured_facegraph_item* t_item =
qobject_cast<Scene_textured_facegraph_item*>(projections.key(current_uv_item));
t_item->add_border_edges(
current_uv_item->concatenated_borders());
if(dock_widget->isHidden()){
dock_widget->setVisible(true);
dock_widget->raise();
}
dock_widget->setWindowTitle(tr("UVMapping for %1").arg(new_item->name()));
ui_widget.component_numberLabel->setText(QString("Component : %1/%2").arg(current_uv_item->current_component()+1).arg(current_uv_item->number_of_components()));
ui_widget.graphicsView->fitInView(projection->boundingRect(), Qt::KeepAspectRatio);
QApplication::restoreOverrideCursor();
}
void CGAL_Lab_parameterization_plugin::on_actionMVC_triggered()
{
std::cerr << "MVC...";
parameterize(PARAM_MVC);
}
void CGAL_Lab_parameterization_plugin::on_actionDCP_triggered()
{
std::cerr << "DCP...";
parameterize(PARAM_DCP);
}
void CGAL_Lab_parameterization_plugin::on_actionLSC_triggered()
{
std::cerr << "LSC...";
parameterize(PARAM_LSC);
}
void CGAL_Lab_parameterization_plugin::on_actionDAP_triggered()
{
std::cerr << "DAP...";
parameterize(PARAM_DAP);
}
void CGAL_Lab_parameterization_plugin::on_actionIAP_triggered()
{
std::cerr << "IAP...";
parameterize(PARAM_IAP);
}
void CGAL_Lab_parameterization_plugin::on_actionARAP_triggered()
{
std::cerr << "ARAP...";
parameterize(PARAM_ARAP);
}
void CGAL_Lab_parameterization_plugin::on_actionOTE_triggered()
{
std::cerr << "OTE...";
parameterize(PARAM_OTE);
}
void CGAL_Lab_parameterization_plugin::on_actionBTP_triggered()
{
std::cerr << "BTP...";
parameterize(PARAM_BTP);
}
#include "Parameterization_plugin.moc"