1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
|
// This example demonstrates how to interrupt the wrapping process before it has terminated,
// and how to resume afterwards.
//
// -------------------------------- !! Warning !! --------------------------------------------------
// By default, the wrapper uses an unsorted LIFO queue of faces to refine. This means that
// the intermediate result is not very useful because the algorithm carves deep and not wide
// (somewhat like a DFS vs a BFS).
//
// The sorted queue option is enabled with the macro below to make the refinement algorithm
// more uniform. The downside is that it is slower.
// -------------------------------------------------------------------------------------------------
#define CGAL_AW3_USE_SORTED_PRIORITY_QUEUE
#include "output_helper.h"
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/alpha_wrap_3.h>
#include <CGAL/IO/polygon_soup_io.h>
#include <CGAL/Polygon_mesh_processing/measure.h>
#include <CGAL/Random.h>
#include <CGAL/Timer.h>
#include <iostream>
#include <string>
namespace AW3 = CGAL::Alpha_wraps_3;
namespace PMP = CGAL::Polygon_mesh_processing;
using K = CGAL::Exact_predicates_inexact_constructions_kernel;
using Point_3 = K::Point_3;
using Points = std::vector<Point_3>;
using Face = std::array<std::size_t, 3>;
using Faces = std::vector<Face>;
using Mesh = CGAL::Surface_mesh<Point_3>;
using face_descriptor = boost::graph_traits<Mesh>::face_descriptor;
struct Interrupter_visitor
: public AW3::internal::Wrapping_default_visitor
{
using Base = AW3::internal::Wrapping_default_visitor;
CGAL::Real_timer timer;
double max_time = -1; // in seconds
public:
void set_max_time(double t) { max_time = t; }
public:
template <typename AlphaWrapper>
void on_flood_fill_begin(const AlphaWrapper&)
{
std::cout << "Starting timer..." << std::endl;
timer.start();
}
template <typename Wrapper>
bool go_further(const Wrapper&)
{
if(timer.time() > max_time)
{
timer.stop();
std::cout << "Paused after " << timer.time() << " s." << std::endl;
return false;
}
return true;
}
};
int main(int argc, char** argv)
{
std::cout.precision(17);
std::cerr.precision(17);
CGAL::Random rng;
std::cout << "Random seed = " << rng.get_seed() << std::endl;
const std::string filename = (argc > 1) ? argv[1] : CGAL::data_file_path("meshes/armadillo.off");
// = read the soup
Points points;
Faces faces;
if(!CGAL::IO::read_polygon_soup(filename, points, faces) || faces.empty())
{
std::cerr << "Invalid soup input: " << filename << std::endl;
return EXIT_FAILURE;
}
std::cout << "Input: " << points.size() << " points, " << faces.size() << " faces" << std::endl;
// Compute the alpha and offset values
const double relative_alpha = (argc > 2) ? std::stod(argv[2]) : rng.get_double(150., 200.);
const double relative_offset = (argc > 3) ? std::stod(argv[3]) : 600.;
std::cout << "relative_alpha = " << relative_alpha << std::endl;
CGAL::Bbox_3 bbox;
for(const Point_3& p : points)
bbox += p.bbox();
const double diag_length = std::sqrt(CGAL::square(bbox.xmax() - bbox.xmin()) +
CGAL::square(bbox.ymax() - bbox.ymin()) +
CGAL::square(bbox.zmax() - bbox.zmin()));
const double alpha = diag_length / relative_alpha;
const double offset = diag_length / relative_offset;
// Build the wrapper
using Oracle = CGAL::Alpha_wraps_3::internal::Triangle_soup_oracle<K>;
Oracle oracle(alpha);
oracle.add_triangle_soup(points, faces, CGAL::parameters::default_values());
CGAL::Alpha_wraps_3::internal::Alpha_wrapper_3<Oracle> aw3(oracle);
// --- Launch the wrapping, and pause when the algorithm has spent 1s flooding
Interrupter_visitor interrupter;
interrupter.set_max_time(1.);
Mesh wrap;
aw3(alpha, offset, wrap, CGAL::parameters::visitor(interrupter));
std::cout << ">>> The current wrap has " << num_vertices(wrap) << " vertices" << std::endl;
CGAL::IO::write_polygon_mesh("stopped_1.off", wrap, CGAL::parameters::stream_precision(17));
// --- Restart from the previous state, and pause a bit further
interrupter.set_max_time(2.);
aw3(alpha, offset, wrap, CGAL::parameters::visitor(interrupter)
.refine_triangulation(true));
std::cout << ">>> The current wrap has " << num_vertices(wrap) << " vertices" << std::endl;
CGAL::IO::write_polygon_mesh("stopped_2.off", wrap, CGAL::parameters::stream_precision(17));
// --- Restart from the previous state, and let it finish
aw3(alpha, offset, wrap, CGAL::parameters::refine_triangulation(true));
std::cout << ">>> The final (resumed) wrap has " << num_vertices(wrap) << " vertices" << std::endl;
std::string output_name = generate_output_name(filename, relative_alpha, relative_offset);
std::cout << "Writing to " << "resumed_" + output_name << std::endl;
CGAL::IO::write_polygon_mesh("resumed_" + output_name, wrap, CGAL::parameters::stream_precision(17));
// --- Get the final wrap, in one go:
Mesh single_pass_wrap;
CGAL::alpha_wrap_3(points, faces, alpha, offset, single_pass_wrap);
std::cout << ">>> The final (from scratch) wrap has " << num_vertices(single_pass_wrap) << " vertices" << std::endl;
output_name = generate_output_name(filename, relative_alpha, relative_offset);
std::cout << "Writing to " << output_name << std::endl;
CGAL::IO::write_polygon_mesh(output_name, single_pass_wrap, CGAL::parameters::stream_precision(17));
// --- Compare the results to ensure both approaches yield identical meshes
std::vector<std::pair<face_descriptor, face_descriptor> > common;
std::vector<face_descriptor> m1_only;
std::vector<face_descriptor> m2_only;
PMP::match_faces(wrap, single_pass_wrap,
std::back_inserter(common),
std::back_inserter(m1_only),
std::back_inserter(m2_only));
if(!m1_only.empty() || !m2_only.empty())
{
std::cerr << "Error: The two wraps should have been identical!" << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
|