From 312aee625ae6a58df292bed260ed0163abfd848b Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Thu, 26 May 2016 14:06:19 +0200 Subject: [PATCH 01/21] New overloads of CGAL::IMAGEIO::static_evaluate() --- CGAL_ImageIO/include/CGAL/ImageIO.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/CGAL_ImageIO/include/CGAL/ImageIO.h b/CGAL_ImageIO/include/CGAL/ImageIO.h index c296b99aa39..70b4ee109e5 100644 --- a/CGAL_ImageIO/include/CGAL/ImageIO.h +++ b/CGAL_ImageIO/include/CGAL/ImageIO.h @@ -614,7 +614,7 @@ static_evaluate(const _image* image, template inline -Word +Word& static_evaluate(const _image* image, const std::size_t i, const std::size_t j, @@ -623,6 +623,15 @@ static_evaluate(const _image* image, return ((Word*)image->data)[(k * image->ydim + j) * image->xdim + i]; } +template +inline +Word& +static_evaluate(const _image* image, + const std::size_t i) +{ + return ((Word*)image->data)[i]; +} + } // end namespace IMAGEIO } // end namespace CGAL From 16d18351ce7e9b295a353e13d9249d41fddc8a87 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Fri, 27 May 2016 16:00:57 +0200 Subject: [PATCH 02/21] Add example showing how to initialize components --- Mesh_3/examples/Mesh_3/CMakeLists.txt | 1 + ...sh_3D_image_with_custom_initialization.cpp | 62 +++++ Mesh_3/examples/Mesh_3/random_labeled_image.h | 42 +++ .../CGAL/Labeled_image_mesh_domain_3.h | 1 + Mesh_3/include/CGAL/Labeled_mesh_domain_3.h | 4 + ...tialize_triangulation_from_labeled_image.h | 237 +++++++++++++++++ ...or_connected_components_in_labeled_image.h | 249 ++++++++++++++++++ .../Mesh_3/Handle_IO_for_pair_of_int.h | 84 ++++++ .../include/CGAL/internal/Mesh_3/get_index.h | 11 +- 9 files changed, 686 insertions(+), 5 deletions(-) create mode 100644 Mesh_3/examples/Mesh_3/mesh_3D_image_with_custom_initialization.cpp create mode 100644 Mesh_3/examples/Mesh_3/random_labeled_image.h create mode 100644 Mesh_3/include/CGAL/Mesh_3/initialize_triangulation_from_labeled_image.h create mode 100644 Mesh_3/include/CGAL/Mesh_3/search_for_connected_components_in_labeled_image.h create mode 100644 Mesh_3/include/CGAL/internal/Mesh_3/Handle_IO_for_pair_of_int.h diff --git a/Mesh_3/examples/Mesh_3/CMakeLists.txt b/Mesh_3/examples/Mesh_3/CMakeLists.txt index 8a1535a53b9..1ea85bed952 100644 --- a/Mesh_3/examples/Mesh_3/CMakeLists.txt +++ b/Mesh_3/examples/Mesh_3/CMakeLists.txt @@ -86,6 +86,7 @@ if ( CGAL_FOUND ) create_single_source_cgal_program( "mesh_optimization_example.cpp" ) create_single_source_cgal_program( "mesh_optimization_lloyd_example.cpp" ) create_single_source_cgal_program( "mesh_3D_image.cpp" ) + create_single_source_cgal_program( "mesh_3D_image_with_custom_initialization.cpp" ) create_single_source_cgal_program( "mesh_3D_image_variable_size.cpp" ) else() message( STATUS "NOTICE: The examples mesh_3D_image.cpp, mesh_3D_image_variable_size.cpp, mesh_optimization_example.cpp and mesh_optimization_lloyd_example.cpp need CGAL_ImageIO to be configured with ZLIB support, and will not be compiled." ) diff --git a/Mesh_3/examples/Mesh_3/mesh_3D_image_with_custom_initialization.cpp b/Mesh_3/examples/Mesh_3/mesh_3D_image_with_custom_initialization.cpp new file mode 100644 index 00000000000..eb97e7bef64 --- /dev/null +++ b/Mesh_3/examples/Mesh_3/mesh_3D_image_with_custom_initialization.cpp @@ -0,0 +1,62 @@ +#include "random_labeled_image.h" +#include + +#include +#include +#include + +#include + +#include +#include +#include + +#include + +// Domain +typedef CGAL::Exact_predicates_inexact_constructions_kernel K; +typedef CGAL::Labeled_image_mesh_domain_3 Mesh_domain; + +#ifdef CGAL_CONCURRENT_MESH_3 +typedef CGAL::Parallel_tag Concurrency_tag; +#else +typedef CGAL::Sequential_tag Concurrency_tag; +#endif + +// Triangulation +typedef CGAL::Mesh_triangulation_3::type Tr; + +typedef CGAL::Mesh_complex_3_in_triangulation_3 C3t3; + +// Criteria +typedef CGAL::Mesh_criteria_3 Mesh_criteria; + +// To avoid verbose function and named parameters call +using namespace CGAL::parameters; + +int main() +{ + CGAL::Image_3 image = random_labeled_image(); + + // Domain + Mesh_domain domain(image); + + // Mesh criteria + Mesh_criteria criteria(facet_angle=30, facet_size=3, facet_distance=1, + cell_radius_edge_ratio=3, cell_size=3); + + // Meshing + C3t3 c3t3; + initialize_triangulation_from_labeled_image(c3t3, + domain, + image, + criteria, + (unsigned char)0); + + CGAL::refine_mesh_3(c3t3, domain, criteria); + + // Output + CGAL::dump_c3t3(c3t3, "out"); + + return 0; +} diff --git a/Mesh_3/examples/Mesh_3/random_labeled_image.h b/Mesh_3/examples/Mesh_3/random_labeled_image.h new file mode 100644 index 00000000000..3452b1ff004 --- /dev/null +++ b/Mesh_3/examples/Mesh_3/random_labeled_image.h @@ -0,0 +1,42 @@ +#include +#include +#include + +CGAL::Image_3 random_labeled_image() +{ + const int dim = 400; + const unsigned char number_of_spheres = 50; + const int max_radius_of_spheres = 10; + _image* image = _createImage(dim, dim, dim, 1, + 1.f, 1.f, 1.f, 1, + WK_FIXED, SGN_UNSIGNED); + unsigned char* ptr = (unsigned char*)(image->data); + std::fill(ptr, ptr+dim*dim*dim, '\0'); + + CGAL::Random rand(0); + for(unsigned char n = number_of_spheres; n > 0 ; --n) { + std::size_t i = rand.uniform_smallint( 1 + max_radius_of_spheres, + dim-2 - max_radius_of_spheres); + std::size_t j = rand.uniform_smallint( 1 + max_radius_of_spheres, + dim-2 - max_radius_of_spheres); + std::size_t k = rand.uniform_smallint( 1 + max_radius_of_spheres, + dim-2 - max_radius_of_spheres); + for(std::ptrdiff_t ii = - max_radius_of_spheres; + ii <= max_radius_of_spheres; ++ii) + { + for(std::ptrdiff_t jj = - max_radius_of_spheres; + jj <= max_radius_of_spheres; ++jj) + { + for(std::ptrdiff_t kk = - max_radius_of_spheres; + kk <= max_radius_of_spheres; ++kk) + { + if(ii*ii + jj*jj + kk*kk > + max_radius_of_spheres * max_radius_of_spheres) continue; + using CGAL::IMAGEIO::static_evaluate; + static_evaluate(image, i+ii, j+jj, k+kk) = n; + } + } + } + } + return CGAL::Image_3(image); +} diff --git a/Mesh_3/include/CGAL/Labeled_image_mesh_domain_3.h b/Mesh_3/include/CGAL/Labeled_image_mesh_domain_3.h index 982b3fa023f..f7a62f59562 100644 --- a/Mesh_3/include/CGAL/Labeled_image_mesh_domain_3.h +++ b/Mesh_3/include/CGAL/Labeled_image_mesh_domain_3.h @@ -32,6 +32,7 @@ #include #include #include +#include namespace CGAL { diff --git a/Mesh_3/include/CGAL/Labeled_mesh_domain_3.h b/Mesh_3/include/CGAL/Labeled_mesh_domain_3.h index af50ca3245f..655bad3442f 100644 --- a/Mesh_3/include/CGAL/Labeled_mesh_domain_3.h +++ b/Mesh_3/include/CGAL/Labeled_mesh_domain_3.h @@ -39,6 +39,10 @@ #include #include +#include + +#include + namespace CGAL { struct Null_subdomain_index { diff --git a/Mesh_3/include/CGAL/Mesh_3/initialize_triangulation_from_labeled_image.h b/Mesh_3/include/CGAL/Mesh_3/initialize_triangulation_from_labeled_image.h new file mode 100644 index 00000000000..422d7949d90 --- /dev/null +++ b/Mesh_3/include/CGAL/Mesh_3/initialize_triangulation_from_labeled_image.h @@ -0,0 +1,237 @@ +// Copyright (c) 2015,2016 GeometryFactory +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// You can redistribute it and/or modify it under the terms of the GNU +// General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. +// +// Licensees holding a valid commercial license may use this file in +// accordance with the commercial license agreement provided with the software. +// +// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +// +// $URL$ +// $Id$ +// +// +// Author(s) : Laurent Rineau + +#ifndef CGAL_MESH_3_INITIALIZE_TRIANGULATION_FROM_LABELED_IMAGE_H +#define CGAL_MESH_3_INITIALIZE_TRIANGULATION_FROM_LABELED_IMAGE_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +template +struct Get_point +{ + const double vx, vy, vz; + Get_point(const CGAL::Image_3* image) + : vx(image->vx()) + , vy(image->vy()) + , vz(image->vz()) + {} + + Point operator()(const std::size_t i, + const std::size_t j, + const std::size_t k) const + { + return Point(i * vx, j * vy, k * vz); + } +}; +template +void init_tr_from_labeled_image_call_init_features(C3T3&, + const MeshDomain&, + const MeshCriteria&, + CGAL::Tag_false) +{ +} +template +void init_tr_from_labeled_image_call_init_features(C3T3& c3t3, + const MeshDomain& domain, + const MeshCriteria& criteria, + CGAL::Tag_true) +{ + CGAL::internal::Mesh_3::init_c3t3_with_features(c3t3, + domain, + criteria); + std::cout << c3t3.triangulation().number_of_vertices() + << " initial points on 1D-features" << std::endl; +} + + +template +void initialize_triangulation_from_labeled_image(C3T3& c3t3, + const MeshDomain& domain, + const CGAL::Image_3& image, + const MeshCriteria& criteria, + Image_word_type, + bool protect_features = false + ) +{ + typedef typename C3T3::Triangulation Tr; + typedef typename Tr::Point Weighted_point; + typedef typename Weighted_point::Point Point_3; + typedef typename Tr::Segment Segment_3; + typedef typename Tr::Geom_traits::Vector_3 Vector_3; + typedef typename Tr::Vertex_handle Vertex_handle; + typedef typename Tr::Cell_handle Cell_handle; + + typedef Point_3 Point; + typedef MeshDomain Mesh_domain; + + Tr& tr = c3t3.triangulation(); + + if(protect_features) { + init_tr_from_labeled_image_call_init_features + (c3t3, domain, criteria, + CGAL::internal::Mesh_3::Has_features()); + } + + const double max_v = (std::max)((std::max)(image.vx(), + image.vy()), + image.vz()); + + typedef std::vector > Seeds; + Seeds seeds; + Get_point get_point(&image); + std::cout << "Searching for connected components..." << std::endl; + CGAL::Identity no_transformation; + search_for_connected_components_in_labeled_image(image, + std::back_inserter(seeds), + CGAL::Emptyset_iterator(), + no_transformation, + get_point, + Image_word_type()); + std::cout << " " << seeds.size() << " components were found." << std::endl; + std::cout << "Construct initial points..." << std::endl; + for(typename Seeds::const_iterator it = seeds.begin(), end = seeds.end(); + it != end; ++it) + { + const double radius = (it->second + 1)* max_v; + CGAL::Random_points_on_sphere_3 points_on_sphere_3(radius); + typename Mesh_domain::Construct_intersection construct_intersection = + domain.construct_intersection_object(); + + std::vector directions; + if(it->second < 2) { + // shoot in six directions + directions.push_back(Vector_3(-radius, 0, 0)); + directions.push_back(Vector_3(+radius, 0, 0)); + directions.push_back(Vector_3(0, -radius, 0)); + directions.push_back(Vector_3(0, +radius, 0)); + directions.push_back(Vector_3(0, 0, -radius)); + directions.push_back(Vector_3(0, 0, +radius)); + } else { + for(int i = 0; i < 20; ++i) + { + // shoot 20 random directions + directions.push_back(*points_on_sphere_3++ - CGAL::ORIGIN); + } + } + + BOOST_FOREACH(const Vector_3& v, directions) + { + const Point test = it->first + v; + const typename Mesh_domain::Intersection intersect = + construct_intersection(Segment_3(it->first, test)); + if (CGAL::cpp11::get<2>(intersect) != 0) + { + Point_3 pi = CGAL::cpp11::get<0>(intersect); + + // This would cause trouble to optimizers + // check pi will not be hidden + typename Tr::Locate_type lt; + Cell_handle c; + int li, lj; + Cell_handle pi_cell = tr.locate(pi, lt, li, lj); + if(lt != Tr::OUTSIDE_AFFINE_HULL) { + switch (tr.dimension()) + { //skip dimension 0 + case 1: + if (tr.side_of_power_segment(pi_cell, pi, true) != CGAL::ON_BOUNDED_SIDE) + continue; + break; + case 2: + if (tr.side_of_power_circle(pi_cell, 3, pi, true) != CGAL::ON_BOUNDED_SIDE) + continue; + break; + case 3: + if (tr.side_of_power_sphere(pi_cell, pi, true) != CGAL::ON_BOUNDED_SIDE) + continue; + } + } + + //check pi is not inside a protecting ball + std::vector conflict_vertices; + if (tr.dimension() == 3) + { + tr.vertices_on_conflict_zone_boundary(pi, pi_cell + , std::back_inserter(conflict_vertices)); + } + else + { + for (typename Tr::Finite_vertices_iterator vit = tr.finite_vertices_begin(); + vit != tr.finite_vertices_end(); ++vit) + { + if (vit->point().weight() > 0.) + conflict_vertices.push_back(vit); + } + } + bool pi_inside_protecting_sphere = false; + BOOST_FOREACH(Vertex_handle cv, conflict_vertices) + { + if (cv->point().weight() == 0.) + continue; + if (CGAL::compare_squared_distance(pi, cv->point(), cv->point().weight()) + != CGAL::LARGER) + { + pi_inside_protecting_sphere = true; + break; + } + } + if (pi_inside_protecting_sphere) + continue; + const typename Mesh_domain::Index index = CGAL::cpp11::get<1>(intersect); + + /// The following lines show how to insert initial points in the + /// `c3t3` object. [insert initial points] + Vertex_handle v = tr.insert(pi); + + // `v` could be null if `pi` is hidden by other vertices of `tr`. + CGAL_assertion(v != Vertex_handle()); + + c3t3.set_dimension(v, 2); // by construction, points are on surface + c3t3.set_index(v, index); + /// [insert initial points] + } + // else + // { + // std::cerr << + // boost::format("Error. Segment (%1%, %2%) does not intersect the surface!\n") + // % it->first % test; + // } + } + } + std::cout << " " << tr.number_of_vertices() << " initial points." << std::endl; + if ( c3t3.triangulation().dimension() != 3 ) + { + std::cout << " not enough points: triangulation.dimension() == " + << c3t3.triangulation().dimension() << std::endl; + CGAL::internal::Mesh_3::init_c3t3(c3t3, domain, criteria, 20); + std::cout << " -> " << tr.number_of_vertices() << " initial points." << std::endl; + } +} + +#endif // CGAL_MESH_3_INITIALIZE_TRIANGULATION_FROM_LABELED_IMAGE_H diff --git a/Mesh_3/include/CGAL/Mesh_3/search_for_connected_components_in_labeled_image.h b/Mesh_3/include/CGAL/Mesh_3/search_for_connected_components_in_labeled_image.h new file mode 100644 index 00000000000..4109b8181f4 --- /dev/null +++ b/Mesh_3/include/CGAL/Mesh_3/search_for_connected_components_in_labeled_image.h @@ -0,0 +1,249 @@ +// Copyright (c) 2015,2016 GeometryFactory +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// You can redistribute it and/or modify it under the terms of the GNU +// General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. +// +// Licensees holding a valid commercial license may use this file in +// accordance with the commercial license agreement provided with the software. +// +// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +// +// $URL$ +// $Id$ +// +// +// Author(s) : Laurent Rineau + +#ifndef CGAL_MESH_3_SEARCH_FOR_CONNECTED_COMPONENTS_IN_LABELED_IMAGE_H +#define CGAL_MESH_3_SEARCH_FOR_CONNECTED_COMPONENTS_IN_LABELED_IMAGE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CGAL_MESH_3_SEARCH_FOR_CONNECTED_COMPONENTS_IN_LABELED_IMAGE_VERBOSE +# include +# include +#endif // CGAL_MESH_3_SEARCH_FOR_CONNECTED_COMPONENTS_IN_LABELED_IMAGE_VERBOSE +template +void +search_for_connected_components_in_labeled_image(const CGAL::Image_3& image, + PointsOutputIterator it, + DomainsOutputIterator dom_it, + TransformOperator transform, + Construct_point point, + Image_word_type) +{ + const std::size_t nx = image.xdim(); + const std::size_t ny = image.ydim(); + const std::size_t nz = image.zdim(); + const std::size_t size = nx * ny * nz; + + typedef boost::uint16_t uint; + + if(nx > 65535 || ny > 65535 || nz > 65535) + { + CGAL_error_msg("The dimensions of the image must be lower than 2^16"); + } + + typedef typename TransformOperator::result_type Label; + + std::vector visited(size, false); + std::vector second_pass(size, false); + typedef boost::tuple Indices; + typedef std::queue > Indices_queue; + typedef std::vector Border_vector; + +#ifdef CGAL_MESH_3_SEARCH_FOR_CONNECTED_COMPONENTS_IN_LABELED_IMAGE_VERBOSE + int number_of_connected_components = 0; +#endif // CGAL_MESH_3_SEARCH_FOR_CONNECTED_COMPONENTS_IN_LABELED_IMAGE_VERBOSE + std::size_t voxel_index = 0; + for(std::size_t k=0; k(image.image(), + voxel_index)); + *dom_it++ = current_label; + if(current_label == Label()) { + visited[voxel_index] = true; + second_pass[voxel_index] = true; + ++voxel_index; + continue; + } + +#ifdef CGAL_MESH_3_SEARCH_FOR_CONNECTED_COMPONENTS_IN_LABELED_IMAGE_VERBOSE + // if we reach here, (i, j, k) is a new connected component + ++number_of_connected_components; + std::cerr << boost::format("Found new connected component (#%5%) " + "at voxel (%1%, %2%, %3%), value=%4%, volume id=%6%\n") + % i % j % k + % (long)static_evaluate(image.image(), i, j, k) + % number_of_connected_components + % (int)current_label; +#endif // CGAL_MESH_3_SEARCH_FOR_CONNECTED_COMPONENTS_IN_LABELED_IMAGE_VERBOSE + + int nb_voxels = 0; + + Indices_queue queue; + Indices indices(i, j ,k, 0); + queue.push(indices); + + Border_vector border; + + /* + * First pass is a BFS to retrieve all the connected component, and + * its border. + * Second pass is a BFS initialized with all voxel of the border. + * The last voxel of that BFS is used as the seed. + */ + int pass = 1; // pass will be equal to 2 in second pass + + Indices bbox_min = indices; + Indices bbox_max = indices; + + while(!queue.empty()) // walk through the connected component + { + Indices indices = queue.front(); + queue.pop(); + + // warning: those indices i, j and k are local to the while loop + const uint i = boost::get<0>(indices); + const uint j = boost::get<1>(indices); + const uint k = boost::get<2>(indices); + const uint depth = boost::get<3>(indices); + + const size_t offset = i + nx * (j + ny * k); + const int m = (visited[offset] ? 1 : 0) + (second_pass[offset] ? 2 : 0); + if(m < pass) + { + if(pass == 1 ) + { + visited[offset] = true; + second_pass[offset] = false; + ++nb_voxels; + boost::get<0>(bbox_min) = (std::min)(i, boost::get<0>(bbox_min)); + boost::get<0>(bbox_max) = (std::max)(i, boost::get<0>(bbox_max)); + boost::get<1>(bbox_min) = (std::min)(j, boost::get<1>(bbox_min)); + boost::get<1>(bbox_max) = (std::max)(j, boost::get<1>(bbox_max)); + boost::get<2>(bbox_min) = (std::min)(k, boost::get<2>(bbox_min)); + boost::get<2>(bbox_max) = (std::max)(k, boost::get<2>(bbox_max)); + } else + { + CGAL_assertion(pass == 2); + visited[offset] = false; + second_pass[offset] = true; + } + + static const int neighbors_offset[6][3] = { { +1, 0, 0 }, + { -1, 0, 0 }, + { 0, +1, 0 }, + { 0, -1, 0 }, + { 0, 0, +1 }, + { 0, 0, -1 } }; + bool voxel_is_on_border = false; + + // Visit neighbors. + // (i_n, j_n, k_n) are indices of neighbors. + for(int n = 0; n < 6; ++n) + { + const ptrdiff_t i_n = i + neighbors_offset[n][0]; + const ptrdiff_t j_n = j + neighbors_offset[n][1]; + const ptrdiff_t k_n = k + neighbors_offset[n][2]; + if(i_n < 0 || i_n >= static_cast(nx) || + j_n < 0 || j_n >= static_cast(ny) || + k_n < 0 || k_n >= static_cast(nz)) + { + voxel_is_on_border = true; + continue; + } + else + { + const std::size_t offset_n = i_n + nx * (j_n + k_n * ny); + if(transform(static_evaluate(image.image(), + offset_n)) + == current_label) + { + const int m_n = (visited[offset_n] ? 1 : 0) + + (second_pass[offset_n] ? 2 : 0); + if(m_n < pass) { + Indices indices(i_n, j_n, k_n, depth+1); + queue.push(indices); + } + } + else + voxel_is_on_border = true; + } + } // end for neighbors + + if(pass == 1 && voxel_is_on_border) + border.push_back(indices); + } // end if voxel not already visited + + if(queue.empty()) { + if(pass == 1) + { // End of first pass. Begin second pass with the voxels of + // the border. + for(typename Border_vector::const_iterator + border_it = border.begin(), border_end = border.end(); + border_it != border_end; ++border_it) + queue.push(*border_it); + pass = 2; + } + else // end of second pass, return the last visited voxel + { +// if(nb_voxels >= 100) + { + *it++ = std::make_pair(point(i, j, k), + depth+1); +#if CGAL_MESH_3_SEARCH_FOR_CONNECTED_COMPONENTS_IN_LABELED_IMAGE_VERBOSE > 1 + std::cerr << boost::format("Found seed %5%, which is voxel " + "(%1%, %2%, %3%), value=%4%\n") + % i % j % k + % (long)static_evaluate(image.image(), i, j, k) + % point(i, j, k); +#endif // CGAL_MESH_3_SEARCH_FOR_CONNECTED_COMPONENTS_IN_LABELED_IMAGE_VERBOSE>1 + } + } + } // end if queue.empty() + } // end while !queue.empty() (with local indices i, j, k) +#ifdef CGAL_MESH_3_SEARCH_FOR_CONNECTED_COMPONENTS_IN_LABELED_IMAGE_VERBOSE + std::cerr + << boost::format("There was %1% voxel(s) in that component.\n" + "The bounding box is (%2% %3% %4%, %5% %6% %7%).\n" + "%8% voxel(s) on border\n") + % nb_voxels + % boost::get<0>(bbox_min) % boost::get<1>(bbox_min) + % boost::get<2>(bbox_min) + % boost::get<0>(bbox_max) % boost::get<1>(bbox_max) + % boost::get<2>(bbox_max) + % border.size(); +#endif // CGAL_MESH_3_SEARCH_FOR_CONNECTED_COMPONENTS_IN_LABELED_IMAGE_VERBOSE + + ++voxel_index; + } // end for i,j,k +} // end function search_for_connected_components_in_labeled_image() + +#endif // CGAL_MESH_3_SEARCH_FOR_CONNECTED_COMPONENTS_IN_LABELED_IMAGE_H diff --git a/Mesh_3/include/CGAL/internal/Mesh_3/Handle_IO_for_pair_of_int.h b/Mesh_3/include/CGAL/internal/Mesh_3/Handle_IO_for_pair_of_int.h new file mode 100644 index 00000000000..4e3c57eb07e --- /dev/null +++ b/Mesh_3/include/CGAL/internal/Mesh_3/Handle_IO_for_pair_of_int.h @@ -0,0 +1,84 @@ +// Copyright (c) 2016 GeometryFactory +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// You can redistribute it and/or modify it under the terms of the GNU +// General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. +// +// Licensees holding a valid commercial license may use this file in +// accordance with the commercial license agreement provided with the software. +// +// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +// +// $URL$ +// $Id$ +// +// +// Author(s) : Laurent Rineau + +#ifndef CGAL_INTERNAL_MESH_3_INTERNAL_HANDLE_IO_FOR_PAIR_OF_INT_H +#define CGAL_INTERNAL_MESH_3_INTERNAL_HANDLE_IO_FOR_PAIR_OF_INT_H + +#include +#include +#include +#include + +namespace CGAL { +template <> +struct Get_io_signature > { + std::string operator()() const + { + return std::string("std::pair"); + } +}; // end Get_io_signature > + +inline std::ostream& operator<<(std::ostream& out, const std::pair& id) { + return out << id.first << " " << id.second; +} +inline std::istream& operator>>(std::istream& in, std::pair& id) { + return in >> id.first >> id.second; +} + +template <> +class Output_rep > { + typedef std::pair T; + const T& t; +public: + //! initialize with a const reference to \a t. + Output_rep( const T& tt) : t(tt) {} + //! perform the output, calls \c operator\<\< by default. + std::ostream& operator()( std::ostream& out) const { + if(is_ascii(out)) { + out << t.first << " " << t.second; + } else { + CGAL::write(out, t.first); + CGAL::write(out, t.second); + } + return out; + } +}; + +template <> +class Input_rep > { + typedef std::pair T; + T& t; +public: + //! initialize with a const reference to \a t. + Input_rep( T& tt) : t(tt) {} + //! perform the output, calls \c operator\<\< by default. + std::istream& operator()( std::istream& in) const { + if(is_ascii(in)) { + in >> t.first >> t.second; + } else { + CGAL::read(in, t.first); + CGAL::read(in, t.second); + } + return in; + } +}; +} // end namespace CGAL + +#endif // CGAL_INTERNAL_MESH_3_INTERNAL_HANDLE_IO_FOR_PAIR_OF_INT_H diff --git a/Mesh_3/include/CGAL/internal/Mesh_3/get_index.h b/Mesh_3/include/CGAL/internal/Mesh_3/get_index.h index 66eb3026449..25a58ff10a9 100644 --- a/Mesh_3/include/CGAL/internal/Mesh_3/get_index.h +++ b/Mesh_3/include/CGAL/internal/Mesh_3/get_index.h @@ -29,6 +29,7 @@ #include #include +#include namespace CGAL { namespace internal { @@ -87,13 +88,13 @@ struct Write_mesh_domain_index { switch(dimension) { case 0: { const Ci& ci = get_index(index); - if(is_ascii(os)) os << ci; + if(is_ascii(os)) os << oformat(ci); else CGAL::write(os, ci); } break; case 1: { const Si& si = get_index(index); - if(is_ascii(os)) os << si; + if(is_ascii(os)) os << oformat(si); else CGAL::write(os, si); } break; @@ -122,7 +123,7 @@ struct Read_mesh_domain_index { break; default: {// 3 typename MT::Subdomain_index di; - if(is_ascii(is)) is >> di; + if(is_ascii(is)) is >> iformat(di); else CGAL::read(is, di); return di; } @@ -146,13 +147,13 @@ struct Write_mesh_domain_index { switch(dimension) { case 2: { const Spi& spi = get_index(index); - if(is_ascii(os)) os << spi; + if(is_ascii(os)) os << oformat(spi); else CGAL::write(os, spi); } break; default: {// 3 const Di& di = get_index(index); - if(is_ascii(os)) os << di; + if(is_ascii(os)) os << oformat(di); else CGAL::write(os, di); } break; From fa5cb831c9c33777d032e3012175fcbb8d81e9f7 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Tue, 14 Jun 2016 16:41:38 +0200 Subject: [PATCH 03/21] Add Output_rep::is_specialized... .. that allows `Dump_c3t3` to detect if a type can be streamed, using either `operator<<` or `Output_rep`. --- Mesh_3/include/CGAL/Mesh_3/Dump_c3t3.h | 18 +++++++++---- .../Mesh_3/Handle_IO_for_pair_of_int.h | 4 +-- Mesh_3/test/Mesh_3/test_meshing_3D_image.cpp | 3 +++ Number_types/include/CGAL/CORE_BigRat.h | 6 +++-- Number_types/include/CGAL/leda_rational.h | 6 +++-- Number_types/include/CGAL/leda_real.h | 6 +++-- Number_types/test/Number_types/ioformat.cpp | 2 ++ Stream_support/include/CGAL/IO/io.h | 25 ++++++++++++++++--- .../test/Stream_support/test_ioformat.cpp | 2 ++ 9 files changed, 56 insertions(+), 16 deletions(-) diff --git a/Mesh_3/include/CGAL/Mesh_3/Dump_c3t3.h b/Mesh_3/include/CGAL/Mesh_3/Dump_c3t3.h index aba5b56c898..85e830cd6a0 100644 --- a/Mesh_3/include/CGAL/Mesh_3/Dump_c3t3.h +++ b/Mesh_3/include/CGAL/Mesh_3/Dump_c3t3.h @@ -32,9 +32,13 @@ namespace CGAL { template ::value && - is_streamable::value && - is_streamable::value && - is_streamable::value + is_streamable::value + && + (is_streamable::value || + Output_rep::is_specialized) + && + (is_streamable::value || + Output_rep::is_specialized) > struct Dump_c3t3 { void dump_c3t3(const C3t3& c3t3, std::string prefix) const { @@ -75,13 +79,17 @@ struct Dump_c3t3 { << "\n"; } - if(!is_streamable::value) { + if(!is_streamable::value && + !CGAL::Output_rep::is_specialized) + { std::cerr << " - C3t3::Surface_patch_index is not streamable\n"; std::cerr << " " << typeid(typename C3t3::Surface_patch_index).name() << "\n"; } - if(!is_streamable::value) { + if(!is_streamable::value && + !CGAL::Output_rep::is_specialized) + { std::cerr << " - C3t3::Subdomain_index is not streamable\n"; std::cerr << " " << typeid(typename C3t3::Subdomain_index).name() diff --git a/Mesh_3/include/CGAL/internal/Mesh_3/Handle_IO_for_pair_of_int.h b/Mesh_3/include/CGAL/internal/Mesh_3/Handle_IO_for_pair_of_int.h index 4e3c57eb07e..777c47a6449 100644 --- a/Mesh_3/include/CGAL/internal/Mesh_3/Handle_IO_for_pair_of_int.h +++ b/Mesh_3/include/CGAL/internal/Mesh_3/Handle_IO_for_pair_of_int.h @@ -43,7 +43,7 @@ inline std::istream& operator>>(std::istream& in, std::pair& id) { } template <> -class Output_rep > { +class Output_rep > : public IO_rep_is_specialized { typedef std::pair T; const T& t; public: @@ -62,7 +62,7 @@ public: }; template <> -class Input_rep > { +class Input_rep > : public IO_rep_is_specialized { typedef std::pair T; T& t; public: diff --git a/Mesh_3/test/Mesh_3/test_meshing_3D_image.cpp b/Mesh_3/test/Mesh_3/test_meshing_3D_image.cpp index 9d8338d92ed..9ad3418c901 100644 --- a/Mesh_3/test/Mesh_3/test_meshing_3D_image.cpp +++ b/Mesh_3/test/Mesh_3/test_meshing_3D_image.cpp @@ -71,6 +71,9 @@ public: // Verify this->verify_c3t3_volume(c3t3, 1772330*0.95, 1772330*1.05); this->verify(c3t3,domain,criteria, Bissection_tag()); + + typedef typename Mesh_domain::Surface_patch_index Patch_id; + CGAL_static_assertion(CGAL::Output_rep::is_specialized); } }; diff --git a/Number_types/include/CGAL/CORE_BigRat.h b/Number_types/include/CGAL/CORE_BigRat.h index 0be849bec7e..6afc12f1fd7 100644 --- a/Number_types/include/CGAL/CORE_BigRat.h +++ b/Number_types/include/CGAL/CORE_BigRat.h @@ -155,7 +155,7 @@ public: }; template -class Output_rep< ::CORE::BigRat, F> { +class Output_rep< ::CORE::BigRat, F> : public IO_rep_is_specialized { const ::CORE::BigRat& t; public: //! initialize with a const reference to \a t. @@ -192,7 +192,9 @@ struct Needs_parens_as_product< ::CORE::BigRat >{ }; template <> -class Output_rep< ::CORE::BigRat, Parens_as_product_tag > { +class Output_rep< ::CORE::BigRat, Parens_as_product_tag > + : public IO_rep_is_specialized +{ const ::CORE::BigRat& t; public: // Constructor diff --git a/Number_types/include/CGAL/leda_rational.h b/Number_types/include/CGAL/leda_rational.h index f242f7637bd..903e3a14545 100644 --- a/Number_types/include/CGAL/leda_rational.h +++ b/Number_types/include/CGAL/leda_rational.h @@ -209,7 +209,7 @@ public: }; template -class Output_rep< leda_rational, F> { +class Output_rep< leda_rational, F> : public IO_rep_is_specialized { const leda_rational& t; public: //! initialize with a const reference to \a t. @@ -246,7 +246,9 @@ struct Needs_parens_as_product< leda_rational >{ }; template <> -class Output_rep< leda_rational, Parens_as_product_tag > { +class Output_rep< leda_rational, Parens_as_product_tag > + : public IO_rep_is_specialized +{ const leda_rational& t; public: // Constructor diff --git a/Number_types/include/CGAL/leda_real.h b/Number_types/include/CGAL/leda_real.h index b3503a86c07..b25aa39354b 100644 --- a/Number_types/include/CGAL/leda_real.h +++ b/Number_types/include/CGAL/leda_real.h @@ -212,7 +212,7 @@ template <> class Real_embeddable_traits< leda_real > template <> -class Output_rep< ::leda::real > { +class Output_rep< ::leda::real > : public IO_rep_is_specialized { const ::leda::real& t; public: //! initialize with a const reference to \a t. @@ -226,7 +226,9 @@ public: }; template <> -class Output_rep< ::leda::real, CGAL::Parens_as_product_tag > { +class Output_rep< ::leda::real, CGAL::Parens_as_product_tag > + : public IO_rep_is_specialized +{ const ::leda::real& t; public: //! initialize with a const reference to \a t. diff --git a/Number_types/test/Number_types/ioformat.cpp b/Number_types/test/Number_types/ioformat.cpp index c8f431898f7..cc270975a85 100644 --- a/Number_types/test/Number_types/ioformat.cpp +++ b/Number_types/test/Number_types/ioformat.cpp @@ -107,6 +107,7 @@ int main() // CORE #ifdef CGAL_USE_CORE + CGAL_static_assertion(CGAL::Output_rep::is_specialized == true); //bug in io for CORE. test_it("CORE::BigInt"); test_it("CORE::BigRat"); @@ -116,6 +117,7 @@ int main() // LEDA based NTs #ifdef CGAL_USE_LEDA + CGAL_static_assertion(CGAL::Output_rep::is_specialized == true); test_it("leda_integer"); test_it("leda_rational"); test_it("leda_bigfloat"); diff --git a/Stream_support/include/CGAL/IO/io.h b/Stream_support/include/CGAL/IO/io.h index 9f7be26865b..e270de17db8 100644 --- a/Stream_support/include/CGAL/IO/io.h +++ b/Stream_support/include/CGAL/IO/io.h @@ -56,8 +56,27 @@ public: enum Mode {ASCII = 0, PRETTY, BINARY}; }; +template +struct IO_rep_is_specialized_aux +{ + static const bool is_specialized = true; +}; +template< class Dummy > +const bool IO_rep_is_specialized_aux::is_specialized; + +template +struct IO_rep_is_not_specialized_aux +{ + static const bool is_specialized = false; +}; +template< class Dummy > +const bool IO_rep_is_not_specialized_aux::is_specialized; + +typedef IO_rep_is_specialized_aux IO_rep_is_specialized; +typedef IO_rep_is_not_specialized_aux IO_rep_is_not_specialized; + template -class Output_rep { +class Output_rep : public IO_rep_is_not_specialized { const T& t; public: //! initialize with a const reference to \a t. @@ -95,7 +114,7 @@ oformat( const T& t, F) { return Output_rep(t); } * for external types not supporting our stream IO format. */ template -class Input_rep { +class Input_rep : public IO_rep_is_not_specialized { T& t; public: //! initialize with a reference to \a t. @@ -107,7 +126,7 @@ public: #if CGAL_FORCE_IFORMAT_DOUBLE || \ ( ( _MSC_VER > 1600 ) && (! defined( CGAL_NO_IFORMAT_DOUBLE )) ) template <> -class Input_rep { +class Input_rep : public IO_rep_is_specialized { double& t; public: //! initialize with a reference to \a t. diff --git a/Stream_support/test/Stream_support/test_ioformat.cpp b/Stream_support/test/Stream_support/test_ioformat.cpp index ec669bc1844..e1a45fd6d5b 100644 --- a/Stream_support/test/Stream_support/test_ioformat.cpp +++ b/Stream_support/test/Stream_support/test_ioformat.cpp @@ -59,6 +59,8 @@ void test_io(const NT& x){ } int main() { + CGAL_static_assertion(CGAL::Output_rep::is_specialized == false); + CGAL_static_assertion(CGAL::Input_rep::is_specialized == false); std::cout << "test_io: short "<< std::endl; test_io(12); From 18e5092ce71977a5c71c90b2029a9dca021ad744 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Tue, 14 Jun 2016 16:52:36 +0200 Subject: [PATCH 04/21] Document `is_specialized` --- Stream_support/doc/Stream_support/CGAL/IO/io.h | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Stream_support/doc/Stream_support/CGAL/IO/io.h b/Stream_support/doc/Stream_support/CGAL/IO/io.h index d4de8ed80db..71de9fec034 100644 --- a/Stream_support/doc/Stream_support/CGAL/IO/io.h +++ b/Stream_support/doc/Stream_support/CGAL/IO/io.h @@ -148,14 +148,19 @@ Specializations of `Output_rep` should provide the following features: template< class F > struct Output_rep< Some_type, F > { -Output_rep( const Some_type& t ); -std::ostream& operator()( std::ostream& out ) const; + static const bool is_specialized = true; + Output_rep( const Some_type& t ); + std::ostream& operator()( std::ostream& out ) const; }; \endcode You can also specialize for a formatting tag `F`. +The constant `is_specialized` can be tested by meta-programming tools to +verify that a given type can be used with `oformat()`. The class template +`Output_rep` defines `is_specialized` to the default value `false`. + */ template< typename T, typename F > class Output_rep { From 00d3b9924b49fa1ffd7e6104653bb94313399062 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Tue, 14 Jun 2016 18:19:37 +0200 Subject: [PATCH 05/21] Better random image: a big sphere plus small ones --- .gitignore | 1 + Mesh_3/examples/Mesh_3/mesh_3D_image.cpp | 1 - Mesh_3/examples/Mesh_3/random_labeled_image.h | 39 ++++++++++++------- 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/.gitignore b/.gitignore index 4c4769bf1ab..1bb95887866 100644 --- a/.gitignore +++ b/.gitignore @@ -373,6 +373,7 @@ Mesh_3/examples/Mesh_3/*.mesh.* Mesh_3/examples/Mesh_3/*.off Mesh_3/examples/Mesh_3/*.png Mesh_3/examples/Mesh_3/.*.deps +Mesh_3/examples/Mesh_3/random-image.inr Mesh_3/examples/Mesh_3/Makefile Mesh_3/examples/Mesh_3/applications Mesh_3/examples/Mesh_3/cgal_test_with_cmake diff --git a/Mesh_3/examples/Mesh_3/mesh_3D_image.cpp b/Mesh_3/examples/Mesh_3/mesh_3D_image.cpp index 6893f5d9594..15400fa0d77 100644 --- a/Mesh_3/examples/Mesh_3/mesh_3D_image.cpp +++ b/Mesh_3/examples/Mesh_3/mesh_3D_image.cpp @@ -1,4 +1,3 @@ - #include #include diff --git a/Mesh_3/examples/Mesh_3/random_labeled_image.h b/Mesh_3/examples/Mesh_3/random_labeled_image.h index 3452b1ff004..b01a87f1edd 100644 --- a/Mesh_3/examples/Mesh_3/random_labeled_image.h +++ b/Mesh_3/examples/Mesh_3/random_labeled_image.h @@ -7,36 +7,47 @@ CGAL::Image_3 random_labeled_image() const int dim = 400; const unsigned char number_of_spheres = 50; const int max_radius_of_spheres = 10; + const int radius_of_big_sphere = 80; _image* image = _createImage(dim, dim, dim, 1, 1.f, 1.f, 1.f, 1, WK_FIXED, SGN_UNSIGNED); unsigned char* ptr = (unsigned char*)(image->data); std::fill(ptr, ptr+dim*dim*dim, '\0'); + std::ptrdiff_t center = dim / 2; CGAL::Random rand(0); for(unsigned char n = number_of_spheres; n > 0 ; --n) { - std::size_t i = rand.uniform_smallint( 1 + max_radius_of_spheres, - dim-2 - max_radius_of_spheres); - std::size_t j = rand.uniform_smallint( 1 + max_radius_of_spheres, - dim-2 - max_radius_of_spheres); - std::size_t k = rand.uniform_smallint( 1 + max_radius_of_spheres, - dim-2 - max_radius_of_spheres); - for(std::ptrdiff_t ii = - max_radius_of_spheres; - ii <= max_radius_of_spheres; ++ii) + std::size_t i, j, k; + do { + i = rand.uniform_smallint(1 + max_radius_of_spheres, + dim-2 - max_radius_of_spheres); + j = rand.uniform_smallint(1 + max_radius_of_spheres, + dim-2 - max_radius_of_spheres); + k = rand.uniform_smallint(1 + max_radius_of_spheres, + dim-2 - max_radius_of_spheres); + } while ( ( CGAL::square(double(center) - i) + + CGAL::square(double(center) - j) + + CGAL::square(double(center) - k) ) + < + CGAL::square(double(radius_of_big_sphere) + 4 * max_radius_of_spheres) ); + std::ptrdiff_t radius = max_radius_of_spheres; + if(n==1) { + i = j = k = center; + radius = radius_of_big_sphere; + } + for(std::ptrdiff_t ii = - radius; ii <= radius; ++ii) { - for(std::ptrdiff_t jj = - max_radius_of_spheres; - jj <= max_radius_of_spheres; ++jj) + for(std::ptrdiff_t jj = - radius; jj <= radius; ++jj) { - for(std::ptrdiff_t kk = - max_radius_of_spheres; - kk <= max_radius_of_spheres; ++kk) + for(std::ptrdiff_t kk = - radius; kk <= radius; ++kk) { - if(ii*ii + jj*jj + kk*kk > - max_radius_of_spheres * max_radius_of_spheres) continue; + if(ii*ii + jj*jj + kk*kk > radius * radius) continue; using CGAL::IMAGEIO::static_evaluate; static_evaluate(image, i+ii, j+jj, k+kk) = n; } } } } + _writeImage(image, "random-image.inr"); return CGAL::Image_3(image); } From f87c007402199bcabfc39ee9d10dc29760763cc6 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Tue, 14 Jun 2016 18:21:01 +0200 Subject: [PATCH 06/21] Add pictures --- Mesh_3/doc/Mesh_3/fig/no-custom-init.png | Bin 0 -> 30037 bytes Mesh_3/doc/Mesh_3/fig/with-custom-init.png | Bin 0 -> 62732 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 Mesh_3/doc/Mesh_3/fig/no-custom-init.png create mode 100644 Mesh_3/doc/Mesh_3/fig/with-custom-init.png diff --git a/Mesh_3/doc/Mesh_3/fig/no-custom-init.png b/Mesh_3/doc/Mesh_3/fig/no-custom-init.png new file mode 100644 index 0000000000000000000000000000000000000000..41c313519fe316ac4325dfba0ba7153dbd2e0f54 GIT binary patch literal 30037 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 lpDF|^_% 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 y6wJQ{e2JjoeEhy6cZUI69* literal 0 HcmV?d00001 diff --git a/Mesh_3/doc/Mesh_3/fig/with-custom-init.png b/Mesh_3/doc/Mesh_3/fig/with-custom-init.png new file mode 100644 index 0000000000000000000000000000000000000000..1ef18a62e1e772d3c7c5cae62fc7b3f381517202 GIT binary patch literal 62732 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 bS?TTxD%0{aiJ%?`K>pN}v=u8sR$>1Sp*Q3W literal 0 HcmV?d00001 From 8ce45aa47c00088301fcc79bb6b0c2aee869d44d Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 15 Jun 2016 11:25:23 +0200 Subject: [PATCH 07/21] Improve the documentation of `is_specialized` --- Stream_support/doc/Stream_support/CGAL/IO/io.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Stream_support/doc/Stream_support/CGAL/IO/io.h b/Stream_support/doc/Stream_support/CGAL/IO/io.h index 71de9fec034..06b6ae5c1a0 100644 --- a/Stream_support/doc/Stream_support/CGAL/IO/io.h +++ b/Stream_support/doc/Stream_support/CGAL/IO/io.h @@ -158,8 +158,10 @@ struct Output_rep< Some_type, F > { You can also specialize for a formatting tag `F`. The constant `is_specialized` can be tested by meta-programming tools to -verify that a given type can be used with `oformat()`. The class template -`Output_rep` defines `is_specialized` to the default value `false`. +verify that a given type can be used with `oformat()`. Its value has to be +`true` in a specialization of `Output_rep`. When there is no specialization +for a type, the class template `Output_rep` defines `is_specialized` to the +default value `false`. */ template< typename T, typename F > From 07b2acdd3c9f5297281d8ab706a18181f7e5cad9 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 15 Jun 2016 13:19:40 +0200 Subject: [PATCH 08/21] Add the documentation --- Mesh_3/doc/Mesh_3/Doxyfile.in | 6 +- Mesh_3/doc/Mesh_3/Mesh_3.txt | 60 +++++++++++++++++++ Mesh_3/doc/Mesh_3/examples.txt | 3 + Mesh_3/examples/Mesh_3/mesh_3D_image.cpp | 6 +- ...sh_3D_image_with_custom_initialization.cpp | 6 +- 5 files changed, 76 insertions(+), 5 deletions(-) diff --git a/Mesh_3/doc/Mesh_3/Doxyfile.in b/Mesh_3/doc/Mesh_3/Doxyfile.in index ed15cf7f4eb..fb3d336a944 100644 --- a/Mesh_3/doc/Mesh_3/Doxyfile.in +++ b/Mesh_3/doc/Mesh_3/Doxyfile.in @@ -6,4 +6,8 @@ HTML_EXTRA_FILES = ${CGAL_PACKAGE_DOC_DIR}/fig/implicit_domain_3.jpg ${CGAL_PACKAGE_DOC_DIR}/fig/implicit_domain_5.jpg \ ${CGAL_PACKAGE_DOC_DIR}/fig/no-protection.png \ ${CGAL_PACKAGE_DOC_DIR}/fig/protection-box.png \ - ${CGAL_PACKAGE_DOC_DIR}/fig/protection-all.png + ${CGAL_PACKAGE_DOC_DIR}/fig/protection-all.png \ + ${CGAL_PACKAGE_DOC_DIR}/fig/no-custom-init.png \ + ${CGAL_PACKAGE_DOC_DIR}/fig/with-custom-init.png + +EXAMPLE_PATH += ${CGAL_PACKAGE_INCLUDE_DIR} diff --git a/Mesh_3/doc/Mesh_3/Mesh_3.txt b/Mesh_3/doc/Mesh_3/Mesh_3.txt index 7c375f4cd52..3cbc40baf27 100644 --- a/Mesh_3/doc/Mesh_3/Mesh_3.txt +++ b/Mesh_3/doc/Mesh_3/Mesh_3.txt @@ -790,6 +790,66 @@ Right: the mesh with added 1D-features in the interior of the bounding box of the image. \cgalFigureCaptionEnd +\subsubsection Mesh_3DomainsFromSegmented3DImagesWithCustomInitialization Domains from Segmented 3D Images, with a Custom Initialization + +The example \ref Mesh_3/mesh_3D_image_with_custom_initialization.cpp is a modification +of \ref Mesh_3/mesh_3D_image.cpp. The goal of that example is to show how +the default initialization of the triangulation, using random rays, can be +replaced by a new implementation. In this case, the initialization detects +all connected components in the 3D segmented image, and insert points in +the triangulation for each connected components. + +For the meshing, the example \ref Mesh_3/mesh_3D_image.cpp calls `make_mesh_3()` as follows. + +\snippet Mesh_3/mesh_3D_image.cpp Meshing + +In the example \ref Mesh_3/mesh_3D_image_with_custom_initialization.cpp, +that call is replaced by: + -# the creation of an empty `%c3t3` object, + -# a call to a non-documented function + `initialize_triangulation_from_labeled_image()` that inserts points in + the triangulation, + -# then the call to `refine_mesh_3()`. + +\snippet Mesh_3/mesh_3D_image_with_custom_initialization.cpp Meshing + +The code of the function `initialize_triangulation_from_labeled_image()` is +in the non-documented header \ref +CGAL/Mesh_3/initialize_triangulation_from_labeled_image.h\. As it is +undocumented and may be removed or modified at any time, if you wish to +use it then you should copy-paste it to your user code. The code of that +function is rather complicated. The following lines show how to insert new +points in the `%c3t3` object, with the calls to +`MeshVertexBase_3::set_dimension()` and +`MeshVertexBase_3::set_index()`. + +\snippet CGAL/Mesh_3/initialize_triangulation_from_labeled_image.h insert initial points + +The value of `index` must be consistent with the possible values of +`Mesh_domain::Index`. In \ref +CGAL/Mesh_3/initialize_triangulation_from_labeled_image.h, it is +constructed using the API of the mesh domain, as follows. First the functor +`construct_intersect` is created: + +\dontinclude CGAL/Mesh_3/initialize_triangulation_from_labeled_image.h +\skip Construct_intersection construct_intersection = +\until construct_intersection_object +then the `%Mesh_domain::Intersection` object (a `%tuple` with three +components) is constructed using a call to the functor `construct_intersection +\skip Intersection intersect = +\until construct_intersection +and eventually `%index` is constructed at the component 1 of `%intersect`. +\skipline get<1>(intersect) + +Note that the example \ref +Mesh_3/mesh_3D_image_with_custom_initialization.cpp also shows how to +create an 3D image using the undocumente API of CGAL_ImageIO. + +\snippet Mesh_3/mesh_3D_image_with_custom_initialization.cpp Create the image + +The code of the function `%random_labeled_image()` is in the example file \ref +Mesh_3/random_labeled_image.h\. + \subsection Mesh_3TuningMeshOptimization Tuning Mesh Optimization \anchor Mesh_3_subsection_examples_optimization diff --git a/Mesh_3/doc/Mesh_3/examples.txt b/Mesh_3/doc/Mesh_3/examples.txt index 682b8406c87..3e4dfaaf201 100644 --- a/Mesh_3/doc/Mesh_3/examples.txt +++ b/Mesh_3/doc/Mesh_3/examples.txt @@ -2,6 +2,9 @@ \example Mesh_3/implicit_functions.cpp \example Mesh_3/mesh_3D_image.cpp \example Mesh_3/mesh_3D_image_with_features.cpp +\example Mesh_3/mesh_3D_image_with_custom_initialization.cpp +\example Mesh_3/random_labeled_image.h +\example CGAL/Mesh_3/initialize_triangulation_from_labeled_image.h \example Mesh_3/mesh_3D_image_variable_size.cpp \example Mesh_3/mesh_implicit_domains.cpp \example Mesh_3/mesh_implicit_domains_2.cpp diff --git a/Mesh_3/examples/Mesh_3/mesh_3D_image.cpp b/Mesh_3/examples/Mesh_3/mesh_3D_image.cpp index 15400fa0d77..8fa9d544102 100644 --- a/Mesh_3/examples/Mesh_3/mesh_3D_image.cpp +++ b/Mesh_3/examples/Mesh_3/mesh_3D_image.cpp @@ -31,13 +31,14 @@ using namespace CGAL::parameters; int main(int argc, char* argv[]) { + /// [Loads image] const char* fname = (argc>1)?argv[1]:"data/liver.inr.gz"; - // Loads image CGAL::Image_3 image; if(!image.read(fname)){ std::cerr << "Error: Cannot read file " << fname << std::endl; return EXIT_FAILURE; } + /// [Loads image] // Domain Mesh_domain domain(image); @@ -46,8 +47,9 @@ int main(int argc, char* argv[]) Mesh_criteria criteria(facet_angle=30, facet_size=6, facet_distance=4, cell_radius_edge_ratio=3, cell_size=8); - // Meshing + /// [Meshing] C3t3 c3t3 = CGAL::make_mesh_3(domain, criteria); + /// [Meshing] // Output std::ofstream medit_file("out.mesh"); diff --git a/Mesh_3/examples/Mesh_3/mesh_3D_image_with_custom_initialization.cpp b/Mesh_3/examples/Mesh_3/mesh_3D_image_with_custom_initialization.cpp index eb97e7bef64..9197c4239bf 100644 --- a/Mesh_3/examples/Mesh_3/mesh_3D_image_with_custom_initialization.cpp +++ b/Mesh_3/examples/Mesh_3/mesh_3D_image_with_custom_initialization.cpp @@ -36,7 +36,9 @@ using namespace CGAL::parameters; int main() { + /// [Create the image] CGAL::Image_3 image = random_labeled_image(); + /// [Create the image] // Domain Mesh_domain domain(image); @@ -45,15 +47,15 @@ int main() Mesh_criteria criteria(facet_angle=30, facet_size=3, facet_distance=1, cell_radius_edge_ratio=3, cell_size=3); - // Meshing + /// [Meshing] C3t3 c3t3; initialize_triangulation_from_labeled_image(c3t3, domain, image, criteria, (unsigned char)0); - CGAL::refine_mesh_3(c3t3, domain, criteria); + /// [Meshing] // Output CGAL::dump_c3t3(c3t3, "out"); From 087138274c8ef9bcc2dab8b93052ba5fb93bf31d Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 15 Jun 2016 14:01:36 +0200 Subject: [PATCH 09/21] Add the pictures --- Mesh_3/doc/Mesh_3/Mesh_3.txt | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/Mesh_3/doc/Mesh_3/Mesh_3.txt b/Mesh_3/doc/Mesh_3/Mesh_3.txt index 3cbc40baf27..a03d9750c0a 100644 --- a/Mesh_3/doc/Mesh_3/Mesh_3.txt +++ b/Mesh_3/doc/Mesh_3/Mesh_3.txt @@ -841,6 +841,31 @@ components) is constructed using a call to the functor `construct_intersection and eventually `%index` is constructed at the component 1 of `%intersect`. \skipline get<1>(intersect) +The result of the custom initialization can be seen in +\cgalFigureRef{mesh3custominitimage3D}. The generated 3D image contains a +big sphere at the center, and 50 smaller spheres, generated +randomly. Without the custom initialization, only the biggest component +(the sphere at the center) was initialized and meshed. With the custom +initialization, the initial `%c3t3` object contains points on all connected +components, and all spheres are meshed. + +\cgalFigureAnchor{mesh3custominitimage3D} +
+ + + + + +
+
+\cgalFigureCaptionBegin{mesh3custominitimage3D} +Left: the mesh without the custom initialization, only the big sphere at +the center is meshed + +Right: the mesh generated after the initialization of all connected components +\cgalFigureCaptionEnd + + Note that the example \ref Mesh_3/mesh_3D_image_with_custom_initialization.cpp also shows how to create an 3D image using the undocumente API of CGAL_ImageIO. From cc49cd456135ad1285891b8058e6d2d740b93772 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 15 Jun 2016 14:05:02 +0200 Subject: [PATCH 10/21] Move the subsubsection to the right subsection --- Mesh_3/doc/Mesh_3/Mesh_3.txt | 170 +++++++++++++++++------------------ 1 file changed, 85 insertions(+), 85 deletions(-) diff --git a/Mesh_3/doc/Mesh_3/Mesh_3.txt b/Mesh_3/doc/Mesh_3/Mesh_3.txt index a03d9750c0a..a864def886a 100644 --- a/Mesh_3/doc/Mesh_3/Mesh_3.txt +++ b/Mesh_3/doc/Mesh_3/Mesh_3.txt @@ -669,6 +669,91 @@ The resulting mesh is shown in \cgalFigureRef{figureliver_3d_image_mesh}. Cut view of a 3D mesh produced from a segmented liver image. Code from subsection \ref Mesh_3_subsection_examples_3d_image generates this file. \cgalFigureEnd +\subsubsection Mesh_3DomainsFromSegmented3DImagesWithCustomInitialization Domains from Segmented 3D Images, with a Custom Initialization + +The example \ref Mesh_3/mesh_3D_image_with_custom_initialization.cpp is a modification +of \ref Mesh_3/mesh_3D_image.cpp. The goal of that example is to show how +the default initialization of the triangulation, using random rays, can be +replaced by a new implementation. In this case, the initialization detects +all connected components in the 3D segmented image, and insert points in +the triangulation for each connected components. + +For the meshing, the example \ref Mesh_3/mesh_3D_image.cpp calls `make_mesh_3()` as follows. + +\snippet Mesh_3/mesh_3D_image.cpp Meshing + +In the example \ref Mesh_3/mesh_3D_image_with_custom_initialization.cpp, +that call is replaced by: + -# the creation of an empty `%c3t3` object, + -# a call to a non-documented function + `initialize_triangulation_from_labeled_image()` that inserts points in + the triangulation, + -# then the call to `refine_mesh_3()`. + +\snippet Mesh_3/mesh_3D_image_with_custom_initialization.cpp Meshing + +The code of the function `initialize_triangulation_from_labeled_image()` is +in the non-documented header \ref +CGAL/Mesh_3/initialize_triangulation_from_labeled_image.h\. As it is +undocumented and may be removed or modified at any time, if you wish to +use it then you should copy-paste it to your user code. The code of that +function is rather complicated. The following lines show how to insert new +points in the `%c3t3` object, with the calls to +`MeshVertexBase_3::set_dimension()` and +`MeshVertexBase_3::set_index()`. + +\snippet CGAL/Mesh_3/initialize_triangulation_from_labeled_image.h insert initial points + +The value of `index` must be consistent with the possible values of +`Mesh_domain::Index`. In \ref +CGAL/Mesh_3/initialize_triangulation_from_labeled_image.h, it is +constructed using the API of the mesh domain, as follows. First the functor +`construct_intersect` is created: + +\dontinclude CGAL/Mesh_3/initialize_triangulation_from_labeled_image.h +\skip Construct_intersection construct_intersection = +\until construct_intersection_object +then the `%Mesh_domain::Intersection` object (a `%tuple` with three +components) is constructed using a call to the functor `construct_intersection +\skip Intersection intersect = +\until construct_intersection +and eventually `%index` is constructed at the component 1 of `%intersect`. +\skipline get<1>(intersect) + +The result of the custom initialization can be seen in +\cgalFigureRef{mesh3custominitimage3D}. The generated 3D image contains a +big sphere at the center, and 50 smaller spheres, generated +randomly. Without the custom initialization, only the biggest component +(the sphere at the center) was initialized and meshed. With the custom +initialization, the initial `%c3t3` object contains points on all connected +components, and all spheres are meshed. + +\cgalFigureAnchor{mesh3custominitimage3D} +
+ + + + + +
+
+\cgalFigureCaptionBegin{mesh3custominitimage3D} +Left: the mesh without the custom initialization, only the big sphere at +the center is meshed + +Right: the mesh generated after the initialization of all connected components +\cgalFigureCaptionEnd + + +Note that the example \ref +Mesh_3/mesh_3D_image_with_custom_initialization.cpp also shows how to +create an 3D image using the undocumente API of CGAL_ImageIO. + +\snippet Mesh_3/mesh_3D_image_with_custom_initialization.cpp Create the image + +The code of the function `%random_labeled_image()` is in the example file \ref +Mesh_3/random_labeled_image.h\. + \subsection Mesh_3UsingVariableSizingField Using Variable Sizing Field \subsubsection Mesh_3SizingFieldasanAnalyticalFunction Sizing Field as an Analytical Function @@ -790,91 +875,6 @@ Right: the mesh with added 1D-features in the interior of the bounding box of the image. \cgalFigureCaptionEnd -\subsubsection Mesh_3DomainsFromSegmented3DImagesWithCustomInitialization Domains from Segmented 3D Images, with a Custom Initialization - -The example \ref Mesh_3/mesh_3D_image_with_custom_initialization.cpp is a modification -of \ref Mesh_3/mesh_3D_image.cpp. The goal of that example is to show how -the default initialization of the triangulation, using random rays, can be -replaced by a new implementation. In this case, the initialization detects -all connected components in the 3D segmented image, and insert points in -the triangulation for each connected components. - -For the meshing, the example \ref Mesh_3/mesh_3D_image.cpp calls `make_mesh_3()` as follows. - -\snippet Mesh_3/mesh_3D_image.cpp Meshing - -In the example \ref Mesh_3/mesh_3D_image_with_custom_initialization.cpp, -that call is replaced by: - -# the creation of an empty `%c3t3` object, - -# a call to a non-documented function - `initialize_triangulation_from_labeled_image()` that inserts points in - the triangulation, - -# then the call to `refine_mesh_3()`. - -\snippet Mesh_3/mesh_3D_image_with_custom_initialization.cpp Meshing - -The code of the function `initialize_triangulation_from_labeled_image()` is -in the non-documented header \ref -CGAL/Mesh_3/initialize_triangulation_from_labeled_image.h\. As it is -undocumented and may be removed or modified at any time, if you wish to -use it then you should copy-paste it to your user code. The code of that -function is rather complicated. The following lines show how to insert new -points in the `%c3t3` object, with the calls to -`MeshVertexBase_3::set_dimension()` and -`MeshVertexBase_3::set_index()`. - -\snippet CGAL/Mesh_3/initialize_triangulation_from_labeled_image.h insert initial points - -The value of `index` must be consistent with the possible values of -`Mesh_domain::Index`. In \ref -CGAL/Mesh_3/initialize_triangulation_from_labeled_image.h, it is -constructed using the API of the mesh domain, as follows. First the functor -`construct_intersect` is created: - -\dontinclude CGAL/Mesh_3/initialize_triangulation_from_labeled_image.h -\skip Construct_intersection construct_intersection = -\until construct_intersection_object -then the `%Mesh_domain::Intersection` object (a `%tuple` with three -components) is constructed using a call to the functor `construct_intersection -\skip Intersection intersect = -\until construct_intersection -and eventually `%index` is constructed at the component 1 of `%intersect`. -\skipline get<1>(intersect) - -The result of the custom initialization can be seen in -\cgalFigureRef{mesh3custominitimage3D}. The generated 3D image contains a -big sphere at the center, and 50 smaller spheres, generated -randomly. Without the custom initialization, only the biggest component -(the sphere at the center) was initialized and meshed. With the custom -initialization, the initial `%c3t3` object contains points on all connected -components, and all spheres are meshed. - -\cgalFigureAnchor{mesh3custominitimage3D} -
- - - - - -
-
-\cgalFigureCaptionBegin{mesh3custominitimage3D} -Left: the mesh without the custom initialization, only the big sphere at -the center is meshed - -Right: the mesh generated after the initialization of all connected components -\cgalFigureCaptionEnd - - -Note that the example \ref -Mesh_3/mesh_3D_image_with_custom_initialization.cpp also shows how to -create an 3D image using the undocumente API of CGAL_ImageIO. - -\snippet Mesh_3/mesh_3D_image_with_custom_initialization.cpp Create the image - -The code of the function `%random_labeled_image()` is in the example file \ref -Mesh_3/random_labeled_image.h\. - \subsection Mesh_3TuningMeshOptimization Tuning Mesh Optimization \anchor Mesh_3_subsection_examples_optimization From d38226804f3a6547f3b53befe5a90f6ee5b37f2a Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 15 Jun 2016 15:43:58 +0200 Subject: [PATCH 11/21] Add buddies and shortcuts --- .../Polyhedron/Plugins/Mesh_3/Image_res_dialog.ui | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Image_res_dialog.ui b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Image_res_dialog.ui index 975d0d2962b..b54e081bb14 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Image_res_dialog.ui +++ b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Image_res_dialog.ui @@ -6,7 +6,7 @@ 0 0 - 448 + 493 282 @@ -36,7 +36,10 @@ - Please choose the image type + Please choose the image &type + + + imageType @@ -77,11 +80,14 @@ 1 - Please choose the image drawing precision + Please choose the image drawing &precision true + + precisionList + From cf66e5350353800119eaf4714f82fc3682d853de Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 15 Jun 2016 15:53:53 +0200 Subject: [PATCH 12/21] Remove the useless height==40 for the spacer --- Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Image_res_dialog.ui | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Image_res_dialog.ui b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Image_res_dialog.ui index b54e081bb14..cc36cdd38e2 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Image_res_dialog.ui +++ b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Image_res_dialog.ui @@ -7,7 +7,7 @@ 0 0 493 - 282 + 241 @@ -103,7 +103,7 @@ 20 - 40 + 0 From 94841297ee94664093a4e5d0a9f3aa4cbd340762 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 15 Jun 2016 15:54:52 +0200 Subject: [PATCH 13/21] Adjust the titles in the dialog --- Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Image_res_dialog.ui | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Image_res_dialog.ui b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Image_res_dialog.ui index cc36cdd38e2..fe7a01cb767 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Image_res_dialog.ui +++ b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Image_res_dialog.ui @@ -17,7 +17,7 @@ - Image Drawing Precision + Load a 3D image @@ -46,7 +46,7 @@ - GroupBox + Drawing settings for a segment image From b322f53e857a6453e9a163463f833bbb2411a6da Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Thu, 16 Jun 2016 12:17:13 +0200 Subject: [PATCH 14/21] fix a few typos --- Mesh_3/doc/Mesh_3/Mesh_3.txt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Mesh_3/doc/Mesh_3/Mesh_3.txt b/Mesh_3/doc/Mesh_3/Mesh_3.txt index a864def886a..f60680f08a8 100644 --- a/Mesh_3/doc/Mesh_3/Mesh_3.txt +++ b/Mesh_3/doc/Mesh_3/Mesh_3.txt @@ -669,14 +669,14 @@ The resulting mesh is shown in \cgalFigureRef{figureliver_3d_image_mesh}. Cut view of a 3D mesh produced from a segmented liver image. Code from subsection \ref Mesh_3_subsection_examples_3d_image generates this file. \cgalFigureEnd -\subsubsection Mesh_3DomainsFromSegmented3DImagesWithCustomInitialization Domains from Segmented 3D Images, with a Custom Initialization +\subsubsection Mesh_3DomainsFromSegmented3DImagesWithCustomInitialization Domains From Segmented 3D Images, with a Custom Initialization The example \ref Mesh_3/mesh_3D_image_with_custom_initialization.cpp is a modification of \ref Mesh_3/mesh_3D_image.cpp. The goal of that example is to show how the default initialization of the triangulation, using random rays, can be replaced by a new implementation. In this case, the initialization detects -all connected components in the 3D segmented image, and insert points in -the triangulation for each connected components. +all connected components in the 3D segmented image, and inserts points in +the triangulation for each connected component. For the meshing, the example \ref Mesh_3/mesh_3D_image.cpp calls `make_mesh_3()` as follows. @@ -708,13 +708,13 @@ The value of `index` must be consistent with the possible values of `Mesh_domain::Index`. In \ref CGAL/Mesh_3/initialize_triangulation_from_labeled_image.h, it is constructed using the API of the mesh domain, as follows. First the functor -`construct_intersect` is created: +`construct_intersect` is created \dontinclude CGAL/Mesh_3/initialize_triangulation_from_labeled_image.h \skip Construct_intersection construct_intersection = \until construct_intersection_object then the `%Mesh_domain::Intersection` object (a `%tuple` with three -components) is constructed using a call to the functor `construct_intersection +components) is constructed using a call to the functor `construct_intersection` \skip Intersection intersect = \until construct_intersection and eventually `%index` is constructed at the component 1 of `%intersect`. @@ -747,11 +747,11 @@ Right: the mesh generated after the initialization of all connected components Note that the example \ref Mesh_3/mesh_3D_image_with_custom_initialization.cpp also shows how to -create an 3D image using the undocumente API of CGAL_ImageIO. +create a 3D image using the undocumented API of CGAL_ImageIO. \snippet Mesh_3/mesh_3D_image_with_custom_initialization.cpp Create the image -The code of the function `%random_labeled_image()` is in the example file \ref +The code of the function `%random_labeled_image()` is in the header file \ref Mesh_3/random_labeled_image.h\. \subsection Mesh_3UsingVariableSizingField Using Variable Sizing Field From cfb6c060af500043d8a9f6df9a6c5adfd6fc60ce Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Thu, 16 Jun 2016 14:29:54 +0200 Subject: [PATCH 15/21] Try to fix a warning with MSVC > include\CGAL/Mesh_3/search_for_connected_components_in_labeled_image.h(111): warning C4267: 'argument': conversion from 'size_t' to 'const unsigned short', possible loss of data --- .../search_for_connected_components_in_labeled_image.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Mesh_3/include/CGAL/Mesh_3/search_for_connected_components_in_labeled_image.h b/Mesh_3/include/CGAL/Mesh_3/search_for_connected_components_in_labeled_image.h index 4109b8181f4..dd11f051b73 100644 --- a/Mesh_3/include/CGAL/Mesh_3/search_for_connected_components_in_labeled_image.h +++ b/Mesh_3/include/CGAL/Mesh_3/search_for_connected_components_in_labeled_image.h @@ -73,9 +73,9 @@ search_for_connected_components_in_labeled_image(const CGAL::Image_3& image, int number_of_connected_components = 0; #endif // CGAL_MESH_3_SEARCH_FOR_CONNECTED_COMPONENTS_IN_LABELED_IMAGE_VERBOSE std::size_t voxel_index = 0; - for(std::size_t k=0; k Date: Thu, 16 Jun 2016 14:35:25 +0200 Subject: [PATCH 16/21] Try to fix a warning > include\CGAL/Mesh_3/search_for_connected_components_in_labeled_image.h(192): warning C4244: 'argument': conversion from 'const ptrdiff_t' to 'const unsigned short', possible loss of data --- .../Mesh_3/search_for_connected_components_in_labeled_image.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mesh_3/include/CGAL/Mesh_3/search_for_connected_components_in_labeled_image.h b/Mesh_3/include/CGAL/Mesh_3/search_for_connected_components_in_labeled_image.h index dd11f051b73..0b0bdbad29e 100644 --- a/Mesh_3/include/CGAL/Mesh_3/search_for_connected_components_in_labeled_image.h +++ b/Mesh_3/include/CGAL/Mesh_3/search_for_connected_components_in_labeled_image.h @@ -189,7 +189,7 @@ search_for_connected_components_in_labeled_image(const CGAL::Image_3& image, const int m_n = (visited[offset_n] ? 1 : 0) + (second_pass[offset_n] ? 2 : 0); if(m_n < pass) { - Indices indices(i_n, j_n, k_n, depth+1); + Indices indices(uint(i_n), uint(j_n), uint(k_n), depth+1); queue.push(indices); } } From 955e9655615c1f10bf1be98de76b3e364d9d6f18 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Thu, 16 Jun 2016 15:08:36 +0200 Subject: [PATCH 17/21] Move code That will fix the initialization of c3t3 from 3D images, in the Polyhedron demo, with the protection of 1D-features is used, but there are no 1D-features. For other use-cases, that does not change anything. --- Mesh_3/include/CGAL/make_mesh_3.h | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/Mesh_3/include/CGAL/make_mesh_3.h b/Mesh_3/include/CGAL/make_mesh_3.h index 4e08d340c12..673066e7b65 100644 --- a/Mesh_3/include/CGAL/make_mesh_3.h +++ b/Mesh_3/include/CGAL/make_mesh_3.h @@ -257,7 +257,15 @@ struct C3t3_initializer < C3T3, MD, MC, true, CGAL::Tag_true > bool with_features, const int nb_initial_points = -1) { - if ( with_features ) { init_c3t3_with_features(c3t3,domain,criteria); } + if ( with_features ) { + init_c3t3_with_features(c3t3,domain,criteria); + + // If c3t3 initialization is not sufficient (may happen if there is only + // a planar curve as feature for example), add some surface points + if ( c3t3.triangulation().dimension() != 3 ) { + init_c3t3(c3t3, domain, criteria, nb_initial_points); + } + } else { init_c3t3(c3t3,domain,criteria,nb_initial_points); } } }; @@ -430,15 +438,8 @@ void make_mesh_3_impl(C3T3& c3t3, with_features, mesh_options.number_of_initial_points); - // If c3t3 initialization is not sufficient (may happen if there is only - // a planar curve as feature for example), add some surface points - if ( c3t3.triangulation().dimension() != 3 ) - { - internal::Mesh_3::init_c3t3(c3t3, domain, criteria, - mesh_options.number_of_initial_points); - } CGAL_assertion( c3t3.triangulation().dimension() == 3 ); - + // Build mesher and launch refinement process // Don't reset c3t3 as we just created it refine_mesh_3(c3t3, domain, criteria, From 14abbfeb3d5f4645ecdc4d986a614f4fb624ef6b Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Thu, 16 Jun 2016 15:10:49 +0200 Subject: [PATCH 18/21] initialization of connected components in the demo For a 3D segmented image, this commit adds the possibility to scan the image and initialize all connected components. --- .../Plugins/Mesh_3/Mesh_3_plugin.cpp | 5 +- .../Mesh_3/Mesh_3_plugin_cgal_code.cpp | 15 +++- .../Plugins/Mesh_3/Mesh_3_plugin_cgal_code.h | 1 + .../Polyhedron/Plugins/Mesh_3/Mesh_function.h | 88 ++++++++++++++----- .../Plugins/Mesh_3/Meshing_dialog.ui | 58 +++++++++--- 5 files changed, 125 insertions(+), 42 deletions(-) diff --git a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Mesh_3_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Mesh_3_plugin.cpp index cf4a8132b82..0c0b371c1d6 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Mesh_3_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Mesh_3_plugin.cpp @@ -187,7 +187,7 @@ void Mesh_3_plugin::mesh_3(const bool surface_only) else if (NULL != image_item) { item = image_item; - features_protection_available = (polylines_item != NULL); + features_protection_available = (polylines_item != NULL) || !image_item->isGray(); bool fit_wrdtp = true; std::size_t img_wdim = image_item->image()->image()->wdim; @@ -297,6 +297,7 @@ void Mesh_3_plugin::mesh_3(const bool surface_only) ui.protect->setEnabled(features_protection_available); ui.protect->setChecked(features_protection_available); + ui.initializationGroup->setVisible(image_item != NULL && !image_item->isGray()); ui.grayImgGroup->setVisible(image_item != NULL && image_item->isGray()); ui.volumeGroup->setVisible(!surface_only && poly_item != NULL && poly_item->polyhedron()->is_closed()); @@ -318,6 +319,7 @@ void Mesh_3_plugin::mesh_3(const bool surface_only) const double tet_sizing = !ui.noTetSizing->isChecked() ? 0 : ui.tetSizing->value(); const double edge_size = !ui.noEdgeSizing->isChecked() ? DBL_MAX : ui.edgeSizing->value(); const bool protect_features = ui.protect->isChecked(); + const bool detect_connected_components = ui.detectComponents->isChecked(); const int manifold = ui.manifoldCheckBox->isChecked() ? 1 : 0; const float iso_value = ui.iso_value_spinBox->value(); const float value_outside = ui.value_outside_spinBox->value(); @@ -399,6 +401,7 @@ void Mesh_3_plugin::mesh_3(const bool surface_only) protect_features, manifold, scene, + detect_connected_components, image_item->isGray(), iso_value, value_outside, diff --git a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Mesh_3_plugin_cgal_code.cpp b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Mesh_3_plugin_cgal_code.cpp index 9691ec013a8..66257245bf9 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Mesh_3_plugin_cgal_code.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Mesh_3_plugin_cgal_code.cpp @@ -81,7 +81,8 @@ Meshing_thread* cgal_code_mesh_3(const Polyhedron* pMesh, param.manifold = manifold; param.protect_features = protect_features; - typedef ::Mesh_function Mesh_function; + typedef ::Mesh_function Mesh_function; Mesh_function* p_mesh_function = new Mesh_function(p_new_item->c3t3(), p_domain, param); return new Meshing_thread(p_mesh_function, p_new_item); @@ -124,7 +125,8 @@ Meshing_thread* cgal_code_mesh_3(const Implicit_function_interface* pfunction, param.edge_sizing = edge_size; param.manifold = manifold; - typedef ::Mesh_function Mesh_function; + typedef ::Mesh_function Mesh_function; Mesh_function* p_mesh_function = new Mesh_function(p_new_item->c3t3(), p_domain, param); return new Meshing_thread(p_mesh_function, p_new_item); @@ -150,6 +152,7 @@ Meshing_thread* cgal_code_mesh_3(const Image* pImage, bool protect_features, const int manifold, CGAL::Three::Scene_interface* scene, + bool detect_connected_components, bool is_gray, float iso_value, float value_outside, @@ -161,6 +164,7 @@ Meshing_thread* cgal_code_mesh_3(const Image* pImage, Mesh_parameters param; param.protect_features = protect_features; + param.detect_connected_components = detect_connected_components; param.facet_angle = facet_angle; param.facet_sizing = facet_sizing; param.facet_approx = facet_approx; @@ -168,6 +172,7 @@ Meshing_thread* cgal_code_mesh_3(const Image* pImage, param.edge_sizing = edge_size; param.tet_shape = tet_shape; param.manifold = manifold; + param.image_3_ptr = pImage; CGAL::Timer timer; Scene_c3t3_item* p_new_item = new Scene_c3t3_item; p_new_item->setScene(scene); @@ -187,7 +192,8 @@ Meshing_thread* cgal_code_mesh_3(const Image* pImage, p_domain->add_features(polylines.begin(), polylines.end()); } timer.start(); - typedef ::Mesh_function Mesh_function; + typedef ::Mesh_function Mesh_function; Mesh_function* p_mesh_function = new Mesh_function(p_new_item->c3t3(), p_domain, param); return new Meshing_thread(p_mesh_function, p_new_item); @@ -214,7 +220,8 @@ Meshing_thread* cgal_code_mesh_3(const Image* pImage, p_domain->add_features(polylines.begin(), polylines.end()); } timer.start(); - typedef ::Mesh_function Mesh_function; + typedef ::Mesh_function Mesh_function; Mesh_function* p_mesh_function = new Mesh_function(p_new_item->c3t3(), p_domain, param); return new Meshing_thread(p_mesh_function, p_new_item); diff --git a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Mesh_3_plugin_cgal_code.h b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Mesh_3_plugin_cgal_code.h index a18743d2902..59faf1b7bfd 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Mesh_3_plugin_cgal_code.h +++ b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Mesh_3_plugin_cgal_code.h @@ -56,6 +56,7 @@ Meshing_thread* cgal_code_mesh_3(const CGAL::Image_3* pImage, bool protect_features, const int manifold, CGAL::Three::Scene_interface* scene, + bool detect_connected_components, bool is_gray = false, float iso_value = 3.f, float value_outside = 0.f, diff --git a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Mesh_function.h b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Mesh_function.h index a6b4a6b3660..25537a96741 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Mesh_function.h +++ b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Mesh_function.h @@ -35,12 +35,17 @@ #include #include #include +#include #include "C3t3_type.h" #include "Meshing_thread.h" #include // for C3t3_initializer #include +namespace CGAL { + class Image_3; +} + struct Mesh_parameters { double facet_angle; @@ -51,13 +56,15 @@ struct Mesh_parameters double tet_sizing; double edge_sizing; bool protect_features; + bool detect_connected_components; int manifold; + const CGAL::Image_3* image_3_ptr; inline QStringList log() const; }; -template < typename Domain_ > +template < typename Domain_, typename Image_tag > class Mesh_function : public Mesh_function_interface { @@ -93,6 +100,9 @@ private: typedef CGAL::Mesh_3::Mesher_3 Mesher; + void initialize(const Mesh_criteria& criteria, CGAL::Tag_true); + void initialize(const Mesh_criteria& criteria, CGAL::Tag_false); + private: C3t3& c3t3_; Domain* domain_; @@ -121,6 +131,8 @@ log() const << QString("facet approx error: %1").arg(facet_approx) << QString("tet shape (radius-edge): %1").arg(tet_shape) << QString("tet max size: %1").arg(tet_sizing) + << QString("detect connected components: %1") + .arg(detect_connected_components) << QString("protect features: %1").arg(protect_features); } @@ -128,8 +140,8 @@ log() const // ----------------------------------- // Class Mesh_function // ----------------------------------- -template < typename D_ > -Mesh_function:: +template < typename D_, typename Tag > +Mesh_function:: Mesh_function(C3t3& c3t3, Domain* domain, const Mesh_parameters& p) : c3t3_(c3t3) , domain_(domain) @@ -146,8 +158,8 @@ Mesh_function(C3t3& c3t3, Domain* domain, const Mesh_parameters& p) } -template < typename D_ > -Mesh_function:: +template < typename D_, typename Tag > +Mesh_function:: ~Mesh_function() { delete domain_; @@ -162,9 +174,47 @@ CGAL::Mesh_facet_topology topology(int manifold) { CGAL::FACET_VERTICES_ON_SAME_SURFACE_PATCH); } -template < typename D_ > +template < typename D_, typename Tag > void -Mesh_function:: +Mesh_function:: +initialize(const Mesh_criteria& criteria, CGAL::Tag_true) // for an image +{ + if(p_.detect_connected_components) { + initialize_triangulation_from_labeled_image(c3t3_ + , *domain_ + , *p_.image_3_ptr + , criteria + , typename D_::Image_word_type() + , p_.protect_features); + } else { + initialize(criteria, CGAL::Tag_false()); + } +} + +template < typename D_, typename Tag > +void +Mesh_function:: +initialize(const Mesh_criteria& criteria, CGAL::Tag_false) // for the other domain types +{ + // Initialization of the mesh, either with the protection of sharp + // features, or with the initial points (or both). + // If `detect_connected_components==true`, the initialization is + // already done. + CGAL::internal::Mesh_3::C3t3_initializer< + C3t3, + Domain, + Mesh_criteria, + CGAL::internal::Mesh_3::has_Has_features::value >() + (c3t3_, + *domain_, + criteria, + p_.protect_features); +} + + +template < typename D_, typename Tag > +void +Mesh_function:: launch() { #ifdef CGAL_MESH_3_INITIAL_POINTS_NO_RANDOM_SHOOTING @@ -180,17 +230,7 @@ launch() Cell_criteria(p_.tet_shape, p_.tet_sizing)); - // Initialization of the mesh, either with the protection of sharp - // features, or with the initial points (or both). - CGAL::internal::Mesh_3::C3t3_initializer< - C3t3, - Domain, - Mesh_criteria, - CGAL::internal::Mesh_3::has_Has_features::value >() - (c3t3_, - *domain_, - criteria, - p_.protect_features); + initialize(criteria, CGAL::Boolean_tag()); // Build mesher and launch refinement process mesher_ = new Mesher(c3t3_, *domain_, criteria); @@ -219,27 +259,27 @@ launch() } -template < typename D_ > +template < typename D_, typename Tag > void -Mesh_function:: +Mesh_function:: stop() { continue_ = false; } -template < typename D_ > +template < typename D_, typename Tag > QStringList -Mesh_function:: +Mesh_function:: parameters_log() const { return p_.log(); } -template < typename D_ > +template < typename D_, typename Tag > QString -Mesh_function:: +Mesh_function:: status(double time_period) const { QString result; diff --git a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Meshing_dialog.ui b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Meshing_dialog.ui index d70ca06d79f..e9bc4d64665 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Meshing_dialog.ui +++ b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Meshing_dialog.ui @@ -10,7 +10,7 @@ 0 0 473 - 637 + 662 @@ -47,6 +47,38 @@ + + + + Mesh initialization + + + + + + + + + + + + + Qt::LeftToRight + + + &Detect all connected components + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + detectComponents + + + + + + @@ -88,9 +120,6 @@ - - Qt::RightToLeft - @@ -102,7 +131,10 @@ - Protect sharp edges + &Protect sharp edges + + + protect @@ -507,8 +539,8 @@ accept() - 397 - 544 + 403 + 655 157 @@ -523,8 +555,8 @@ reject() - 397 - 544 + 403 + 655 286 @@ -539,12 +571,12 @@ setVisible(bool) - 69 - 283 + 83 + 339 - 72 - 297 + 78 + 199 From b95a960058f52b28645a64fff47b7709fe24a774 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Thu, 16 Jun 2016 15:30:08 +0200 Subject: [PATCH 19/21] Minor modifications ... after feedback from @afabri. --- Mesh_3/doc/Mesh_3/Mesh_3.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Mesh_3/doc/Mesh_3/Mesh_3.txt b/Mesh_3/doc/Mesh_3/Mesh_3.txt index f60680f08a8..90f27f8e693 100644 --- a/Mesh_3/doc/Mesh_3/Mesh_3.txt +++ b/Mesh_3/doc/Mesh_3/Mesh_3.txt @@ -678,7 +678,7 @@ replaced by a new implementation. In this case, the initialization detects all connected components in the 3D segmented image, and inserts points in the triangulation for each connected component. -For the meshing, the example \ref Mesh_3/mesh_3D_image.cpp calls `make_mesh_3()` as follows. +For the meshing, in the previous example (\ref Mesh_3/mesh_3D_image.cpp), we called `make_mesh_3()` as follows. \snippet Mesh_3/mesh_3D_image.cpp Meshing @@ -714,10 +714,10 @@ constructed using the API of the mesh domain, as follows. First the functor \skip Construct_intersection construct_intersection = \until construct_intersection_object then the `%Mesh_domain::Intersection` object (a `%tuple` with three -components) is constructed using a call to the functor `construct_intersection` +elements) is constructed using a call to the functor `construct_intersection` \skip Intersection intersect = \until construct_intersection -and eventually `%index` is constructed at the component 1 of `%intersect`. +and eventually `%index` is the element \#1 of `%intersect`. \skipline get<1>(intersect) The result of the custom initialization can be seen in From bbc05a11d9d985dde6e25751d5e1575a7cc2925e Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Thu, 16 Jun 2016 16:35:20 +0200 Subject: [PATCH 20/21] Fix -Wconversion warnings All conversions are safe. --- Mesh_3/examples/Mesh_3/random_labeled_image.h | 6 +++--- .../Mesh_3/initialize_triangulation_from_labeled_image.h | 4 ++-- .../search_for_connected_components_in_labeled_image.h | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Mesh_3/examples/Mesh_3/random_labeled_image.h b/Mesh_3/examples/Mesh_3/random_labeled_image.h index b01a87f1edd..8d03d462d49 100644 --- a/Mesh_3/examples/Mesh_3/random_labeled_image.h +++ b/Mesh_3/examples/Mesh_3/random_labeled_image.h @@ -25,9 +25,9 @@ CGAL::Image_3 random_labeled_image() dim-2 - max_radius_of_spheres); k = rand.uniform_smallint(1 + max_radius_of_spheres, dim-2 - max_radius_of_spheres); - } while ( ( CGAL::square(double(center) - i) + - CGAL::square(double(center) - j) + - CGAL::square(double(center) - k) ) + } while ( ( CGAL::square(double(center) - double(i)) + + CGAL::square(double(center) - double(j)) + + CGAL::square(double(center) - double(k)) ) < CGAL::square(double(radius_of_big_sphere) + 4 * max_radius_of_spheres) ); std::ptrdiff_t radius = max_radius_of_spheres; diff --git a/Mesh_3/include/CGAL/Mesh_3/initialize_triangulation_from_labeled_image.h b/Mesh_3/include/CGAL/Mesh_3/initialize_triangulation_from_labeled_image.h index 422d7949d90..db729c8ec6d 100644 --- a/Mesh_3/include/CGAL/Mesh_3/initialize_triangulation_from_labeled_image.h +++ b/Mesh_3/include/CGAL/Mesh_3/initialize_triangulation_from_labeled_image.h @@ -46,7 +46,7 @@ struct Get_point const std::size_t j, const std::size_t k) const { - return Point(i * vx, j * vy, k * vz); + return Point(double(i) * vx, double(j) * vy, double(k) * vz); } }; template @@ -119,7 +119,7 @@ void initialize_triangulation_from_labeled_image(C3T3& c3t3, for(typename Seeds::const_iterator it = seeds.begin(), end = seeds.end(); it != end; ++it) { - const double radius = (it->second + 1)* max_v; + const double radius = double(it->second + 1)* max_v; CGAL::Random_points_on_sphere_3 points_on_sphere_3(radius); typename Mesh_domain::Construct_intersection construct_intersection = domain.construct_intersection_object(); diff --git a/Mesh_3/include/CGAL/Mesh_3/search_for_connected_components_in_labeled_image.h b/Mesh_3/include/CGAL/Mesh_3/search_for_connected_components_in_labeled_image.h index 0b0bdbad29e..205d0e8af1e 100644 --- a/Mesh_3/include/CGAL/Mesh_3/search_for_connected_components_in_labeled_image.h +++ b/Mesh_3/include/CGAL/Mesh_3/search_for_connected_components_in_labeled_image.h @@ -189,7 +189,7 @@ search_for_connected_components_in_labeled_image(const CGAL::Image_3& image, const int m_n = (visited[offset_n] ? 1 : 0) + (second_pass[offset_n] ? 2 : 0); if(m_n < pass) { - Indices indices(uint(i_n), uint(j_n), uint(k_n), depth+1); + Indices indices(uint(i_n), uint(j_n), uint(k_n), uint(depth+1)); queue.push(indices); } } From 378e22aa242e5fceeedb7168252559ec350d53ea Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 22 Jun 2016 12:41:26 +0200 Subject: [PATCH 21/21] Fix a warning with -DCGAL_NDEBUG --- Mesh_3/test/Mesh_3/test_meshing_3D_image.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Mesh_3/test/Mesh_3/test_meshing_3D_image.cpp b/Mesh_3/test/Mesh_3/test_meshing_3D_image.cpp index 9ad3418c901..81b9d189cd1 100644 --- a/Mesh_3/test/Mesh_3/test_meshing_3D_image.cpp +++ b/Mesh_3/test/Mesh_3/test_meshing_3D_image.cpp @@ -46,8 +46,6 @@ public: typedef typename Mesh_criteria::Facet_criteria Facet_criteria; typedef typename Mesh_criteria::Cell_criteria Cell_criteria; - CGAL_USE_TYPE(typename Mesh_domain::Surface_patch_index); - //------------------------------------------------------- // Data generation //------------------------------------------------------- @@ -74,6 +72,7 @@ public: typedef typename Mesh_domain::Surface_patch_index Patch_id; CGAL_static_assertion(CGAL::Output_rep::is_specialized); + CGAL_USE_TYPE(Patch_id); } };