mirror of https://github.com/CGAL/cgal
Added Orbifold Tutte Embeddings to the polyhedron demo
This commit is contained in:
parent
22c1ea15ff
commit
05647d928a
|
|
@ -1,6 +1,6 @@
|
|||
include( polyhedron_demo_macros )
|
||||
if(EIGEN3_FOUND)
|
||||
qt5_wrap_ui( parameterizationUI_FILES Parameterization_widget.ui ARAP_dialog.ui)
|
||||
qt5_wrap_ui( parameterizationUI_FILES Parameterization_widget.ui ARAP_dialog.ui OTE_dialog.ui)
|
||||
polyhedron_demo_plugin(parameterization_plugin Parameterization_plugin ${parameterizationUI_FILES})
|
||||
target_link_libraries(parameterization_plugin scene_polyhedron_item scene_textured_polyhedron_item scene_polyhedron_selection_item)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,137 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>OTE_dialog</class>
|
||||
<widget class="QDialog" name="OTE_dialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>355</width>
|
||||
<height>100</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="cursor">
|
||||
<cursorShape>ArrowCursor</cursorShape>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Meshing criteria</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout" columnstretch="0,0">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="orbLabel">
|
||||
<property name="text">
|
||||
<string>Orbifold type</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="OrbComboBox">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::LeftToRight</enum>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>I (Square)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>II (Diamond)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>III (Triangle)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>IV (Parallelogram)</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>OTE_dialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>384</x>
|
||||
<y>191</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>195</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>OTE_dialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>384</x>
|
||||
<y>191</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>195</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
|
|
@ -20,18 +20,33 @@
|
|||
#include <CGAL/Polygon_mesh_processing/border.h>
|
||||
#include <CGAL/Polygon_mesh_processing/measure.h>
|
||||
#include <CGAL/property_map.h>
|
||||
|
||||
#include <CGAL/Polyhedron_3.h>
|
||||
#include <CGAL/boost/graph/graph_traits_Polyhedron_3.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/Discrete_authalic_parameterizer_3.h>
|
||||
#include <CGAL/Surface_mesh_parameterization/Discrete_conformal_map_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/orbital_cone_helper.h>
|
||||
#include <CGAL/Surface_mesh_parameterization/Orbital_Tutte_parameterizer_3.h>
|
||||
#include <CGAL/Surface_mesh_parameterization/parameterize.h>
|
||||
|
||||
#include <CGAL/Textured_polyhedron_builder.h>
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include <boost/unordered_set.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <vector>
|
||||
|
||||
#include <CGAL/Three/Polyhedron_demo_plugin_helper.h>
|
||||
#include <CGAL/Three/Polyhedron_demo_plugin_interface.h>
|
||||
|
|
@ -41,6 +56,7 @@
|
|||
|
||||
#include "ui_Parameterization_widget.h"
|
||||
#include "ui_ARAP_dialog.h"
|
||||
#include "ui_OTE_dialog.h"
|
||||
|
||||
namespace SMP = CGAL::Surface_mesh_parameterization;
|
||||
|
||||
|
|
@ -143,13 +159,15 @@ private:
|
|||
QPointF prev_pos;
|
||||
};
|
||||
|
||||
namespace SMP = CGAL::Surface_mesh_parameterization;
|
||||
|
||||
typedef Kernel::FT FT;
|
||||
typedef boost::graph_traits<Polyhedron>::vertex_descriptor vertex_descriptor;
|
||||
typedef boost::graph_traits<Polyhedron>::vertex_descriptor P_vertex_descriptor;
|
||||
typedef Kernel::Point_2 Point_2;
|
||||
typedef boost::graph_traits<Polyhedron>::edge_descriptor P_edge_descriptor;
|
||||
typedef boost::graph_traits<Polyhedron>::halfedge_descriptor P_halfedge_descriptor;
|
||||
|
||||
|
||||
// Textured polyhedron
|
||||
typedef boost::graph_traits<Textured_polyhedron::Base>::
|
||||
edge_descriptor T_edge_descriptor;
|
||||
typedef boost::graph_traits<Textured_polyhedron::Base>::
|
||||
|
|
@ -157,7 +175,7 @@ typedef boost::graph_traits<Textured_polyhedron::Base>::
|
|||
typedef boost::graph_traits<Textured_polyhedron::Base>::
|
||||
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;
|
||||
|
|
@ -166,9 +184,19 @@ 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<Textured_polyhedron::Base, Seam_edge_pmap, Seam_vertex_pmap> Seam_mesh;
|
||||
|
||||
typedef boost::graph_traits<Seam_mesh>::vertex_descriptor vertex_descriptor;
|
||||
typedef boost::graph_traits<Seam_mesh>::halfedge_descriptor halfedge_descriptor;
|
||||
typedef boost::graph_traits<Seam_mesh>::face_descriptor face_descriptor;
|
||||
|
||||
typedef boost::graph_traits<Seam_mesh>::face_iterator face_iterator;
|
||||
|
||||
typedef boost::unordered_set<Textured_polyhedron::Base::Facet_handle> Component;
|
||||
typedef std::vector<Component> Components;
|
||||
|
||||
typedef boost::unordered_set<face_descriptor> SComponent;
|
||||
typedef std::vector<SComponent> SComponents;
|
||||
|
||||
class UVItem : public QGraphicsItem
|
||||
{
|
||||
|
|
@ -263,17 +291,19 @@ public:
|
|||
QAction* actionLSC = new QAction("Least Square Conformal Map", mw);
|
||||
QAction* actionDAP = new QAction("Discrete Authalic", mw);
|
||||
QAction* actionARAP = new QAction("As Rigid As Possible", mw);
|
||||
QAction* actionOTE = new QAction("Orbifold Tutte Embedding", mw);
|
||||
actionMVC->setObjectName("actionMVC");
|
||||
actionDCP->setObjectName("actionDCP");
|
||||
actionLSC->setObjectName("actionLSC");
|
||||
actionDAP->setObjectName("actionDAP");
|
||||
actionARAP->setObjectName("actionARAP");
|
||||
actionOTE->setObjectName("actionOTE");
|
||||
|
||||
_actions << actionMVC
|
||||
<< actionDCP
|
||||
<< actionLSC
|
||||
<< actionDAP
|
||||
<< actionARAP;
|
||||
<< actionOTE;
|
||||
autoConnectActions();
|
||||
Q_FOREACH(QAction *action, _actions)
|
||||
action->setProperty("subMenuName",
|
||||
|
|
@ -309,6 +339,7 @@ public Q_SLOTS:
|
|||
void on_actionLSC_triggered();
|
||||
void on_actionDAP_triggered();
|
||||
void on_actionARAP_triggered();
|
||||
void on_actionOTE_triggered();
|
||||
void on_prevButton_pressed();
|
||||
void on_nextButton_pressed();
|
||||
void replacePolyline()
|
||||
|
|
@ -318,8 +349,7 @@ public Q_SLOTS:
|
|||
int id = scene->mainSelectionIndex();
|
||||
Q_FOREACH(UVItem* pl, projections)
|
||||
{
|
||||
if(pl==NULL
|
||||
|| pl != projections[scene->item(id)])
|
||||
if(pl==NULL || pl != projections[scene->item(id)])
|
||||
continue;
|
||||
current_uv_item = pl;
|
||||
break;
|
||||
|
|
@ -363,8 +393,10 @@ public Q_SLOTS:
|
|||
}
|
||||
|
||||
protected:
|
||||
enum Parameterization_method { PARAM_MVC, PARAM_DCP, PARAM_LSC, PARAM_DAP, PARAM_ARAP};
|
||||
enum Parameterization_method { PARAM_MVC, PARAM_DCP, PARAM_LSC,
|
||||
PARAM_DAP, PARAM_ARAP, PARAM_OTE};
|
||||
void parameterize(Parameterization_method method);
|
||||
|
||||
private:
|
||||
Messages_interface *messages;
|
||||
QList<QAction*> _actions;
|
||||
|
|
@ -439,9 +471,9 @@ void Polyhedron_demo_parameterization_plugin::parameterize(const Parameterizatio
|
|||
sel_item = qobject_cast<Scene_polyhedron_selection_item*>(scene->item(id));
|
||||
if(!sel_item)
|
||||
continue;
|
||||
if(sel_item->selected_edges.empty()
|
||||
|| !sel_item->selected_facets.empty()
|
||||
|| !sel_item->selected_vertices.empty())
|
||||
if(sel_item->selected_edges.empty())
|
||||
continue;
|
||||
if(method == PARAM_OTE && sel_item->selected_vertices.empty())
|
||||
continue;
|
||||
is_seamed = true;
|
||||
}
|
||||
|
|
@ -514,9 +546,26 @@ void Polyhedron_demo_parameterization_plugin::parameterize(const Parameterizatio
|
|||
}
|
||||
}
|
||||
|
||||
// map the cones from the selection plugin to the textured polyhedron
|
||||
boost::unordered_set<T_vertex_descriptor> unordered_cones;
|
||||
if(method == PARAM_OTE) {
|
||||
BOOST_FOREACH(P_vertex_descriptor vd, sel_item->selected_vertices) {
|
||||
Polyhedron::Vertex_handle pvd(vd);
|
||||
Textured_polyhedron::Vertex_iterator it = tMesh.vertices_begin(),
|
||||
end = tMesh.vertices_end();
|
||||
for(; it!=end; ++it) {
|
||||
Textured_polyhedron::Vertex_handle tvd(it);
|
||||
if(it->id() == pvd->id()) {
|
||||
unordered_cones.insert(tvd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Seam_mesh sMesh(tMesh, seam_edge_pm, seam_vertex_pm);
|
||||
sMesh.set_seam_edges_number(seam_edges.size());
|
||||
|
||||
// The parameterized values
|
||||
UV_uhm uv_uhm;
|
||||
UV_pmap uv_pm(uv_uhm);
|
||||
|
||||
|
|
@ -537,24 +586,45 @@ void Polyhedron_demo_parameterization_plugin::parameterize(const Parameterizatio
|
|||
CGAL::Polygon_mesh_processing::parameters::edge_is_constrained_map(
|
||||
edge_pmap));
|
||||
|
||||
Components t_components(number_of_components);
|
||||
|
||||
// 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 according 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);
|
||||
|
||||
Textured_polyhedron::Base::Facet_iterator fit;
|
||||
for(fit = tMesh.facets_begin(); fit != tMesh.facets_end(); ++fit)
|
||||
{
|
||||
t_components.at(fccmap[fit]).insert(fit);
|
||||
for(fit = tMesh.facets_begin(); fit != tMesh.facets_end(); ++fit) {
|
||||
s_components.at(fccmap[fit]).insert(face_descriptor(fit));
|
||||
}
|
||||
|
||||
//once per component
|
||||
std::vector<std::vector<float> >uv_borders;
|
||||
uv_borders.resize(number_of_components);
|
||||
|
||||
for(int current_component=0; current_component<number_of_components; ++current_component)
|
||||
{
|
||||
std::vector<T_halfedge_descriptor> border;
|
||||
PMP::border_halfedges(t_components.at(current_component),
|
||||
tMesh,
|
||||
std::back_inserter(border));
|
||||
std::vector<halfedge_descriptor> border;
|
||||
|
||||
BOOST_FOREACH(T_halfedge_descriptor hd, border)
|
||||
// using `impl` to avoid face_index_t property maps
|
||||
// PMP::internal::border_halfedges_impl(s_components.at(current_component),
|
||||
// std::back_inserter(border),
|
||||
// sMesh);
|
||||
|
||||
PMP::internal::border_halfedges_impl(s_components.at(current_component),
|
||||
std::back_inserter(border),
|
||||
sMesh);
|
||||
|
||||
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;
|
||||
|
||||
BOOST_FOREACH(halfedge_descriptor hd, border)
|
||||
{
|
||||
uv_borders[current_component].push_back(source(hd, tMesh)->point().x());
|
||||
uv_borders[current_component].push_back(source(hd, tMesh)->point().y());
|
||||
|
|
@ -569,12 +639,8 @@ void Polyhedron_demo_parameterization_plugin::parameterize(const Parameterizatio
|
|||
halfedge_descriptor bhd; // a halfedge on the (possibly virtual) border
|
||||
boost::unordered_set<halfedge_descriptor> visited;
|
||||
FT result_len = 0;
|
||||
BOOST_FOREACH(T_halfedge_descriptor thd, border)
|
||||
BOOST_FOREACH(halfedge_descriptor hd, border)
|
||||
{
|
||||
halfedge_descriptor hd(thd);
|
||||
if(sMesh.has_on_seam(thd))
|
||||
hd.seam = true; // virtual border halfedge
|
||||
|
||||
assert(is_border(hd, sMesh));
|
||||
|
||||
if(visited.find(hd) == visited.end())
|
||||
|
|
@ -593,7 +659,8 @@ void Polyhedron_demo_parameterization_plugin::parameterize(const Parameterizatio
|
|||
}
|
||||
}
|
||||
}
|
||||
assert(bhd != halfedge_descriptor() && is_border(bhd, sMesh));
|
||||
CGAL_postcondition(bhd != halfedge_descriptor());
|
||||
CGAL_postcondition(is_border(bhd, sMesh));
|
||||
|
||||
bool success = false;
|
||||
switch(method)
|
||||
|
|
@ -660,6 +727,64 @@ void Polyhedron_demo_parameterization_plugin::parameterize(const Parameterizatio
|
|||
success = (err == SMP::OK);
|
||||
break;
|
||||
}
|
||||
case PARAM_OTE:
|
||||
{
|
||||
new_item_name = tr("%1 (parameterized (OTE))").arg(poly_item->name());
|
||||
std::cout << "Parameterize (OTE)...";
|
||||
|
||||
// does not 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.?))
|
||||
CGAL_assertion(number_of_components == 1);
|
||||
|
||||
CGAL_precondition(!unordered_cones.empty());
|
||||
|
||||
typedef SMP::Orbital_Tutte_parameterizer_3<Seam_mesh> Parameterizer;
|
||||
|
||||
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()));
|
||||
|
||||
// Get values
|
||||
QApplication::restoreOverrideCursor();
|
||||
|
||||
int i = dialog.exec();
|
||||
if (i == QDialog::Rejected)
|
||||
return;
|
||||
|
||||
SMP::Orbifold_type orb = static_cast<SMP::Orbifold_type>(ui.OrbComboBox->currentIndex());
|
||||
std::cout << "selected orbifold type: " << ui.OrbComboBox->currentText().toStdString() << std::endl;
|
||||
|
||||
QApplication::setOverrideCursor(Qt::WaitCursor);
|
||||
|
||||
// Now, parameterize
|
||||
Parameterizer parameterizer(orb);
|
||||
|
||||
// mark cones in the seam mesh
|
||||
typedef boost::unordered_map<vertex_descriptor, SMP::Cone_type> Cones;
|
||||
Cones cmap;
|
||||
SMP::internal::locate_unordered_cones<Seam_mesh,
|
||||
Textured_polyhedron::Base,
|
||||
boost::unordered_set<T_vertex_descriptor>,
|
||||
Cones>(sMesh, unordered_cones, cmap);
|
||||
|
||||
// vimap and uvmap
|
||||
typedef boost::unordered_map<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
|
||||
SMP::Error_code err = parameterizer.parameterize(sMesh, bhd, cmap, uv_pm, vimap);
|
||||
success = (err == SMP::OK);
|
||||
break;
|
||||
}
|
||||
}//end switch
|
||||
|
||||
QApplication::restoreOverrideCursor();
|
||||
|
|
@ -770,4 +895,10 @@ void Polyhedron_demo_parameterization_plugin::on_actionARAP_triggered()
|
|||
parameterize(PARAM_ARAP);
|
||||
}
|
||||
|
||||
void Polyhedron_demo_parameterization_plugin::on_actionOTE_triggered()
|
||||
{
|
||||
std::cerr << "OTE...";
|
||||
parameterize(PARAM_OTE);
|
||||
}
|
||||
|
||||
#include "Parameterization_plugin.moc"
|
||||
|
|
|
|||
|
|
@ -46,87 +46,16 @@ typedef SurfaceMesh::Property_map<SM_halfedge_descriptor, Point_2> UV_pmap;
|
|||
|
||||
namespace SMP = CGAL::Surface_mesh_parameterization;
|
||||
|
||||
/// Read the cones from the input file.
|
||||
SMP::Error_code read_cones(const SurfaceMesh& sm, const char* filename,
|
||||
std::vector<SM_vertex_descriptor>& cone_vds_in_sm)
|
||||
{
|
||||
std::ifstream in(filename);
|
||||
std::string vertices_line;
|
||||
std::getline(in, vertices_line); // read the first line of the file
|
||||
std::istringstream iss(vertices_line);
|
||||
std::vector<int> cones;
|
||||
cones.reserve(4);
|
||||
int cone_index;
|
||||
while(iss >> cone_index) {
|
||||
cones.push_back(cone_index);
|
||||
}
|
||||
|
||||
if(cones.size() < 3 || cones.size() > 4) {
|
||||
std::cerr << "Error: problem loading the input cones" << std::endl;
|
||||
return SMP::ERROR_WRONG_PARAMETER;
|
||||
}
|
||||
|
||||
std::cout << "Cones: ";
|
||||
for(std::size_t i=0; i<cones.size(); ++i)
|
||||
std::cout << cones[i] << " ";
|
||||
std::cout << std::endl;
|
||||
|
||||
// Locate the cones in the underlying mesh 'sm'
|
||||
CGAL_assertion(cone_vds_in_sm.empty());
|
||||
cone_vds_in_sm.resize(cones.size());
|
||||
|
||||
for(std::size_t i=0; i<cones.size(); ++i) {
|
||||
int counter = 0;
|
||||
BOOST_FOREACH(SM_vertex_descriptor vd, vertices(sm)) {
|
||||
if(counter == cones[i]) {
|
||||
cone_vds_in_sm[i] = vd;
|
||||
break;
|
||||
}
|
||||
++counter;
|
||||
}
|
||||
CGAL_postcondition(cone_vds_in_sm[i] != SM_vertex_descriptor());
|
||||
}
|
||||
|
||||
return SMP::OK;
|
||||
}
|
||||
|
||||
/// Locate the cones on the seam mesh (find the corresponding seam mesh
|
||||
/// vertex_descriptor) and mark them with a tag that indicates whether it is a
|
||||
/// simple cone or a duplicated cone.
|
||||
template<typename ConeMap>
|
||||
void locate_cones(const Mesh& mesh,
|
||||
const std::vector<SM_vertex_descriptor>& cone_vds_in_sm,
|
||||
ConeMap& cones)
|
||||
{
|
||||
// property map to go from SM_vertex_descriptor to Point_3
|
||||
typedef SMP::internal::Kernel_traits<SurfaceMesh>::PPM SM_PPM;
|
||||
const SM_PPM sm_ppmap = get(boost::vertex_point, mesh.mesh());
|
||||
|
||||
// property map to go from vertex_descriptor to Point_3
|
||||
typedef SMP::internal::Kernel_traits<Mesh>::PPM PPM;
|
||||
const PPM ppmap = get(boost::vertex_point, mesh);
|
||||
|
||||
// the cones in the underlying mesh
|
||||
std::size_t cvdss = cone_vds_in_sm.size();
|
||||
for(std::size_t i=0; i<cvdss; ++i) {
|
||||
SM_vertex_descriptor smvd = cone_vds_in_sm[i];
|
||||
BOOST_FOREACH(vertex_descriptor vd, vertices(mesh)) {
|
||||
if(get(ppmap, vd) == get(sm_ppmap, smvd)) {
|
||||
SMP::Cone_type ct = (i == 0 || i == cvdss-1) ? SMP::Unique_cone : SMP::Duplicated_cone;
|
||||
cones.insert(std::make_pair(vd, ct));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CGAL_postcondition((cone_vds_in_sm.size() == 3 && cones.size() == 4) ||
|
||||
(cone_vds_in_sm.size() == 4 && cones.size() == 6));
|
||||
|
||||
std::cout << cone_vds_in_sm.size() << " cones in sm" << std::endl;
|
||||
std::cout << cones.size() << " cones in mesh" << std::endl;
|
||||
}
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
std::cout.precision(20);
|
||||
|
||||
#if (EIGEN_WORLD_VERSION == 3 && EIGEN_MAJOR_VERSION == 3)
|
||||
std::cout << "Using eigen 3.3" << std::endl;
|
||||
#elif (EIGEN_WORLD_VERSION == 3 && EIGEN_MAJOR_VERSION == 2)
|
||||
std::cout << "Using eigen 3.2" << std::endl;
|
||||
#endif
|
||||
|
||||
CGAL::Timer task_timer;
|
||||
task_timer.start();
|
||||
|
||||
|
|
@ -147,8 +76,9 @@ int main(int argc, char * argv[])
|
|||
const char* cone_filename = (argc>2) ? argv[2] : "../data/bear.selection.txt";
|
||||
|
||||
// Read the cones and find the corresponding vertex_descriptor in the underlying mesh 'sm'
|
||||
std::vector<SM_vertex_descriptor> cone_vds_in_sm;
|
||||
read_cones(sm, cone_filename, cone_vds_in_sm);
|
||||
typedef std::vector<SM_vertex_descriptor> Cones_in_smesh_container;
|
||||
Cones_in_smesh_container cone_vds_in_sm;
|
||||
SMP::internal::read_cones<SurfaceMesh>(sm, cone_filename, cone_vds_in_sm);
|
||||
|
||||
// Two property maps to store the seam edges and vertices
|
||||
Seam_edge_pmap seam_edge_pm = sm.add_property_map<SM_edge_descriptor, bool>("e:on_seam", false).first;
|
||||
|
|
@ -185,7 +115,9 @@ int main(int argc, char * argv[])
|
|||
// Mark the cones in the seam mesh
|
||||
typedef boost::unordered_map<vertex_descriptor, SMP::Cone_type> Cones;
|
||||
Cones cmap;
|
||||
locate_cones(mesh, cone_vds_in_sm, cmap);
|
||||
SMP::internal::locate_cones<Mesh, SurfaceMesh,
|
||||
Cones_in_smesh_container,
|
||||
Cones>(mesh, cone_vds_in_sm, cmap);
|
||||
|
||||
// The 2D points of the uv parametrisation will be written into this map
|
||||
// Note that this is a halfedge property map, and that uv values
|
||||
|
|
|
|||
|
|
@ -5,16 +5,17 @@
|
|||
#include <CGAL/Surface_mesh.h>
|
||||
#include <CGAL/boost/graph/graph_traits_Surface_mesh.h>
|
||||
|
||||
#include <CGAL/Polyhedron_3.h>
|
||||
#include <CGAL/boost/graph/graph_traits_Polyhedron_3.h>
|
||||
|
||||
#include <CGAL/boost/graph/Seam_mesh.h>
|
||||
#include <CGAL/boost/graph/graph_traits_Seam_mesh.h>
|
||||
|
||||
#include <CGAL/Polyhedron_3.h>
|
||||
#include <CGAL/Polygon_mesh_processing/measure.h>
|
||||
#include <CGAL/boost/graph/graph_traits_Polyhedron_3.h>
|
||||
|
||||
#include <CGAL/Surface_mesh_parameterization/Error_code.h>
|
||||
#include <CGAL/surface_mesh_parameterization.h>
|
||||
|
||||
#include <CGAL/Polygon_mesh_processing/measure.h>
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/functional/hash.hpp>
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
#include <CGAL/Surface_mesh_parameterization/internal/angles.h>
|
||||
#include <CGAL/Surface_mesh_parameterization/internal/kernel_traits.h>
|
||||
#include <CGAL/Surface_mesh_parameterization/internal/orbital_cone_helper.h>
|
||||
#include <CGAL/Surface_mesh_parameterization/IO/File_off.h>
|
||||
|
||||
#include <CGAL/Surface_mesh_parameterization/Error_code.h>
|
||||
|
|
@ -58,20 +59,16 @@
|
|||
|
||||
// @todo checks that cones are different, are on seams, seam is one connected
|
||||
// component
|
||||
// @todo Should the order of cones provided in entry matter ? Map the first cone
|
||||
// to [-1, -1] for example ?
|
||||
|
||||
namespace CGAL {
|
||||
|
||||
namespace Surface_mesh_parameterization {
|
||||
|
||||
enum Cone_type
|
||||
{
|
||||
Unique_cone,
|
||||
Duplicated_cone
|
||||
};
|
||||
|
||||
enum Orbifold_type
|
||||
{
|
||||
Square,
|
||||
Square = 0,
|
||||
Diamond,
|
||||
Triangle,
|
||||
Parallelogram
|
||||
|
|
@ -246,10 +243,12 @@ public:
|
|||
++id_r; // current line index in A is increased
|
||||
}
|
||||
|
||||
void parameterize_seam_segment(const std::vector<std::pair<int, int> >& seam_segment,
|
||||
void constrain_seam_segment(const std::vector<std::pair<int, int> >& seam_segment,
|
||||
NT ang, int& current_line_id_in_A,
|
||||
Matrix& A, Vector& B) const
|
||||
{
|
||||
std::cout << "constraining segment of length " << seam_segment.size() << std::endl;
|
||||
|
||||
// check that if there is a common vertex, it is at the beginning
|
||||
bool is_reversed = (seam_segment.back().first == seam_segment.back().second);
|
||||
|
||||
|
|
@ -323,7 +322,7 @@ public:
|
|||
|
||||
// points between two cones, and the corresponding points on the opposite side of the seam
|
||||
std::vector<std::pair<int, int> > seam_segment;
|
||||
int segment_index = 0; // counting the segments (3 max)
|
||||
std::size_t segment_index = 0; // counting the segments (3 max)
|
||||
|
||||
// Go through the seam, marking rotation and cone constraints
|
||||
while(true) { // breaking at the last cone
|
||||
|
|
@ -366,7 +365,7 @@ public:
|
|||
|
||||
CGAL_assertion(segment_index < angs.size());
|
||||
NT ang = angs[segment_index];
|
||||
parameterize_seam_segment(seam_segment, ang, current_line_id_in_A, A, B);
|
||||
constrain_seam_segment(seam_segment, ang, current_line_id_in_A, A, B);
|
||||
|
||||
// the last cone of the seam is constrained
|
||||
if(is_in_map->second == Unique_cone) { // reached the end of the seam
|
||||
|
|
@ -378,7 +377,7 @@ public:
|
|||
|
||||
std::cout << "-------------------------" << std::endl;
|
||||
seam_segment.clear();
|
||||
segment_index++;
|
||||
++segment_index;
|
||||
}
|
||||
|
||||
// move to the next halfedge couple (walking on the border of the seam)
|
||||
|
|
@ -482,7 +481,7 @@ public:
|
|||
const VertexIndexMap vimap) const
|
||||
{
|
||||
std::cout << "size of X: " << X.size() << std::endl;
|
||||
CGAL_assertion(X.size() == 2 * num_vertices(mesh) );
|
||||
CGAL_assertion(X.size() == static_cast<int>(2 * num_vertices(mesh)));
|
||||
|
||||
BOOST_FOREACH(vertex_descriptor vd, vertices(mesh)) {
|
||||
int index = get(vimap, vd);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,236 @@
|
|||
// Copyright (c) 2016 GeometryFactory (France).
|
||||
// All rights reserved.
|
||||
//
|
||||
// This file is part of CGAL (www.cgal.org).
|
||||
// You can redistribute it and/or modify it under the terms of the GNU
|
||||
// General Public License as published by the Free Software Foundation,
|
||||
// either version 3 of the License, or (at your option) any later version.
|
||||
//
|
||||
// Licensees holding a valid commercial license may use this file in
|
||||
// accordance with the commercial license agreement provided with the software.
|
||||
//
|
||||
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
||||
// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// $URL$
|
||||
// $Id$
|
||||
//
|
||||
//
|
||||
// Author(s) :
|
||||
|
||||
#ifndef CGAL_SURFACE_MESH_PARAMETERIZATION_INTERNAL_CONE_HELPER_H
|
||||
#define CGAL_SURFACE_MESH_PARAMETERIZATION_INTERNAL_CONE_HELPER_H
|
||||
|
||||
#include <CGAL/Surface_mesh_parameterization/internal/kernel_traits.h>
|
||||
#include <CGAL/Surface_mesh_parameterization/Error_code.h>
|
||||
|
||||
#include <CGAL/boost/graph/properties.h>
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/graph/graph_traits.hpp>
|
||||
|
||||
#include <fstream>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace CGAL {
|
||||
|
||||
namespace Surface_mesh_parameterization {
|
||||
|
||||
enum Cone_type
|
||||
{
|
||||
Unique_cone,
|
||||
Duplicated_cone
|
||||
};
|
||||
|
||||
namespace internal {
|
||||
|
||||
/// Read the cones from the input file.
|
||||
template<typename Polygon_mesh>
|
||||
Error_code read_cones(const Polygon_mesh& pm, const char* filename,
|
||||
std::vector<typename boost::graph_traits<Polygon_mesh>::vertex_descriptor>& cone_vds_in_pm)
|
||||
{
|
||||
typedef typename boost::graph_traits<Polygon_mesh>::vertex_descriptor PM_vertex_descriptor;
|
||||
|
||||
std::ifstream in(filename);
|
||||
std::string vertices_line;
|
||||
std::getline(in, vertices_line); // read the first line of the file
|
||||
std::istringstream iss(vertices_line);
|
||||
std::vector<int> cones;
|
||||
cones.reserve(4);
|
||||
int cone_index;
|
||||
while(iss >> cone_index) {
|
||||
cones.push_back(cone_index);
|
||||
}
|
||||
|
||||
if(cones.size() < 3 || cones.size() > 4) {
|
||||
std::cerr << "Error: Not enough or too many input cones" << std::endl;
|
||||
return ERROR_WRONG_PARAMETER;
|
||||
}
|
||||
|
||||
std::cout << "Cones: ";
|
||||
for(std::size_t i=0; i<cones.size(); ++i)
|
||||
std::cout << cones[i] << " ";
|
||||
std::cout << std::endl;
|
||||
|
||||
// Locate the cones in the underlying mesh 'pm'
|
||||
CGAL_assertion(cone_vds_in_pm.empty());
|
||||
cone_vds_in_pm.resize(cones.size());
|
||||
|
||||
for(std::size_t i=0; i<cones.size(); ++i) {
|
||||
int counter = 0;
|
||||
BOOST_FOREACH(PM_vertex_descriptor vd, vertices(pm)) {
|
||||
if(counter == cones[i]) {
|
||||
cone_vds_in_pm[i] = vd;
|
||||
break;
|
||||
}
|
||||
++counter;
|
||||
}
|
||||
CGAL_postcondition(cone_vds_in_pm[i] != PM_vertex_descriptor());
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/// Locate the cones on the seam mesh (find the corresponding seam mesh
|
||||
/// vertex_descriptor) and mark them with a tag that indicates whether it is a
|
||||
/// simple cone or a duplicated cone.
|
||||
///
|
||||
/// The cones are ordered: the first and last cones are the extremetities of the seam.
|
||||
///
|
||||
/// \tparam Mesh is a seam mesh
|
||||
/// \tparam BaseMesh is the underlying mesh of `Mesh`
|
||||
/// \tparam ConeMap a map vertex_descriptor --> Cone_type
|
||||
template<typename Mesh,
|
||||
typename BaseMesh,
|
||||
typename Cones_in_pmesh_vector,
|
||||
typename ConeMap>
|
||||
void locate_cones(const Mesh& mesh,
|
||||
const Cones_in_pmesh_vector& cone_vds_in_sm,
|
||||
ConeMap& cones)
|
||||
{
|
||||
CGAL_precondition(cones.empty());
|
||||
|
||||
typedef typename boost::graph_traits<BaseMesh>::vertex_descriptor BM_vertex_descriptor;
|
||||
typedef typename boost::graph_traits<Mesh>::vertex_descriptor vertex_descriptor;
|
||||
|
||||
// property map to go from BM_vertex_descriptor to Point_3
|
||||
typedef typename Kernel_traits<BaseMesh>::PPM PM_PPM;
|
||||
const PM_PPM pm_ppmap = get(boost::vertex_point, mesh.mesh());
|
||||
|
||||
// property map to go from vertex_descriptor to Point_3
|
||||
typedef typename Kernel_traits<Mesh>::PPM PPM;
|
||||
const PPM ppmap = get(boost::vertex_point, mesh);
|
||||
|
||||
// the cones in the underlying mesh
|
||||
std::size_t cvdss = cone_vds_in_sm.size();
|
||||
for(std::size_t i=0; i<cvdss; ++i) {
|
||||
BM_vertex_descriptor smvd = cone_vds_in_sm[i];
|
||||
BOOST_FOREACH(vertex_descriptor vd, vertices(mesh)) {
|
||||
if(get(ppmap, vd) == get(pm_ppmap, smvd)) {
|
||||
Cone_type ct = (i == 0 || i == cvdss-1) ? Unique_cone : Duplicated_cone;
|
||||
cones.insert(std::make_pair(vd, ct));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CGAL_postcondition((cone_vds_in_sm.size() == 3 && cones.size() == 4) ||
|
||||
(cone_vds_in_sm.size() == 4 && cones.size() == 6));
|
||||
|
||||
std::cout << cone_vds_in_sm.size() << " cones in sm" << std::endl;
|
||||
std::cout << cones.size() << " cones in mesh" << std::endl;
|
||||
}
|
||||
|
||||
/// Same as above, but the cones are NOT ordered and we thus use seam mesh
|
||||
/// information to determine which cones are Duplicate_cones and which cones
|
||||
/// are unique
|
||||
///
|
||||
/// \tparam Mesh is a seam mesh
|
||||
/// \tparam BaseMesh is the type of the underlying mesh in the seam mesh
|
||||
/// \tparam ConeMap a map vertex_descriptor --> Cone_type
|
||||
template<typename Mesh,
|
||||
typename BaseMesh,
|
||||
typename Cones_in_pmesh_set,
|
||||
typename ConeMap>
|
||||
void locate_unordered_cones(const Mesh& mesh,
|
||||
const Cones_in_pmesh_set& cone_vds_in_sm,
|
||||
ConeMap& cones)
|
||||
{
|
||||
CGAL_precondition(cones.empty());
|
||||
|
||||
typedef typename boost::graph_traits<BaseMesh>::vertex_descriptor BM_vertex_descriptor;
|
||||
typedef typename boost::graph_traits<Mesh>::vertex_descriptor vertex_descriptor;
|
||||
typedef typename boost::graph_traits<Mesh>::halfedge_descriptor halfedge_descriptor;
|
||||
|
||||
// find a vertex on the seam
|
||||
vertex_descriptor vertex_on_seam;
|
||||
BOOST_FOREACH(vertex_descriptor vd, vertices(mesh)) {
|
||||
if(mesh.has_on_seam(vd)) {
|
||||
vertex_on_seam = vd;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CGAL_assertion(vertex_on_seam != vertex_descriptor());
|
||||
|
||||
// property map to go from BM_vertex_descriptor to Point_3
|
||||
typedef typename Kernel_traits<BaseMesh>::PPM PM_PPM;
|
||||
const PM_PPM pm_ppmap = get(boost::vertex_point, mesh.mesh());
|
||||
|
||||
// property map to go from vertex_descriptor to Point_3
|
||||
typedef typename Kernel_traits<Mesh>::PPM PPM;
|
||||
const PPM ppmap = get(boost::vertex_point, mesh);
|
||||
|
||||
// walk on the seam and mark if we encounter a cone
|
||||
vertex_descriptor end = vertex_on_seam;
|
||||
do {
|
||||
BOOST_FOREACH(BM_vertex_descriptor smvd, cone_vds_in_sm) {
|
||||
if(get(ppmap, vertex_on_seam) == get(pm_ppmap, smvd)) { // the seam mesh vertex is a cone
|
||||
|
||||
// we have encountered a cone. Must check if the cone is a Unique_cone
|
||||
// or a Duplicated_cone.
|
||||
|
||||
// a check is to look at the sources of two halfedges with the same direction
|
||||
// on either side of the seam. If the sources are the same, it's a Unique_cone;
|
||||
// if they differ, it's a duplicated_cone.
|
||||
|
||||
halfedge_descriptor hd = halfedge(vertex_on_seam, mesh);
|
||||
halfedge_descriptor other_hd = opposite(hd, mesh);
|
||||
|
||||
// little trick to go from border halfedge on one side of the seam to
|
||||
// interior halfedge on the other side of the seam
|
||||
CGAL_assertion(other_hd.seam);
|
||||
other_hd.seam = false;
|
||||
|
||||
Cone_type ct = (target(hd, mesh) == source(other_hd, mesh)) ? Unique_cone
|
||||
: Duplicated_cone;
|
||||
|
||||
std::cout << "new cone with type: " << ct << std::endl;
|
||||
|
||||
cones.insert(std::make_pair(vertex_on_seam, ct));
|
||||
}
|
||||
}
|
||||
|
||||
// move to the next vertex_descriptor on the seam
|
||||
vertex_on_seam = source(halfedge(vertex_on_seam, mesh), mesh);
|
||||
CGAL_assertion(mesh.has_on_seam(vertex_on_seam));
|
||||
|
||||
} while(vertex_on_seam != end);
|
||||
|
||||
CGAL_postcondition((cone_vds_in_sm.size() == 3 && cones.size() == 4) ||
|
||||
(cone_vds_in_sm.size() == 4 && cones.size() == 6));
|
||||
|
||||
std::cout << cone_vds_in_sm.size() << " cones in sm" << std::endl;
|
||||
std::cout << cones.size() << " cones in mesh" << std::endl;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
} // namespace Surface_mesh_parameterization
|
||||
|
||||
} // namespace CGAL
|
||||
|
||||
#endif // CGAL_SURFACE_MESH_PARAMETERIZATION_INTERNAL_CONE_HELPER_H
|
||||
Loading…
Reference in New Issue