Skip to content

Commit 93fa24e

Browse files
committed
Mesh_2: fix bug with Mesh_2/test/Mesh_2/triwild-10070-min1.obj
The bug was reproduced by ```shell /path/to/build/test/Mesh_2/test_mesh_obj Mesh_2/test/Mesh_2/triwild-10070-min1.obj ``` The log of the error, with all the debug options of Mesh_2 was: ```plain Before conforming Gabriel: 4 vertices. Smallest squared distance between constraint endpoints: 0.010522898628224919 edge #1= 453.57378188125 445.0501073328125 -- #3= 454.05836694375 445.48749872343745 is encroached by #2= 453.77453855000005 445.23131233749996 add_constrained_edge_to_be_conformed(#1= 453.57378188125 445.0501073328125, #3= 454.05836694375 445.48749872343745) edge #2= 453.77453855000005 445.23131233749996 -- #0= 453.49763280000002 444.98137439999999 is encroached by #1= 453.57378188125 445.0501073328125 add_constrained_edge_to_be_conformed(#2= 453.77453855000005 445.23131233749996, #0= 453.49763280000002 444.98137439999999) split_cluster_point(454.05836694375 445.48749872343745 , 453.57378188125 445.0501073328125) reduced: 0 result: 453.77453855000005 445.23131233750001 edge #1= 453.57378188125 445.0501073328125 -- #3= 454.05836694375 445.48749872343745 is encroached by #2= 453.77453855000005 445.23131233749996 Refine_edges_with_clusters::refinement_point_impl(Edge: (#1= 453.57378188125 445.0501073328125, #3= 454.05836694375 445.48749872343745) = 453.77453855000005 445.23131233750001 (453.77453855000005 445.23131233750001) accepted on edge, insert(453.77453855000005 445.23131233750001): 3 boundary edges in the zone inserted new vertex #4= 453.77453855000005 445.23131233750001 update_clusters va_has_a_cluster=0 vb_has_a_cluster=1 clusters.size()=2 E edge #3= 454.05836694375 445.48749872343745 -- #2= 453.77453855000005 445.23131233749996 is encroached by #4= 453.77453855000005 445.23131233750001 add_constrained_edge_to_be_conformed(#3= 454.05836694375 445.48749872343745, #2= 453.77453855000005 445.23131233749996) edge #1= 453.57378188125 445.0501073328125 -- #4= 453.77453855000005 445.23131233750001 is encroached by #2= 453.77453855000005 445.23131233749996 add_constrained_edge_to_be_conformed(#1= 453.57378188125 445.0501073328125, #4= 453.77453855000005 445.23131233750001) Cluster at #3= 454.05836694375 445.48749872343745 is updated. vm: #4= 453.77453855000005 445.23131233750001 reduction: 1 min_sq_len: 0.1461900214383674 clusters.size() after update_cluster=2 split_cluster_point(453.49763280000002 444.98137439999999 , 453.77453855000005 445.23131233749996) reduced: 0 result: 453.64993096249998 445.11884026562501 edge #2= 453.77453855000005 445.23131233749996 -- #0= 453.49763280000002 444.98137439999999 is encroached by #1= 453.57378188125 445.0501073328125 Refine_edges_with_clusters::refinement_point_impl(Edge: (#2= 453.77453855000005 445.23131233749996, #0= 453.49763280000002 444.98137439999999) = 453.64993096249998 445.11884026562501 (453.64993096249998 445.11884026562501) accepted on edge, insert(453.64993096249998 445.11884026562501): 0 boundary edges in the zone terminate called after throwing an instance of 'CGAL::Precondition_exception' what(): CGAL ERROR: precondition violation! Expr: i >= 0 && i < 3 ``` The following edge `(#1, #3)` is encroached by the vertex `#2`: ```plain edge #1= 453.57378188125 445.0501073328125 -- #3= 454.05836694375 445.48749872343745 is encroached by #2= 453.77453855000005 445.23131233749996 ``` The inserted vertex `#4= 453.77453855000005 445.23131233750001` is the projection of the vertex `#2= 453.77453855000005 445.23131233749996` (rules to refine a segment part of a cluster. The distance between `#2` and its projection `#4` is: - `0` on the x-axis, - `ulp(v2.y()))` on the y-axis. ... and then, Mesh_2 cannot do anything with that. The fix is to implement a snapping strategy: if the encroaching vertex is close enough, then chose it be the refinement point of the encroached segment. "Close enough" means: - create tiny `Bbox_2` centered on the encroaching vertex, with snaps equal to 8 ulp on both axis, - if that tiny bbox intersects the encroached segment, then the refinement is the encroaching vertex.
1 parent 2af8445 commit 93fa24e

5 files changed

Lines changed: 111 additions & 40 deletions

File tree

Kernel_23/include/CGAL/Kernel_23/internal/Projection_traits_3.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@
1212
#ifndef CGAL_INTERNAL_PROJECTION_TRAITS_3_H
1313
#define CGAL_INTERNAL_PROJECTION_TRAITS_3_H
1414

15+
#include <CGAL/Bbox_2.h>
16+
#include <CGAL/Bbox_3.h>
1517
#include <CGAL/assertions.h>
18+
#include <CGAL/enum.h>
1619
#include <CGAL/tags.h>
1720
#include <CGAL/Point_3.h>
1821
#include <CGAL/Segment_3.h>
@@ -22,6 +25,11 @@
2225
#include <CGAL/Kernel_23/internal/Has_boolean_tags.h>
2326
#include <CGAL/Triangulation_structural_filtering_traits.h>
2427

28+
#include <array>
29+
#include <limits>
30+
#include <optional>
31+
#include <variant>
32+
2533
namespace CGAL {
2634

2735
namespace internal {
@@ -409,6 +417,24 @@ class Compute_determinant_projected_3
409417
}
410418
};
411419

420+
template <class R,int dim>
421+
struct Do_intersect_projected_3
422+
{
423+
template <typename T> bool operator()(const Bbox_2& bbox, const T& obj) const {
424+
static constexpr double infinity = std::numeric_limits<double>::infinity();
425+
std::array<double, 3> bbox_min = {-infinity, -infinity, -infinity};
426+
std::array<double, 3> bbox_max = {infinity, infinity, infinity};
427+
428+
using Proj = Projector<R, dim>;
429+
bbox_min[Proj::x_index] = bbox.xmin();
430+
bbox_min[Proj::y_index] = bbox.ymin();
431+
bbox_max[Proj::x_index] = bbox.xmax();
432+
bbox_max[Proj::y_index] = bbox.ymax();
433+
Bbox_3 bbox3(bbox_min[0], bbox_min[1], bbox_min[2], bbox_max[0], bbox_max[1], bbox_max[2]);
434+
return do_intersect(bbox3, obj);
435+
}
436+
};
437+
412438
template <class R,int dim>
413439
class Intersect_projected_3
414440
{
@@ -951,6 +977,7 @@ class Projection_traits_3
951977
typedef Collinear_are_ordered_along_line_projected_3<Rp,dim> Collinear_are_ordered_along_line_2;
952978
typedef Squared_distance_projected_3<Rp,dim> Compute_squared_distance_2;
953979
typedef Intersect_projected_3<Rp,dim> Intersect_2;
980+
typedef Do_intersect_projected_3<Rp,dim> Do_intersect_2;
954981
typedef Compute_squared_radius_projected<Rp,dim> Compute_squared_radius_2;
955982
typedef Compute_scalar_product_projected_3<Rp,dim> Compute_scalar_product_2;
956983
typedef Compute_squared_length_projected_3<Rp,dim> Compute_squared_length_2;
@@ -1151,6 +1178,12 @@ class Projection_traits_3
11511178
return Intersect_2();
11521179
}
11531180

1181+
Do_intersect_2
1182+
do_intersect_2_object() const
1183+
{
1184+
return Do_intersect_2();
1185+
}
1186+
11541187
Construct_point_2 construct_point_2_object() const
11551188
{ return Construct_point_2();}
11561189

Kernel_23/include/CGAL/Kernel_23/internal/Projection_traits_base_3.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,7 @@ class Projection_traits_base_3
503503

504504
typedef typename K::Compute_area_3 Compute_area_2;
505505
typedef typename K::Construct_bbox_3 Construct_bbox_2;
506+
typedef typename K::Do_intersect_3 Do_intersect_2; // for do_intersect(bbox_2, segment_2)
506507

507508
Less_x_2
508509
less_x_2_object() const
@@ -607,6 +608,9 @@ class Projection_traits_base_3
607608
Construct_bbox_2 construct_bbox_2_object() const
608609
{return Construct_bbox_2();}
609610

611+
Do_intersect_2 do_intersect_2_object() const
612+
{return Do_intersect_2();}
613+
610614

611615
// Special functor, not in the Kernel concept
612616
class Projection_to_plan {

Mesh_2/include/CGAL/Mesh_2/Refine_edges.h

Lines changed: 58 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,9 @@
2929
#include <CGAL/tags.h>
3030

3131
#include <boost/iterator/filter_iterator.hpp>
32-
3332
#include <boost/iterator/transform_iterator.hpp>
33+
3434
#include <iterator>
35-
#include <limits>
3635
#include <optional>
3736
#include <stack>
3837
#include <type_traits>
@@ -625,43 +624,68 @@ class Refine_edges_base :
625624
<< IO::oformat(vb, With_point_tag{}) << ") = ";
626625
std::cerr << p << '\n';
627626
#endif // CGAL_MESH_2_DEBUG_REFINEMENT_POINTS
627+
return maybe_snap_to_existing_vertex(edge, p);
628+
}
629+
630+
auto get_closest_encroaching_vertex(Edge e) const {
631+
struct Result {
632+
Vertex_handle vertex;
633+
FT squared_distance;
634+
};
635+
std::optional<Result> result;
636+
Is_locally_conforming_Gabriel<Tr> is_locally_conforming_Gabriel{};
637+
638+
for(int i = 0; i < 2; ++i, e = tr.mirror_edge(e)) {
639+
auto [c, index] = e;
640+
if(tr.is_infinite(c))
641+
continue;
642+
auto v = c->vertex(index);
643+
auto p = v->point();
644+
if(is_locally_conforming_Gabriel(tr, c, index, p, v))
645+
continue;
646+
auto va = e.first->vertex(tr.cw (e.second));
647+
auto vb = e.first->vertex(tr.ccw(e.second));
648+
typename Tr::Segment ab(va->point(), vb->point());
649+
auto height_squared = squared_distance(p, ab);
650+
if(!result || height_squared < result->squared_distance) {
651+
result = Result{c->vertex(index), height_squared};
652+
}
653+
}
654+
return result;
655+
}
656+
657+
Point maybe_snap_to_existing_vertex(const Edge& edge, Point p) const {
628658
if constexpr(std::is_floating_point_v<FT>) {
629-
Is_locally_conforming_Gabriel<Tr> is_locally_conforming_Gabriel{};
630-
constexpr FT max_FT = (std::numeric_limits<FT>::max)();
631-
auto ulp = [&](const FT& x) { return std::nextafter(x, max_FT) - x; };
632-
auto sq_delta = 256 * CGAL::square((std::min)(ulp(p.x()), ulp(p.y())));
633-
634-
typename Tr::Segment ab(this->va->point(), this->vb->point());
635-
std::optional<FT> min_height_squared;
636-
Vertex_handle closest_encroaching_vertex;
637-
auto e = edge;
638-
for(int i = 0; i < 2; ++i, e = triangulation_ref_impl().mirror_edge(e)) {
639-
auto [c, index] = e;
640-
if(triangulation_ref_impl().is_infinite(c))
641-
continue;
642-
auto cp = c->vertex(index)->point();
643-
if(is_locally_conforming_Gabriel(triangulation_ref_impl(), c, index, cp))
644-
continue;
645-
auto height_squared = squared_distance(cp, ab);
646-
if(!min_height_squared || height_squared < *min_height_squared) {
647-
closest_encroaching_vertex = c->vertex(index);
648-
min_height_squared = height_squared;
649-
}
659+
auto cstr_bbox = tr.geom_traits().construct_bbox_2_object();
660+
auto do_intersect = tr.geom_traits().do_intersect_2_object();
661+
auto va = edge.first->vertex(tr.cw (edge.second));
662+
auto vb = edge.first->vertex(tr.ccw(edge.second));
663+
664+
auto closest_encroaching_vertex = get_closest_encroaching_vertex(edge);
665+
if(!closest_encroaching_vertex) {
666+
return p;
667+
}
668+
FT edge_length_squared = squared_distance(va->point(), vb->point());
669+
if(closest_encroaching_vertex->squared_distance * FT(10000) > edge_length_squared) {
670+
return p;
650671
}
651-
if(min_height_squared) {
652-
FT edge_length_squared = squared_distance(this->va->point(), this->vb->point());
653-
if(*min_height_squared * FT(10000) < edge_length_squared && *min_height_squared < sq_delta) {
654-
// the angle is smaller than approx 0.5 degree, and the distance is very small
655-
// so we consider that the point is aligned with ab
672+
673+
// here, the angle is smaller than approx 0.5 degree, and the distance is very small
674+
// so we consider that the point is aligned with ab
675+
676+
auto bbox = cstr_bbox(closest_encroaching_vertex->vertex->point());
677+
bbox.dilate(4);
678+
if(do_intersect(bbox, typename Tr::Segment(va->point(), vb->point()))
679+
)
680+
{
656681
#ifdef CGAL_MESH_2_DEBUG_REFINEMENT_POINTS
657-
std::cerr << "return point of existing vertex " << IO::oformat(closest_encroaching_vertex, With_point_tag{})
658-
<< std::endl;
659-
#endif // CGAL_MESH_2_DEBUG_REFINEMENT_POINTS
660-
return closest_encroaching_vertex->point();
661-
}
682+
std::cerr << "return point of existing vertex "
683+
<< IO::oformat(closest_encroaching_vertex->vertex, With_point_tag{}) << std::endl;
684+
#endif // CGAL_MESH_2_DEBUG_REFINEMENT_POINTS
685+
p = closest_encroaching_vertex->vertex->point();
686+
return p;
662687
}
663688
}
664-
665689
return p;
666690
}
667691

Mesh_2/include/CGAL/Mesh_2/Refine_edges_with_clusters.h

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -120,19 +120,21 @@ class Refine_edges_base_with_clusters :
120120
std::cerr << "midpoint(" << IO::oformat(this->va, With_point_tag{})
121121
<< " , " << IO::oformat(this->vb, With_point_tag{}) << ")\n";
122122
#endif // CGAL_MESH_2_DEBUG_CLUSTERS
123-
return display_return_point(midpoint(this->va->point(), this->vb->point()));
124-
}
125-
else {
123+
return display_return_point(
124+
this->maybe_snap_to_existing_vertex(edge, midpoint(this->va->point(), this->vb->point())));
125+
} else {
126126
// va only is a cluster
127127
va_has_a_cluster = true;
128-
return display_return_point(split_cluster_point(this->va,this->vb,ca));
128+
return display_return_point(
129+
this->maybe_snap_to_existing_vertex(edge, split_cluster_point(this->va, this->vb, ca)));
129130
}
130131
} else
131132
if( clusters.get_cluster(this->vb,this->va,cb,cb_it) ){
132133
// vb only is a cluster
133134
vb_has_a_cluster = true;
134-
return display_return_point(split_cluster_point(this->vb,this->va,cb));
135-
}else{
135+
return display_return_point(
136+
this->maybe_snap_to_existing_vertex(edge, split_cluster_point(this->vb, this->va, cb)));
137+
} else {
136138
// no cluster
137139

138140
#ifdef CGAL_MESH_2_DEBUG_CLUSTERS
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
v 453.49763280000002 444.98137439999999 0
2+
v 453.57378188125 445.0501073328125 0
3+
v 453.77453855000005 445.23131233749996 0
4+
v 454.05836694375 445.48749872343745 0
5+
l 1 2
6+
l 3 4
7+
l 1 3
8+
l 4 2

0 commit comments

Comments
 (0)