Merge branch 'Polyhedron_HoleFilling-GF-old' into Polyhedron_HoleFilling-GF

Conflicts:
	Documentation/doc/Documentation/dependencies
	Documentation/doc/Documentation/packages.txt
	Polyhedron/demo/Polyhedron/CMakeLists.txt
	Polyhedron/demo/Polyhedron/Polyhedron_demo_selection_plugin.cpp
	Polyhedron/demo/Polyhedron/Scene.cpp
	Polyhedron/demo/Polyhedron/Scene_polyhedron_item_decorator.h
	Polyhedron/demo/Polyhedron/Scene_polyhedron_selection_item.h
	Solver_interface/include/CGAL/Eigen_solver_traits.h
This commit is contained in:
Sébastien Loriot 2014-08-12 07:11:07 +02:00
commit bbfddeb0d9
39 changed files with 5562 additions and 225 deletions

1
.gitattributes vendored
View File

@ -74,7 +74,6 @@ Scripts/scripts/cgal_create_assertions.sh text eol=lf
*.bat text eol=crlf
*.nsh text eol=crlf
*.vcproj text eol=crlf
/Maintenance/infrastructure/picasso.geometryfactory.com/reference_platforms/*/CMakeCache.txt eol=crlf
# Denote all files that are truly binary and should not be modified.
*.png binary

View File

@ -80,3 +80,5 @@ Surface_mesh_segmentation
Stream_lines_2
Stream_support
Surface_modeling
Hole_filling

View File

@ -103,6 +103,7 @@ h1 {
\package_listing{Jet_fitting_3}
\package_listing{Point_set_processing_3}
\package_listing{Surface_modeling}
\package_listing{Hole_filling}
\section PartSearchStructures Spatial Searching and Sorting

View File

@ -0,0 +1,26 @@
/// \ingroup PkgHoleFillingConcepts
/// \cgalConcept
///
/// @brief Concept describing requirements for calculating weights for edges and vertices.
class FairWeightCalculator
{
public:
/// \name Types
/// @{
/// a model of Polyhedron
typedef Hidden_type Polyhedron;
/// @}
/// \name Operations
/// @{
/// Function computing the edge weight of edge `e`
double w_ij(Polyhedron::Halfedge_handle e, const Polyhedron& polyhedron);
/// Function computing the vertex weight of vertex `v`
double w_i(Polyhedron::Vertex_handle v, const Polyhedron& polyhedron);
/// @}
};

View File

@ -0,0 +1,10 @@
@INCLUDE = ${CGAL_DOC_PACKAGE_DEFAULTS}
PROJECT_NAME = "CGAL ${CGAL_CREATED_VERSION_NUM} - Hole Filling"
INPUT = ${CMAKE_SOURCE_DIR}/Hole_filling/doc/Hole_filling/ \
${CMAKE_SOURCE_DIR}/Hole_filling/include
# custom options for this package
EXTRACT_ALL = false
HIDE_UNDOC_CLASSES = true
WARN_IF_UNDOCUMENTED = false

View File

@ -0,0 +1,78 @@
namespace CGAL {
/*!
\mainpage Hole Filling
\anchor Chapter_HoleFilling
\cgalAutoToc
\author ... Ilker %O. Yaz ...
\image html neptun_head.png
\image latex neptun_head.png
<BR>
\section HoleFillingIntroduction Introduction
This package provides algorithms for filling a hole that is either in a triangulated surface mesh (<B>mesh</B> in the following) or defined by a sequence of points.
The main steps of the algorithm are described in \cgalCite{Lipea2003FillingHoles} and can be summarized as follows.
First, a triangular patch for the hole is generated without introducing any new vertex.
The patch minimizes a quality function for all possible triangular patches.
The quality function first minimizes the worst dihedral angle between patch triangles, then the total surface area as a tiebreaker.
Following the suggestions in \cgalCite{Zou2013AnAlgorithm}, the performance of the algorithm is significantly improved
by narrowing the search space to faces of a 3D Delaunay triangulation of the border vertices, from all possible patches, while searching for the best patch.
Then, the generated patch is refined by creating new vertices to approximate the density of near boundary triangles and flipping edges to obtain a Delaunay-like triangulation.
Using a criteria presented in \cgalCite{Lipea2003FillingHoles},
an edge is only flipped if the opposite edge does not exist in the mesh and if no degenerate triangle is produced.
Finally, the refined region is faired to obtain a tangential continuous and smooth patch.
The fairing step minimizes a linear bi-Laplacian system with boundary constraints \cgalCite{Botsch2008OnLinearVariational}.
The visual results of aforementioned steps can be seen in \cgalFigureRef{Mech_steps}
\cgalFigureBegin{Mech_steps, mech_hole_horz.png}
Results of the main steps of the algorithm. Respectively: the hole, after triangulation, after triangulation and refinement, after triangulation, refinement and fairing.
\cgalFigureEnd
\section HoleFillingAPI API
This package provides four functions for hole filling:
- `triangulate_hole_polyline()` : given a sequence of points defining the hole, triangulates the hole.
- `triangulate_hole()` : given a border halfedge defining the hole on a mesh, triangulates the hole.
- `triangulate_and_refine_hole()` : in addition to `triangulate_hole()` the generated patch is also refined.
- `triangulate_refine_and_fair_hole()` : in addition to `triangulate_and_refine_hole()` the generated patch is also faired.
Note that refinement and fairing functions can be applied to an arbitrary region on a mesh:
- `refine()` : given a set of facets on a mesh, refines the region.
- `fair()` : given a set of vertices on a mesh, fairs the region.
\section Examples Examples
\subsection Example_1 Example: Triangulating a Polyline
\cgalExample{Hole_filling/triangulate_polyline_example.cpp}
\subsection Example_2 Example: Refining a Region on a Mesh
\cgalExample{Hole_filling/refine_polyhedron_example.cpp}
\cgalFigureBegin{Max_refine, max_refine.png}
Result of refine example.
\cgalFigureEnd
\subsection Example_3 Example: Fairing a Region on a Mesh
\cgalExample{Hole_filling/fair_polyhedron_example.cpp}
\cgalFigureBegin{Max_fair, max_fair.png}
Result of fairing example.
\cgalFigureEnd
*/
\cgalFigureBegin{Triangulated_fork, fork.gif}
Holes in fork model are filled with only triangulating.
\cgalFigureEnd
/*!
*/
} /* namespace CGAL */

View File

@ -0,0 +1,27 @@
/// \defgroup PkgHoleFilling Hole Filling Reference
/// \defgroup PkgHoleFillingConcepts Concepts
/// \ingroup PkgHoleFilling
/*!
\addtogroup PkgHoleFilling
\cgalPkgDescriptionBegin{Hole Filling, PkgHoleFillingSummary}
\cgalPkgPicture{hole_filling_ico.png}
\cgalPkgSummaryBegin
\cgalPkgAuthor{Ilker O. Yaz}
\cgalPkgDesc{This package provides functionality to fill holes, refine and fair triangulated regions on surface mesh.}
\cgalPkgManuals{Chapter_HoleFilling,PkgHoleFilling}
\cgalPkgSummaryEnd
\cgalPkgShortInfoBegin
\cgalPkgSince{4.3}
\cgalPkgDependsOn{\ref PkgTriangulation3Summary, Sparse square solver such as those from \ref thirdpartyEigen}
\cgalPkgBib{cgal-tmp}
\cgalPkgLicense{\ref licensesGPL "GPL"}
\cgalPkgDemo{Operations on Polyhedra,polyhedron_3.zip}
\cgalPkgShortInfoEnd
\cgalPkgDescriptionEnd
*/

View File

@ -0,0 +1,7 @@
Manual
Kernel_23
STL_Extension
Algebraic_foundations
Circulator
Stream_support
Polyhedron

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -0,0 +1,173 @@
#define CGAL_SUPERLU_ENABLED
#undef NDEBUG
#define DEBUG_TRACE
#include <CGAL/Hole_filling.h>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/IO/Polyhedron_iostream.h>
#include <boost/tuple/tuple.hpp>
typedef CGAL::Simple_cartesian<double> Kernel;
typedef CGAL::Polyhedron_3<Kernel> Polyhedron;
typedef Polyhedron::Facet_handle Facet_handle;
typedef Polyhedron::Traits::Point_3 Point_3;
//
//void test_triangulate_polyline(Polyhedron& poly) {
// typedef std::vector< std::vector<boost::tuple<int, int, int> > > Triangles_list;
// Triangles_list tris;
// // construct polyline from border
// std::vector<Point_3> polyline;
// for(Polyhedron::Halfedge_iterator it = poly.halfedges_begin(); it != poly.halfedges_end(); ++it){
// if(!it->is_border()) { continue; }
// Polyhedron::Halfedge_around_facet_circulator hf_around_facet = it->facet_begin();
// do {
// polyline.push_back(hf_around_facet->vertex()->point());
// } while(++hf_around_facet != it->facet_begin());
// }
// std::cout << "tri begin" << std::endl;
// for(int i = 0; i < 100; ++i) {
// tris.push_back(std::vector<boost::tuple<int, int, int> >());
// //CGAL::triangulate_hole_polyline(polyline.begin(), polyline.end(),std::back_inserter(tris.back()));
// }
// std::cout << "tri end" << std::endl;
// bool is_all_equal = true;
// for(std::size_t i = 0; i + 1 < tris.size() && is_all_equal; ++i)
// {
// for(std::size_t j = 0; j < tris[0].size(); ++j) {
// if(tris[i][j].get<0>() != tris[i+1][j].get<0>()
// || tris[i][j].get<1>() != tris[i+1][j].get<1>()
// || tris[i][j].get<2>() != tris[i+1][j].get<2>())
// {
// is_all_equal = false;
// std::cout << "---------not equal points---------" << std::endl;
// std::cout << tris[i][j].get<0>() << tris[i][j].get<1>() << tris[i][j].get<2>() << std::endl;
// std::cout << tris[i][j+1].get<0>() << tris[i][j+1].get<1>() << tris[i][j+1].get<2>() << std::endl;
// std::cout << "----------------------------------" << std::endl;
// }
// }
// }
//
// if(!is_all_equal) {
// std::cout << "Error: test_triangulate results are different!" << std::endl;
// }
// else {
// std::cout << "OK: test_triangulate results are the same" << std::endl;
// }
//}
//
//void test_triangulate(Polyhedron& poly) {
// typedef std::vector< std::vector<Point_3> > Triangles_list; // 3 point for each tri
// Triangles_list tris; //hold results
//
// for(int i = 0; i < 100; ++i) {
// Polyhedron tmp = poly;
// for(Polyhedron::Halfedge_iterator it = tmp.halfedges_begin(); it != tmp.halfedges_end(); ++it){
// if(it->is_border()) {
// std::vector<Facet_handle> facets;
// CGAL::triangulate_hole(tmp, it, std::back_inserter(facets));
// tris.push_back(std::vector<Point_3>());
// for(std::vector<Facet_handle>::iterator it = facets.begin(); it != facets.end(); ++it) {
// Polyhedron::Halfedge_around_facet_circulator b = (*it)->facet_begin(), e(b);
// do{
// tris.back().push_back(b->vertex()->point());
// }while(++b != e);
// }
// break;
// }
// }
// }
//
// bool is_all_equal = true;
// for(std::size_t i = 0; i + 1 < tris.size() && is_all_equal; ++i)
// {
// for(std::size_t j = 0; j < tris[0].size(); ++j) {
// if(tris[i][j] != tris[i+1][j]) {
// is_all_equal = false;
// std::cout << "---------not equal points---------" << std::endl;
// std::cout << tris[i][j] << std::endl;
// std::cout << tris[i+1][j] << std::endl;
// std::cout << "----------------------------------" << std::endl;
// }
// }
// }
//
// if(!is_all_equal) {
// std::cout << "Error: test_triangulate results are different!" << std::endl;
// }
// else {
// std::cout << "OK: test_triangulate results are the same" << std::endl;
// }
//}
//void test_triangulate_fair(Polyhedron& poly) {
// typedef std::vector< std::vector<Point_3> > Triangles_list;
// Triangles_list tris; //hold results
//
// for(int i = 0; i < 100; ++i) {
// Polyhedron tmp = poly;
// for(Polyhedron::Halfedge_iterator it = tmp.halfedges_begin(); it != tmp.halfedges_end(); ++it){
// if(it->is_border()) {
// std::vector<Facet_handle> facets;
// CGAL::triangulate_and_refine_hole(tmp, it, std::back_inserter(facets));
// tris.push_back(std::vector<Point_3>());
// for(std::vector<Facet_handle>::iterator it = facets.begin(); it != facets.end(); ++it) {
// Polyhedron::Halfedge_around_facet_circulator b = (*it)->facet_begin(), e(b);
// do{
// tris.back().push_back(b->vertex()->point());
// }while(++b != e);
// }
// break;
// }
// }
// }
//
// bool is_all_equal = true;
// for(std::size_t i = 0; i + 1 < tris.size() && is_all_equal; ++i)
// {
// if(tris[i] != tris[i+1]) { is_all_equal = false; }
// }
//
// if(!is_all_equal) {
// std::cout << "Error: test_triangulate_fair results are different!" << std::endl;
// }
// else {
// std::cout << "OK: test_triangulate_fair results are the same" << std::endl;
// }
//}
#include <boost/function_output_iterator.hpp>
struct Nop_functor {
template<class T>
void operator()(const T& /* t */) const {}
};
typedef boost::function_output_iterator<Nop_functor> Nop_out;
int main() {
Polyhedron poly;
std::ifstream input("data/mech-holes-shark-bug.off");
if ( !input || !(input >> poly) || poly.empty() ) {
std::cerr<< "Cannot open file" << std::endl;
return 1;
}
for(Polyhedron::Halfedge_iterator it = poly.halfedges_begin(); it != poly.halfedges_end(); ++it){
if(it->is_border()) {
//CGAL::triangulate_and_refine_hole(poly, it, Nop_out(), Nop_out(), 14.21);
CGAL::triangulate_refine_and_fair_hole(poly, it, Nop_out(), Nop_out(), 10.0);
break;
}
}
//test_triangulate(poly);
//test_triangulate_polyline(poly);
std::ofstream out("data/out.off");
out << poly;
out.close();
std::cout << "Done!" << std::endl;
std::cin.get();
}

View File

@ -0,0 +1,56 @@
#include <CGAL/Hole_filling.h>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/IO/Polyhedron_iostream.h>
#include <iostream>
#include <fstream>
typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
typedef CGAL::Polyhedron_3<Kernel> Polyhedron;
typedef Polyhedron::Vertex_handle Vertex_handle;
typedef Polyhedron::Vertex_iterator Vertex_iterator;
typedef Polyhedron::Halfedge_around_vertex_circulator Halfedge_around_vertex_circulator;
// extract vertices which are at most k (inclusive) far from vertex v
std::vector<Vertex_handle> extract_k_ring(Vertex_handle v, int k)
{
std::map<Vertex_handle, int> D;
std::vector<Vertex_handle> Q;
Q.push_back(v); D[v] = 0;
std::size_t current_index = 0;
int dist_v;
while( current_index < Q.size() && (dist_v = D[ Q[current_index] ]) < k ) {
v = Q[current_index++];
Halfedge_around_vertex_circulator e(v->vertex_begin()), e_end(e);
do {
Vertex_handle new_v = e->opposite()->vertex();
if(D.insert(std::make_pair(new_v, dist_v + 1)).second) {
Q.push_back(new_v);
}
} while(++e != e_end);
}
return Q;
}
int main() {
Polyhedron poly;
std::ifstream input("data/max.off");
if ( !input || !(input >> poly) || poly.empty() ) {
std::cerr << "Not a valid off file." << std::endl;
return 1;
}
Vertex_iterator v = poly.vertices_begin();
std::advance(v, 8286);
const std::vector<Vertex_handle>& region = extract_k_ring(v, 45);
bool success = CGAL::fair(poly, region.begin(), region.end());
std::cout << "Is fairing successful: " << success << std::endl;
std::ofstream faired_off("data/faired.off");
faired_off << poly;
faired_off.close();
}

View File

@ -0,0 +1,69 @@
#include <CGAL/Hole_filling.h>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/IO/Polyhedron_iostream.h>
#include <iostream>
#include <fstream>
#include <vector>
#include <boost/function_output_iterator.hpp>
struct Nop_functor
{
template<class T>
void operator()(const T & /*t*/) const {}
};
typedef boost::function_output_iterator<Nop_functor> Nop_out;
typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
typedef CGAL::Polyhedron_3<Kernel> Polyhedron;
typedef Polyhedron::Halfedge_iterator Halfedge_iterator;
typedef Polyhedron::Facet_handle Facet_handle;
typedef Polyhedron::Vertex_handle Vertex_handle;
int main() {
Polyhedron poly_1;
std::ifstream input("data/max.off");
if ( !input || !(input >> poly_1) || poly_1.empty() ) {
std::cerr << "Not a valid off file." << std::endl;
return 1;
}
Polyhedron poly_2(poly_1), poly_3(poly_1);
for(Halfedge_iterator h = poly_1.halfedges_begin(); h != poly_1.halfedges_end(); ++h) {
if(h->is_border()) {
std::vector<Facet_handle> patch;
CGAL::triangulate_hole(poly_1, h, back_inserter(patch));
std::cout << "Number of facets in constructed patch: " << patch.size() << std::endl;
}
}
for(Halfedge_iterator h = poly_2.halfedges_begin(); h != poly_2.halfedges_end(); ++h) {
if(h->is_border()) {
std::vector<Facet_handle> patch_facets;
std::vector<Vertex_handle> patch_vertices;
CGAL::triangulate_and_refine_hole(poly_2,
h,
back_inserter(patch_facets),
back_inserter(patch_vertices));
std::cout << "Number of facets in constructed patch: " << patch_facets.size() << std::endl;
std::cout << "Number of vertices in constructed patch: " << patch_vertices.size() << std::endl;
}
}
for(Halfedge_iterator h = poly_3.halfedges_begin(); h != poly_3.halfedges_end(); ++h) {
if(h->is_border()) {
std::vector<Facet_handle> patch_facets;
std::vector<Vertex_handle> patch_vertices;
bool success = CGAL::triangulate_refine_and_fair_hole(poly_3,
h,
back_inserter(patch_facets),
back_inserter(patch_vertices)).get<0>();
std::cout << "Number of facets in constructed patch: " << patch_facets.size() << std::endl;
std::cout << "Number of vertices in constructed patch: " << patch_vertices.size() << std::endl;
std::cout << "Is fairing successful: " << success << std::endl;
}
}
}

View File

@ -0,0 +1,57 @@
#include <CGAL/Hole_filling.h>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/IO/Polyhedron_iostream.h>
#include <CGAL/iterator.h>
#include <iostream>
#include <fstream>
#include <functional>
#include <boost/iterator/transform_iterator.hpp>
typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
typedef CGAL::Polyhedron_3<Kernel> Polyhedron;
typedef Polyhedron::Facet_handle Facet_handle;
typedef Polyhedron::Vertex_handle Vertex_handle;
typedef Polyhedron::Facet Facet;
struct Facet_to_facet_handle
: public std::unary_function<Facet&, Facet_handle>
{
result_type operator()(argument_type f) const
{ return f.halfedge()->facet(); }
};
int main() {
Polyhedron poly_1;
std::ifstream input("data/max.off");
if ( !input || !(input >> poly_1) || poly_1.empty() ) {
std::cerr << "Not a valid off file." << std::endl;
return 1;
}
Polyhedron poly_2 = poly_1;
std::vector<Facet_handle> new_facets;
std::vector<Vertex_handle> new_vertices;
// `facets_begin()` returns `Facet_iterator` which is an iterator over `Facet`,
// what `refine()` requires is an iterator over `Facet_handle`, hence a transformer is used
CGAL::refine(poly_1,
boost::make_transform_iterator(poly_1.facets_begin(), Facet_to_facet_handle()),
boost::make_transform_iterator(poly_1.facets_end() , Facet_to_facet_handle()),
back_inserter(new_facets), back_inserter(new_vertices), 2.0);
std::ofstream poly_1_off("data/poly_1.off");
poly_1_off << poly_1;
poly_1_off.close();
CGAL::refine(poly_2,
boost::make_transform_iterator(poly_2.facets_begin(), Facet_to_facet_handle()),
boost::make_transform_iterator(poly_2.facets_end() , Facet_to_facet_handle()),
CGAL::Emptyset_iterator(), CGAL::Emptyset_iterator(), 3.0);
std::ofstream poly_2_off("data/poly_2.off");
poly_2_off << poly_2;
poly_2_off.close();
}

View File

@ -0,0 +1,54 @@
#include <CGAL/Hole_filling.h>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <vector>
#include <boost/tuple/tuple.hpp>
#include <CGAL/utility.h>
typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
typedef Kernel::Point_3 Point_3;
struct My_triangle {
int v0, v1, v2;
My_triangle(int v0, int v1, int v2) : v0(v0), v1(v1), v2(v2) { }
};
int main() {
std::vector<Point_3> polyline;
polyline.push_back(Point_3( 1.,0.,0.));
polyline.push_back(Point_3( 0.,1.,0.));
polyline.push_back(Point_3(-1.,0.,0.));
polyline.push_back(Point_3( 1.,1.,0.));
// repeating first point (i.e. polyline.push_back(Point_3(1.,0.,0.)) ) is optional
// any type, having Type(int, int, int) constructor available, can be used to hold output triangles
std::vector<boost::tuple<int, int, int> > patch_1;
std::vector<CGAL::Triple<int, int, int> > patch_2;
std::vector<My_triangle> patch_3;
patch_1.reserve(polyline.size() -2); // there will be exactly n-2 triangles in the patch
CGAL::triangulate_hole_polyline(polyline.begin(), polyline.end(), back_inserter(patch_1));
CGAL::triangulate_hole_polyline(polyline.begin(), polyline.end(), back_inserter(patch_2));
CGAL::triangulate_hole_polyline(polyline.begin(), polyline.end(), back_inserter(patch_3));
for(std::size_t i = 0; i < patch_1.size(); ++i) {
std::cout << "Triangle " << i << ": " << patch_1[i].get<0>() << " "
<< patch_1[i].get<1>() << " " << patch_1[i].get<2>() << std::endl;
assert(patch_1[i].get<0>() == patch_2[i].first && patch_2[i].first == patch_3[i].v0);
assert(patch_1[i].get<1>() == patch_2[i].second && patch_2[i].second == patch_3[i].v1);
assert(patch_1[i].get<2>() == patch_2[i].third && patch_2[i].third == patch_3[i].v2);
}
// note that no degenerate triangle is constructed in patch
std::vector<Point_3> polyline_collinear;
polyline_collinear.push_back(Point_3(1.,0.,0.));
polyline_collinear.push_back(Point_3(2.,0.,0.));
polyline_collinear.push_back(Point_3(3.,0.,0.));
polyline_collinear.push_back(Point_3(4.,0.,0.));
std::vector<My_triangle> patch_will_be_empty;
CGAL::triangulate_hole_polyline(polyline_collinear.begin(),
polyline_collinear.end(),
back_inserter(patch_will_be_empty));
assert(patch_will_be_empty.empty());
}

View File

@ -0,0 +1,168 @@
#ifndef CGAL_HOLE_FILLING_H
#define CGAL_HOLE_FILLING_H
// Helper functions which combine triangulate, fair, and refine
#include <CGAL/internal/Hole_filling/Fair_Polyhedron_3.h>
#include <CGAL/internal/Hole_filling/Refine_Polyhedron_3.h>
#include <CGAL/internal/Hole_filling/Triangulate_hole_Polyhedron_3.h>
#include <vector>
#include <boost/tuple/tuple.hpp>
namespace CGAL {
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*!
\ingroup PkgHoleFilling
@brief Function triangulating and refining a hole in a surface mesh.
@tparam Polyhedron a \cgal polyhedron
@tparam FacetOutputIterator iterator holding `Polyhedron::Facet_handle` for patch facets.
@tparam VertexOutputIterator iterator holding `Polyhedron::Vertex_handle` for patch vertices.
@param polyhedron surface mesh which has the hole
@param border_halfedge a border halfedge incident to the hole
@param facet_out iterator over patch facets
@param vertex_out iterator over patch vertices without including the boundary
@param density_control_factor factor for density where larger values cause denser refinements
@return pair of @a facet_out and @a vertex_out
*/
template<
class Polyhedron,
class FacetOutputIterator,
class VertexOutputIterator
>
std::pair<FacetOutputIterator, VertexOutputIterator>
triangulate_and_refine_hole(Polyhedron& polyhedron,
typename Polyhedron::Halfedge_handle border_halfedge,
FacetOutputIterator facet_out,
VertexOutputIterator vertex_out,
double density_control_factor = std::sqrt(2.0),
bool use_delaunay_triangulation = false)
{
std::vector<typename Polyhedron::Facet_handle> patch;
triangulate_hole(polyhedron, border_halfedge, std::back_inserter(patch), use_delaunay_triangulation);
facet_out = std::copy(patch.begin(), patch.end(), facet_out);
return refine(polyhedron, patch.begin(), patch.end(), facet_out, vertex_out, density_control_factor);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*!
\ingroup PkgHoleFilling
@brief Function triangulating, refining and fairing a hole in surface mesh.
@tparam SparseLinearSolver a model of `SparseLinearAlgebraTraitsWithPreFactor_d` and can be omitted if Eigen is defined...(give exact models etc)
@tparam WeightCalculator a model of `FairWeightCalculator` and can be omitted to use default Cotangent weights
@tparam Polyhedron a \cgal polyhedron
@tparam FacetOutputIterator iterator holding `Polyhedron::Facet_handle` for patch facets.
@tparam VertexOutputIterator iterator holding `Polyhedron::Vertex_handle` for patch vertices.
@param polyhedron surface mesh which has the hole
@param border_halfedge a border halfedge incident to the hole
@param facet_out iterator over patch facets
@param vertex_out iterator over patch vertices without including the boundary
@param weight_calculator function object to calculate weights, default to Cotangent weights and can be omitted
@param density_control_factor factor for density where larger values cause denser refinements
@param continuity tangential continuity, default to `FAIRING_C_1` and can be omitted
@return tuple of
- bool: `true` if fairing is successful
- @a facet_out
- @a vertex_out
*/
template<
class SparseLinearSolver,
class WeightCalculator,
class Polyhedron,
class FacetOutputIterator,
class VertexOutputIterator
>
boost::tuple<bool, FacetOutputIterator, VertexOutputIterator>
triangulate_refine_and_fair_hole(Polyhedron& polyhedron,
typename Polyhedron::Halfedge_handle border_halfedge,
FacetOutputIterator facet_out,
VertexOutputIterator vertex_out,
WeightCalculator weight_calculator,
double density_control_factor = std::sqrt(2.0),
bool use_delaunay_triangulation = false,
Fairing_continuity continuity = FAIRING_C_1)
{
std::vector<typename Polyhedron::Vertex_handle> patch;
facet_out = triangulate_and_refine_hole
(polyhedron, border_halfedge, facet_out, std::back_inserter(patch), density_control_factor, use_delaunay_triangulation)
.first;
bool fair_success = fair<SparseLinearSolver>(polyhedron, patch.begin(), patch.end(), weight_calculator, continuity);
vertex_out = std::copy(patch.begin(), patch.end(), vertex_out);
return boost::make_tuple(fair_success, facet_out, vertex_out);
}
//use default SparseLinearSolver
template<
class WeightCalculator,
class Polyhedron,
class FacetOutputIterator,
class VertexOutputIterator
>
boost::tuple<bool, FacetOutputIterator, VertexOutputIterator>
triangulate_refine_and_fair_hole(Polyhedron& polyhedron,
typename Polyhedron::Halfedge_handle border_halfedge,
FacetOutputIterator facet_out,
VertexOutputIterator vertex_out,
WeightCalculator weight_calculator,
double density_control_factor = std::sqrt(2.0),
bool use_delaunay_triangulation = false,
Fairing_continuity continuity = FAIRING_C_1)
{
typedef CGAL::internal::Fair_default_sparse_linear_solver::Solver Sparse_linear_solver;
return triangulate_refine_and_fair_hole<Sparse_linear_solver, WeightCalculator, Polyhedron, FacetOutputIterator, VertexOutputIterator>
(polyhedron, border_halfedge, facet_out, vertex_out, weight_calculator, density_control_factor, use_delaunay_triangulation, continuity);
}
//use default WeightCalculator
template<
class SparseLinearSolver,
class Polyhedron,
class FacetOutputIterator,
class VertexOutputIterator
>
boost::tuple<bool, FacetOutputIterator, VertexOutputIterator>
triangulate_refine_and_fair_hole(Polyhedron& polyhedron,
typename Polyhedron::Halfedge_handle border_halfedge,
FacetOutputIterator facet_out,
VertexOutputIterator vertex_out,
double density_control_factor = std::sqrt(2.0),
bool use_delaunay_triangulation = false,
Fairing_continuity continuity = FAIRING_C_1)
{
typedef CGAL::internal::Cotangent_weight_with_voronoi_area_fairing<Polyhedron> Weight_calculator;
return triangulate_refine_and_fair_hole<SparseLinearSolver, Weight_calculator, Polyhedron, FacetOutputIterator, VertexOutputIterator>
(polyhedron, border_halfedge, facet_out, vertex_out, Weight_calculator(), density_control_factor, use_delaunay_triangulation, continuity);
}
//use default SparseLinearSolver and WeightCalculator
template<
class Polyhedron,
class FacetOutputIterator,
class VertexOutputIterator
>
boost::tuple<bool, FacetOutputIterator, VertexOutputIterator>
triangulate_refine_and_fair_hole(Polyhedron& polyhedron,
typename Polyhedron::Halfedge_handle border_halfedge,
FacetOutputIterator facet_out,
VertexOutputIterator vertex_out,
double density_control_factor = std::sqrt(2.0),
bool use_delaunay_triangulation = false,
Fairing_continuity continuity = FAIRING_C_1)
{
typedef CGAL::internal::Fair_default_sparse_linear_solver::Solver Sparse_linear_solver;
return triangulate_refine_and_fair_hole<Sparse_linear_solver, Polyhedron, FacetOutputIterator, VertexOutputIterator>
(polyhedron, border_halfedge, facet_out, vertex_out, density_control_factor, use_delaunay_triangulation, continuity);
}
} // namespace CGAL
#endif

View File

@ -0,0 +1,270 @@
#ifndef CGAL_HOLE_FILLING_FAIR_POLYHEDRON_3_H
#define CGAL_HOLE_FILLING_FAIR_POLYHEDRON_3_H
#include <map>
#include <set>
#include <CGAL/assertions.h>
#include <CGAL/internal/Hole_filling/Weights.h>
#include <CGAL/Timer.h>
#include <CGAL/trace.h>
// for default parameters
#if defined(CGAL_EIGEN3_ENABLED)
#include <CGAL/Eigen_solver_traits.h> // for sparse linear system solver
#endif
namespace CGAL {
/*!
\ingroup PkgHoleFilling
@brief Fairing continuity type
*/
enum Fairing_continuity
{
FAIRING_C_0 = 0, /**< C0 continuity */
FAIRING_C_1 = 1, /**< C1 continuity */
FAIRING_C_2 = 2 /**< C2 continuity */
};
namespace internal {
struct Fair_default_sparse_linear_solver {
typedef
#if defined(CGAL_EIGEN3_ENABLED)
#if defined(CGAL_SUPERLU_ENABLED)
CGAL::Eigen_solver_traits<Eigen::SuperLU<CGAL::Eigen_sparse_matrix<double>::EigenType> >
#else
#if EIGEN_VERSION_AT_LEAST(3,2,0)
CGAL::Eigen_solver_traits<
Eigen::SparseLU<
CGAL::Eigen_sparse_matrix<double>::EigenType,
Eigen::COLAMDOrdering<int> > >
#else
Fair_default_sparse_linear_solver // dummy type to make it compile
#endif
#endif
#else
Fair_default_sparse_linear_solver // dummy type to make it compile
#endif
Solver;
};
// [On Linear Variational Surface Deformation Methods-2008]
template<class Polyhedron, class SparseLinearSolver, class WeightCalculator>
class Fair_Polyhedron_3 {
// typedefs
typedef typename Polyhedron::Traits::Point_3 Point_3;
typedef typename Polyhedron::Vertex_handle Vertex_handle;
typedef typename Polyhedron::Halfedge_handle Halfedge_handle;
typedef typename Polyhedron::Halfedge_around_vertex_circulator Halfedge_around_vertex_circulator;
typedef SparseLinearSolver Sparse_linear_solver;
// members
WeightCalculator weight_calculator;
public:
Fair_Polyhedron_3(WeightCalculator weight_calculator = WeightCalculator())
: weight_calculator(weight_calculator) { }
private:
double sum_weight(Vertex_handle v, Polyhedron& polyhedron) {
double weight = 0;
Halfedge_around_vertex_circulator circ = v->vertex_begin();
do {
weight += weight_calculator.w_ij(circ, polyhedron);
} while(++circ != v->vertex_begin());
return weight;
}
// recursively computes a row (use depth parameter to compute L, L^2, L^3)
// Equation 6 in [On Linear Variational Surface Deformation Methods]
void compute_row(
Vertex_handle v,
Polyhedron& polyhedron,
std::size_t row_id, // which row to insert in [ frees stay left-hand side ]
typename Sparse_linear_solver::Matrix& matrix,
double& x, double& y, double& z, // constants transfered to right-hand side
double multiplier,
const std::map<Vertex_handle, std::size_t>& vertex_id_map,
unsigned int depth)
{
if(depth == 0) {
typename std::map<Vertex_handle, std::size_t>::const_iterator vertex_id_it = vertex_id_map.find(v);
if(vertex_id_it != vertex_id_map.end()) {
matrix.add_coef(row_id, vertex_id_it->second, multiplier);
}
else {
x += multiplier * -v->point().x();
y += multiplier * -v->point().y();
z += multiplier * -v->point().z();
}
}
else {
double w_i = weight_calculator.w_i(v, polyhedron);
Halfedge_around_vertex_circulator circ = v->vertex_begin();
do {
double w_i_w_ij = w_i * weight_calculator.w_ij(circ, polyhedron) ;
Vertex_handle nv = circ->opposite()->vertex();
compute_row(nv, polyhedron, row_id, matrix, x, y, z, -w_i_w_ij*multiplier, vertex_id_map, depth-1);
} while(++circ != v->vertex_begin());
double w_i_w_ij_sum = w_i * sum_weight(v, polyhedron);
compute_row(v, polyhedron, row_id, matrix, x, y, z, w_i_w_ij_sum*multiplier, vertex_id_map, depth-1);
}
}
public:
template<class InputIterator>
bool fair(Polyhedron& polyhedron, InputIterator vb, InputIterator ve, Fairing_continuity fc)
{
int depth = static_cast<int>(fc) + 1;
if(depth < 1 || depth > 3) {
CGAL_warning(!"Continuity should be between 0 and 2 inclusively!");
return false;
}
std::set<Vertex_handle> interior_vertices(vb, ve);
if(interior_vertices.empty()) { return true; }
CGAL::Timer timer; timer.start();
const std::size_t nb_vertices = interior_vertices.size();
typename Sparse_linear_solver::Vector X(nb_vertices), Bx(nb_vertices);
typename Sparse_linear_solver::Vector Y(nb_vertices), By(nb_vertices);
typename Sparse_linear_solver::Vector Z(nb_vertices), Bz(nb_vertices);
std::map<Vertex_handle, std::size_t> vertex_id_map;
std::size_t id = 0;
for(typename std::set<Vertex_handle>::iterator it = interior_vertices.begin(); it != interior_vertices.end(); ++it, ++id) {
if( !vertex_id_map.insert(std::make_pair(*it, id)).second ) {
CGAL_warning(!"Duplicate vertex is found!");
return false;
}
}
typename Sparse_linear_solver::Matrix A(nb_vertices);
for(typename std::set<Vertex_handle>::iterator vb = interior_vertices.begin(); vb != interior_vertices.end(); ++vb) {
std::size_t v_id = vertex_id_map[*vb];
compute_row(*vb, polyhedron, v_id, A, Bx[v_id], By[v_id], Bz[v_id], 1, vertex_id_map, depth);
}
CGAL_TRACE_STREAM << "**Timer** System construction: " << timer.time() << std::endl; timer.reset();
// factorize
double D;
Sparse_linear_solver m_solver;
bool prefactor_ok = m_solver.pre_factor(A, D);
if(!prefactor_ok) {
CGAL_warning(!"pre_factor failed!");
return false;
}
CGAL_TRACE_STREAM << "**Timer** System factorization: " << timer.time() << std::endl; timer.reset();
// solve
bool is_all_solved = m_solver.linear_solver(Bx, X) && m_solver.linear_solver(By, Y) && m_solver.linear_solver(Bz, Z);
if(!is_all_solved) {
CGAL_warning(!"linear_solver failed!");
return false;
}
CGAL_TRACE_STREAM << "**Timer** System solver: " << timer.time() << std::endl; timer.reset();
/* This relative error is to large for cases that the results are not good */
/*
double rel_err_x = (A.eigen_object()*X - Bx).norm() / Bx.norm();
double rel_err_y = (A.eigen_object()*Y - By).norm() / By.norm();
double rel_err_z = (A.eigen_object()*Z - Bz).norm() / Bz.norm();
CGAL_TRACE_STREAM << "rel error: " << rel_err_x
<< " " << rel_err_y
<< " " << rel_err_z << std::endl;
*/
// update
id = 0;
for(typename std::set<Vertex_handle>::iterator it = interior_vertices.begin(); it != interior_vertices.end(); ++it, ++id) {
(*it)->point() = Point_3(X[id], Y[id], Z[id]);
}
return true;
}
};
}//namespace internal
/*!
\ingroup PkgHoleFilling
@brief Function fairing a region on surface mesh.
The region denoted by @a vertex_begin and @a vertex_end might contain multiple disconnected components.
Note that the structure is not altered in any way, only positions of the vertices get updated.
Fairing might fail if fixed vertices, which are used as boundary conditions, do not suffice to solve constructed linear system.
The larger @a continuity gets, the more fixed vertices are required.
@tparam SparseLinearSolver a model of SparseLinearAlgebraTraitsWithPreFactor_d. If \ref thirdpartyEigen "Eigen" 3.2 (or greater) is available
and `CGAL_EIGEN3_ENABLED` is defined, then an overload of `Eigen_solver_traits` is provided as default parameter.
@tparam WeightCalculator a model of FairWeightCalculator and can be omitted to use default Cotangent weights
@tparam Polyhedron a %CGAL polyhedron
@tparam InputIterator iterator over input vertices
@param polyhedron surface mesh to be faired
@param vertex_begin first iterator of the range of vertices
@param vertex_end past-the-end iterator of the range of vertices
@param weight_calculator function object to calculate weights, default to Cotangent weights and can be omitted
@param continuity tangential continuity, default to FAIRING_C_1 and can be omitted
@return true if fairing is successful, otherwise no vertex position is changed
@todo accuracy of solvers are not good, for example when there is no boundary condition pre_factor should fail, but it does not.
*/
template<class SparseLinearSolver, class WeightCalculator, class Polyhedron, class InputIterator>
bool fair(Polyhedron& polyhedron,
InputIterator vertex_begin,
InputIterator vertex_end,
WeightCalculator weight_calculator,
Fairing_continuity continuity = FAIRING_C_1
)
{
internal::Fair_Polyhedron_3<Polyhedron, SparseLinearSolver, WeightCalculator> fair_functor(weight_calculator);
return fair_functor.fair(polyhedron, vertex_begin, vertex_end, continuity);
}
//use default SparseLinearSolver
template<class WeightCalculator, class Polyhedron, class InputIterator>
bool fair(Polyhedron& poly,
InputIterator vb,
InputIterator ve,
WeightCalculator weight_calculator,
Fairing_continuity continuity = FAIRING_C_1
)
{
typedef internal::Fair_default_sparse_linear_solver::Solver Sparse_linear_solver;
return fair<Sparse_linear_solver, WeightCalculator, Polyhedron, InputIterator>
(poly, vb, ve, weight_calculator, continuity);
}
//use default WeightCalculator
template<class SparseLinearSolver, class Polyhedron, class InputIterator>
bool fair(Polyhedron& poly,
InputIterator vb,
InputIterator ve,
Fairing_continuity continuity = FAIRING_C_1
)
{
typedef internal::Cotangent_weight_with_voronoi_area_fairing<Polyhedron> Weight_calculator;
return fair<SparseLinearSolver, Weight_calculator, Polyhedron, InputIterator>
(poly, vb, ve, Weight_calculator(), continuity);
}
//use default SparseLinearSolver and WeightCalculator
template<class Polyhedron, class InputIterator>
bool fair(Polyhedron& poly,
InputIterator vb,
InputIterator ve,
Fairing_continuity continuity = FAIRING_C_1
)
{
typedef internal::Fair_default_sparse_linear_solver::Solver Sparse_linear_solver;
return fair<Sparse_linear_solver, Polyhedron, InputIterator>(poly, vb, ve, continuity);
}
}//namespace CGAL
#endif //CGAL_HOLE_FILLING_FAIR_POLYHEDRON_3_H

View File

@ -0,0 +1,310 @@
#ifndef CGAL_HOLE_FILLING_REFINE_POLYHEDRON_3_H
#define CGAL_HOLE_FILLING_REFINE_POLYHEDRON_3_H
#include <cmath>
#include <map>
#include <set>
#include <CGAL/assertions.h>
#include <CGAL/trace.h>
#include <CGAL/Timer.h>
#include <CGAL/squared_distance_3.h>
#include <CGAL/Kernel/global_functions_3.h>
namespace CGAL {
namespace internal {
template<class Polyhedron>
class Refine_Polyhedron_3 {
// typedefs
typedef typename Polyhedron::Traits::Point_3 Point_3;
typedef typename Polyhedron::Vertex_handle Vertex_handle;
typedef typename Polyhedron::Halfedge_handle Halfedge_handle;
typedef typename Polyhedron::Facet_handle Facet_handle;
typedef typename Polyhedron::Halfedge_around_facet_circulator Halfedge_around_facet_circulator;
typedef typename Polyhedron::Halfedge_around_vertex_circulator Halfedge_around_vertex_circulator;
private:
bool flippable(Halfedge_handle h) {
// this check is added so that edge flip does not break manifoldness
// it might happen when there is an edge where flip_edge(h) will be placed (i.e. two edges collide after flip)
Vertex_handle v_tip_0 = h->next()->vertex();
Vertex_handle v_tip_1 = h->opposite()->next()->vertex();
Halfedge_around_vertex_circulator v_cir(v_tip_0->vertex_begin()), v_end(v_cir);
do {
if(v_cir->opposite()->vertex() == v_tip_1) { return false; }
} while(++v_cir != v_end);
// also eliminate collinear triangle generation
if( CGAL::collinear(v_tip_0->point(), v_tip_1->point(), h->vertex()->point()) ||
CGAL::collinear(v_tip_0->point(), v_tip_1->point(), h->opposite()->vertex()->point()) ) {
return false;
}
return true;
}
bool relax(Polyhedron& poly, Halfedge_handle h)
{
const Point_3& p = h->vertex()->point();
const Point_3& q = h->opposite()->vertex()->point();
const Point_3& r = h->next()->vertex()->point();
const Point_3& s = h->opposite()->next()->vertex()->point();
if( (CGAL::ON_UNBOUNDED_SIDE != CGAL::side_of_bounded_sphere(p,q,r,s)) ||
(CGAL::ON_UNBOUNDED_SIDE != CGAL::side_of_bounded_sphere(p,q,s,r)) )
{
if(flippable(h)) {
poly.flip_edge(h);
return true;
}
}
return false;
}
template<class VertexOutputIterator, class FacetOutputIterator>
bool subdivide(Polyhedron& poly,
std::vector<Facet_handle>& facets,
const std::set<Halfedge_handle>& border_edges,
std::map<Vertex_handle, double>& scale_attribute,
VertexOutputIterator& vertex_out,
FacetOutputIterator& facet_out,
double alpha)
{
std::size_t facet_size = facets.size();
for(std::size_t i = 0; i < facet_size; ++i){
CGAL_assertion(facets[i] != Facet_handle());
Halfedge_handle hh = facets[i]->halfedge();
Vertex_handle vi = facets[i]->halfedge()->vertex();
Vertex_handle vj = facets[i]->halfedge()->next()->vertex();
Vertex_handle vk = facets[i]->halfedge()->prev()->vertex();
Point_3 c = CGAL::centroid(vi->point(), vj->point(), vk->point());
double sac = (scale_attribute[vi] + scale_attribute[vj] + scale_attribute[vk])/3.0;
double dist_c_vi = std::sqrt(CGAL::squared_distance(c,vi->point()));
double dist_c_vj = std::sqrt(CGAL::squared_distance(c,vj->point()));
double dist_c_vk = std::sqrt(CGAL::squared_distance(c,vk->point()));
if((alpha * dist_c_vi > sac) &&
(alpha * dist_c_vj > sac) &&
(alpha * dist_c_vk > sac) &&
(alpha * dist_c_vi > scale_attribute[vi]) &&
(alpha * dist_c_vj > scale_attribute[vj]) &&
(alpha * dist_c_vk > scale_attribute[vk])){
Halfedge_handle h = poly.create_center_vertex(facets[i]->halfedge());
h->vertex()->point() = c;
scale_attribute[h->vertex()] = sac;
*vertex_out++ = h->vertex();
// collect 2 new facets for next round
Facet_handle h1 = h->next()->opposite()->face();
Facet_handle h2 = h->opposite()->face();
facets.push_back(h1); facets.push_back(h2);
*facet_out++ = h1; *facet_out++ = h2;
// relax edges of the patching mesh
Halfedge_handle e_ij = h->prev();
Halfedge_handle e_ik = h->opposite()->next();
Halfedge_handle e_jk = h->next()->opposite()->prev();
if(border_edges.find(e_ij) == border_edges.end()){
relax(poly, e_ij);
}
if(border_edges.find(e_ik) == border_edges.end()){
relax(poly, e_ik);
}
if(border_edges.find(e_jk) == border_edges.end()){
relax(poly, e_jk);
}
}
}
return facets.size() != facet_size;
}
bool relax(Polyhedron& poly,
const std::vector<Facet_handle>& facets,
const std::set<Halfedge_handle>& border_edges)
{
int flips = 0;
std::list<Halfedge_handle> interior_edges;
std::set<Halfedge_handle> included_map;
for(typename std::vector<Facet_handle>::const_iterator it = facets.begin(); it!= facets.end(); ++it) {
Halfedge_around_facet_circulator circ = (*it)->facet_begin(), done(circ);
do {
Halfedge_handle h = circ;
if(border_edges.find(h) == border_edges.end()){
// do not remove included_map and use if(&*h < &*oh) { interior_edges.push_back(h) }
// which will change the order of edges from run to run
Halfedge_handle oh = h->opposite();
Halfedge_handle h_rep = (&*h < &*oh) ? h : oh;
if(included_map.insert(h_rep).second) {
interior_edges.push_back(h_rep);
}
}
} while(++circ != done);
}
CGAL_TRACE_STREAM << "Test " << interior_edges.size() << " edges " << std::endl;
//do not just use std::set (included_map) for iteration, the order effects the output (we like to make it deterministic)
for(typename std::list<Halfedge_handle>::iterator it = interior_edges.begin(); it != interior_edges.end();++it) {
if(relax(poly,*it)) {
++flips;
}
}
CGAL_TRACE_STREAM << "|flips| = " << flips << std::endl;
return flips > 0;
}
double average_length(Vertex_handle vh,
const std::set<Facet_handle>& interior_map,
bool accept_internal_facets)
{
const Point_3& vp = vh->point();
Halfedge_around_vertex_circulator circ(vh->vertex_begin()), done(circ);
int deg = 0;
double sum = 0;
do {
Facet_handle f(circ->facet()), f_op(circ->opposite()->facet());
if(!accept_internal_facets) {
if(interior_map.find(f) != interior_map.end() && interior_map.find(f_op) != interior_map.end())
{ continue; } // which means current edge is an interior edge and should not be included in scale attribute calculation
}
const Point_3& vq = circ->opposite()->vertex()->point();
sum += std::sqrt(CGAL::squared_distance(vp, vq));
++deg;
} while(++circ != done);
CGAL_assertion(deg != 0); // this might happen when accept_internal_facets = false but there is
return sum/deg;
}
void calculate_scale_attribute(const std::vector<Facet_handle>& facets,
const std::set<Facet_handle>& interior_map,
std::map<Vertex_handle, double>& scale_attribute,
bool accept_internal_facets)
{
for(typename std::vector<Facet_handle>::const_iterator f_it = facets.begin(); f_it != facets.end(); ++f_it) {
Halfedge_around_facet_circulator circ((*f_it)->facet_begin()), done(circ);
do {
Vertex_handle v = circ->vertex();
std::pair<typename std::map<Vertex_handle, double>::iterator, bool> v_insert
= scale_attribute.insert(std::make_pair(v, 0));
if(!v_insert.second) { continue; } // already calculated
v_insert.first->second = average_length(v, interior_map, accept_internal_facets);
} while(++circ != done);
}
}
bool contain_internal_facets(const std::vector<Facet_handle>& facets,
const std::set<Facet_handle>& interior_map) const
{
for(typename std::vector<Facet_handle>::const_iterator f_it = facets.begin(); f_it != facets.end(); ++f_it) {
Halfedge_around_facet_circulator circ((*f_it)->facet_begin()), done(circ);
do {
Vertex_handle v = circ->vertex();
Halfedge_around_vertex_circulator circ_v(v->vertex_begin()), done_v(circ_v);
bool internal_v = true;
do {
Facet_handle f(circ_v->facet()), f_op(circ_v->opposite()->facet());
if(interior_map.find(f) == interior_map.end() || interior_map.find(f_op) == interior_map.end()) {
internal_v = false;
break;
}
} while(++circ_v != done_v);
if(internal_v) { return true; }
} while(++circ != done);
}
return false;
}
public:
template<class InputIterator, class FacetOutputIterator, class VertexOutputIterator>
void refine(Polyhedron& poly,
InputIterator facet_begin,
InputIterator facet_end,
FacetOutputIterator& facet_out,
VertexOutputIterator& vertex_out,
double alpha)
{
std::vector<Facet_handle> facets(facet_begin, facet_end); // do not use just std::set, the order effects the output (for the same input we want to get same output)
std::set<Facet_handle> interior_map(facet_begin, facet_end);
// store boundary edges - to be used in relax
std::set<Halfedge_handle> border_edges;
for(typename std::vector<Facet_handle>::const_iterator it = facets.begin(); it!= facets.end(); ++it){
Halfedge_around_facet_circulator circ = (*it)->facet_begin(), done(circ);
do {
if(interior_map.find(circ->opposite()->face()) == interior_map.end()) {
border_edges.insert(circ);
}
} while(++circ != done);
}
// check whether there is any need to accept internal facets
bool accept_internal_facets = contain_internal_facets(facets, interior_map);
std::map<Vertex_handle, double> scale_attribute;
calculate_scale_attribute(facets, interior_map, scale_attribute, accept_internal_facets);
CGAL::Timer total_timer; total_timer.start();
for(int i = 0; i < 10; ++i) {
CGAL::Timer timer; timer.start();
bool is_subdivided = subdivide(poly, facets, border_edges, scale_attribute, vertex_out, facet_out, alpha);
CGAL_TRACE_STREAM << "**Timer** subdivide() :" << timer.time() << std::endl; timer.reset();
if(!is_subdivided) { break; }
bool is_relaxed = relax(poly, facets, border_edges);
CGAL_TRACE_STREAM << "**Timer** relax() :" << timer.time() << std::endl;
if(!is_relaxed) { break; }
}
CGAL_TRACE_STREAM << "**Timer** TOTAL: " << total_timer.time() << std::endl;
}
};
}//namespace internal
/*!
\ingroup PkgHoleFilling
@brief Function refining a region on surface mesh
@tparam Polyhedron a %CGAL polyhedron
@tparam InputIterator iterator over input facets
@tparam FacetOutputIterator iterator holding `Polyhedron::Facet_handle` for patch facets
@tparam VertexOutputIterator iterator holding `Polyhedron::Vertex_handle` for patch vertices
@param polyhedron surface mesh to be refined
@param facet_begin first iterator of the range of facets
@param facet_end past-the-end iterator of the range of facets
@param facet_out iterator over newly created facets
@param vertex_out iterator over newly created vertices
@param density_control_factor factor for density where larger values cause denser refinements
@return pair of @a facet_out and @a vertex_out
@todo current algorithm iterates 10 times at most, since (I guess) there is no termination proof.
*/
template<
class Polyhedron,
class InputIterator,
class FacetOutputIterator,
class VertexOutputIterator
>
std::pair<FacetOutputIterator, VertexOutputIterator>
refine(Polyhedron& poly,
InputIterator facet_begin,
InputIterator facet_end,
FacetOutputIterator facet_out,
VertexOutputIterator vertex_out,
double density_control_factor = std::sqrt(2.0))
{
internal::Refine_Polyhedron_3<Polyhedron> refine_functor;
refine_functor.refine
(poly, facet_begin, facet_end, facet_out, vertex_out, density_control_factor);
return std::make_pair(facet_out, vertex_out);
}
}//namespace CGAL
#endif //CGAL_HOLE_FILLING_REFINE_POLYHEDRON_3_H

View File

@ -0,0 +1 @@
Weight.h is copied from surface_modeling branch, and a new weight calculator is added. Merge it accordingly.

View File

@ -0,0 +1,179 @@
#ifndef CGAL_HOLE_FILLING_TRIANGULATE_HOLE_POLYHEDRON_3_H
#define CGAL_HOLE_FILLING_TRIANGULATE_HOLE_POLYHEDRON_3_H
#include <CGAL/internal/Hole_filling/Triangulate_hole_polyline.h>
#include <CGAL/Timer.h>
#include <vector>
namespace CGAL {
namespace internal {
template<class Polyhedron, class OutputIterator>
struct Tracer_polyhedron
{
typedef typename Polyhedron::Halfedge_handle Halfedge_handle;
typedef typename Polyhedron::Facet_handle Facet_handle;
Tracer_polyhedron(OutputIterator out,
Polyhedron& polyhedron,
std::vector<Halfedge_handle>& P)
: out(out), polyhedron(polyhedron), P(P)
{ }
template <class LookupTable>
Halfedge_handle
operator()(const LookupTable& lambda,
int i, int k,
bool last = true)
{
if(i + 1 == k) { return P[i+1]; }
Halfedge_handle h, g;
if(i+2 == k){
if(last)
{ h = polyhedron.fill_hole(P[i+1]); }
else
{ h = polyhedron.add_facet_to_border(P[i+1]->prev(), P[i+2/*k*/]); }
CGAL_assertion(h->facet() != Facet_handle());
*out++ = h->facet();
return h->opposite();
}
else
{
int la = lambda.get(i, k);
h = operator()(lambda, i, la, false);
g = operator()(lambda, la, k, false);
if(last)
{ h = polyhedron.fill_hole(g); }
else
{ h = polyhedron.add_facet_to_border(h->prev(), g); }
CGAL_assertion(h->facet() != Facet_handle());
*out++ = h->facet();
return h->opposite();
}
}
OutputIterator out;
Polyhedron& polyhedron;
std::vector<Halfedge_handle>& P;
};
// This function is used in test cases (since it returns not just OutputIterator but also Weight)
template<class Polyhedron, class OutputIterator>
std::pair<OutputIterator, Weight_min_max_dihedral_and_area>
triangulate_hole_Polyhedron(Polyhedron& polyhedron,
typename Polyhedron::Halfedge_handle border_halfedge,
OutputIterator out,
bool use_delaunay_triangulation = false)
{
typedef typename Polyhedron::Halfedge_around_vertex_circulator Halfedge_around_vertex_circulator;
typedef typename Polyhedron::Halfedge_around_facet_circulator Halfedge_around_facet_circulator;
typedef typename Polyhedron::Vertex_handle Vertex_handle;
typedef typename Polyhedron::Halfedge_handle Halfedge_handle;
typedef typename Polyhedron::Traits::Point_3 Point_3;
typedef typename std::map<Vertex_handle, int>::iterator Vertex_set_it;
std::vector<Point_3> P, Q;
std::vector<Halfedge_handle> P_edges;
std::map<Vertex_handle, int> vertex_set;
int n = 0;
Halfedge_around_facet_circulator circ(border_halfedge), done(circ);
do{
P.push_back(circ->vertex()->point());
Q.push_back(circ->next()->opposite()->next()->vertex()->point());
P_edges.push_back(circ);
if(!vertex_set.insert(std::make_pair(circ->vertex(), n++)).second) {
CGAL_warning(!"Returning no output. Non-manifold vertex is found on boundary!");
return std::make_pair(out, Weight_min_max_dihedral_and_area::NOT_VALID());
}
} while (++circ != done);
// existing_edges contains neighborhood information between boundary vertices
// more precisely if v_i is neighbor to any other vertex than v_(i-1) and v_(i+1),
// this edge is put into existing_edges
std::vector<std::pair<int, int> > existing_edges;
for(Vertex_set_it v_it = vertex_set.begin(); v_it != vertex_set.end(); ++v_it) {
int v_it_id = v_it->second;
int v_it_prev = v_it_id == 0 ? n-1 : v_it_id-1;
int v_it_next = v_it_id == n-1 ? 0 : v_it_id+1;
Halfedge_around_vertex_circulator circ_vertex(v_it->first->vertex_begin()), done_vertex(circ_vertex);
do {
Vertex_set_it v_it_neigh_it = vertex_set.find(circ_vertex->opposite()->vertex());
if(v_it_neigh_it != vertex_set.end()) {
int v_it_neigh_id = v_it_neigh_it->second;
if( v_it_neigh_id != v_it_prev && v_it_neigh_id != v_it_next ) {
if(v_it_id < v_it_neigh_id) // to include one edge just one time
{ existing_edges.push_back(std::make_pair(v_it_id, v_it_neigh_id)); }
}
}
} while(++circ_vertex != done_vertex);
}
CGAL::Timer timer; timer.start();
//#define CGAL_USE_WEIGHT_INCOMPLETE
#ifdef CGAL_USE_WEIGHT_INCOMPLETE
typedef Weight_calculator<Weight_incomplete<Weight_min_max_dihedral_and_area>, Is_valid_existing_edges_and_degenerate_triangle> WC;
#else
typedef Weight_calculator<Weight_min_max_dihedral_and_area, Is_valid_existing_edges_and_degenerate_triangle> WC;
#endif
Is_valid_existing_edges_and_degenerate_triangle iv(existing_edges);
// fill hole using polyline function, with custom tracer for Polyhedron
Tracer_polyhedron<Polyhedron, OutputIterator>
tracer(out, polyhedron, P_edges);
Weight_min_max_dihedral_and_area weight =
internal::triangulate_hole_polyline(P, Q,
tracer, WC(iv), use_delaunay_triangulation)
#ifdef CGAL_USE_WEIGHT_INCOMPLETE
.weight; // get actual weight in Weight_incomplete
#else
;
#endif
CGAL_TRACE_STREAM << "Hole filling: " << timer.time() << " sc." << std::endl; timer.reset();
return std::make_pair(tracer.out, weight);
}
}// namespace internal
/*!
\ingroup PkgHoleFilling
Function triangulating a hole in surface mesh.
The hole should contain no non-manifold vertex. Generated patch is guaranteed to not to break edge manifoldness and contain no degenerate triangle.
If no possible patch is found, @a polyhedron is not altered in any way, and no facet handle is put into @a out.
@tparam Polyhedron a %CGAL polyhedron
@tparam OutputIterator iterator holding `Polyhedron::Facet_handle` for patch facets.
@param polyhedron surface mesh containing the hole
@param border_halfedge a border halfedge incident to the hole
@param output iterator over patch facets.
@param use_delaunay_triangulation
@return @a out
*/
template<class Polyhedron, class OutputIterator>
OutputIterator
triangulate_hole(Polyhedron& polyhedron,
typename Polyhedron::Halfedge_handle border_halfedge,
OutputIterator out,
bool use_delaunay_triangulation = false)
{
CGAL_precondition(border_halfedge->is_border());
return internal::triangulate_hole_Polyhedron
(polyhedron, border_halfedge, out, use_delaunay_triangulation).first;
}
}//namespace CGAL
#endif //CGAL_HOLE_FILLING_TRIANGULATE_HOLE_POLYHEDRON_3_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,458 @@
// Copyright (c) 2011-2013 GeometryFactory
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org); you may redistribute it under
// the terms of the Q Public License version 1.0.
// See the file LICENSE.QPL distributed with CGAL.
//
// 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) : Yin Xu, Andreas Fabri and Ilker O. Yaz
#ifndef CGAL_SURFACE_MODELING_WEIGHTS_H
#define CGAL_SURFACE_MODELING_WEIGHTS_H
/// @cond CGAL_DOCUMENT_INTERNAL
#include <CGAL/boost/graph/graph_traits_Polyhedron_3.h>
#include <CGAL/boost/graph/properties_Polyhedron_3.h>
#include <CGAL/boost/graph/halfedge_graph_traits_Polyhedron_3.h>
#include <CGAL/Kernel/global_functions_3.h>
namespace CGAL {
namespace internal {
/////////////////////////////////////////////////////////////////////////////////////////
// Returns the cotangent value of half angle v0 v1 v2
template<class Polyhedron>
class Cotangent_value
{
public:
typedef typename boost::graph_traits<Polyhedron>::vertex_descriptor vertex_descriptor;
typedef typename Polyhedron::Traits::Vector_3 Vector;
double operator()(vertex_descriptor v0, vertex_descriptor v1, vertex_descriptor v2)
{
Vector vec0 = v1->point() - v2->point();
Vector vec1 = v2->point() - v0->point();
Vector vec2 = v0->point() - v1->point();
double e0_square = vec0.squared_length();
double e1_square = vec1.squared_length();
double e2_square = vec2.squared_length();
double e0 = std::sqrt(e0_square);
double e2 = std::sqrt(e2_square);
double cos_angle = ( e0_square + e2_square - e1_square ) / 2.0 / e0 / e2;
double sin_angle = std::sqrt(1-cos_angle*cos_angle);
return (cos_angle/sin_angle);
}
};
// Returns the cotangent value of half angle v0 v1 v2
// using formula in -[Meyer02] Discrete Differential-Geometry Operators for- page 19
// The potential problem with previous one (Cotangent_value) is that it does not produce symmetric results
// (i.e. for v0, v1, v2 and v2, v1, v0 returned cot weights can be slightly different)
// This one provides stable results.
template<class Polyhedron>
class Cotangent_value_Meyer
{
public:
typedef typename boost::graph_traits<Polyhedron>::vertex_descriptor vertex_descriptor;
typedef typename Polyhedron::Traits::Vector_3 Vector;
double operator()(vertex_descriptor v0, vertex_descriptor v1, vertex_descriptor v2)
{
Vector a = v0->point() - v1->point();
Vector b = v2->point() - v1->point();
double dot_ab = a[0]*b[0] + a[1]*b[1] + a[2]*b[2];
// rewritten for safer fp arithmetic
//double dot_aa = a.squared_length();
//double dot_bb = b.squared_length();
//double divider = std::sqrt( dot_aa * dot_bb - dot_ab * dot_ab );
Vector cross_ab = CGAL::cross_product(a, b);
double divider = std::sqrt(cross_ab*cross_ab);
if(divider == 0 /*|| divider != divider*/)
{
CGAL::collinear(v0->point(), v1->point(), v2->point()) ?
CGAL_warning(!"Infinite Cotangent value with degenerate triangle!") :
CGAL_warning(!"Infinite Cotangent value due to floating point arithmetic!");
return dot_ab > 0 ? (std::numeric_limits<double>::max)() :
-(std::numeric_limits<double>::max)();
}
return dot_ab / divider;
}
};
// Returns the cotangent value of half angle v0 v1 v2 by clamping between [1, 89] degrees
// as suggested by -[Friedel] Unconstrained Spherical Parameterization-
template<class Polyhedron, class CotangentValue = Cotangent_value_Meyer<Polyhedron> >
class Cotangent_value_clamped : CotangentValue
{
public:
typedef typename boost::graph_traits<Polyhedron>::vertex_descriptor vertex_descriptor;
double operator()(vertex_descriptor v0, vertex_descriptor v1, vertex_descriptor v2)
{
const double cot_1 = 57.289962;
const double cot_89 = 0.017455;
double value = CotangentValue::operator()(v0, v1, v2);
return (std::max)(cot_89, (std::min)(value, cot_1));
}
};
template<class Polyhedron, class CotangentValue = Cotangent_value_Meyer<Polyhedron> >
class Cotangent_value_clamped_2 : CotangentValue
{
public:
typedef typename boost::graph_traits<Polyhedron>::vertex_descriptor vertex_descriptor;
double operator()(vertex_descriptor v0, vertex_descriptor v1, vertex_descriptor v2)
{
const double cot_5 = 5.671282;
const double cot_175 = -cot_5;
double value = CotangentValue::operator()(v0, v1, v2);
return (std::max)(cot_175, (std::min)(value, cot_5));
}
};
template<class Polyhedron, class CotangentValue = Cotangent_value_Meyer<Polyhedron> >
class Cotangent_value_minimum_zero : CotangentValue
{
public:
typedef typename boost::graph_traits<Polyhedron>::vertex_descriptor vertex_descriptor;
double operator()(vertex_descriptor v0, vertex_descriptor v1, vertex_descriptor v2)
{
double value = CotangentValue::operator()(v0, v1, v2);
return (std::max)(0.0, value);
}
};
template<class Polyhedron,
class CotangentValue = Cotangent_value_Meyer<Polyhedron> >
class Voronoi_area : CotangentValue
{
public:
typedef typename boost::graph_traits<Polyhedron>::vertex_descriptor vertex_descriptor;
typedef typename boost::graph_traits<Polyhedron>::edge_descriptor edge_descriptor;
typedef typename boost::graph_traits<Polyhedron>::in_edge_iterator in_edge_iterator;
typedef typename Polyhedron::Traits::Point_3 Point;
double operator()(vertex_descriptor v0, Polyhedron& polyhedron) {
//return 1.0;
double voronoi_area = 0.0;
in_edge_iterator e, e_end;
for (boost::tie(e,e_end) = boost::in_edges(v0, polyhedron); e != e_end; e++)
{
if(boost::get(CGAL::edge_is_border, polyhedron, *e)) { continue; }
vertex_descriptor v1 = boost::source(*e, polyhedron);
vertex_descriptor v_op = boost::target(CGAL::next_edge(*e, polyhedron), polyhedron);
const Point& v0_p = v0->point();
const Point& v1_p = v1->point();
const Point& v_op_p = v_op->point();
// (?) check if there is a better way to predicate triangle is obtuse or not
CGAL::Angle angle0 = CGAL::angle(v1_p, v0_p, v_op_p);
CGAL::Angle angle1 = CGAL::angle(v_op_p, v1_p, v0_p);
CGAL::Angle angle_op = CGAL::angle(v0_p, v_op_p, v1_p);
bool obtuse = (angle0 == CGAL::OBTUSE) || (angle1 == CGAL::OBTUSE) || (angle_op == CGAL::OBTUSE);
if(!obtuse) {
double cot_v1 = CotangentValue::operator()(v_op, v1, v0);
double cot_v_op = CotangentValue::operator()(v0, v_op, v1);
double term1 = cot_v1 * (v_op_p - v0_p).squared_length();
double term2 = cot_v_op * (v1_p - v0_p).squared_length();
voronoi_area += (1.0 / 8.0) * (term1 + term2);
}
else {
double area_t = std::sqrt(squared_area(v0_p, v1_p, v_op_p));
if(angle0 == CGAL::OBTUSE) {
voronoi_area += area_t / 2.0;
}
else {
voronoi_area += area_t / 4.0;
}
}
}
CGAL_warning(voronoi_area != 0 && "Zero voronoi area!");
return voronoi_area;
}
};
// Returns the cotangent value of half angle v0 v1 v2 by dividing the triangle area
// as suggested by -[Mullen08] Spectral Conformal Parameterization-
template<class Polyhedron,
class CotangentValue = Cotangent_value_Meyer<Polyhedron> >
class Cotangent_value_area_weighted : CotangentValue
{
public:
typedef typename boost::graph_traits<Polyhedron>::vertex_descriptor vertex_descriptor;
double operator()(vertex_descriptor v0, vertex_descriptor v1, vertex_descriptor v2)
{
return CotangentValue::operator()(v0, v1, v2)
/ std::sqrt(squared_area(v0->point(), v1->point(), v2->point()));
}
};
/////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////// Edge Weight Calculators ///////////////////////////////////
// Cotangent weight calculator
// Cotangent_value: as suggested by -[Sorkine07] ARAP Surface Modeling-
// Cotangent_value_area_weighted: as suggested by -[Mullen08] Spectral Conformal Parameterization-
template<class Polyhedron,
class CotangentValue = Cotangent_value_minimum_zero<Polyhedron> >
class Cotangent_weight : CotangentValue
{
public:
typedef typename boost::graph_traits<Polyhedron>::edge_descriptor edge_descriptor;
typedef typename boost::graph_traits<Polyhedron>::vertex_descriptor vertex_descriptor;
typedef typename Polyhedron::Traits::Vector_3 Vector;
typedef typename Polyhedron::Traits::Point_3 Point;
// Returns the cotangent weight of specified edge_descriptor
// Edge orientation is trivial
double operator()(edge_descriptor e, Polyhedron& polyhedron)
{
vertex_descriptor v0 = boost::target(e, polyhedron);
vertex_descriptor v1 = boost::source(e, polyhedron);
// Only one triangle for border edges
if (boost::get(CGAL::edge_is_border, polyhedron, e) ||
boost::get(CGAL::edge_is_border, polyhedron, CGAL::opposite_edge(e, polyhedron)))
{
edge_descriptor e_cw = CGAL::next_edge_cw(e, polyhedron);
vertex_descriptor v2 = boost::source(e_cw, polyhedron);
if (boost::get(CGAL::edge_is_border, polyhedron, e_cw) ||
boost::get(CGAL::edge_is_border, polyhedron, CGAL::opposite_edge(e_cw, polyhedron)) )
{
edge_descriptor e_ccw = CGAL::next_edge_ccw(e, polyhedron);
v2 = boost::source(e_ccw, polyhedron);
}
return ( CotangentValue::operator()(v0, v2, v1)/2.0 );
}
else
{
edge_descriptor e_cw = CGAL::next_edge_cw(e, polyhedron);
vertex_descriptor v2 = boost::source(e_cw, polyhedron);
edge_descriptor e_ccw = CGAL::next_edge_ccw(e, polyhedron);
vertex_descriptor v3 = boost::source(e_ccw, polyhedron);
return ( CotangentValue::operator()(v0, v2, v1)/2.0 + CotangentValue::operator()(v0, v3, v1)/2.0 );
}
}
};
// Single cotangent from -[Chao10] Simple Geometric Model for Elastic Deformation
template<class Polyhedron,
class CotangentValue = Cotangent_value_Meyer<Polyhedron> >
class Single_cotangent_weight : CotangentValue
{
public:
typedef typename boost::graph_traits<Polyhedron>::edge_descriptor edge_descriptor;
typedef typename boost::graph_traits<Polyhedron>::vertex_descriptor vertex_descriptor;
typedef typename Polyhedron::Traits::Vector_3 Vector;
typedef typename Polyhedron::Traits::Point_3 Point;
// Returns the cotangent of the opposite angle of the edge
// 0 for border edges (which does not have an opposite angle)
double operator()(edge_descriptor e, Polyhedron& polyhedron)
{
if(boost::get(CGAL::edge_is_border, polyhedron, e)) { return 0.0;}
vertex_descriptor v0 = boost::target(e, polyhedron);
vertex_descriptor v1 = boost::source(e, polyhedron);
vertex_descriptor v_op = boost::target(CGAL::next_edge(e, polyhedron), polyhedron);
return CotangentValue::operator()(v0, v_op, v1);
}
};
// Mean value calculator described in -[Floater04] Mean Value Coordinates-
template<class Polyhedron>
class Mean_value_weight
{
public:
typedef typename boost::graph_traits<Polyhedron>::edge_descriptor edge_descriptor;
typedef typename boost::graph_traits<Polyhedron>::vertex_descriptor vertex_descriptor;
typedef typename Polyhedron::Traits::Vector_3 Vector;
typedef typename Polyhedron::Traits::Point_3 Point;
// Returns the mean-value coordinate of specified edge_descriptor
// Returns different value for different edge orientation (which is a normal behaivour according to formula)
double operator()(edge_descriptor e, Polyhedron& polyhedron)
{
vertex_descriptor v0 = boost::target(e, polyhedron);
vertex_descriptor v1 = boost::source(e, polyhedron);
Vector vec = v0->point() - v1->point();
double norm = std::sqrt( vec.squared_length() );
// Only one triangle for border edges
if (boost::get(CGAL::edge_is_border, polyhedron, e) ||
boost::get(CGAL::edge_is_border, polyhedron, CGAL::opposite_edge(e, polyhedron)))
{
edge_descriptor e_cw = CGAL::next_edge_cw(e, polyhedron);
vertex_descriptor v2 = boost::source(e_cw, polyhedron);
if (boost::get(CGAL::edge_is_border, polyhedron, e_cw) ||
boost::get(CGAL::edge_is_border, polyhedron, CGAL::opposite_edge(e_cw, polyhedron)) )
{
edge_descriptor e_ccw = CGAL::next_edge_ccw(e, polyhedron);
v2 = boost::source(e_ccw, polyhedron);
}
return ( half_tan_value_2(v1, v0, v2)/norm);
}
else
{
edge_descriptor e_cw = CGAL::next_edge_cw(e, polyhedron);
vertex_descriptor v2 = boost::source(e_cw, polyhedron);
edge_descriptor e_ccw = CGAL::next_edge_ccw(e, polyhedron);
vertex_descriptor v3 = boost::source(e_ccw, polyhedron);
return ( half_tan_value_2(v1, v0, v2)/norm + half_tan_value_2(v1, v0, v3)/norm);
}
}
private:
// Returns the tangent value of half angle v0_v1_v2/2
double half_tan_value(vertex_descriptor v0, vertex_descriptor v1, vertex_descriptor v2)
{
Vector vec0 = v1->point() - v2->point();
Vector vec1 = v2->point() - v0->point();
Vector vec2 = v0->point() - v1->point();
double e0_square = vec0.squared_length();
double e1_square = vec1.squared_length();
double e2_square = vec2.squared_length();
double e0 = std::sqrt(e0_square);
double e2 = std::sqrt(e2_square);
double cos_angle = ( e0_square + e2_square - e1_square ) / 2.0 / e0 / e2;
cos_angle = (std::max)(-1.0, (std::min)(1.0, cos_angle)); // clamp into [-1, 1]
double angle = acos(cos_angle);
return ( tan(angle/2.0) );
}
// My deviation built on Meyer_02
double half_tan_value_2(vertex_descriptor v0, vertex_descriptor v1, vertex_descriptor v2)
{
Vector a = v0->point() - v1->point();
Vector b = v2->point() - v1->point();
double dot_ab = a[0]*b[0] + a[1]*b[1] + a[2]*b[2];
double dot_aa = a.squared_length();
double dot_bb = b.squared_length();
double dot_aa_bb = dot_aa * dot_bb;
double cos_rep = dot_ab;
double sin_rep = std::sqrt(dot_aa_bb - dot_ab * dot_ab);
double normalizer = std::sqrt(dot_aa_bb); // |a|*|b|
return (normalizer - cos_rep) / sin_rep; // formula from [Floater04] page 4
// tan(Q/2) = (1 - cos(Q)) / sin(Q)
}
};
template< class Polyhedron,
class PrimaryWeight = Cotangent_weight<Polyhedron>,
class SecondaryWeight = Mean_value_weight<Polyhedron> >
class Hybrid_weight : public PrimaryWeight, SecondaryWeight
{
public:
typedef typename boost::graph_traits<Polyhedron>::edge_descriptor edge_descriptor;
double operator()(edge_descriptor e, Polyhedron& polyhedron)
{
double weight = PrimaryWeight::operator()(e, polyhedron);
//if(weight < 0) { std::cout << "Negative weight" << std::endl; }
return (weight >= 0) ? weight : SecondaryWeight::operator()(e, polyhedron);
}
};
// Trivial uniform weights (created for test purposes)
template<class Polyhedron>
class Uniform_weight
{
public:
typedef typename boost::graph_traits<Polyhedron>::edge_descriptor edge_descriptor;
double operator()(edge_descriptor /*e*/, Polyhedron& /*polyhedron*/)
{ return 1.0; }
};
////////////////////////////////////////////////////////////////////////////
// FAIRING //
template<class Polyhedron>
class Scale_dependent_weight_fairing
{
public:
typedef typename boost::graph_traits<Polyhedron>::edge_descriptor edge_descriptor;
typedef typename boost::graph_traits<Polyhedron>::vertex_descriptor vertex_descriptor;
typedef typename Polyhedron::Traits::Vector_3 Vector;
double w_i(vertex_descriptor /*v_i*/, Polyhedron& /*polyhedron*/) { return 1.0; }
double w_ij(edge_descriptor e, Polyhedron& polyhedron)
{
Vector v = boost::target(e, polyhedron)->point() - boost::source(e, polyhedron)->point();
double divider = std::sqrt(v.squared_length());
if(divider == 0.0) {
CGAL_warning(!"Scale dependent weight - zero length edge.");
return (std::numeric_limits<double>::max)();
}
return 1.0 / divider;
}
};
template<class Polyhedron>
class Cotangent_weight_with_voronoi_area_fairing {
public:
typedef typename boost::graph_traits<Polyhedron>::edge_descriptor edge_descriptor;
typedef typename boost::graph_traits<Polyhedron>::vertex_descriptor vertex_descriptor;
double w_i(vertex_descriptor v_i, Polyhedron& polyhedron) {
Voronoi_area<Polyhedron> voronoi_functor;
return 0.5 / voronoi_functor(v_i, polyhedron);
}
double w_ij(edge_descriptor e, Polyhedron& polyhedron) {
Cotangent_weight<Polyhedron, Cotangent_value_Meyer<Polyhedron> > cotangent_functor;
return cotangent_functor(e, polyhedron) * 2.0;
}
};
template<class Polyhedron>
class Uniform_weight_fairing
{
public:
typedef typename boost::graph_traits<Polyhedron>::edge_descriptor edge_descriptor;
typedef typename boost::graph_traits<Polyhedron>::vertex_descriptor vertex_descriptor;
double w_ij(edge_descriptor /*e*/, Polyhedron& /*polyhedron*/) { return 1.0; }
double w_i(vertex_descriptor /*v_i*/, Polyhedron& /*polyhedron*/) { return 1.0; }
};
////////////////////////////////////////////////////////////////////////////
}//namespace internal
/// @endcond
}//namespace CGAL
#endif //CGAL_SURFACE_MODELING_WEIGHTS_H

View File

@ -0,0 +1,216 @@
/************************************************************************
* Currently not useful code pieces, in case there will be any need in the future.
* Also they might not work when they plugged-in due to recent changes.
************************************************************************/
// It can produce a patch from both complete and incomplete lambda
// WARNING: Not working good for all cases
// For holes, this code first close them then erase them.
// However the algorithm might produce holes which are invalid to close (closing them breaks edge manifoldness, so erasing doesn't work)
template<class Polyhedron, class OutputIteratorPatch, class OutputIteratorHole>
struct Tracer_polyhedron_incomplete
{
typedef typename Polyhedron::Halfedge_handle Halfedge_handle;
typedef typename Polyhedron::Facet_handle Facet_handle;
Tracer_polyhedron_incomplete(OutputIteratorPatch out,
OutputIteratorHole out_hole,
Polyhedron& polyhedron,
std::vector<Halfedge_handle>& P)
: out(out), out_hole(out_hole), polyhedron(polyhedron), P(P)
{ }
template <class LookupTable>
void
operator()(const LookupTable& lambda, int i, int k)
{
std::vector<Facet_handle> facets_to_delete;
(*this)(lambda, i, k, facets_to_delete, true);
for(typename std::vector<Facet_handle>::iterator it = facets_to_delete.begin();
it != facets_to_delete.end(); ++it)
{
*out_hole++=(*it)->halfedge(); // each deleted facet corresponds to a new hole
polyhedron.erase_facet((*it)->halfedge());
}
}
private:
template <class LookupTable>
Halfedge_handle
operator()(const LookupTable& lambda,
int i, int k,
std::vector<Facet_handle>& facets_to_delete,
bool last)
{
if(i + 1 == k) { return P[i+1]; }
Halfedge_handle h, g;
if(i+2 == k){
if(last)
{ h = polyhedron.fill_hole(P[i+1]); }
else
{ h = polyhedron.add_facet_to_border(P[i+1]->prev(), P[i+2/*k*/]); }
CGAL_assertion(h->facet() != Facet_handle());
int la = lambda.get(i, k);
if(la == -1) {
facets_to_delete.push_back(h->facet());
}
else {
*out++ = h->facet();
}
return h->opposite();
}
else
{
int la = lambda.get(i, k);
if(la == -1) {
if(last)
{ h = polyhedron.fill_hole(P[i+1]); }
else
{ h = polyhedron.add_facet_to_border(P[i+1]->prev(), P[i+2/*k*/]); }
facets_to_delete.push_back(h->facet());
return h->opposite();
}
else {
h = operator()(lambda, i, la, facets_to_delete, false);
g = operator()(lambda, la, k, facets_to_delete, false);
if(last)
{ h = polyhedron.fill_hole(g); }
else
{ h = polyhedron.add_facet_to_border(h->prev(), g); }
CGAL_assertion(h->facet() != Facet_handle());
*out++ = h->facet();
return h->opposite();
}
}
}
public:
OutputIteratorPatch out;
OutputIteratorHole out_hole;
Polyhedron& polyhedron;
std::vector<Halfedge_handle>& P;
};
// Try closing holes by gathering incomplete patches together (an external approach)
template <typename OutputIteratorValueType, typename InputIterator, typename OutputIterator>
OutputIterator
triangulate_hole_polyline_incomplete(InputIterator pbegin, InputIterator pend,
InputIterator qbegin, InputIterator qend,
OutputIterator out)
{
typedef typename std::iterator_traits<InputIterator>::value_type Point_3;
typedef Weight_incomplete<Weight_min_max_dihedral_and_area> Weight;
typedef Weight_calculator<Weight, Is_valid_degenerate_triangle> WC;
typedef std::vector<boost::tuple<int, int, int> > Facet_vector; /* deliberately not OutputIteratorValueType*/
typedef std::back_insert_iterator<Facet_vector> OutIt;
typedef Tracer_polyline_incomplete<Facet_vector::value_type, OutIt> Tracer;
typedef std::pair<int, int> Range;
std::vector<Point_3> P(pbegin, pend);
std::vector<Point_3> Q(qbegin, qend);
if(P.front() != P.back()){
P.push_back(P.front());
if( !Q.empty() && P.size() > Q.size()) {
Q.push_back(Q.front());
}
}
std::vector<OutputIteratorValueType> patch_facets;
std::stack<Range> remaining_holes;
remaining_holes.push(Range(0, P.size() -2));
while(!remaining_holes.empty()) {
Range h = remaining_holes.top();
remaining_holes.pop();
std::vector<Point_3> P_r(&P[h.first], (&P[h.second]) + 1);
std::vector<Point_3> Q_r;
if(!Q.empty()) { Q_r.insert(Q_r.begin(), &Q[h.first], (&Q[h.second]) + 1); };
Facet_vector new_facets;
OutIt new_facets_out(new_facets);
std::vector<Range> new_holes;
std::back_insert_iterator<std::vector<Range> > new_holes_out(new_holes);
Tracer tracer(new_facets_out, new_holes_out);
triangulate_hole_polyline(P_r, Q_r, tracer, WC(), true, true);
if(new_facets.empty()) {
new_holes.clear();
//triangulate_hole_polyline(P_r, Q_r, tracer, WC(), false, true);
if(new_facets.empty()) {
// if no patch facets created and also we are using brute force approach, then there is nothing to do,
// leave `out` intact and return
CGAL_warning(!"Returning no output. Filling hole with incomplete patches is not successful!");
return out;
}
}
// put new borders to remaining_holes
for(typename std::vector<Range>::iterator it = new_holes.begin(); it != new_holes.end(); ++it) {
remaining_holes.push(std::make_pair(it->first + h.first, it->second + h.first));
}
tracer.remaining_holes.clear();
for(Facet_vector::iterator it = new_facets.begin(); it != new_facets.end(); ++it) {
patch_facets.push_back(
OutputIteratorValueType(it->get<0>() + h.first, it->get<1>() + h.first, it->get<2>() + h.first));
}
}
return std::copy(patch_facets.begin(), patch_facets.end(), out);
}
// (for Polyhedron_3) Try closing holes by gathering incomplete patches together (an external approach)
template<class Polyhedron, class OutputIterator>
std::pair<OutputIterator, Weight_min_max_dihedral_and_area>
triangulate_hole_Polyhedron_incomplete(Polyhedron& polyhedron,
typename Polyhedron::Halfedge_handle border_halfedge,
OutputIterator out)
{
typedef typename Polyhedron::Halfedge_handle Halfedge_handle;
typedef typename Polyhedron::Facet_handle Facet_handle;
Weight_min_max_dihedral_and_area weight_total = Weight_min_max_dihedral_and_area::DEFAULT();
std::vector<Facet_handle> patch_facets;
std::stack<Halfedge_handle> remaining_holes;
remaining_holes.push(border_halfedge);
while(!remaining_holes.empty()) {
Halfedge_handle h = remaining_holes.top();
remaining_holes.pop();
std::vector<Halfedge_handle> holes_h;
std::size_t patch_facets_before = patch_facets.size();
Weight_min_max_dihedral_and_area w =
triangulate_hole_Polyhedron(polyhedron, h, back_inserter(patch_facets), back_inserter(holes_h), true);
if(patch_facets_before == patch_facets.size()) {
holes_h.clear();
patch_facets_before = patch_facets.size();
w = triangulate_hole_Polyhedron(polyhedron, h, back_inserter(patch_facets), back_inserter(holes_h), false);
if(patch_facets_before == patch_facets.size()) {
// if no patch facets created and also we are using brute force approach, then there is nothing to do,
// leave `out` intact and return
CGAL_warning(!"Returning no output. Filling hole with incomplete patches is not successful!");
return std::make_pair(out, Weight_min_max_dihedral_and_area::NOT_VALID());
}
}
// put new borders to remaining_holes and update weight
for(typename std::vector<Halfedge_handle>::iterator it = holes_h.begin(); it != holes_h.end(); ++it) {
//remaining_holes.push(*it);
}
weight_total = weight_total + w;
}
out = std::copy(patch_facets.begin(), patch_facets.end(), out);
return std::make_pair(out, weight_total);
}

View File

@ -0,0 +1 @@
GeometryFactory (France)

View File

@ -0,0 +1 @@
GPL (v3 or later)

View File

@ -0,0 +1 @@
Andreas Fabri <Andreas.Fabri@geometryfactory.com>

View File

@ -0,0 +1,330 @@
#include <CGAL/Hole_filling.h>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/IO/Polyhedron_iostream.h>
#include <CGAL/assertions.h>
#include <cassert>
#include <vector>
#include <set>
typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
typedef CGAL::Polyhedron_3<Kernel> Polyhedron;
typedef Polyhedron::Facet_handle Facet_handle;
typedef Polyhedron::Vertex_handle Vertex_handle;
typedef Polyhedron::Halfedge_handle Halfedge_handle;
typedef Polyhedron::Halfedge_iterator Halfedge_iterator;
typedef Polyhedron::Halfedge_around_facet_circulator Halfedge_around_facet_circulator;
void read_poly_with_borders(const char* file_name, Polyhedron& poly, std::vector<Halfedge_handle>& border_reps) {
border_reps.clear();
poly.clear();
std::ifstream input(file_name);
if ( !input || !(input >> poly) || poly.empty() ){
std::cerr << " Error: can not read file." << std::endl;
assert(false);
}
std::set<Halfedge_handle> border_map;
for(Halfedge_iterator it = poly.halfedges_begin(); it != poly.halfedges_end(); ++it){
if(it->is_border() && border_map.find(it) == border_map.end()){
border_reps.push_back(it);
Halfedge_around_facet_circulator hf_around_facet = it->facet_begin();
do {
bool insertion_ok = border_map.insert(hf_around_facet).second;
CGAL_assertion(insertion_ok);
} while(++hf_around_facet != it->facet_begin());
}
}
}
/******************************************************************/
// This part test internal functions with Weight_min_max_dihedral_and_area
template<class Iterator>
CGAL::internal::Weight_min_max_dihedral_and_area
calculate_weight_for_patch(Iterator begin, Iterator end)
{
typedef Polyhedron::Traits::Point_3 Point_3;
typedef Polyhedron::Halfedge_handle Halfedge_handle;
typedef CGAL::internal::Weight_min_max_dihedral_and_area Weight;
Weight res(0,0);
for(; begin!=end; ++begin) {
Halfedge_handle edge_it = (*begin)->halfedge();
double ang_max = 0;
for(int i = 0; i < 3; ++i) {
double angle = 180 - CGAL::abs(
CGAL::Mesh_3::dihedral_angle(edge_it->vertex()->point(),
edge_it->opposite()->vertex()->point(),
edge_it->next()->vertex()->point(),
edge_it->opposite()->next()->vertex()->point()) );
edge_it = edge_it->next();
ang_max = (std::max)(angle, ang_max);
}
double area = std::sqrt(CGAL::squared_area(
edge_it->vertex()->point(),
edge_it->next()->vertex()->point(),
edge_it->prev()->vertex()->point()));
res = res + Weight(ang_max,area);
}
return res;
}
void test_triangulate_hole_weight(const char* file_name, bool use_DT) {
typedef CGAL::internal::Weight_min_max_dihedral_and_area Weight;
std::cerr << "test_triangulate_hole_weight + useDT: " << use_DT << std::endl;
std::cerr << " File: "<< file_name << std::endl;
Polyhedron poly;
std::vector<Halfedge_iterator> border_reps;
read_poly_with_borders(file_name, poly, border_reps);
for(std::vector<Halfedge_iterator>::iterator it = border_reps.begin(); it != border_reps.end(); ++it) {
std::vector<Facet_handle> patch;
Weight w_algo = CGAL::internal::triangulate_hole_Polyhedron(poly, *it, back_inserter(patch), use_DT).second;
if(patch.empty()) { continue; }
Weight w_test = calculate_weight_for_patch(patch.begin(), patch.end());
std::cerr << " Weight returned by algo : " << w_algo << std::endl;
std::cerr << " Weight calculated by test : " << w_test << std::endl;
const double epsilon = 1e-10;
if(std::abs(w_algo.w.first - w_test.w.first) > epsilon) {
assert(false);
}
if(std::abs(w_algo.w.second - w_test.w.second) > epsilon) {
assert(false);
}
}
std::cerr << " Done!" << std::endl;
}
/******************************************************************/
void test_triangulate_hole(const char* file_name) {
std::cerr << "test_triangulate_hole:" << std::endl;
std::cerr << " File: "<< file_name << std::endl;
Polyhedron poly;
std::vector<Halfedge_iterator> border_reps;
read_poly_with_borders(file_name, poly, border_reps);
for(std::vector<Halfedge_iterator>::iterator it = border_reps.begin(); it != border_reps.end(); ++it) {
std::vector<Facet_handle> patch;
CGAL::triangulate_hole(poly, *it, back_inserter(patch));
if(patch.empty()) {
std::cerr << " Error: empty patch created." << std::endl;
assert(false);
}
}
if(!poly.is_valid() || !poly.is_closed()) {
std::cerr << " Error: patched polyhedron is not valid or closed." << std::endl;
assert(false);
}
std::cerr << " Done!" << std::endl;
}
void test_triangulate_hole_should_be_no_output(const char* file_name) {
std::cerr << "test_triangulate_hole_should_be_no_output:" << std::endl;
std::cerr << " File: "<< file_name << std::endl;
Polyhedron poly;
std::vector<Halfedge_iterator> border_reps;
read_poly_with_borders(file_name, poly, border_reps);
for(std::vector<Halfedge_iterator>::iterator it = border_reps.begin(); it != border_reps.end(); ++it) {
std::vector<Facet_handle> patch;
CGAL::triangulate_hole(poly, *it, back_inserter(patch), false);
if(!patch.empty()) {
std::cerr << " Error: patch should be empty" << std::endl;
assert(false);
}
CGAL::triangulate_hole(poly, *it, back_inserter(patch), true);
if(!patch.empty()) {
std::cerr << " Error: patch should be empty" << std::endl;
assert(false);
}
}
std::cerr << " Done!" << std::endl;
}
void test_triangulate_and_refine_hole(const char* file_name) {
std::cerr << "test_triangulate_and_refine_hole:" << std::endl;
std::cerr << " File: "<< file_name << std::endl;
Polyhedron poly;
std::vector<Halfedge_iterator> border_reps;
read_poly_with_borders(file_name, poly, border_reps);
for(std::vector<Halfedge_iterator>::iterator it = border_reps.begin(); it != border_reps.end(); ++it) {
std::vector<Facet_handle> patch_facets;
std::vector<Vertex_handle> patch_vertices;
CGAL::triangulate_and_refine_hole(poly, *it,
back_inserter(patch_facets), back_inserter(patch_vertices));
if(patch_facets.empty()) {
std::cerr << " Error: empty patch created." << std::endl;
assert(false);
}
}
if(!poly.is_valid() || !poly.is_closed()) {
std::cerr << " Error: patched polyhedron is not valid or closed." << std::endl;
assert(false);
}
std::cerr << " Done!" << std::endl;
}
void test_triangulate_refine_and_fair_hole(const char* file_name) {
std::cerr << "test_triangulate_refine_and_fair_hole:" << std::endl;
std::cerr << " File: "<< file_name << std::endl;
Polyhedron poly;
std::vector<Halfedge_iterator> border_reps;
read_poly_with_borders(file_name, poly, border_reps);
for(std::vector<Halfedge_iterator>::iterator it = border_reps.begin(); it != border_reps.end(); ++it) {
std::vector<Facet_handle> patch_facets;
std::vector<Vertex_handle> patch_vertices;
CGAL::triangulate_refine_and_fair_hole(poly, *it, back_inserter(patch_facets), back_inserter(patch_vertices));
if(patch_facets.empty()) {
std::cerr << " Error: empty patch created." << std::endl;
assert(false);
}
}
if(!poly.is_valid() || !poly.is_closed()) {
std::cerr << " Error: patched polyhedron is not valid or closed." << std::endl;
assert(false);
}
std::cerr << " Done!" << std::endl;
}
void test_ouput_iterators_triangulate_hole(const char* file_name) {
std::cerr << "test_ouput_iterators_triangulate_hole:" << std::endl;
std::cerr << " File: "<< file_name << std::endl;
Polyhedron poly, poly_2;
std::vector<Halfedge_iterator> border_reps, border_reps_2;
read_poly_with_borders(file_name, poly, border_reps);
read_poly_with_borders(file_name, poly_2, border_reps_2);
std::vector<Halfedge_iterator>::iterator it_2 = border_reps_2.begin();
for(std::vector<Halfedge_iterator>::iterator it = border_reps.begin(); it != border_reps.end(); ++it, ++it_2) {
std::vector<Facet_handle> patch;
CGAL::triangulate_hole(poly, *it, back_inserter(patch));
std::vector<Facet_handle> patch_2 = patch;
Facet_handle* output_it = CGAL::triangulate_hole(poly_2, *it_2, &*patch_2.begin());
if(patch.size() != (output_it - &*patch_2.begin())) {
std::cerr << " Error: returned facet output iterator is not valid!" << std::endl;
std::cerr << " " << patch.size() << " vs " << (output_it - &*patch_2.begin()) << std::endl;
assert(false);
}
}
std::cerr << " Done!" << std::endl;
}
void test_ouput_iterators_triangulate_and_refine_hole(const char* file_name) {
std::cerr << "test_ouput_iterators_triangulate_and_refine_hole:" << std::endl;
std::cerr << " File: "<< file_name << std::endl;
Polyhedron poly, poly_2;
std::vector<Halfedge_iterator> border_reps, border_reps_2;;
read_poly_with_borders(file_name, poly, border_reps);
read_poly_with_borders(file_name, poly_2, border_reps_2);
std::vector<Halfedge_iterator>::iterator it_2 = border_reps_2.begin();
for(std::vector<Halfedge_iterator>::iterator it = border_reps.begin(); it != border_reps.end(); ++it, ++it_2) {
std::vector<Facet_handle> patch_facets;
std::vector<Vertex_handle> patch_vertices;
CGAL::triangulate_and_refine_hole(poly, *it, back_inserter(patch_facets), back_inserter(patch_vertices));
// create enough space to hold outputs
std::vector<Facet_handle> patch_facets_2 = patch_facets;
std::vector<Vertex_handle> patch_vertices_2 = patch_vertices;
if(patch_vertices_2.empty()) { patch_vertices_2.push_back(Vertex_handle()); } //just allocate space for dereferencing
std::pair<Facet_handle*, Vertex_handle*> output_its =
CGAL::triangulate_and_refine_hole(poly_2, *it_2, &*patch_facets_2.begin(), &*patch_vertices_2.begin());
if(patch_facets.size() != (output_its.first - &*patch_facets_2.begin())) {
std::cerr << " Error: returned facet output iterator is not valid!" << std::endl;
std::cerr << " " << patch_facets.size() << " vs " << (output_its.first - &*patch_facets_2.begin()) << std::endl;
assert(false);
}
if(patch_vertices.size() != (output_its.second - &*patch_vertices_2.begin())) {
std::cerr << " Error: returned vertex output iterator is not valid!" << std::endl;
std::cerr << " " << patch_vertices.size() << " vs " << (output_its.second - &*patch_vertices_2.begin()) << std::endl;
assert(false);
}
}
std::cerr << " Done!" << std::endl;
}
void test_triangulate_refine_and_fair_hole_compile() {
typedef CGAL::Eigen_solver_traits<
Eigen::SparseLU<
CGAL::Eigen_sparse_matrix<double>::EigenType,
Eigen::COLAMDOrdering<int> > >
Default_solver;
Polyhedron poly;
std::vector<Halfedge_iterator> border_reps;
std::vector<Facet_handle> patch_facets;
std::vector<Vertex_handle> patch_vertices;
// use all param
read_poly_with_borders("data/elephant_quad_hole.off", poly, border_reps);
CGAL::triangulate_refine_and_fair_hole<Default_solver>
(poly, border_reps[0], back_inserter(patch_facets), back_inserter(patch_vertices),
CGAL::internal::Uniform_weight_fairing<Polyhedron>());
// default weight
read_poly_with_borders("data/elephant_quad_hole.off", poly, border_reps);
CGAL::triangulate_refine_and_fair_hole<Default_solver>
(poly, border_reps[0], back_inserter(patch_facets), back_inserter(patch_vertices));
// default solver
read_poly_with_borders("data/elephant_quad_hole.off", poly, border_reps);
CGAL::triangulate_refine_and_fair_hole
(poly, border_reps[0], back_inserter(patch_facets), back_inserter(patch_vertices),
CGAL::internal::Uniform_weight_fairing<Polyhedron>());
// default solver and weight
read_poly_with_borders("data/elephant_quad_hole.off", poly, border_reps);
CGAL::triangulate_refine_and_fair_hole<Default_solver>
(poly, border_reps[0], back_inserter(patch_facets), back_inserter(patch_vertices));
}
int main() {
std::vector<std::string> input_files;
input_files.push_back("data/elephant_triangle_hole.off");
input_files.push_back("data/elephant_quad_hole.off");
input_files.push_back("data/mech-holes-shark.off");
// std::cerr.precision(15);
for(std::vector<std::string>::iterator it = input_files.begin(); it != input_files.end(); ++it) {
test_triangulate_hole(it->c_str());
test_triangulate_and_refine_hole(it->c_str());
test_triangulate_refine_and_fair_hole(it->c_str());
test_ouput_iterators_triangulate_and_refine_hole(it->c_str());
test_ouput_iterators_triangulate_hole(it->c_str());
test_triangulate_hole_weight(it->c_str(), true);
test_triangulate_hole_weight(it->c_str(), false);
std::cerr << "------------------------------------------------" << std::endl;
}
test_triangulate_hole_weight("data/RedCircleBox.off", true);
test_triangulate_hole_weight("data/RedCircleBox.off", false);
test_triangulate_hole_should_be_no_output("data/non_manifold_vertex.off");
test_triangulate_hole_should_be_no_output("data/two_tris_collinear.off");
test_triangulate_refine_and_fair_hole_compile();
std::cerr << "All Done!" << std::endl;
}

View File

@ -0,0 +1,205 @@
#include <CGAL/Hole_filling.h>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <cassert>
#include <vector>
#include <boost/tuple/tuple.hpp>
#include <CGAL/Polyhedron_incremental_builder_3.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/IO/Polyhedron_iostream.h>
typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
typedef Kernel::Point_3 Point_3;
typedef CGAL::Polyhedron_3<Kernel> Polyhedron;
// to debug visually, construct polyhedron from patch
template<class HDS>
class Polyhedron_builder : public CGAL::Modifier_base<HDS> {
public:
Polyhedron_builder(std::vector<boost::tuple<int, int, int> >* triangles,
std::vector<Point_3>* polyline)
: triangles(triangles), polyline(polyline)
{ }
void operator()(HDS& hds) {
CGAL::Polyhedron_incremental_builder_3<HDS> B(hds, true);
B.begin_surface(polyline->size() -1, triangles->size());
for(std::vector<Point_3>::iterator it = polyline->begin();
it != --polyline->end(); ++it) {
B.add_vertex(*it);
}
for(std::vector<boost::tuple<int, int, int> >::iterator it = triangles->begin();
it != triangles->end(); ++it) {
B.begin_facet();
B.add_vertex_to_facet(it->get<0>());
B.add_vertex_to_facet(it->get<1>());
B.add_vertex_to_facet(it->get<2>());
B.end_facet();
}
B.end_surface();
}
private:
std::vector<boost::tuple<int, int, int> >* triangles;
std::vector<Point_3>* polyline;
};
// it reads .polylines.txt (there should be one polyline with last point repeated)
void read_polyline_one_line(const char* file_name, std::vector<Point_3>& points) {
std::ifstream stream(file_name);
if(!stream) { assert(false); }
int count;
if(!(stream >> count)) { assert(false); }
while(count-- > 0) {
Point_3 p;
if(!(stream >> p)) { assert(false); }
points.push_back(p);
}
}
// last point should be repeated
void read_polyline_with_extra_points(
const char* file_name,
std::vector<Point_3>& points,
std::vector<Point_3>& extras)
{
std::ifstream stream(file_name);
if(!stream) { assert(false); }
for(int i =0; i < 2; ++i) {
int count;
if(!(stream >> count)) { assert(false); }
while(count-- > 0) {
Point_3 p;
if(!(stream >> p)) { assert(false); }
i == 0 ? points.push_back(p) : extras.push_back(p);
}
}
}
void check_triangles(std::vector<Point_3>& points, std::vector<boost::tuple<int, int, int> >& tris) {
if(points.size() - 3 != tris.size()) {
std::cerr << " Error: there should be n-2 triangles in generated patch." << std::endl;
assert(false);
}
const int max_index = points.size()-1;
for(std::vector<boost::tuple<int, int, int> >::iterator it = tris.begin(); it != tris.end(); ++it) {
if(it->get<0>() == it->get<1>() ||
it->get<0>() == it->get<2>() ||
it->get<1>() == it->get<2>() )
{
std::cerr << "Error: indices of triangles should be all different." << std::endl;
assert(false);
}
if(it->get<0>() >= max_index ||
it->get<1>() >= max_index ||
it->get<2>() >= max_index )
{
std::cerr << " Error: max possible index check failed." << std::endl;
assert(false);
}
}
}
void check_constructed_polyhedron(const char* file_name,
std::vector<boost::tuple<int, int, int> >* triangles,
std::vector<Point_3>* polyline)
{
Polyhedron poly;
Polyhedron_builder<Polyhedron::HalfedgeDS> patch_builder(triangles, polyline);
poly.delegate(patch_builder);
if(!poly.is_valid()) {
std::cerr << " Error: constructed patch does not constitute a valid polyhedron." << std::endl;
assert(false);
}
std::string out_file_name;
out_file_name.append(file_name).append(".off");
std::ofstream out(out_file_name);
out << poly; out.close();
}
void test_1(const char* file_name, bool use_DT) {
std::cerr << "test_1 + useDT: " << use_DT << std::endl;
std::cerr << " File: "<< file_name << std::endl;
std::vector<Point_3> points; // this will contain n and +1 repeated point
read_polyline_one_line(file_name, points);
std::vector<boost::tuple<int, int, int> > tris;
CGAL::triangulate_hole_polyline(points.begin(), --points.end(), std::back_inserter(tris), use_DT);
check_triangles(points, tris);
check_constructed_polyhedron(file_name, &tris, &points);
std::cerr << " Done!" << std::endl;
}
void test_2(const char* file_name, bool use_DT) {
std::cerr << "test_2 + useDT: " << use_DT << std::endl;
std::cerr << " File: "<< file_name << std::endl;
std::vector<Point_3> points; // this will contain n and +1 repeated point
std::vector<Point_3> extras;
read_polyline_with_extra_points(file_name, points, extras);
std::vector<boost::tuple<int, int, int> > tris;
CGAL::triangulate_hole_polyline(points.begin(), points.end(),
extras.begin(), extras.end(), std::back_inserter(tris), use_DT);
check_triangles(points, tris);
check_constructed_polyhedron(file_name, &tris, &points);
std::cerr << " Done!" << std::endl;
}
void test_should_be_no_output(const char* file_name, bool use_DT) {
std::cerr << "test_should_be_no_output + useDT: " <<use_DT<< std::endl;
std::cerr << " File: "<< file_name << std::endl;
std::vector<Point_3> points; // this will contain n and +1 repeated point
read_polyline_one_line(file_name, points);
std::vector<boost::tuple<int, int, int> > tris;
CGAL::triangulate_hole_polyline(points.begin(), points.end(), std::back_inserter(tris), use_DT);
if(!tris.empty()) {
std::cerr << " Error: patch should be empty" << std::endl;
assert(false);
}
std::cerr << " Done!" << std::endl;
}
int main() {
std::vector<std::string> input_files_1;
input_files_1.push_back("data/triangle.polylines.txt");
input_files_1.push_back("data/quad.polylines.txt");
input_files_1.push_back("data/U.polylines.txt");
input_files_1.push_back("data/planar.polylines.txt");
for(std::vector<std::string>::iterator it = input_files_1.begin(); it != input_files_1.end(); ++it) {
test_1(it->c_str(), true);
test_1(it->c_str(), false);
}
std::vector<std::string> input_files_2;
input_files_2.push_back("data/hole1.txt");
input_files_2.push_back("data/hole2.txt");
input_files_2.push_back("data/hole3.txt");
input_files_2.push_back("data/hole4.txt");
for(std::vector<std::string>::iterator it = input_files_2.begin(); it != input_files_2.end(); ++it) {
if(it != input_files_2.begin())
{ test_2(it->c_str(), true); } // to skip hole1.txt (DT does not include all border edges)
test_2(it->c_str(), false);
}
test_should_be_no_output("data/collinear.polylines.txt", true);
test_should_be_no_output("data/collinear.polylines.txt", false);
std::cerr << "All Done!" << std::endl;
}

View File

@ -99,6 +99,8 @@ if(CGAL_Qt4_FOUND AND QT4_FOUND AND OPENGL_FOUND AND QGLVIEWER_FOUND)
qt4_wrap_ui( point_inside_polyhedronUI_FILES Point_inside_polyhedron_widget.ui)
qt4_wrap_ui( polyhedron_slicerUI_FILES Polyhedron_slicer_widget.ui)
qt4_wrap_ui( segmentationUI_FILES Mesh_segmentation_widget.ui)
qt4_wrap_ui( hole_fillingUI_FILES Hole_filling_widget.ui)
qt4_wrap_ui( fairingUI_FILES Fairing_widget.ui)
qt4_wrap_ui( selectionUI_FILES Selection_widget.ui)
qt4_wrap_ui( funcUI_FILES Function_dialog.ui )
@ -384,7 +386,7 @@ if(CGAL_Qt4_FOUND AND QT4_FOUND AND OPENGL_FOUND AND QGLVIEWER_FOUND)
endif(EIGEN3_FOUND)
polyhedron_demo_plugin(self_intersection_plugin Polyhedron_demo_self_intersection_plugin)
target_link_libraries(self_intersection_plugin scene_polyhedron_item)
target_link_libraries(self_intersection_plugin scene_polyhedron_item scene_polyhedron_selection_item)
polyhedron_demo_plugin(polyhedron_stitching_plugin Polyhedron_demo_polyhedron_stitching_plugin)
target_link_libraries(polyhedron_stitching_plugin scene_polyhedron_item scene_polylines_item)
@ -446,6 +448,19 @@ if(CGAL_Qt4_FOUND AND QT4_FOUND AND OPENGL_FOUND AND QGLVIEWER_FOUND)
polyhedron_demo_plugin(point_set_outliers_removal_plugin Polyhedron_demo_point_set_outliers_removal_plugin ${ps_outliers_removal_UI_FILES})
target_link_libraries(point_set_outliers_removal_plugin scene_points_with_normal_item)
if(EIGEN3_FOUND AND ${EIGEN3_VERSION} VERSION_GREATER "3.1.90")
polyhedron_demo_plugin(hole_filling_plugin Polyhedron_demo_hole_filling_plugin ${hole_fillingUI_FILES})
target_link_libraries(hole_filling_plugin scene_polyhedron_item scene_polylines_item)
polyhedron_demo_plugin(hole_filling_polyline_plugin Polyhedron_demo_hole_filling_polyline_plugin)
target_link_libraries(hole_filling_polyline_plugin scene_polyhedron_item scene_polylines_item)
polyhedron_demo_plugin(fairing_plugin Polyhedron_demo_fairing_plugin ${fairingUI_FILES})
target_link_libraries(fairing_plugin scene_polyhedron_selection_item)
else()
message(STATUS "NOTICE: The hole filling and fairing plugins require Eigen 3.2 (or higher) and will not be available.")
endif()
polyhedron_demo_plugin(selection_plugin Polyhedron_demo_selection_plugin ${selectionUI_FILES})
target_link_libraries(selection_plugin scene_polyhedron_selection_item scene_points_with_normal_item)
#

View File

@ -0,0 +1,144 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Fairing</class>
<widget class="QDockWidget" name="Fairing">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>296</width>
<height>248</height>
</rect>
</property>
<property name="windowTitle">
<string>Fairing</string>
</property>
<widget class="QWidget" name="dockWidgetContents">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Fairing</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Weight Type:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="Weight_combo_box">
<property name="currentIndex">
<number>0</number>
</property>
<item>
<property name="text">
<string>Cotangent Weight</string>
</property>
</item>
<item>
<property name="text">
<string>Uniform Weight</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Continuity:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="Continuity_spin_box">
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>2</number>
</property>
<property name="value">
<number>1</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QPushButton" name="Fair_button">
<property name="text">
<string>Fair</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Refining</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Density Control Factor:</string>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="Density_control_factor_spin_box">
<property name="maximum">
<double>96.989999999999995</double>
</property>
<property name="singleStep">
<double>0.200000000000000</double>
</property>
<property name="value">
<double>2.410000000000000</double>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QPushButton" name="Refine_button">
<property name="text">
<string>Refine</string>
</property>
</widget>
</item>
</layout>
</widget>
</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>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,253 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>HoleFilling</class>
<widget class="QDockWidget" name="HoleFilling">
<property name="enabled">
<bool>true</bool>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>383</width>
<height>396</height>
</rect>
</property>
<property name="windowTitle">
<string>Hole Filling</string>
</property>
<widget class="QWidget" name="dockWidgetContents">
<property name="enabled">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Action:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="action_combo_box">
<property name="currentIndex">
<number>2</number>
</property>
<item>
<property name="text">
<string>Triangulate</string>
</property>
</item>
<item>
<property name="text">
<string>Triangulate + Refine</string>
</property>
</item>
<item>
<property name="text">
<string>Triangulate + Refine + Fair</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Parameters</string>
</property>
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Use 3D Delaunay Triangulation Space:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="Use_delaunay_triangulation_check_box">
<property name="text">
<string/>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Density Control Factor for Refining:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="Density_control_factor_spin_box">
<property name="maximum">
<double>96.989999999999995</double>
</property>
<property name="singleStep">
<double>0.200000000000000</double>
</property>
<property name="value">
<double>1.410000000000000</double>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Weight Type for Fairing:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QComboBox" name="weight_combo_box">
<property name="currentIndex">
<number>1</number>
</property>
<item>
<property name="text">
<string>Uniform Weight</string>
</property>
</item>
<item>
<property name="text">
<string>Cotangent Weight</string>
</property>
</item>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Continuity for Fairing:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QSpinBox" name="Continuity_spin_box">
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>2</number>
</property>
<property name="value">
<number>1</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QCheckBox" name="Skip_self_intersection_check_box">
<property name="text">
<string>Skip Holes which Result Self Intersection</string>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string/>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QPushButton" name="Create_polyline_items_button">
<property name="text">
<string>Create Polyline Items for Selected Holes</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QPushButton" name="Select_all_holes_button">
<property name="text">
<string>Select All Holes</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="Deselect_all_holes_button">
<property name="text">
<string>Deselect All Holes</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QPushButton" name="Fill_selected_holes_button">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Fill Selected Holes</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="Visualize_holes_button">
<property name="enabled">
<bool>true</bool>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Visualize Holes</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QPushButton" name="Accept_button">
<property name="text">
<string>Accept</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="Reject_button">
<property name="text">
<string>Reject</string>
</property>
</widget>
</item>
</layout>
</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>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>317</width>
<height>194</height>
<height>200</height>
</rect>
</property>
<property name="windowTitle">
@ -59,6 +59,19 @@
</property>
</widget>
</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>
</layout>
</widget>
</widget>

View File

@ -0,0 +1,123 @@
#undef NDEBUG
//#define CGAL_SUPERLU_ENABLED
#include <QtCore/qglobal.h>
#include "Messages_interface.h"
#include "Scene_polyhedron_selection_item.h"
#include "Polyhedron_demo_plugin_helper.h"
#include "ui_Fairing_widget.h"
#include "Polyhedron_type.h"
#include <CGAL/Hole_filling.h>
#include <CGAL/iterator.h>
#include <QTime>
#include <QAction>
#include <QMainWindow>
#include <QApplication>
#include <QDockWidget>
#include <QEvent>
#include <QKeyEvent>
#include <QMouseEvent>
#include <QFileDialog>
#include <vector>
#include <algorithm>
#include <queue>
class Polyhedron_demo_fairing_plugin :
public QObject,
public Polyhedron_demo_plugin_helper
{
Q_OBJECT
Q_INTERFACES(Polyhedron_demo_plugin_interface)
public:
bool applicable() const {
return qobject_cast<Scene_polyhedron_item*>(scene->item(scene->mainSelectionIndex()))
|| qobject_cast<Scene_polyhedron_selection_item*>(scene->item(scene->mainSelectionIndex()));
}
void print_message(QString message) { messages->information(message);}
QList<QAction*> actions() const { return QList<QAction*>() << actionFairing; }
void init(QMainWindow* mainWindow, Scene_interface* scene_interface, Messages_interface* m) {
mw = mainWindow;
scene = scene_interface;
messages = m;
actionFairing = new QAction(tr("Fairing"), mw);
connect(actionFairing, SIGNAL(triggered()), this, SLOT(fairing_action()));
dock_widget = new QDockWidget("Fairing", mw);
dock_widget->setVisible(false);
ui_widget.setupUi(dock_widget);
add_dock_widget(dock_widget);
connect(ui_widget.Fair_button, SIGNAL(clicked()), this, SLOT(on_Fair_button_clicked()));
connect(ui_widget.Refine_button, SIGNAL(clicked()), this, SLOT(on_Refine_button_clicked()));
}
public slots:
void fairing_action() {
dock_widget->show();
dock_widget->raise();
}
void on_Fair_button_clicked() {
Scene_polyhedron_selection_item* selection_item = get_selected_item<Scene_polyhedron_selection_item>();
if(!selection_item) { return; }
if(selection_item->selected_vertices.empty()) {
print_message("Error: please select a region of vertices!");
}
QApplication::setOverrideCursor(Qt::WaitCursor);
int weight_index = ui_widget.Weight_combo_box->currentIndex();
CGAL::Fairing_continuity continuity = static_cast<CGAL::Fairing_continuity>(ui_widget.Continuity_spin_box->value());
if(weight_index == 1)
CGAL::fair(*selection_item->polyhedron(), selection_item->selected_vertices.begin(),
selection_item->selected_vertices.end(),
CGAL::internal::Uniform_weight_fairing<Polyhedron>(),
continuity);
if(weight_index == 0)
CGAL::fair(*selection_item->polyhedron(), selection_item->selected_vertices.begin(),
selection_item->selected_vertices.end(),
CGAL::internal::Cotangent_weight_with_voronoi_area_fairing<Polyhedron>(),
continuity);
selection_item->changed_with_poly_item();
QApplication::restoreOverrideCursor();
}
void on_Refine_button_clicked() {
Scene_polyhedron_selection_item* selection_item = get_selected_item<Scene_polyhedron_selection_item>();
if(!selection_item) { return; }
if(selection_item->selected_facets.empty()) {
print_message("Error: please select a region of facets!");
}
QApplication::setOverrideCursor(Qt::WaitCursor);
double alpha = ui_widget.Density_control_factor_spin_box->value();
std::vector<Polyhedron::Facet_handle> new_facets;
CGAL::refine(*selection_item->polyhedron(), selection_item->selected_facets.begin(),
selection_item->selected_facets.end(), std::back_inserter(new_facets), CGAL::Emptyset_iterator(), alpha);
// add new facets to selection
for(std::vector<Polyhedron::Facet_handle>::iterator it = new_facets.begin(); it != new_facets.end(); ++it) {
selection_item->selected_facets.insert(*it);
}
selection_item->changed_with_poly_item();
QApplication::restoreOverrideCursor();
}
private:
Messages_interface* messages;
QAction* actionFairing;
QDockWidget* dock_widget;
Ui::Fairing ui_widget;
}; // end Polyhedron_demo_fairing_plugin
Q_EXPORT_PLUGIN2(Polyhedron_demo_fairing_plugin, Polyhedron_demo_fairing_plugin)
#include "Polyhedron_demo_fairing_plugin.moc"

View File

@ -0,0 +1,649 @@
//#define CGAL_SUPERLU_ENABLED
#undef NDEBUG
#define DEBUG_TRACE
#include <QtCore/qglobal.h>
#include "Messages_interface.h"
#include "Scene_polyhedron_item.h"
#include "Scene_polylines_item.h"
#include "Scene.h"
#include "Polyhedron_demo_plugin_helper.h"
#include "ui_Hole_filling_widget.h"
#include "Polyhedron_type.h"
#include <CGAL/Hole_filling.h>
#include <CGAL/Timer.h>
#include <CGAL/iterator.h>
#include <QTime>
#include <QAction>
#include <QMainWindow>
#include <QApplication>
#include <QDockWidget>
#include <QEvent>
#include <QKeyEvent>
#include <QMouseEvent>
#include <vector>
#include <algorithm>
#include <QGLViewer/qglviewer.h>
#include <CGAL/gl_render.h>
#include <CGAL/Self_intersection_polyhedron_3.h>
#include "Kernel_type.h"
#include <boost/function_output_iterator.hpp>
// Class for visualizing holes in a polyhedron
// provides mouse selection functionality
class Q_DECL_EXPORT Scene_hole_visualizer : public Scene_item
{
Q_OBJECT
public:
// structs
struct Polyline_data {
Scene_polylines_item* polyline;
Polyhedron::Halfedge_handle halfedge;
qglviewer::Vec position;
};
struct Mouse_keyboard_state
{
bool ctrl_pressing, left_button_pressing;
Mouse_keyboard_state() : ctrl_pressing(false), left_button_pressing(false) { }
};
public: typedef std::list<Polyline_data> Polyline_data_list;
private:
struct List_iterator_comparator {
bool operator()(Polyline_data_list::const_iterator it_1, Polyline_data_list::const_iterator it_2) const
{ return (&*it_1) < (&*it_2); }
};
public:
typedef std::set<Polyline_data_list::const_iterator, List_iterator_comparator> Selected_holes_set;
Scene_hole_visualizer(Scene_polyhedron_item* poly_item, QMainWindow* mainWindow)
: poly_item(poly_item), block_poly_item_changed(false)
{
get_holes();
active_hole = polyline_data_list.end();
QGLViewer* viewer = *QGLViewer::QGLViewerPool().begin();
viewer->installEventFilter(this);
mainWindow->installEventFilter(this);
connect(poly_item, SIGNAL(item_is_about_to_be_changed()), this, SLOT(poly_item_changed()));
}
~Scene_hole_visualizer() {
clear();
}
bool isFinite() const { return true; }
bool isEmpty() const { return polyline_data_list.empty(); }
Bbox bbox() const {
if(polyline_data_list.empty()) { return Bbox(); }
Bbox bbox = polyline_data_list.begin()->polyline->bbox();
for(Polyline_data_list::const_iterator it = polyline_data_list.begin(); it != polyline_data_list.end(); ++it) {
bbox = bbox + it->polyline->bbox();
}
return bbox;
}
Scene_hole_visualizer* clone() const {
return 0;
}
QString toolTip() const {
return tr("%1 with %2 holes").arg(name()).arg(polyline_data_list.size());
}
bool supportsRenderingMode(RenderingMode m) const {
return (m == Wireframe);
}
void draw() const {}
void draw_edges() const {
for(Polyline_data_list::const_iterator it = polyline_data_list.begin(); it != polyline_data_list.end(); ++it) {
if(it == active_hole) { ::glLineWidth(7.f); }
else { ::glLineWidth(3.f); }
if(selected_holes.find(it) != selected_holes.end())
{ it->polyline->setRbgColor(255, 0, 0); }
else
{ it->polyline->setRbgColor(0, 0, 255); }
it->polyline->draw_edges();
}
}
void select_deselect_all(bool select) {
if(select) {
for(Polyline_data_list::iterator it = polyline_data_list.begin(); it != polyline_data_list.end(); ++it)
{ selected_holes.insert(it); }
}
else {
selected_holes.clear();
}
emit itemChanged();
}
// filter events for selecting / activating holes with mouse input
bool eventFilter(QObject* /*target*/, QEvent *event)
{
// This filter is both filtering events from 'viewer' and 'main window'
Mouse_keyboard_state old_state = state;
// key events
if(event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) {
QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
Qt::KeyboardModifiers modifiers = keyEvent->modifiers();
state.ctrl_pressing = modifiers.testFlag(Qt::ControlModifier);
}
// mouse events
if(event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonRelease) {
QMouseEvent* mouse_event = static_cast<QMouseEvent*>(event);
if(mouse_event->button() == Qt::LeftButton) {
state.left_button_pressing = event->type() == QEvent::MouseButtonPress;
}
}
if(!visible()) { return false; } // if not visible just update event state but don't do any action
// activate closest hole
if(event->type() == QEvent::HoverMove)
{
QGLViewer* viewer = *QGLViewer::QGLViewerPool().begin();
const QPoint& p = viewer->mapFromGlobal(QCursor::pos());
bool need_repaint = activate_closest_hole(p.x(), p.y());
if(need_repaint) { emit itemChanged(); }
}
// select closest hole
bool left_clicked_now = state.left_button_pressing && !old_state.left_button_pressing;
if(left_clicked_now && state.ctrl_pressing) {
Selected_holes_set::iterator active_it = selected_holes.find(active_hole);
if(active_it == selected_holes.end()) {
selected_holes.insert(active_hole);
}
else { selected_holes.erase(active_it); }
emit itemChanged();
}
return false;
}
private:
// find holes in polyhedron and construct a internal polyline for each
void get_holes() {
typedef Polyhedron::Halfedge_iterator Halfedge_iterator;
typedef Polyhedron::Halfedge_around_facet_circulator Halfedge_around_facet_circulator;
// save selected hole positions to keep selected holes selected
// we just use center position of holes for identification which might not work good for advanced cases...
std::vector<qglviewer::Vec> selected_hole_positions;
for(Selected_holes_set::const_iterator it = selected_holes.begin(); it != selected_holes.end(); ++it) {
selected_hole_positions.push_back((*it)->position);
}
clear();
Polyhedron& poly = *poly_item->polyhedron();
for(Halfedge_iterator it = poly.halfedges_begin(); it != poly.halfedges_end(); ++it)
{ it->id() = 0; }
for(Halfedge_iterator it = poly.halfedges_begin(); it != poly.halfedges_end(); ++it){
if(it->is_border() && it->id() == 0){
polyline_data_list.push_back(Polyline_data());
Polyline_data& polyline_data = polyline_data_list.back();
polyline_data.polyline = new Scene_polylines_item();
polyline_data.polyline->polylines.push_back(Scene_polylines_item::Polyline());
polyline_data.halfedge = it;
qglviewer::Vec center;
int counter = 0;
Halfedge_around_facet_circulator hf_around_facet = it->facet_begin();
do {
CGAL_assertion(hf_around_facet->id() == 0);
hf_around_facet->id() = 1;
const Polyhedron::Traits::Point_3& p = hf_around_facet->vertex()->point();
polyline_data.polyline->polylines.front().push_back(p);
center += qglviewer::Vec(p.x(), p.y(), p.z());
++counter;
} while(++hf_around_facet != it->facet_begin());
polyline_data.polyline->polylines.front().push_back(hf_around_facet->vertex()->point());
polyline_data.position = center / counter;
}
}
//keep previous selected holes selected
for(Polyline_data_list::const_iterator it = polyline_data_list.begin(); it != polyline_data_list.end(); ++it) {
if(std::find(selected_hole_positions.begin(), selected_hole_positions.end(), it->position) != selected_hole_positions.end()) {
selected_holes.insert(it);
}
}
}
// finds closest polyline from polyline_data_list and makes it active_hole
bool activate_closest_hole(int x, int y) {
typedef Polyhedron::Halfedge_around_facet_circulator Halfedge_around_facet_circulator;
if(polyline_data_list.empty()) { return false; }
QGLViewer* viewer = *QGLViewer::QGLViewerPool().begin();
qglviewer::Camera* camera = viewer->camera();
Polyline_data_list::const_iterator min_it;
double min_dist = (std::numeric_limits<double>::max)();
Kernel::Point_2 xy(x,y);
for(Polyline_data_list::const_iterator it = polyline_data_list.begin(); it != polyline_data_list.end(); ++it)
{
#if 0
/* use center of polyline to measure distance - performance wise */
const qglviewer::Vec& pos_it = camera->projectedCoordinatesOf(it->position);
float dist = std::pow(pos_it.x - x, 2) + std::pow(pos_it.y - y, 2);
if(dist < min_dist) {
min_dist = dist;
min_it = it;
}
#else
/* use polyline points to measure distance - might hurt performance for large holes */
Halfedge_around_facet_circulator hf_around_facet = it->halfedge->facet_begin();
do {
const Polyhedron::Traits::Point_3& p_1 = hf_around_facet->vertex()->point();
const qglviewer::Vec& pos_it_1 = camera->projectedCoordinatesOf(qglviewer::Vec(p_1.x(), p_1.y(), p_1.z()));
const Polyhedron::Traits::Point_3& p_2 = hf_around_facet->opposite()->vertex()->point();
const qglviewer::Vec& pos_it_2 = camera->projectedCoordinatesOf(qglviewer::Vec(p_2.x(), p_2.y(), p_2.z()));
Kernel::Segment_2 s(Kernel::Point_2(pos_it_1.x, pos_it_1.y), Kernel::Point_2(pos_it_2.x, pos_it_2.y));
double dist = CGAL::squared_distance(s, xy);
if(dist < min_dist) {
min_dist = dist;
min_it = it;
}
} while(++hf_around_facet != it->halfedge->facet_begin());
#endif
}
if(min_it == active_hole) {
return false;
}
active_hole = min_it;
return true;
}
// clears internal data except poly_item
void clear() {
for(Polyline_data_list::const_iterator it = polyline_data_list.begin(); it != polyline_data_list.end(); ++it) {
delete it->polyline;
}
polyline_data_list.clear();
selected_holes.clear();
active_hole = polyline_data_list.end();
}
Polyline_data_list::const_iterator active_hole;
Mouse_keyboard_state state;
public:
Selected_holes_set selected_holes;
Scene_polyhedron_item* poly_item;
Polyline_data_list polyline_data_list;
bool block_poly_item_changed;
public slots:
void poly_item_changed() {
if(block_poly_item_changed) { return; }
get_holes();
emit itemChanged();
}
}; // end class Scene_hole_visualizer
///////////////////////////////////////////////////////////////////////////////////////////////////
class Polyhedron_demo_hole_filling_plugin :
public QObject,
public Polyhedron_demo_plugin_helper
{
Q_OBJECT
Q_INTERFACES(Polyhedron_demo_plugin_interface)
public:
bool applicable() const { return qobject_cast<Scene_polyhedron_item*>(scene->item(scene->mainSelectionIndex())); }
void print_message(QString message) { messages->information(message); }
QList<QAction*> actions() const { return QList<QAction*>() << actionHoleFilling; }
void init(QMainWindow* mainWindow, Scene_interface* scene_interface, Messages_interface* m);
Scene_hole_visualizer* get_hole_visualizer(Scene_polyhedron_item* poly_item) {
// did not use a map to assoc Scene_polyhedron_item with Scene_hole_visualizer to prevent crowded code
for(Scene_interface::Item_id i = 0, end = scene->numberOfEntries(); i < end; ++i) {
Scene_hole_visualizer* hole_visualizer = qobject_cast<Scene_hole_visualizer*>(scene->item(i));
if(hole_visualizer && hole_visualizer->poly_item == poly_item)
{ return hole_visualizer; }
}
return NULL;
}
public slots:
void hole_filling_action() {
dock_widget->show();
dock_widget->raise();
if(scene->numberOfEntries() < 2) { on_Visualize_holes_button(); }
}
void on_Select_all_holes_button();
void on_Deselect_all_holes_button();
void on_Visualize_holes_button();
void on_Fill_selected_holes_button();
void on_Create_polyline_items_button();
void on_Accept_button();
void on_Reject_button();
void item_about_to_be_destroyed(Scene_item*);
void hole_visualizer_changed();
void dock_widget_closed();
protected:
bool eventFilter(QObject *, QEvent *event) {
if(event->type() == QEvent::Close) {
dock_widget_closed();
}
return false;
}
void change_poly_item_by_blocking(Scene_polyhedron_item* poly_item, Scene_hole_visualizer* collection) {
if(collection) collection->block_poly_item_changed = true;
scene->itemChanged(poly_item);
if(collection) collection->block_poly_item_changed = false;
}
private:
Messages_interface* messages;
QAction* actionHoleFilling;
QDockWidget* dock_widget;
Ui::HoleFilling ui_widget;
// hold created facet for accept reject functionality
std::vector<Polyhedron::Facet_handle> new_facets;
Scene_polyhedron_item* last_active_item; // always keep it NULL while not active-reject state
bool fill(Polyhedron& polyhedron, Polyhedron::Halfedge_handle halfedge);
bool self_intersecting(Polyhedron& polyhedron);
void accept_reject_toggle(bool activate_accept_reject) {
if(activate_accept_reject) {
ui_widget.Accept_button->setVisible(true);
ui_widget.Reject_button->setVisible(true);
foreach( QWidget* w, ui_widget.dockWidgetContents->findChildren<QWidget*>() )
{ w->setEnabled(false); }
ui_widget.Accept_button->setEnabled(true);
ui_widget.Reject_button->setEnabled(true);
}
else {
ui_widget.Accept_button->setVisible(false);
ui_widget.Reject_button->setVisible(false);
foreach( QWidget* w, ui_widget.dockWidgetContents->findChildren<QWidget*>() )
{ w->setEnabled(true); }
}
}
static QString no_selected_hole_visualizer_error_message() {
return "Error: please select a hole visualizer from Geometric Objects list."
"Use 'Visualize Holes' button to create one by selecting the polyhedron item!";
}
}; // end Polyhedron_demo_hole_filling_plugin
void Polyhedron_demo_hole_filling_plugin::init(QMainWindow* mainWindow,
Scene_interface* scene_interface,
Messages_interface* m)
{
last_active_item = NULL;
mw = mainWindow;
scene = scene_interface;
messages = m;
actionHoleFilling = new QAction(tr("Hole Filling"), mw);
connect(actionHoleFilling, SIGNAL(triggered()), this, SLOT(hole_filling_action()));
dock_widget = new QDockWidget("Hole Filling", mw);
dock_widget->setVisible(false);
dock_widget->installEventFilter(this);
ui_widget.setupUi(dock_widget);
ui_widget.Accept_button->setVisible(false);
ui_widget.Reject_button->setVisible(false);
add_dock_widget(dock_widget);
connect(ui_widget.Visualize_holes_button, SIGNAL(clicked()), this, SLOT(on_Visualize_holes_button()));
connect(ui_widget.Fill_selected_holes_button, SIGNAL(clicked()), this, SLOT(on_Fill_selected_holes_button()));
connect(ui_widget.Select_all_holes_button, SIGNAL(clicked()), this, SLOT(on_Select_all_holes_button()));
connect(ui_widget.Deselect_all_holes_button, SIGNAL(clicked()), this, SLOT(on_Deselect_all_holes_button()));
connect(ui_widget.Create_polyline_items_button, SIGNAL(clicked()), this, SLOT(on_Create_polyline_items_button()));
connect(ui_widget.Accept_button, SIGNAL(clicked()), this, SLOT(on_Accept_button()));
connect(ui_widget.Reject_button, SIGNAL(clicked()), this, SLOT(on_Reject_button()));
if(Scene* scene_casted = dynamic_cast<Scene*>(scene_interface))
{ connect(scene_casted, SIGNAL(itemAboutToBeDestroyed(Scene_item*)), this, SLOT(item_about_to_be_destroyed(Scene_item*))); }
}
void Polyhedron_demo_hole_filling_plugin::item_about_to_be_destroyed(Scene_item* scene_item) {
Scene_polyhedron_item* poly_item = qobject_cast<Scene_polyhedron_item*>(scene_item);
if(poly_item) {
// erase assoc polylines item
scene->erase( scene->item_id( get_hole_visualizer(poly_item) ) );
// close accept-reject dialog if it is open
if(last_active_item == poly_item) {
on_Accept_button();
}
}
}
// removes Scene_hole_visualizer items
void Polyhedron_demo_hole_filling_plugin::dock_widget_closed() {
// remove all Scene_hole_visualizer items
for(Scene_interface::Item_id i = 0, end = scene->numberOfEntries();
i < end; ++i)
{
Scene_hole_visualizer* hole_visualizer = qobject_cast<Scene_hole_visualizer*>(scene->item(i));
if(hole_visualizer) {
scene->erase( scene->item_id(hole_visualizer) );
}
}
on_Accept_button();
}
// creates a Scene_hole_visualizer and associate it with active Scene_polyhedron_item
void Polyhedron_demo_hole_filling_plugin::on_Visualize_holes_button() {
Scene_polyhedron_item* poly_item = get_selected_item<Scene_polyhedron_item>();
if(!poly_item) {
print_message("Error: please select a polyhedron item from Geometric Objects list!");
return;
}
if(get_hole_visualizer(poly_item)) {
print_message("Error: selected polyhedron item already has an associated hole visualizer!");
return;
}
Scene_hole_visualizer* hole_visualizer = new Scene_hole_visualizer(poly_item, mw);
connect(hole_visualizer, SIGNAL(itemChanged()), this, SLOT(hole_visualizer_changed()));
if(hole_visualizer->polyline_data_list.empty()) {
print_message("There is no hole in selected polyhedron item!");
delete hole_visualizer;
return;
}
else {
// poly_item->setFlatMode(); // for better visualization
int item_id = scene->addItem(hole_visualizer);
scene->setSelectedItem(item_id);
hole_visualizer->setName(tr("%1-hole visualizer").arg(poly_item->name()));
}
}
// fills selected holes on active Scene_hole_visualizer
void Polyhedron_demo_hole_filling_plugin::on_Fill_selected_holes_button() {
// get active polylines item
Scene_hole_visualizer* hole_visualizer = get_selected_item<Scene_hole_visualizer>();
if(!hole_visualizer) {
print_message(no_selected_hole_visualizer_error_message());
return;
}
if(hole_visualizer->selected_holes.empty()) {
print_message("Error: there is no selected holes in hole visualizer!");
return;
}
QApplication::setOverrideCursor(Qt::WaitCursor);
// fill selected holes
int counter = 0;
int filled_counter = 0;
for(Scene_hole_visualizer::Selected_holes_set::iterator it = hole_visualizer->selected_holes.begin();
it != hole_visualizer->selected_holes.end(); ++it, ++counter) {
print_message(tr("Hole %1:").arg(counter));
if( fill(*(hole_visualizer->poly_item->polyhedron()), (*it)->halfedge) ) { ++filled_counter;}
}
if(filled_counter > 0) {
change_poly_item_by_blocking(hole_visualizer->poly_item, hole_visualizer);
last_active_item = hole_visualizer->poly_item;
accept_reject_toggle(true);
}
print_message(tr("%1 of %2 holes are filled!").arg(filled_counter).arg(counter));
QApplication::restoreOverrideCursor();
};
// fills all holes and removes associated Scene_hole_visualizer if any
void Polyhedron_demo_hole_filling_plugin::on_Select_all_holes_button() {
Scene_hole_visualizer* hole_visualizer = get_selected_item<Scene_hole_visualizer>();
if(!hole_visualizer) {
print_message(no_selected_hole_visualizer_error_message());
return;
}
hole_visualizer->select_deselect_all(true);
}
void Polyhedron_demo_hole_filling_plugin::on_Deselect_all_holes_button() {
Scene_hole_visualizer* hole_visualizer = get_selected_item<Scene_hole_visualizer>();
if(!hole_visualizer) {
print_message(no_selected_hole_visualizer_error_message());
return;
}
hole_visualizer->select_deselect_all(false);
}
// Simply create polyline items and put them into scene - nothing related with other parts of the plugin
void Polyhedron_demo_hole_filling_plugin::on_Create_polyline_items_button(){
Scene_hole_visualizer* hole_visualizer = get_selected_item<Scene_hole_visualizer>();
if(!hole_visualizer) {
print_message(no_selected_hole_visualizer_error_message());
return;
}
if(hole_visualizer->selected_holes.empty()) {
print_message("Error: there is no selected holes in hole visualizer!");
return;
}
int counter = 0;
for(Scene_hole_visualizer::Selected_holes_set::iterator it = hole_visualizer->selected_holes.begin();
it != hole_visualizer->selected_holes.end(); ++it) {
Scene_polylines_item* polyline_item = new Scene_polylines_item();
polyline_item->polylines = (*it)->polyline->polylines;
polyline_item->setName(QString("selected hole %1").arg(counter++));
scene->addItem(polyline_item);
}
}
void Polyhedron_demo_hole_filling_plugin::on_Accept_button() {
if(last_active_item == NULL) { return; }
accept_reject_toggle(false);
if(Scene_hole_visualizer* hole_visualizer = get_hole_visualizer(last_active_item))
{ hole_visualizer->poly_item_changed();}
new_facets.clear();
last_active_item = NULL;
}
void Polyhedron_demo_hole_filling_plugin::on_Reject_button() {
if(last_active_item == NULL) { return; }
accept_reject_toggle(false);
for(std::vector<Polyhedron::Facet_handle>::iterator it = new_facets.begin(); it != new_facets.end(); ++it) {
last_active_item->polyhedron()->erase_facet((*it)->halfedge());
}
change_poly_item_by_blocking(last_active_item, get_hole_visualizer(last_active_item));
new_facets.clear();
last_active_item = NULL;
}
// To delete Scene_hole_visualizer when it becomes empty
void Polyhedron_demo_hole_filling_plugin::hole_visualizer_changed() {
Scene_hole_visualizer* hole_visualizer = qobject_cast<Scene_hole_visualizer*>(this->sender());
if(hole_visualizer && hole_visualizer->polyline_data_list.empty()) {
scene->erase( scene->item_id(hole_visualizer));
}
}
// helper function for filling holes
bool Polyhedron_demo_hole_filling_plugin::fill
(Polyhedron& poly, Polyhedron::Halfedge_handle it) {
int action_index = ui_widget.action_combo_box->currentIndex();
double alpha = ui_widget.Density_control_factor_spin_box->value();
bool use_DT = ui_widget.Use_delaunay_triangulation_check_box->isChecked();
CGAL::Fairing_continuity continuity = static_cast<CGAL::Fairing_continuity>(ui_widget.Continuity_spin_box->value());
CGAL::Timer timer; timer.start();
std::vector<Polyhedron::Facet_handle> patch;
if(action_index == 0) {
CGAL::triangulate_hole(poly, it, std::back_inserter(patch), use_DT);
}
else if(action_index == 1) {
CGAL::triangulate_and_refine_hole(poly, it, std::back_inserter(patch), CGAL::Emptyset_iterator(), alpha, use_DT);
}
else {
int weight_index = ui_widget.weight_combo_box->currentIndex();
bool success;
if(weight_index == 0) {
success = CGAL::triangulate_refine_and_fair_hole(poly, it, std::back_inserter(patch), CGAL::Emptyset_iterator(),
CGAL::internal::Uniform_weight_fairing<Polyhedron>(), alpha, use_DT, continuity).get<0>();
}
else {
success = CGAL::triangulate_refine_and_fair_hole(poly, it, std::back_inserter(patch), CGAL::Emptyset_iterator(),
CGAL::internal::Cotangent_weight_with_voronoi_area_fairing<Polyhedron>(), alpha, use_DT, continuity).get<0>();
}
if(!success) { print_message("Error: fairing is not successful, only triangulation and refinement are applied!"); }
}
print_message(QString("Took %1 sec.").arg(timer.time()));
if(patch.empty()) {
print_message(tr("Warning: generating patch is not successful! %1")
.arg(use_DT ? "Please try without 'Use 3D Delaunay Triangulation'!" : ""));
return false;
}
// Self intersection test
if(ui_widget.Skip_self_intersection_check_box->checkState() == Qt::Checked) {
timer.reset();
typedef std::vector<std::pair<Polyhedron::Facet_const_handle, Polyhedron::Facet_const_handle> > Intersected_facets;
Intersected_facets intersected_facets;
CGAL::self_intersect<Polyhedron::Traits>(poly, std::back_inserter(intersected_facets));
print_message(QString("Self intersecting test: finding intersecting triangles in %1 sec.").arg(timer.time()));
timer.reset();
// this part might need speed-up
bool intersected = false;
for(Intersected_facets::iterator it = intersected_facets.begin();
it != intersected_facets.end() && !intersected; ++it) {
for(std::vector<Polyhedron::Facet_handle>::iterator it_patch = patch.begin();
it_patch != patch.end() && !intersected; ++it_patch) {
if(it->first == (*it_patch) || it->second == (*it_patch)) {
intersected = true;
}
}
}
print_message(QString("Self intersecting test: iterate on patch in %1 sec.").arg(timer.time()));
if(intersected) {
for(std::vector<Polyhedron::Facet_handle>::iterator it = patch.begin(); it != patch.end(); ++it) {
poly.erase_facet((*it)->halfedge());
}
print_message("Self intersecting patch is generated, and it is removed.");
return false;
}
else { print_message("No Self intersection found, patch is valid."); }
}
// save facets for accept-reject
new_facets.insert(new_facets.end(), patch.begin(), patch.end());
return true;
}
Q_EXPORT_PLUGIN2(Polyhedron_demo_hole_filling_plugin, Polyhedron_demo_hole_filling_plugin)
#include "Polyhedron_demo_hole_filling_plugin.moc"

View File

@ -0,0 +1,164 @@
#include <QtCore/qglobal.h>
#include "Messages_interface.h"
#include "Scene_polyhedron_item.h"
#include "Scene_polylines_item.h"
#include "Scene_interface.h"
#include "Polyhedron_demo_plugin_interface.h"
#include "Polyhedron_type.h"
#include <CGAL/Hole_filling.h>
#include <CGAL/Polyhedron_incremental_builder_3.h>
#include <CGAL/Timer.h>
#include <QAction>
#include <QMainWindow>
#include <QApplication>
#include <QInputDialog>
#include <QMessageBox>
#include <vector>
#include <algorithm>
#include <boost/function_output_iterator.hpp>
#include <boost/iterator/transform_iterator.hpp>
template<class HDS>
class Polyhedron_builder : public CGAL::Modifier_base<HDS> {
public:
Polyhedron_builder(std::vector<CGAL::Triple<int, int, int> >* triangles,
Scene_polylines_item::Polyline* polyline)
: triangles(triangles), polyline(polyline)
{ }
void operator()(HDS& hds) {
CGAL::Polyhedron_incremental_builder_3<HDS> B(hds, true);
B.begin_surface(polyline->size() -1, triangles->size());
for(Scene_polylines_item::Polyline::iterator it = polyline->begin();
it != --polyline->end(); ++it) {
B.add_vertex(*it);
}
for(std::vector<CGAL::Triple<int, int, int> >::iterator it = triangles->begin();
it != triangles->end(); ++it) {
B.begin_facet();
B.add_vertex_to_facet(it->first);
B.add_vertex_to_facet(it->second);
B.add_vertex_to_facet(it->third);
B.end_facet();
}
B.end_surface();
}
private:
std::vector<CGAL::Triple<int, int, int> >* triangles;
Scene_polylines_item::Polyline* polyline;
};
class Polyhedron_demo_hole_filling_polyline_plugin :
public QObject,
public Polyhedron_demo_plugin_interface
{
Q_OBJECT
Q_INTERFACES(Polyhedron_demo_plugin_interface)
public:
bool applicable() const { return qobject_cast<Scene_polylines_item*>(scene->item(scene->mainSelectionIndex())); }
void print_message(QString message) { messages->information(message); }
QList<QAction*> actions() const { return QList<QAction*>() << actionHoleFillingPolyline; }
void init(QMainWindow* mainWindow, Scene_interface* scene_interface, Messages_interface* m){
mw = mainWindow;
scene = scene_interface;
messages = m;
actionHoleFillingPolyline = new QAction(tr("Polyline Hole Filling"), mw);
connect(actionHoleFillingPolyline, SIGNAL(triggered()),
this, SLOT(hole_filling_polyline_action()));
}
private:
struct Nop_functor {
template<class T>
void operator()(const T & /*t*/) const {}
};
typedef boost::function_output_iterator<Nop_functor> Nop_out;
struct Get_handle {
typedef Polyhedron::Facet_handle result_type;
result_type operator()(Polyhedron::Facet& f) const
{ return f.halfedge()->facet(); }
};
public slots:
void hole_filling_polyline_action() {
Scene_polylines_item* polylines_item = qobject_cast<Scene_polylines_item*>(scene->item(scene->mainSelectionIndex()));
if(!polylines_item) {
print_message("Error: there is no selected polyline item!");
return;
}
bool also_refine;
const double density_control_factor =
QInputDialog::getDouble(mw, tr("Density Control Factor"),
tr("Density Control Factor (Cancel for not Refine): "), 1.41, 0.0, 100.0, 2, &also_refine);
bool use_DT =
QMessageBox::Yes == QMessageBox::question(
NULL, "Use Delaunay Triangulation", "Use Delaunay Triangulation ?", QMessageBox::Yes|QMessageBox::No);
QApplication::setOverrideCursor(Qt::WaitCursor);
std::size_t counter = 0;
for(Scene_polylines_item::Polylines_container::iterator it = polylines_item->polylines.begin();
it != polylines_item->polylines.end(); ++it, ++counter)
{
if(it->front() != it->back()) { //not closed, skip it
print_message("Warning: skipping not closed polyline!");
continue;
}
if(it->size() < 4) { // no triangle, skip it (needs at least 3 + 1 repeat)
print_message("Warning: skipping polyline which has less than 4 points!");
continue;
}
CGAL::Timer timer; timer.start();
std::vector<CGAL::Triple<int, int, int> > patch;
CGAL::triangulate_hole_polyline(it->begin(), --it->end(), std::back_inserter(patch), use_DT);
print_message(QString("Triangulated in %1 sec.").arg(timer.time()));
if(patch.empty()) {
print_message("Warning: generating patch is not successful, please try it without 'Delaunay Triangulation'!");
continue;
}
Polyhedron* poly = new Polyhedron;
Polyhedron_builder<Polyhedron::HalfedgeDS> patch_builder(&patch, &(*it));
poly->delegate(patch_builder);
if(also_refine) {
timer.reset();
CGAL::refine(*poly,
boost::make_transform_iterator(poly->facets_begin(), Get_handle()),
boost::make_transform_iterator(poly->facets_end(), Get_handle()),
Nop_out(), Nop_out(), density_control_factor);
print_message(QString("Refined in %1 sec.").arg(timer.time()));
}
Scene_polyhedron_item* poly_item = new Scene_polyhedron_item(poly);
poly_item->setName(tr("%1-filled-%2").arg(polylines_item->name()).arg(counter));
poly_item->setRenderingMode(FlatPlusEdges);
scene->setSelectedItem(scene->addItem(poly_item));
}
QApplication::restoreOverrideCursor();
}
private:
QMainWindow* mw;
Scene_interface* scene;
Messages_interface* messages;
QAction* actionHoleFillingPolyline;
}; // end Polyhedron_demo_hole_filling_polyline_plugin
Q_EXPORT_PLUGIN2(Polyhedron_demo_hole_filling_polyline_plugin, Polyhedron_demo_hole_filling_polyline_plugin)
#include "Polyhedron_demo_hole_filling_polyline_plugin.moc"

View File

@ -6,7 +6,7 @@
#include "Scene_points_with_normal_item.h"
#include "Scene_interface.h"
#include "Polyhedron_demo_plugin_interface.h"
#include "Polyhedron_demo_plugin_helper.h"
#include "Polyhedron_type.h"
#include <CGAL/Timer.h>
@ -30,7 +30,7 @@ typedef CGAL::Exact_predicates_inexact_constructions_kernel Epic_kernel;
class Polyhedron_demo_point_inside_polyhedron_plugin :
public QObject,
public Polyhedron_demo_plugin_interface
public Polyhedron_demo_plugin_helper
{
Q_OBJECT
Q_INTERFACES(Polyhedron_demo_plugin_interface)
@ -64,12 +64,12 @@ public:
dock_widget = new QDockWidget("Point Inside Polyhedron", mw);
dock_widget->setVisible(false);
ui_widget = new Ui::Point_inside_polyhedron();
ui_widget->setupUi(dock_widget);
mw->addDockWidget(Qt::LeftDockWidgetArea, dock_widget);
ui_widget.setupUi(dock_widget);
connect(ui_widget->Select_button, SIGNAL(clicked()), this, SLOT(on_Select_button()));
connect(ui_widget->Sample_random_points_from_bbox, SIGNAL(clicked()), this, SLOT(on_Sample_random_points_from_bbox()));
add_dock_widget(dock_widget);
connect(ui_widget.Select_button, SIGNAL(clicked()), this, SLOT(on_Select_button()));
connect(ui_widget.Sample_random_points_from_bbox, SIGNAL(clicked()), this, SLOT(on_Sample_random_points_from_bbox()));
}
private:
@ -81,12 +81,16 @@ private:
};
public slots:
void point_inside_polyhedron_action() { dock_widget->show(); }
void point_inside_polyhedron_action() {
dock_widget->show();
dock_widget->raise();
}
void on_Select_button()
{
bool inside = ui_widget->Inside_check_box->isChecked();
bool on_boundary = ui_widget->On_boundary_check_box->isChecked();
bool outside = ui_widget->Outside_check_box->isChecked();
bool inside = ui_widget.Inside_check_box->isChecked();
bool on_boundary = ui_widget.On_boundary_check_box->isChecked();
bool outside = ui_widget.Outside_check_box->isChecked();
if(!(inside || on_boundary || outside)) {
print_message("Error: please check at least one parameter check box.");
@ -210,13 +214,11 @@ public slots:
scene->addItem(point_item);
}
private:
QMainWindow* mw;
Scene_interface* scene;
Messages_interface* messages;
QAction* actionPointInsidePolyhedron;
QDockWidget* dock_widget;
Ui::Point_inside_polyhedron* ui_widget;
Ui::Point_inside_polyhedron ui_widget;
}; // end Polyhedron_demo_point_inside_polyhedron_plugin

View File

@ -6,8 +6,9 @@
#include "Scene_plane_item.h"
#include "Scene_polyhedron_item.h"
#include "Scene_polylines_item.h"
#include "Scene.h"
#include "Polyhedron_demo_plugin_interface.h"
#include "Polyhedron_demo_plugin_helper.h"
#include "ui_Polyhedron_slicer_widget.h"
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
@ -29,7 +30,7 @@ typedef CGAL::Exact_predicates_inexact_constructions_kernel Epic_kernel;
class Polyhedron_demo_polyhedron_slicer_plugin :
public QObject,
public Polyhedron_demo_plugin_interface
public Polyhedron_demo_plugin_helper
{
Q_OBJECT
Q_INTERFACES(Polyhedron_demo_plugin_interface)
@ -40,30 +41,15 @@ public:
void init(QMainWindow* mainWindow, Scene_interface* scene_interface, Messages_interface* m);
QList<QAction*> actions() const;
Scene_polyhedron_item* get_selected_item() {
int item_id = scene->mainSelectionIndex();
Scene_polyhedron_item* poly_item = qobject_cast<Scene_polyhedron_item*>(scene->item(item_id));
if(!poly_item) {
int counter = 0;
for(Scene_interface::Item_id i = 0, end = scene->numberOfEntries(); i < end && counter < 2; ++i) {
if(Scene_polyhedron_item* tmp = qobject_cast<Scene_polyhedron_item*>(scene->item(i))) {
poly_item = tmp;
counter++;
}
}
if(counter != 1) { return NULL; }
}
return poly_item;
}
bool get_base_1_2(double bases[6]) {
bool oks[6];
bases[0] = ui_widget->Base_1_x->text().toDouble(&oks[0]);
bases[1] = ui_widget->Base_1_y->text().toDouble(&oks[1]);
bases[2] = ui_widget->Base_1_z->text().toDouble(&oks[2]);
bases[0] = ui_widget.Base_1_x->text().toDouble(&oks[0]);
bases[1] = ui_widget.Base_1_y->text().toDouble(&oks[1]);
bases[2] = ui_widget.Base_1_z->text().toDouble(&oks[2]);
bases[3] = ui_widget->Base_2_x->text().toDouble(&oks[3]);
bases[4] = ui_widget->Base_2_y->text().toDouble(&oks[4]);
bases[5] = ui_widget->Base_2_z->text().toDouble(&oks[5]);
bases[3] = ui_widget.Base_2_x->text().toDouble(&oks[3]);
bases[4] = ui_widget.Base_2_y->text().toDouble(&oks[4]);
bases[5] = ui_widget.Base_2_z->text().toDouble(&oks[5]);
bool total_ok = true;
for(int i = 0; i < 6; ++i && total_ok) { total_ok &= oks[i];}
@ -74,16 +60,24 @@ public slots:
void on_Generate_button_clicked();
bool on_Update_plane_button_clicked();
void plane_manipulated_frame_modified();
void plane_destroyed();
void item_about_to_be_destroyed(Scene_item* scene_item);
void dock_widget_closed();
protected:
bool eventFilter(QObject *, QEvent *event) {
if(event->type() == QEvent::Close) {
dock_widget_closed();
}
return false;
}
private:
Scene_interface* scene;
Messages_interface* messages;
Scene_plane_item* plane_item;
QAction* actionSlicerWidget;
QDockWidget* dock_widget;
Ui::Polyhedron_slicer_widget* ui_widget;
Ui::Polyhedron_slicer ui_widget;
void intersection_of_plane_Polyhedra_3_using_AABB_wrapper(Polyhedron& mesh,
const std::vector<Epic_kernel::Plane_3>& planes,
@ -92,28 +86,27 @@ private:
}; // end Polyhedron_demo_polyhedron_slicer_plugin
void Polyhedron_demo_polyhedron_slicer_plugin::init(QMainWindow* mw,
void Polyhedron_demo_polyhedron_slicer_plugin::init(QMainWindow* mainWindow,
Scene_interface* scene_interface,
Messages_interface* m)
{
mw = mainWindow;
scene = scene_interface;
messages = m;
actionSlicerWidget = new QAction(tr("Polyhedron slicer"), mw);
connect(actionSlicerWidget, SIGNAL(triggered()),
this, SLOT(slicer_widget_action()));
plane_item = NULL;
dock_widget = new QDockWidget("Polyhedron slicer parameters", mw);
dock_widget->setVisible(false); // do not show at the beginning
dock_widget->setObjectName("PolyhedronSlicerParametersDialog");
ui_widget = new Ui::Polyhedron_slicer_widget();
actionSlicerWidget = new QAction(tr("Polyhedron Slicer"), mw);
connect(actionSlicerWidget, SIGNAL(triggered()), this, SLOT(slicer_widget_action()));
QWidget* qw =new QWidget();
ui_widget->setupUi(qw);
dock_widget->setWidget(qw);
mw->addDockWidget(Qt::LeftDockWidgetArea, dock_widget);
dock_widget = new QDockWidget("Polyhedron Slicer", mw);
dock_widget->setVisible(false);
dock_widget->installEventFilter(this);
ui_widget.setupUi(dock_widget);
connect(ui_widget->Generate_button, SIGNAL(clicked()), this, SLOT(on_Generate_button_clicked()));
connect(ui_widget->Update_plane_button, SIGNAL(clicked()), this, SLOT(on_Update_plane_button_clicked()));
add_dock_widget(dock_widget);
connect(ui_widget.Generate_button, SIGNAL(clicked()), this, SLOT(on_Generate_button_clicked()));
connect(ui_widget.Update_plane_button, SIGNAL(clicked()), this, SLOT(on_Update_plane_button_clicked()));
}
QList<QAction*> Polyhedron_demo_polyhedron_slicer_plugin::actions() const {
@ -121,53 +114,55 @@ QList<QAction*> Polyhedron_demo_polyhedron_slicer_plugin::actions() const {
}
void Polyhedron_demo_polyhedron_slicer_plugin::slicer_widget_action(){
if(dock_widget != NULL && !dock_widget->isVisible()) {
dock_widget->show();
if(dock_widget->isVisible()) { return; }
dock_widget->show();
dock_widget->raise();
///// from cut plugin /////
CGAL_assertion(plane_item == NULL);
///// from cut plugin /////
plane_item = new Scene_plane_item(scene);
const Scene_interface::Bbox& bbox = scene->bbox();
plane_item->setPosition((bbox.xmin + bbox.xmax)/2.f,
(bbox.ymin+bbox.ymax)/2.f,
(bbox.zmin+bbox.zmax)/2.f);
plane_item->setNormal(0., 0., 1.);
plane_item->setManipulatable(true);
plane_item->setClonable(false);
plane_item->setColor(Qt::green);
plane_item->setName(tr("Cutting plane"));
connect(plane_item->manipulatedFrame(), SIGNAL(modified()),
this, SLOT(plane_manipulated_frame_modified()));
connect(plane_item, SIGNAL(destroyed()),
this, SLOT(plane_destroyed()));
scene->addItem(plane_item);
plane_item = new Scene_plane_item(scene);
const Scene_interface::Bbox& bbox = scene->bbox();
plane_item->setPosition((bbox.xmin + bbox.xmax)/2.f,
(bbox.ymin+bbox.ymax)/2.f,
(bbox.zmin+bbox.zmax)/2.f);
plane_item->setNormal(0., 0., 1.);
plane_item->setManipulatable(true);
plane_item->setClonable(false);
plane_item->setColor(Qt::green);
plane_item->setName(tr("Cutting plane"));
connect(plane_item->manipulatedFrame(), SIGNAL(modified()),
this, SLOT(plane_manipulated_frame_modified()));
// set distance_with_planes = bbox_diagona / 30
double diagonal = std::sqrt(
CGAL::squared_distanceC3( bbox.xmin, bbox.ymin, bbox.zmin, bbox.xmax, bbox.ymax, bbox.zmax) );
ui_widget->Distance_with_planes->setText(QString::number(diagonal / 30.0));
if(Scene* scene_casted = dynamic_cast<Scene*>(scene))
{ connect(scene_casted, SIGNAL(itemAboutToBeDestroyed(Scene_item*)), this, SLOT(item_about_to_be_destroyed(Scene_item*))); }
scene->addItem(plane_item);
plane_manipulated_frame_modified(); // update text boxes
}
// set distance_with_planes = bbox_diagona / 30
double diagonal = std::sqrt(
CGAL::squared_distanceC3( bbox.xmin, bbox.ymin, bbox.zmin, bbox.xmax, bbox.ymax, bbox.zmax) );
ui_widget.Distance_with_planes->setText(QString::number(diagonal / 30.0));
plane_manipulated_frame_modified(); // update text boxes
}
// when manipulated frame of plane is modified, update line-edits
void Polyhedron_demo_polyhedron_slicer_plugin::plane_manipulated_frame_modified() {
qglviewer::ManipulatedFrame* mf = plane_item->manipulatedFrame();
const qglviewer::Vec& pos = mf->position();
ui_widget->Center_x->setText(QString::number(pos.x));
ui_widget->Center_y->setText(QString::number(pos.y));
ui_widget->Center_z->setText(QString::number(pos.z));
ui_widget.Center_x->setText(QString::number(pos.x));
ui_widget.Center_y->setText(QString::number(pos.y));
ui_widget.Center_z->setText(QString::number(pos.z));
const qglviewer::Vec& base_1 = mf->inverseTransformOf(qglviewer::Vec(1., 0., 0.));
const qglviewer::Vec& base_2 = mf->inverseTransformOf(qglviewer::Vec(0., 1., 0.));
ui_widget->Base_1_x->setText(QString::number(base_1.x));
ui_widget->Base_1_y->setText(QString::number(base_1.y));
ui_widget->Base_1_z->setText(QString::number(base_1.z));
ui_widget.Base_1_x->setText(QString::number(base_1.x));
ui_widget.Base_1_y->setText(QString::number(base_1.y));
ui_widget.Base_1_z->setText(QString::number(base_1.z));
ui_widget->Base_2_x->setText(QString::number(base_2.x));
ui_widget->Base_2_y->setText(QString::number(base_2.y));
ui_widget->Base_2_z->setText(QString::number(base_2.z));
ui_widget.Base_2_x->setText(QString::number(base_2.x));
ui_widget.Base_2_y->setText(QString::number(base_2.y));
ui_widget.Base_2_z->setText(QString::number(base_2.z));
}
// when Update Plane button is clicked, update manipulated frame of plane with line-edits
@ -175,9 +170,9 @@ bool Polyhedron_demo_polyhedron_slicer_plugin::on_Update_plane_button_clicked()
qglviewer::ManipulatedFrame* mf = plane_item->manipulatedFrame();
// get center
bool ok_1 = true, ok_2 = true, ok_3 = true;
double center_x = ui_widget->Center_x->text().toDouble(&ok_1);
double center_y = ui_widget->Center_y->text().toDouble(&ok_2);
double center_z = ui_widget->Center_z->text().toDouble(&ok_3);
double center_x = ui_widget.Center_x->text().toDouble(&ok_1);
double center_y = ui_widget.Center_y->text().toDouble(&ok_2);
double center_z = ui_widget.Center_z->text().toDouble(&ok_3);
if(!ok_1 || !ok_2 || !ok_3)
{ print_message("Error: center coordinates not convertible to double."); return false; }
@ -212,7 +207,7 @@ bool Polyhedron_demo_polyhedron_slicer_plugin::on_Update_plane_button_clicked()
// generate multiple cuts, until any cut does not intersect with bbox
void Polyhedron_demo_polyhedron_slicer_plugin::on_Generate_button_clicked()
{
Scene_polyhedron_item* item = get_selected_item();
Scene_polyhedron_item* item = get_selected_item<Scene_polyhedron_item>();
if(!item) {
print_message("Error: There is no selected Scene_polyhedron_item!");
return;
@ -236,7 +231,7 @@ void Polyhedron_demo_polyhedron_slicer_plugin::on_Generate_button_clicked()
// get distance between planes
bool to_double_ok = true;
double distance_with_planes = ui_widget->Distance_with_planes->text().toDouble(&to_double_ok);
double distance_with_planes = ui_widget.Distance_with_planes->text().toDouble(&to_double_ok);
if(!to_double_ok) {
print_message("Error: Set Distance_with_planes text box!");
return;
@ -274,7 +269,7 @@ void Polyhedron_demo_polyhedron_slicer_plugin::on_Generate_button_clicked()
}
print_message(QString("Created %1 cuts inside bbox...").arg(planes.size()));
bool new_polyline_item_for_polylines = ui_widget->newPolylineItemCheckBox->checkState() == Qt::Checked;
bool new_polyline_item_for_polylines = ui_widget.newPolylineItemCheckBox->checkState() == Qt::Checked;
if(!new_polyline_item_for_polylines)
{
Scene_polylines_item* new_polylines_item = new Scene_polylines_item();
@ -313,10 +308,21 @@ void Polyhedron_demo_polyhedron_slicer_plugin::on_Generate_button_clicked()
}
}
void Polyhedron_demo_polyhedron_slicer_plugin::plane_destroyed() {
dock_widget->hide();
void Polyhedron_demo_polyhedron_slicer_plugin::item_about_to_be_destroyed(Scene_item* scene_item) {
if(plane_item == NULL) { return; }// which means this plugin erased plane_item
Scene_plane_item* destroyed_plane = qobject_cast<Scene_plane_item*>(scene_item);
if(destroyed_plane && destroyed_plane == plane_item) {
plane_item = NULL;
dock_widget->hide();
}
}
void Polyhedron_demo_polyhedron_slicer_plugin::dock_widget_closed() {
CGAL_assertion(plane_item != NULL);
Scene_interface::Item_id id = scene->item_id(plane_item);
plane_item = NULL;
scene->erase(id);
}
// this function assumes 'planes' are parallel
void Polyhedron_demo_polyhedron_slicer_plugin::intersection_of_plane_Polyhedra_3_using_AABB_wrapper(
Polyhedron& poly,

View File

@ -4,6 +4,7 @@
#include "Kernel_type.h"
#include "Polyhedron_type.h"
#include "Scene_polyhedron_item.h"
#include "Scene_polyhedron_selection_item.h"
#include "Polyhedron_demo_plugin_helper.h"
#include "Polyhedron_demo_plugin_interface.h"
@ -52,8 +53,8 @@ void Polyhedron_demo_self_intersection_plugin::on_actionSelfIntersection_trigger
// compute self-intersections
typedef Polyhedron::Facet_const_handle Facet_const_handle;
std::vector<std::pair<Facet_const_handle, Facet_const_handle> > facets;
typedef Polyhedron::Facet_handle Facet_handle;
std::vector<std::pair<Facet_handle, Facet_handle> > facets;
CGAL::self_intersect<Kernel>(*pMesh, back_inserter(facets));
std::cout << "ok (" << facets.size() << " triangle pair(s))" << std::endl;
@ -61,23 +62,14 @@ void Polyhedron_demo_self_intersection_plugin::on_actionSelfIntersection_trigger
// add intersecting triangles as a new polyhedron, i.e., a triangle soup.
if(!facets.empty())
{
Polyhedron *pSoup = new Polyhedron;
std::vector<Facet_const_handle> facets_flat;
facets_flat.reserve(2 * facets.size());
for(std::size_t i = 0; i < facets.size(); ++i)
{ facets_flat.push_back(facets[i].first);
facets_flat.push_back(facets[i].second);
Scene_polyhedron_selection_item* selection_item = new Scene_polyhedron_selection_item(item, mw);
for(std::vector<std::pair<Facet_handle, Facet_handle> >::iterator fb = facets.begin();
fb != facets.end(); ++fb) {
selection_item->selected_facets.insert(fb->first);
selection_item->selected_facets.insert(fb->second);
}
Make_triangle_soup<Polyhedron,Kernel,std::vector<Facet_const_handle>::iterator> soup_builder;
soup_builder.run(facets_flat.begin(), facets_flat.end(),*pSoup);
Scene_polyhedron_item* new_item = new Scene_polyhedron_item(pSoup);
new_item->setName(tr("%1 (intersecting triangles)").arg(item->name()));
new_item->setColor(Qt::magenta);
new_item->setRenderingMode(item->renderingMode());
scene->addItem(new_item);
selection_item->setName(tr("%1 (selection) (intersecting triangles)").arg(item->name()));
scene->addItem(selection_item);
item->setRenderingMode(Wireframe);
scene->itemChanged(item);
}

View File

@ -1,120 +1,135 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Polyhedron_slicer_widget</class>
<widget class="QWidget" name="Polyhedron_slicer_widget">
<class>Polyhedron_slicer</class>
<widget class="QDockWidget" name="Polyhedron_slicer">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>295</width>
<height>180</height>
<width>289</width>
<height>208</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
<string>Polyhedron Slicer</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<layout class="QGridLayout">
<item row="1" column="1">
<widget class="QLineEdit" name="Base_2_x"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="Base_2_label">
<property name="text">
<string>Base 2</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="Base_1_label">
<property name="text">
<string>Base 1</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="Center_label">
<property name="text">
<string>Center</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="Base_1_x"/>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="Center_x"/>
</item>
<item row="0" column="3">
<widget class="QLineEdit" name="Base_1_z"/>
</item>
<item row="0" column="2">
<widget class="QLineEdit" name="Base_1_y"/>
</item>
<item row="2" column="2">
<widget class="QLineEdit" name="Center_y"/>
</item>
<item row="2" column="3">
<widget class="QLineEdit" name="Center_z"/>
</item>
<item row="1" column="2">
<widget class="QLineEdit" name="Base_2_y"/>
</item>
<item row="1" column="3">
<widget class="QLineEdit" name="Base_2_z"/>
</item>
<item row="3" column="3">
<widget class="QPushButton" name="Update_plane_button">
<property name="text">
<string>Update Plane</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="Distance_with_planes_label">
<property name="text">
<string>Distance with planes</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="Distance_with_planes"/>
</item>
</layout>
</item>
<item row="3" column="0">
<layout class="QHBoxLayout">
<item>
<widget class="QCheckBox" name="newPolylineItemCheckBox">
<property name="text">
<string>New polyline item for each polyline</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="Generate_button">
<property name="text">
<string>Generate</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
<widget class="QWidget" name="dockWidgetContents">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QGridLayout">
<item row="1" column="1">
<widget class="QLineEdit" name="Base_2_x"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="Base_2_label">
<property name="text">
<string>Base 2</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="Base_1_label">
<property name="text">
<string>Base 1</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="Center_label">
<property name="text">
<string>Center</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="Base_1_x"/>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="Center_x"/>
</item>
<item row="0" column="3">
<widget class="QLineEdit" name="Base_1_z"/>
</item>
<item row="0" column="2">
<widget class="QLineEdit" name="Base_1_y"/>
</item>
<item row="2" column="2">
<widget class="QLineEdit" name="Center_y"/>
</item>
<item row="2" column="3">
<widget class="QLineEdit" name="Center_z"/>
</item>
<item row="1" column="2">
<widget class="QLineEdit" name="Base_2_y"/>
</item>
<item row="1" column="3">
<widget class="QLineEdit" name="Base_2_z"/>
</item>
<item row="3" column="3">
<widget class="QPushButton" name="Update_plane_button">
<property name="text">
<string>Update Plane</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="Distance_with_planes_label">
<property name="text">
<string>Distance with planes</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="Distance_with_planes"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="_2">
<item>
<widget class="QCheckBox" name="newPolylineItemCheckBox">
<property name="text">
<string>New polyline item for each polyline</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="Generate_button">
<property name="text">
<string>Generate</string>
</property>
</widget>
</item>
</layout>
</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>
</layout>
</widget>
</widget>
<resources/>
<connections/>

View File

@ -84,7 +84,7 @@ public:
{
CGAL_precondition(rows > 0);
CGAL_precondition(columns > 0);
if (is_symmetric) {
if (m_is_symmetric) {
CGAL_precondition(rows == columns);
}