add a function to stitch boundary edges in a polyhedron

also brings:
  a demo plugin that also display boundary edges
  a testsuite with examples
This commit is contained in:
Sébastien Loriot 2013-11-27 15:43:17 +01:00
parent a3a3e9f69d
commit 05bff9cf8f
9 changed files with 631 additions and 0 deletions

View File

@ -0,0 +1,269 @@
// Copyright (c) 2014 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) : Sebastien Loriot
#ifndef CGAL_POLYHEDRON_STITCHING_H
#define CGAL_POLYHEDRON_STITCHING_H
#include <CGAL/Modifier_base.h>
#include <CGAL/HalfedgeDS_decorator.h>
#include <vector>
#include <set>
namespace CGAL{
namespace Polyhedron_stitching{
template <class Halfedge_handle, class Point_3>
struct Less_for_halfedge{
bool operator()( Halfedge_handle h1, Halfedge_handle h2 ) const
{
const Point_3& s1=h1->opposite()->vertex()->point();
const Point_3& t1=h1->vertex()->point();
const Point_3& s2=h2->opposite()->vertex()->point();
const Point_3& t2=h2->vertex()->point();
return
( s1 < t1? std::make_pair(s1,t1) : std::make_pair(t1, s1) )
<
( s2 < t2? std::make_pair(s2,t2) : std::make_pair(t2, s2) );
}
};
template <class LessHedge, class Polyhedron, class OutputIterator>
OutputIterator
detect_duplicated_boundary_edges
(Polyhedron& P, OutputIterator out, LessHedge less_hedge)
{
typedef typename Polyhedron::Halfedge_handle Halfedge_handle;
P.normalize_border();
typedef
std::set<Halfedge_handle, LessHedge > Border_halfedge_set;
Border_halfedge_set border_halfedge_set(less_hedge);
for (typename Polyhedron::Halfedge_iterator
it=P.border_halfedges_begin(), it_end=P.halfedges_end();
it!=it_end; ++it)
{
if ( !it->is_border() ) continue;
typename Border_halfedge_set::iterator set_it;
bool insertion_ok;
CGAL::cpp11::tie(set_it,insertion_ok)=
border_halfedge_set.insert(it);
if ( !insertion_ok ) *out++=std::make_pair(*set_it, it);
}
return out;
}
template <class Polyhedron>
struct Naive_border_stitching_modifier:
CGAL::Modifier_base<typename Polyhedron::HalfedgeDS>
{
typedef typename Polyhedron::HalfedgeDS HDS;
typedef typename HDS::Halfedge_handle Halfedge_handle;
typedef typename HDS::Vertex_handle Vertex_handle;
typedef typename HDS::Halfedge::Base HBase;
std::vector <std::pair<Halfedge_handle,Halfedge_handle> >& hedge_pairs_to_stitch;
Naive_border_stitching_modifier(
std::vector< std::pair<Halfedge_handle,Halfedge_handle> >&
hedge_pairs_to_stitch_) :hedge_pairs_to_stitch(hedge_pairs_to_stitch_)
{}
void update_target_vertex(Halfedge_handle h,
Vertex_handle v,
CGAL::HalfedgeDS_decorator<HDS>& decorator)
{
Halfedge_handle start=h;
do{
decorator.set_vertex(h, v);
h=h->next()->opposite();
} while( h!=start );
decorator.set_vertex_halfedge(v, h);
}
void operator() (HDS& hds)
{
std::size_t nb_hedges=hedge_pairs_to_stitch.size();
std::set <Halfedge_handle> hedges_to_stitch;
for (std::size_t k=0; k<nb_hedges; ++k)
{
hedges_to_stitch.insert( hedge_pairs_to_stitch[k].first );
hedges_to_stitch.insert( hedge_pairs_to_stitch[k].second );
}
CGAL::HalfedgeDS_decorator<HDS> decorator(hds);
for (std::size_t k=0; k<nb_hedges; ++k)
{
Halfedge_handle h1=hedge_pairs_to_stitch[k].first;
Halfedge_handle h2=hedge_pairs_to_stitch[k].second;
CGAL_assertion(h1->vertex()->point() == h2->opposite()->vertex()->point() );
CGAL_assertion(h2->vertex()->point() == h1->opposite()->vertex()->point() );
CGAL_assertion( h1->is_border() );
CGAL_assertion( h2->is_border() );
CGAL_assertion( !h1->opposite()->is_border() );
CGAL_assertion( !h2->opposite()->is_border() );
//update next/prev of neighbor halfedges
if ( hedges_to_stitch.find(h1->next())==hedges_to_stitch.end() )
{
CGAL_assertion( hedges_to_stitch.find(h2->prev())==hedges_to_stitch.end() );
Halfedge_handle prev=h2->prev();
Halfedge_handle next=h1->next();
prev->HBase::set_next(next);
decorator.set_prev(next, prev);
}
else
{
CGAL_assertion( hedges_to_stitch.find(h2->prev())!=hedges_to_stitch.end() );
}
if ( hedges_to_stitch.find(h2->next())==hedges_to_stitch.end() )
{
CGAL_assertion( hedges_to_stitch.find(h1->prev())==hedges_to_stitch.end() );
Halfedge_handle prev=h1->prev();
Halfedge_handle next=h2->next();
prev->HBase::set_next(next);
decorator.set_prev(next, prev);
}
else
{
CGAL_assertion( hedges_to_stitch.find(h1->prev())!=hedges_to_stitch.end() );
}
//we are going to remove h2 and its opposite
//set face-halfedge relationship
decorator.set_face(h1,h2->opposite()->face());
decorator.set_face_halfedge(h1->face(),h1);
//update next/prev pointers
Halfedge_handle tmp=h2->opposite()->prev();
tmp->HBase::set_next(h1);
decorator.set_prev(h1,tmp);
tmp=h2->opposite()->next();
h1->HBase::set_next(tmp);
decorator.set_prev(tmp,h1);
//remove h2
hds.edges_erase(h2);
}
//now update the vertex-halfedge relationship
std::vector<Vertex_handle> vertices_to_delete;
for (std::size_t k=0; k<nb_hedges; ++k)
{
Halfedge_handle h1=hedge_pairs_to_stitch[k].first;
Vertex_handle h1_tgt=h1->vertex();
Vertex_handle h2_src=h1->next()->opposite()->vertex();
//update vertex pointers: target of h1 vs source of h2
if ( h1_tgt != h2_src )
{
CGAL_assertion( h1_tgt->point() == h2_src->point() );
//we remove h2->opposite()->vertex()
vertices_to_delete.push_back( h2_src );
update_target_vertex(h1, h1_tgt,decorator);
}
else
decorator.set_vertex_halfedge(h1_tgt, h1);
Vertex_handle h1_src=h1->opposite()->vertex();
Vertex_handle h2_tgt=h1->prev()->vertex();
//update vertex pointers: target of h1 vs source of h2
if ( h1_src!= h2_tgt )
{
CGAL_assertion( h1_src->point() == h2_tgt->point() );
//we remove h1->opposite()->vertex()
vertices_to_delete.push_back( h1_src );
update_target_vertex(h1->opposite(), h2_tgt,decorator);
}
else
decorator.set_vertex_halfedge(h1_src, h1->opposite());
}
for(typename std::vector<Vertex_handle>::iterator
itv=vertices_to_delete.begin(),itv_end=vertices_to_delete.end();
itv!=itv_end; ++itv)
{
hds.vertices_erase(*itv);
}
}
};
} //end of namespace Polyhedron_stitching
/// Stitches together border halfedges in a polyhedron.
/// The halfedge to be stitched are provided in `hedge_pairs_to_stitch`.
/// Foreach pair `p` in this vector, p.second and its opposite will be removed
/// from `P`.
/// The vertices that get removed from `P` are selected as follow:
/// The pair of halfedges in `hedge_pairs_to_stitch` are processed linearly.
/// Let `p` be such a pair.
/// If the target of p.first has not been marked for deletion,
/// then the source of p.second is.
/// If the target of p.second has not been marked for deletion,
/// then the source of p.first is.
template <class Polyhedron>
void polyhedron_stitching(
Polyhedron& P,
std::vector <std::pair<typename Polyhedron::Halfedge_handle,
typename Polyhedron::Halfedge_handle> >&
hedge_pairs_to_stitch)
{
Polyhedron_stitching::Naive_border_stitching_modifier<Polyhedron>
modifier(hedge_pairs_to_stitch);
P.delegate(modifier);
}
/// Same as above but the pair of halfedges to be stitched are found
/// using `less_hedge`. Two halfedges `h1` and `h2` are set to be stitched
/// if `less_hedge(h1,h2)=less_hedge(h2,h1)=true`.
/// `LessHedge` is a key comparison function that is used to sort halfedges
template <class Polyhedron, class LessHedge>
void polyhedron_stitching(Polyhedron& P, LessHedge less_hedge)
{
typedef typename Polyhedron::Halfedge_handle Halfedge_handle;
std::vector <std::pair<Halfedge_handle,Halfedge_handle> > hedge_pairs_to_stitch;
Polyhedron_stitching::detect_duplicated_boundary_edges(
P, std::back_inserter(hedge_pairs_to_stitch), less_hedge );
polyhedron_stitching(P, hedge_pairs_to_stitch);
}
/// Same as above using the sorted pair of vertices incidents to the halfedges
/// as comparision
template <class Polyhedron>
void polyhedron_stitching(Polyhedron& P)
{
typedef typename Polyhedron::Halfedge_handle Halfedge_handle;
typedef typename Polyhedron::Vertex::Point_3 Point_3;
Polyhedron_stitching::Less_for_halfedge<Halfedge_handle, Point_3> less_hedge;
polyhedron_stitching(P, less_hedge);
}
} //end of namespace CGAL
#endif //CGAL_POLYHEDRON_STITCHING_H

View File

@ -0,0 +1,40 @@
OFF
20 16 0
1 0 0
0.75 0 0
0.5 0 0
0.25 0 0
0 0 0
0 1 0
0.25 1 0
0.5 1 0
0.75 1 0
1 1 0
1 1 0
0.75 1 0
0.5 1 0
0.25 1 0
0 1 0
0 2 0
0.25 2 0
0.5 2 0
0.75 2 0
1 2 0
3 3 6 2
3 2 6 7
3 5 6 3
3 1 2 7
3 5 3 4
3 1 7 8
3 1 8 0
3 8 9 0
3 13 16 12
3 12 16 17
3 15 16 13
3 11 12 17
3 15 13 14
3 11 17 18
3 11 18 10
3 18 19 10

View File

@ -0,0 +1,32 @@
OFF
20 8 0
1 0 0
0.75 0 0
0.5 0 0
0.25 0 0
0 0 0
0 1 0
0.25 1 0
0.5 1 0
0.75 1 0
1 1 0
1 1 0
0.75 1 0
0.5 1 0
0.25 1 0
0 1 0
0 2 0
0.25 2 0
0.5 2 0
0.75 2 0
1 2 0
4 5 6 3 4
4 3 6 7 2
4 1 2 7 8
4 1 8 9 0
4 15 16 13 14
4 13 16 17 12
4 11 12 17 18
4 11 18 19 10

View File

@ -0,0 +1,37 @@
OFF
17 16 0
1 0 0
0.75 0 0
0.5 0 0
0.25 0 0
0 0 0
0 1 0
0.25 1 0
0.5 1 0
0.75 1 0
1 1 0
1 1 0
0.75 1 0
0 2 0
0.25 2 0
0.5 2 0
0.75 2 0
1 2 0
3 3 6 2
3 2 6 7
3 5 6 3
3 1 2 7
3 5 3 4
3 1 7 8
3 1 8 0
3 8 9 0
3 6 13 7
3 7 13 14
3 12 13 6
3 11 7 14
3 12 6 5
3 11 14 15
3 11 15 10
3 15 16 10

View File

@ -0,0 +1,36 @@
OFF
16 16 0
1 0 0
0.75 0 0
0.5 0 0
0.25 0 0
0 0 0
0 1 0
0.25 1 0
0.5 1 0
0.75 1 0
1 1 0
0.5 1 0
0 2 0
0.25 2 0
0.5 2 0
0.75 2 0
1 2 0
3 3 6 2
3 2 6 7
3 5 6 3
3 1 2 7
3 5 3 4
3 1 7 8
3 1 8 0
3 8 9 0
3 6 12 10
3 10 12 13
3 11 12 6
3 8 10 13
3 11 6 5
3 8 13 14
3 8 14 9
3 14 15 9

View File

@ -0,0 +1,77 @@
OFF
40 32 0
1 0 0
0.75 0 0
0.5 0 0
0.25 0 0
0 0 0
0 1 0
0.25 1 0
0.5 1 0
0.75 1 0
1 1 0
1 1 0
0.75 1 0
0.5 1 0
0.25 1 0
0 1 0
0 2 0
0.25 2 0
0.5 2 0
0.75 2 0
1 2 0
2 0 0
1.75 0 0
1.5 0 0
1.25 0 0
1 0 0
1 1 0
1.25 1 0
1.5 1 0
1.75 1 0
2 1 0
2 1 0
1.75 1 0
1.5 1 0
1.25 1 0
1 1 0
1 2 0
1.25 2 0
1.5 2 0
1.75 2 0
2 2 0
3 3 6 2
3 2 6 7
3 5 6 3
3 1 2 7
3 5 3 4
3 1 7 8
3 1 8 0
3 8 9 0
3 13 16 12
3 12 16 17
3 15 16 13
3 11 12 17
3 15 13 14
3 11 17 18
3 11 18 10
3 18 19 10
3 23 26 22
3 22 26 27
3 25 26 23
3 21 22 27
3 25 23 24
3 21 27 28
3 21 28 20
3 28 29 20
3 33 36 32
3 32 36 37
3 35 36 33
3 31 32 37
3 35 33 34
3 31 37 38
3 31 38 30
3 38 39 30

View File

@ -0,0 +1,39 @@
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/IO/Polyhedron_iostream.h>
#include <CGAL/Polyhedron_stitching.h>
#include <iostream>
#include <fstream>
#include <set>
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef CGAL::Polyhedron_3<K> Polyhedron;
void test(const char* fname)
{
std::cout << "Testing " << fname << "..." << std::flush;
std::ifstream input(fname);
Polyhedron P;
input >> P;
CGAL::polyhedron_stitching(P);
std::ofstream output("output.off");
output << P;
output.close();
assert(P.is_valid(false, 5));
std::cout << "OK\n";
}
int main()
{
test("F17.off");
test("full_border.off");
test("full_border_quads.off");
test("half_border.off");
test("mid_border.off");
test("multiple_incidence.off");
}

View File

@ -376,6 +376,9 @@ if(CGAL_Qt4_FOUND AND QT4_FOUND AND OPENGL_FOUND AND QGLVIEWER_FOUND)
polyhedron_demo_plugin(self_intersection_plugin Polyhedron_demo_self_intersection_plugin)
target_link_libraries(self_intersection_plugin scene_polyhedron_item)
polyhedron_demo_plugin(polyhedron_stitching_plugin Polyhedron_demo_polyhedron_stitching_plugin)
target_link_libraries(polyhedron_stitching_plugin scene_polyhedron_item scene_polylines_item)
polyhedron_demo_plugin(subdivision_methods_plugin Polyhedron_demo_subdivision_methods_plugin)
target_link_libraries(subdivision_methods_plugin scene_polyhedron_item)

View File

@ -0,0 +1,98 @@
#include <QApplication>
#include <QMessageBox>
#include <QMainWindow>
#include "Kernel_type.h"
#include "Polyhedron_type.h"
#include "Scene_polyhedron_item.h"
#include "Scene_polylines_item.h"
#include "Polyhedron_demo_plugin_helper.h"
#include "Polyhedron_demo_plugin_interface.h"
#include <CGAL/Polyhedron_stitching.h>
class Polyhedron_demo_polyhedron_stitching_plugin :
public QObject,
public Polyhedron_demo_plugin_helper
{
Q_OBJECT
Q_INTERFACES(Polyhedron_demo_plugin_interface)
QAction* actionDetectBorders;
QAction* actionStitchBorders;
public:
QList<QAction*> actions() const { return QList<QAction*>() << actionDetectBorders << actionStitchBorders; }
void init(QMainWindow* mainWindow, Scene_interface* scene_interface, Messages_interface* /* m */)
{
actionDetectBorders= new QAction(tr("Detect polyhedron boundaries"), mainWindow);
actionStitchBorders= new QAction(tr("Stitch polyhedron duplicated boundaries"), mainWindow);
actionDetectBorders->setObjectName("actionDetectBorders");
actionStitchBorders->setObjectName("actionStitchBorders");
Polyhedron_demo_plugin_helper::init(mainWindow, scene_interface);
}
bool applicable() const {
return qobject_cast<Scene_polyhedron_item*>(scene->item(scene->mainSelectionIndex()));
}
public slots:
void on_actionDetectBorders_triggered();
void on_actionStitchBorders_triggered();
}; // end Polyhedron_demo_polyhedron_stitching_plugin
void Polyhedron_demo_polyhedron_stitching_plugin::on_actionDetectBorders_triggered()
{
Scene_interface::Item_id index = scene->mainSelectionIndex();
Scene_polyhedron_item* item =
qobject_cast<Scene_polyhedron_item*>(scene->item(index));
if(item)
{
Scene_polylines_item* new_item = new Scene_polylines_item();
Polyhedron* pMesh = item->polyhedron();
pMesh->normalize_border();
for (Polyhedron::Halfedge_iterator
it=pMesh->border_halfedges_begin(), it_end=pMesh->halfedges_end();
it!=it_end; ++it)
{
if (!it->is_border()) continue;
/// \todo build cycles and graph with nodes of valence 2.
new_item->polylines.push_back( Scene_polylines_item::Polyline() );
new_item->polylines.back().push_back( it->opposite()->vertex()->point() );
new_item->polylines.back().push_back( it->vertex()->point() );
}
if (new_item->polylines.empty())
{
delete new_item;
}
else
{
new_item->setName(tr("Boundary of %1").arg(item->name()));
new_item->setColor(Qt::red);
scene->addItem(new_item);
}
}
}
void Polyhedron_demo_polyhedron_stitching_plugin::on_actionStitchBorders_triggered()
{
Scene_interface::Item_id index = scene->mainSelectionIndex();
Scene_polyhedron_item* item =
qobject_cast<Scene_polyhedron_item*>(scene->item(index));
if(item)
{
Polyhedron* pMesh = item->polyhedron();
CGAL::polyhedron_stitching(*pMesh);
scene->itemChanged(item);
}
}
Q_EXPORT_PLUGIN2(Polyhedron_demo_polyhedron_stitching_plugin, Polyhedron_demo_polyhedron_stitching_plugin)
#include "Polyhedron_demo_polyhedron_stitching_plugin.moc"