Added Orbifold Tutte Embeddings to the polyhedron demo

This commit is contained in:
Mael Rouxel-Labbé 2016-11-29 17:40:01 +01:00
parent 22c1ea15ff
commit 05647d928a
7 changed files with 576 additions and 140 deletions

View File

@ -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)

View File

@ -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>

View File

@ -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"

View File

@ -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

View File

@ -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>

View File

@ -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);

View File

@ -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