mirror of https://github.com/CGAL/cgal
Misc minor improvements
This commit is contained in:
parent
847795ec00
commit
d51d71a563
|
|
@ -2,7 +2,7 @@
|
||||||
// and how to resume afterwards.
|
// and how to resume afterwards.
|
||||||
//
|
//
|
||||||
// -------------------------------- !! Warning !! --------------------------------------------------
|
// -------------------------------- !! Warning !! --------------------------------------------------
|
||||||
// By default, the wrap uses an unsorted LIFO queue of faces to refine. This means that
|
// 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
|
// the intermediate result is not very useful because the algorithm carves deep and not wide
|
||||||
// (somewhat like a DFS vs a BFS).
|
// (somewhat like a DFS vs a BFS).
|
||||||
//
|
//
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,21 @@
|
||||||
#define CGAL_AW3_TIMER
|
// In this example, we reuse the underlying triangulation of the previous state, and carve using
|
||||||
|
// a new (smaller) alpha value. This enables considerable speed-up: the cumulated time taken
|
||||||
|
// to run `n` successive instances of `{alpha_wrap(alpha_i)}_(i=1...n)` will be roughly equal
|
||||||
|
// to the time taken to the single instance of alpha_wrap(alpha_n) from scratch.
|
||||||
|
//
|
||||||
|
// The speed-up increases with the number of intermediate results, and on the gap between
|
||||||
|
// alpha values: if alpha_2 is close to alpha_1, practically no new computation are required,
|
||||||
|
// and the speed-up is almost 100%.
|
||||||
|
//
|
||||||
|
// -------------------------------- !! Warning !! --------------------------------------------------
|
||||||
|
// The result of:
|
||||||
|
// > alpha_wrap(alpha_1, ...)
|
||||||
|
// > alpha_wrap(alpha_2, ..., reuse)
|
||||||
|
// is not exactly identical to calling directly:
|
||||||
|
// > alpha_wrap(alpha_2, ..., do_not_reuse)
|
||||||
|
// because the queues are sorted slightly differently and the AABB tree is rebuilt differently
|
||||||
|
// to optimize the runtime.
|
||||||
|
// -------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
#include "output_helper.h"
|
#include "output_helper.h"
|
||||||
|
|
||||||
|
|
@ -21,10 +38,6 @@ using Point_3 = K::Point_3;
|
||||||
|
|
||||||
using Mesh = CGAL::Surface_mesh<Point_3>;
|
using Mesh = CGAL::Surface_mesh<Point_3>;
|
||||||
|
|
||||||
// We want decreasing alphas, and these are relative ratios, so they need to be increasing
|
|
||||||
const std::vector<FT> relative_alphas = { 1, 2/*50, 100, 150, 200, 250*/ };
|
|
||||||
const FT relative_offset = 600;
|
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
std::cout.precision(17);
|
std::cout.precision(17);
|
||||||
|
|
@ -48,6 +61,10 @@ int main(int argc, char** argv)
|
||||||
CGAL::square(bbox.ymax() - bbox.ymin()) +
|
CGAL::square(bbox.ymax() - bbox.ymin()) +
|
||||||
CGAL::square(bbox.zmax() - bbox.zmin()));
|
CGAL::square(bbox.zmax() - bbox.zmin()));
|
||||||
|
|
||||||
|
// We want decreasing alphas, and these are relative ratios, so they need to be increasing
|
||||||
|
const std::vector<FT> relative_alphas = { 1, 50, 100, 150, 200, 250 };
|
||||||
|
const FT relative_offset = 600;
|
||||||
|
|
||||||
// ===============================================================================================
|
// ===============================================================================================
|
||||||
// Naive approach:
|
// Naive approach:
|
||||||
|
|
||||||
|
|
@ -64,17 +81,12 @@ int main(int argc, char** argv)
|
||||||
std::cout << ">>> [" << i << "] alpha: " << alpha << " offset: " << offset << std::endl;
|
std::cout << ">>> [" << i << "] alpha: " << alpha << " offset: " << offset << std::endl;
|
||||||
|
|
||||||
Mesh wrap;
|
Mesh wrap;
|
||||||
CGAL::alpha_wrap_3(mesh, alpha, offset, wrap,
|
CGAL::alpha_wrap_3(mesh, alpha, offset, wrap);
|
||||||
CGAL::parameters::do_enforce_manifoldness(false));
|
|
||||||
|
|
||||||
t.stop();
|
t.stop();
|
||||||
std::cout << " Result: " << num_vertices(wrap) << " vertices, " << num_faces(wrap) << " faces" << std::endl;
|
std::cout << " Result: " << num_vertices(wrap) << " vertices, " << num_faces(wrap) << " faces" << std::endl;
|
||||||
std::cout << " Elapsed time: " << t.time() << " s." << std::endl;
|
std::cout << " Elapsed time: " << t.time() << " s." << std::endl;
|
||||||
|
|
||||||
const std::string output_name = generate_output_name(filename, relative_alphas[i], relative_offset);
|
|
||||||
std::cout << "Writing to " << output_name << std::endl;
|
|
||||||
CGAL::IO::write_polygon_mesh(output_name, wrap, CGAL::parameters::stream_precision(17));
|
|
||||||
|
|
||||||
total_time += t.time();
|
total_time += t.time();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -82,29 +94,6 @@ int main(int argc, char** argv)
|
||||||
|
|
||||||
// ===============================================================================================
|
// ===============================================================================================
|
||||||
// Re-use approach
|
// Re-use approach
|
||||||
//
|
|
||||||
// Here, we restart from the triangulation of the previous state, and carve according
|
|
||||||
// to a (smaller) alpha value. This enables considerable speed-up: the cumulated time taken
|
|
||||||
// to run `n` successive instances of `{alpha_wrap(alpha_i)}_(i=1...n)` will be equal to the
|
|
||||||
// time taken to run alpha_wrap(alpha_n) from scratch.
|
|
||||||
//
|
|
||||||
// For example:
|
|
||||||
// naive:
|
|
||||||
// alpha_wrap(alpha_1, ...) ---> 2s
|
|
||||||
// alpha_wrap(alpha_2, ...) ---> 4s
|
|
||||||
// alpha_wrap(alpha_3, ...) ---> 8s
|
|
||||||
// will become with reusability:
|
|
||||||
// alpha_wrap(alpha_1, ..., reuse) ---> 2s
|
|
||||||
// alpha_wrap(alpha_2, ..., reuse) ---> 2s // 2+2 = 4s = naive alpha_2
|
|
||||||
// alpha_wrap(alpha_3, ..., reuse) ---> 4s // 2+2+4 = 8s = naive alpha_3
|
|
||||||
// Thus, if we care about the intermediate results, we save 6s (8s instead of 14s).
|
|
||||||
// The speed-up increases with the number of intermediate results, and if the alpha values
|
|
||||||
// are close.
|
|
||||||
//
|
|
||||||
// !! Warning !!
|
|
||||||
// The result of alpha_wrap(alpha_1, ...) followed by alpha_wrap(alpha_2, ...) with alpha_2
|
|
||||||
// smaller than alpha_1 is going to be close but NOT exactly equal to that produced by calling
|
|
||||||
// alpha_wrap(alpha_2, ...) directly.
|
|
||||||
|
|
||||||
total_time = 0.;
|
total_time = 0.;
|
||||||
t.reset();
|
t.reset();
|
||||||
|
|
@ -122,7 +111,7 @@ int main(int argc, char** argv)
|
||||||
const double offset = diag_length / relative_offset;
|
const double offset = diag_length / relative_offset;
|
||||||
std::cout << ">>> [" << i << "] alpha: " << alpha << " offset: " << offset << std::endl;
|
std::cout << ">>> [" << i << "] alpha: " << alpha << " offset: " << offset << std::endl;
|
||||||
|
|
||||||
// The triangle mesh oracle can be initialized with alpha to internally perform a split
|
// The triangle mesh oracle should be initialized with alpha to internally perform a split
|
||||||
// of too-big facets while building the AABB Tree. This split in fact yields a significant
|
// of too-big facets while building the AABB Tree. This split in fact yields a significant
|
||||||
// speed-up for meshes with elements that are large compared to alpha. This speed-up makes it
|
// speed-up for meshes with elements that are large compared to alpha. This speed-up makes it
|
||||||
// faster to re-build the AABB tree for every value of alpha than to use a non-optimized tree.
|
// faster to re-build the AABB tree for every value of alpha than to use a non-optimized tree.
|
||||||
|
|
@ -131,9 +120,7 @@ int main(int argc, char** argv)
|
||||||
wrapper.oracle() = oracle;
|
wrapper.oracle() = oracle;
|
||||||
|
|
||||||
Mesh wrap;
|
Mesh wrap;
|
||||||
wrapper(alpha, offset, wrap,
|
wrapper(alpha, offset, wrap, CGAL::parameters::refine_triangulation((i != 0)));
|
||||||
CGAL::parameters::do_enforce_manifoldness(false)
|
|
||||||
.refine_triangulation((i != 0)));
|
|
||||||
|
|
||||||
t.stop();
|
t.stop();
|
||||||
std::cout << " Result: " << num_vertices(wrap) << " vertices, " << num_faces(wrap) << " faces" << std::endl;
|
std::cout << " Result: " << num_vertices(wrap) << " vertices, " << num_faces(wrap) << " faces" << std::endl;
|
||||||
|
|
|
||||||
|
|
@ -125,7 +125,7 @@ class Alpha_wrapper_3
|
||||||
|
|
||||||
using Default_Vb = Alpha_wrap_triangulation_vertex_base_3<Default_Gt>;
|
using Default_Vb = Alpha_wrap_triangulation_vertex_base_3<Default_Gt>;
|
||||||
using Default_Cb = Alpha_wrap_triangulation_cell_base_3<Default_Gt>;
|
using Default_Cb = Alpha_wrap_triangulation_cell_base_3<Default_Gt>;
|
||||||
using Default_Cbt = Cell_base_with_timestamp<Default_Cb>; // determinism
|
using Default_Cbt = Cell_base_with_timestamp<Default_Cb>; // for determinism
|
||||||
using Default_Tds = CGAL::Triangulation_data_structure_3<Default_Vb, Default_Cbt>;
|
using Default_Tds = CGAL::Triangulation_data_structure_3<Default_Vb, Default_Cbt>;
|
||||||
using Default_Triangulation = CGAL::Delaunay_triangulation_3<Default_Gt, Default_Tds, Fast_location>;
|
using Default_Triangulation = CGAL::Delaunay_triangulation_3<Default_Gt, Default_Tds, Fast_location>;
|
||||||
|
|
||||||
|
|
@ -143,12 +143,13 @@ private:
|
||||||
|
|
||||||
using Gate = internal::Gate<Triangulation>;
|
using Gate = internal::Gate<Triangulation>;
|
||||||
|
|
||||||
// A sorted queue is a priority queue sorted by circumradius, and is experimentally much slower,
|
// A sorted queue is a priority queue sorted by circumradius, and is experimentally significantly
|
||||||
// but intermediate results are visually nice: somewhat uniform meshes.
|
// slower. However, intermediate results are usable: at each point of the algorithm, the wrap
|
||||||
|
// has a somewhat uniform mesh element size.
|
||||||
//
|
//
|
||||||
// An unsorted queue is a LIFO queue, and is experimentally much faster (~35%),
|
// An unsorted queue is a LIFO queue, and is experimentally much faster (~35%),
|
||||||
// but intermediate results are not useful: a LIFO will mean carving is done very deep
|
// but intermediate results are not useful: a LIFO carves deep before than wide,
|
||||||
// before than wide
|
// and can thus for example leave scaffolding faces till almost the end of the refinement.
|
||||||
#ifdef CGAL_AW3_USE_SORTED_PRIORITY_QUEUE
|
#ifdef CGAL_AW3_USE_SORTED_PRIORITY_QUEUE
|
||||||
using Alpha_PQ = Modifiable_priority_queue<Gate, Less_gate, Gate_ID_PM<Triangulation>, CGAL_BOOST_PAIRING_HEAP>;
|
using Alpha_PQ = Modifiable_priority_queue<Gate, Less_gate, Gate_ID_PM<Triangulation>, CGAL_BOOST_PAIRING_HEAP>;
|
||||||
#else
|
#else
|
||||||
|
|
@ -270,7 +271,7 @@ public:
|
||||||
Wrapping_default_visitor default_visitor;
|
Wrapping_default_visitor default_visitor;
|
||||||
Visitor visitor = choose_parameter(get_parameter_reference(in_np, internal_np::visitor), default_visitor);
|
Visitor visitor = choose_parameter(get_parameter_reference(in_np, internal_np::visitor), default_visitor);
|
||||||
|
|
||||||
//
|
// Points used to create initial cavities
|
||||||
m_seeds = choose_parameter(get_parameter_reference(in_np, internal_np::seed_points), Seeds());
|
m_seeds = choose_parameter(get_parameter_reference(in_np, internal_np::seed_points), Seeds());
|
||||||
|
|
||||||
// Whether or not some cells should be reflagged as "inside" after the refinement+carving loop
|
// Whether or not some cells should be reflagged as "inside" after the refinement+carving loop
|
||||||
|
|
@ -278,16 +279,16 @@ public:
|
||||||
// geometrically manifold.
|
// geometrically manifold.
|
||||||
const bool do_enforce_manifoldness = choose_parameter(get_parameter(in_np, internal_np::do_enforce_manifoldness), true);
|
const bool do_enforce_manifoldness = choose_parameter(get_parameter(in_np, internal_np::do_enforce_manifoldness), true);
|
||||||
|
|
||||||
// Whether to keep pockets of OUTSIDE cells that are not connected to the exterior (or to the
|
// Whether to keep pockets of "outside" cells that are not connected to the exterior (or to the
|
||||||
// initial cavities, if used).
|
// initial cavities, if used).
|
||||||
const bool keep_inner_ccs = choose_parameter(get_parameter(in_np, internal_np::keep_inner_connected_components), true);
|
const bool keep_inner_ccs = choose_parameter(get_parameter(in_np, internal_np::keep_inner_connected_components), true);
|
||||||
|
|
||||||
// This parameter enables avoiding recomputing the triangulation from scratch when wrapping
|
// This parameter enables avoiding recomputing the triangulation from scratch when wrapping
|
||||||
// the same input for multiple values of alpha (and typically the same offset values).
|
// the same input for multiple values of alpha (and typically the same offset values).
|
||||||
// /!\ Warning /!\
|
|
||||||
//
|
//
|
||||||
// If this is enabled, the 3D triangulation will NOT be re-initialized
|
// /!\ Warning /!\
|
||||||
// at launch. This means that the triangulation is NOT cleared, even if:
|
// If this is enabled, the 3D triangulation will NOT be re-initialized at launch.
|
||||||
|
// This means that the triangulation is NOT cleared, even if:
|
||||||
// - you use an alpha value that is greater than what was used in a previous run; you will
|
// - you use an alpha value that is greater than what was used in a previous run; you will
|
||||||
// obtain the same result as the last run.
|
// obtain the same result as the last run.
|
||||||
// - you use a different offset value between runs, you might then get points that are not
|
// - you use a different offset value between runs, you might then get points that are not
|
||||||
|
|
@ -423,12 +424,12 @@ private:
|
||||||
// boundary considers them inside.
|
// boundary considers them inside.
|
||||||
bool is_on_inside_boundary(Cell_handle ch, Cell_handle nh) const
|
bool is_on_inside_boundary(Cell_handle ch, Cell_handle nh) const
|
||||||
{
|
{
|
||||||
return (ch->is_inside() != nh->is_inside()); // one is INSIDE, the other is not
|
return (ch->is_inside() != nh->is_inside()); // one is "inside", the other is not
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_on_outside_boundary(Cell_handle ch, Cell_handle nh) const
|
bool is_on_outside_boundary(Cell_handle ch, Cell_handle nh) const
|
||||||
{
|
{
|
||||||
return (ch->is_outside() != nh->is_outside()); // one is OUTSIDE, the other is not
|
return (ch->is_outside() != nh->is_outside()); // one is "outside", the other is not
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
@ -552,7 +553,7 @@ private:
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark the seeds and icosahedron vertices as "scaffolding vertices" such that the facets
|
// Mark the seeds and icosahedron vertices as "scaffolding" vertices such that the facets
|
||||||
// incident to these vertices are always traversable regardless of their circumcenter.
|
// incident to these vertices are always traversable regardless of their circumcenter.
|
||||||
// This is done because otherwise some cavities can appear on the mesh: non-traversable facets
|
// This is done because otherwise some cavities can appear on the mesh: non-traversable facets
|
||||||
// with two vertices on the offset, and the third being a deeper inside seed / ico_seed.
|
// with two vertices on the offset, and the third being a deeper inside seed / ico_seed.
|
||||||
|
|
@ -633,7 +634,7 @@ private:
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// When the algorithm starts from a manually dug hole, infinite cells are initialized
|
// When the algorithm starts from a manually dug hole, infinite cells are initialized
|
||||||
// as INSIDE such that they do not appear on the boundary
|
// as "inside" such that they do not appear on the boundary
|
||||||
CGAL_assertion(!m_tr.is_infinite(ch));
|
CGAL_assertion(!m_tr.is_infinite(ch));
|
||||||
|
|
||||||
for(int i=0; i<4; ++i)
|
for(int i=0; i<4; ++i)
|
||||||
|
|
@ -651,7 +652,7 @@ private:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// tag all infinite cells OUTSIDE and all finite cells INSIDE
|
// tag all infinite cells "outside" and all finite cells "inside"
|
||||||
// init queue with all convex hull facets
|
// init queue with all convex hull facets
|
||||||
bool initialize_from_infinity()
|
bool initialize_from_infinity()
|
||||||
{
|
{
|
||||||
|
|
@ -688,9 +689,6 @@ private:
|
||||||
std::cout << "Restart from a DT of " << m_tr.number_of_cells() << " cells" << std::endl;
|
std::cout << "Restart from a DT of " << m_tr.number_of_cells() << " cells" << std::endl;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Real_timer t;
|
|
||||||
t.start();
|
|
||||||
|
|
||||||
for(Cell_handle ch : m_tr.all_cell_handles())
|
for(Cell_handle ch : m_tr.all_cell_handles())
|
||||||
{
|
{
|
||||||
if(ch->is_inside())
|
if(ch->is_inside())
|
||||||
|
|
@ -698,15 +696,11 @@ private:
|
||||||
|
|
||||||
for(int i=0; i<4; ++i)
|
for(int i=0; i<4; ++i)
|
||||||
{
|
{
|
||||||
if(ch->neighbor(i)->is_outside())
|
if(ch->neighbor(i)->is_inside())
|
||||||
continue;
|
|
||||||
|
|
||||||
push_facet(std::make_pair(ch, i));
|
push_facet(std::make_pair(ch, i));
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
t.stop();
|
}
|
||||||
std::cout << t.time() << " for scanning a queue of size " << m_queue.size() << std::endl;
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -751,7 +745,6 @@ private:
|
||||||
|
|
||||||
if(cell->tds_data().processed())
|
if(cell->tds_data().processed())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
cell->tds_data().mark_processed();
|
cell->tds_data().mark_processed();
|
||||||
|
|
||||||
for(int fid=0; fid<4; ++fid)
|
for(int fid=0; fid<4; ++fid)
|
||||||
|
|
@ -1024,12 +1017,12 @@ public:
|
||||||
<< m_tr.point(f.first, Triangulation::vertex_triple_index(f.second, 2)) << std::endl;
|
<< m_tr.point(f.first, Triangulation::vertex_triple_index(f.second, 2)) << std::endl;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// skip if neighbor is OUTSIDE or infinite
|
// skip if neighbor is "outside" or infinite
|
||||||
const Cell_handle ch = f.first;
|
const Cell_handle ch = f.first;
|
||||||
const int id = f.second;
|
const int id = f.second;
|
||||||
CGAL_precondition(ch->label() == Cell_label::INSIDE || ch->label() == Cell_label::OUTSIDE);
|
CGAL_precondition(ch->label() == Cell_label::INSIDE || ch->label() == Cell_label::OUTSIDE);
|
||||||
|
|
||||||
if(!ch->is_outside()) // INSIDE or MANIFOLD
|
if(!ch->is_outside()) // "inside" or "manifold"
|
||||||
{
|
{
|
||||||
#ifdef CGAL_AW3_DEBUG_FACET_STATUS
|
#ifdef CGAL_AW3_DEBUG_FACET_STATUS
|
||||||
std::cout << "Facet is inside" << std::endl;
|
std::cout << "Facet is inside" << std::endl;
|
||||||
|
|
@ -1168,6 +1161,10 @@ private:
|
||||||
// and we can resume with the current queue
|
// and we can resume with the current queue
|
||||||
if(resuming)
|
if(resuming)
|
||||||
{
|
{
|
||||||
|
#ifdef CGAL_AW3_DEBUG
|
||||||
|
std::cout << "Resuming with a queue of size: " << m_queue.size() << std::endl;
|
||||||
|
#endif
|
||||||
|
|
||||||
reset_manifold_labels();
|
reset_manifold_labels();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -1181,7 +1178,7 @@ private:
|
||||||
if(refining)
|
if(refining)
|
||||||
{
|
{
|
||||||
// If we are re-using the triangulation, change the label of the extra elements
|
// If we are re-using the triangulation, change the label of the extra elements
|
||||||
// that we have added to ensure a manifold result back to external (MANIFOLD -> OUTSIDE)
|
// that we have added to ensure a manifold result back to external ("manifold" -> "outside")
|
||||||
reset_manifold_labels();
|
reset_manifold_labels();
|
||||||
|
|
||||||
return initialize_from_existing_triangulation();
|
return initialize_from_existing_triangulation();
|
||||||
|
|
@ -1190,7 +1187,6 @@ private:
|
||||||
{
|
{
|
||||||
m_tr.clear();
|
m_tr.clear();
|
||||||
|
|
||||||
|
|
||||||
insert_bbox_corners();
|
insert_bbox_corners();
|
||||||
|
|
||||||
if(m_seeds.empty())
|
if(m_seeds.empty())
|
||||||
|
|
@ -1512,7 +1508,7 @@ private:
|
||||||
if(ic->is_outside())
|
if(ic->is_outside())
|
||||||
outside_start = ic;
|
outside_start = ic;
|
||||||
else if(inside_start == Cell_handle())
|
else if(inside_start == Cell_handle())
|
||||||
inside_start = ic; // can be INSIDE or MANIFOLD
|
inside_start = ic; // can be "inside" or "manifold"
|
||||||
}
|
}
|
||||||
|
|
||||||
// fully inside / outside
|
// fully inside / outside
|
||||||
|
|
@ -1659,12 +1655,12 @@ public:
|
||||||
|
|
||||||
FT base_vol = wrap_volume();
|
FT base_vol = wrap_volume();
|
||||||
if(!is_positive(base_vol))
|
if(!is_positive(base_vol))
|
||||||
std::cerr << "Warning: empty wrap?" << std::endl;
|
std::cerr << "Warning: wrap with non-positive volume?" << std::endl;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// This seems more harmful than useful after the priority queue has been introduced since
|
// This ends up more harmful than useful after the priority queue has been introduced since
|
||||||
// it adds a lot of flat cells into the triangulation, which then get added to the mesh
|
// it usually results in a lot of flat cells into the triangulation, which then get added
|
||||||
// during manifoldness fixing.
|
// to the mesh during manifoldness fixing.
|
||||||
// remove_bbox_vertices();
|
// remove_bbox_vertices();
|
||||||
|
|
||||||
std::stack<Vertex_handle> non_manifold_vertices; // @todo sort somehow?
|
std::stack<Vertex_handle> non_manifold_vertices; // @todo sort somehow?
|
||||||
|
|
@ -1694,8 +1690,8 @@ public:
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// This seemed like a good idea, but in the end it can have strong cascading issues,
|
// This originally seemed like a good idea, but in the end it can have strong cascading issues,
|
||||||
// whereas some cells with much lower volume would have solved the non-manifoldness.
|
// whereas some cells with much smaller volume could have solved the non-manifoldness.
|
||||||
// auto is_on_boundary = [](Cell_handle c, int i) -> bool
|
// auto is_on_boundary = [](Cell_handle c, int i) -> bool
|
||||||
// {
|
// {
|
||||||
// return is_on_outside_boundary(c, c->neighbor(i));
|
// return is_on_outside_boundary(c, c->neighbor(i));
|
||||||
|
|
@ -1716,14 +1712,14 @@ public:
|
||||||
// return boundary_facets;
|
// return boundary_facets;
|
||||||
// };
|
// };
|
||||||
|
|
||||||
// longest edge works better
|
// Experimentally, longest edge works better
|
||||||
// auto sq_circumradius = [&](Cell_handle c) -> FT
|
// auto sq_circumradius = [&](Cell_handle c) -> FT
|
||||||
// {
|
// {
|
||||||
// const Point_3& cc = circumcenter(c);
|
// const Point_3& cc = circumcenter(c);
|
||||||
// return geom_traits().compute_squared_distance_3_object()(m_tr.point(c, 0), cc);
|
// return geom_traits().compute_squared_distance_3_object()(m_tr.point(c, 0), cc);
|
||||||
// };
|
// };
|
||||||
|
|
||||||
// the reasoning behind using longest edge rather than volume is that we want to avoid
|
// The reasoning behind using longest edge rather than volume is that we want to avoid
|
||||||
// spikes (which would have a small volume), and can often happen since we do not spend
|
// spikes (which would have a small volume), and can often happen since we do not spend
|
||||||
// any care on the quality of tetrahedra.
|
// any care on the quality of tetrahedra.
|
||||||
auto sq_longest_edge = [&](Cell_handle c) -> FT
|
auto sq_longest_edge = [&](Cell_handle c) -> FT
|
||||||
|
|
@ -1756,9 +1752,9 @@ public:
|
||||||
// - cells without bbox vertices
|
// - cells without bbox vertices
|
||||||
// - small cells when equal number of boundary facets
|
// - small cells when equal number of boundary facets
|
||||||
//
|
//
|
||||||
// Note that these are properties that do not depend on where the surface is, so we can
|
// Note that these are properties that do not depend on the cell labels, and so we only need
|
||||||
// sort once. However, if a criterion such as the number of inside cells were added,
|
// to sort once. However, if a criterion such as the number of incident inside cells were added,
|
||||||
// one would need to sort again after each modification of is_outside() statuses.
|
// we would need to sort after each modification of "inside"/"outside" labels.
|
||||||
auto comparer = [&](Cell_handle l, Cell_handle r) -> bool
|
auto comparer = [&](Cell_handle l, Cell_handle r) -> bool
|
||||||
{
|
{
|
||||||
CGAL_precondition(!m_tr.is_infinite(l) && !m_tr.is_infinite(r));
|
CGAL_precondition(!m_tr.is_infinite(l) && !m_tr.is_infinite(r));
|
||||||
|
|
@ -1780,7 +1776,7 @@ public:
|
||||||
|
|
||||||
// 'std::stable_sort' to have determinism without having to write something like:
|
// 'std::stable_sort' to have determinism without having to write something like:
|
||||||
// if(longest_edge(l) == longest_edge(r)) return ...
|
// if(longest_edge(l) == longest_edge(r)) return ...
|
||||||
// in the comparer. It's a small range, so the cost does not matter.
|
// in the comparer. It's almost always a small range, so the extra cost does not matter.
|
||||||
std::stable_sort(finite_outside_inc_cells.begin(), finite_outside_inc_cells.end(), comparer);
|
std::stable_sort(finite_outside_inc_cells.begin(), finite_outside_inc_cells.end(), comparer);
|
||||||
|
|
||||||
for(Cell_handle ic : finite_outside_inc_cells)
|
for(Cell_handle ic : finite_outside_inc_cells)
|
||||||
|
|
@ -1803,6 +1799,8 @@ public:
|
||||||
|
|
||||||
CGAL_assertion(!is_non_manifold(v));
|
CGAL_assertion(!is_non_manifold(v));
|
||||||
|
|
||||||
|
// Check if the new material has not created a non-manifold configuration.
|
||||||
|
// @speed this could be done on only the vertices of cells whose labels have changed.
|
||||||
std::vector<Vertex_handle> adj_vertices;
|
std::vector<Vertex_handle> adj_vertices;
|
||||||
adj_vertices.reserve(64);
|
adj_vertices.reserve(64);
|
||||||
m_tr.finite_adjacent_vertices(v, std::back_inserter(adj_vertices));
|
m_tr.finite_adjacent_vertices(v, std::back_inserter(adj_vertices));
|
||||||
|
|
|
||||||
|
|
@ -70,12 +70,12 @@ struct Less_gate
|
||||||
template <typename Tr>
|
template <typename Tr>
|
||||||
bool operator()(const Gate<Tr>& a, const Gate<Tr>& b) const
|
bool operator()(const Gate<Tr>& a, const Gate<Tr>& b) const
|
||||||
{
|
{
|
||||||
// If one is permissive and the other is not, give priority to the permissive facet
|
// If one is permissive and the other is not, give priority to the permissive facet.
|
||||||
//
|
//
|
||||||
// The permissive facet are given highest priority because they need to be treated
|
// The permissive facet are given highest priority because they need to be treated
|
||||||
// regardless of their circumradius. Treating them first allow the part that depends
|
// regardless of their circumradius. Treating them first allow the part that depends
|
||||||
// on alpha to be treated uniformly in a way: whatever the alpha, all scaffolding faces
|
// on alpha to be treated uniformly in a way: whatever the alpha, all permissive faces
|
||||||
// will first be treated
|
// will first be treated.
|
||||||
if(a.is_permissive_facet() != b.is_permissive_facet())
|
if(a.is_permissive_facet() != b.is_permissive_facet())
|
||||||
return a.is_permissive_facet();
|
return a.is_permissive_facet();
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue