@@ -65,12 +65,17 @@ struct Snapping_result
6565template <typename Exact_kernel>
6666Snapping_result<Exact_kernel>
6767exact_snapping (const typename Exact_kernel::Segment_3& s0,
68- const typename Exact_kernel::Segment_3& s1)
68+ const typename Exact_kernel::Segment_3& s1,
69+ const typename Exact_kernel::FT& tolerance)
6970{
7071 using Point_3 = typename Exact_kernel::Point_3;
72+ using Point_2 = typename Exact_kernel::Point_2;
7173 using Segment_3 = typename Exact_kernel::Segment_3;
74+ using Segment_2 = typename Exact_kernel::Segment_2;
7275 using Line_3 = typename Exact_kernel::Line_3;
76+ using Line_2 = typename Exact_kernel::Line_2;
7377 using Vector_3 = typename Exact_kernel::Vector_3;
78+ using Vector_2 = typename Exact_kernel::Vector_2;
7479 using Plane_3 = typename Exact_kernel::Plane_3;
7580 using FT = typename Exact_kernel::FT;
7681 using Result = Snapping_result<Exact_kernel>;
@@ -85,34 +90,146 @@ exact_snapping (const typename Exact_kernel::Segment_3& s0,
8590 // Collinear segments
8691 if (normal == CGAL::NULL_VECTOR)
8792 {
88- Point_3 proj0 = v1.supporting_line ().projection (s0.source ());
89- Point_3 med = midpoint (s0.source (), proj0);
90- Line_3 support (med, v0);
91- Vector ref (med, support.projection (s0.target ()));
93+ Line_3 l0 = s0.supporting_line ();
94+ Line_3 l1 = s1.supporting_line ();
95+ Vector_3 v0 = s0.to_vector ();
96+ v0 = v0 / approximate_sqrt (v0 * v0);
97+ Vector_3 v1 = s1.to_vector ();
98+ v1 = v1 / approximate_sqrt (v1 * v1);
9299
93- std::vector<std::pair<FT, Point_3 >> points_along_line ;
100+ std::vector<std::pair<FT, FT >> points_along_lines ;
94101
95102 auto create_projection = [&](const Point_3& p)
96103 {
97- Point_3 proj = support.projection (p);
98- Vector vec (med, proj);
99- FT position = vec * ref;
100- points_along_line.emplace_back (position, proj);
104+ Point_3 p0 = l0.projection (p);
105+ Vector_3 vec0 (s0.source (), p0);
106+ FT pos0 = vec0 * v0;
107+
108+ Point_3 p1 = l1.projection (p);
109+ Vector_3 vec1 (s1.source (), p1);
110+ FT pos1 = vec1 * v1;
111+
112+ points_along_lines.emplace_back (pos0, pos1);
101113 };
102114 create_projection (s0.source ());
103115 create_projection (s0.target ());
104116 create_projection (s1.source ());
105117 create_projection (s1.target ());
106118
107- std::sort (points_along_line .begin (), points_along_line .end (),
119+ std::sort (points_along_lines .begin (), points_along_lines .end (),
108120 [](const auto & a, const auto & b) -> bool
109121 {
110122 return a.first < b.first ;
111123 });
112124
125+ Segment_3 seg0 (s0.source () + points_along_lines[1 ].first * v0,
126+ s0.source () + points_along_lines[2 ].first * v0);
127+ Segment_3 seg1 (s1.source () + points_along_lines[1 ].second * v1,
128+ s1.source () + points_along_lines[2 ].second * v1);
129+
130+
131+ return Result (seg0, seg1);
132+ }
133+
134+ // Coplanar segments
135+ if (coplanar (s0.source (), s0.target (), s1.source (), s1.target ()))
136+ {
137+ Plane_3 plane
138+ (s0.source (), s0.target (),
139+ collinear (s0.source (), s0.target (), s1.target ()) ? s1.source () : s1.target ());
140+ Vector_3 base1 = plane.base1 ();
141+ base1 = base1 / approximate_sqrt (base1 * base1);
142+ Vector_3 base2 = plane.base2 ();
143+ base2 = base2 / approximate_sqrt (base2 * base2);
144+
145+ auto to_2d = [&](const Point_3& p) -> Point_2
146+ {
147+ Vector_3 v (s0.source (), p);
148+ return Point_2 (v * base1, v * base2);
149+ };
150+ auto to_3d = [&](const Point_2& p) -> Point_3
151+ {
152+ return s0.source () + p.x () * base1 + p.y () * base2;
153+ };
154+
155+ auto coplanar_snapping
156+ = [&](const Segment_3& seg, const Segment_3& ref) -> std::pair<Point_3, typename Result::Type>
157+ {
158+ Segment_2 seg2 (to_2d (seg.source ()), to_2d (seg.target ()));
159+ Segment_2 ref2 (to_2d (ref.source ()), to_2d (ref.target ()));
160+
161+ auto seg2_i_ref2 = intersection (seg2, ref2);
162+ if (seg2_i_ref2)
163+ {
164+ const Point_2* point = boost::get<Point_2>(&*seg2_i_ref2);
165+ CGAL_assertion (point != nullptr );
166+ return std::make_pair (to_3d (*point), Result::POINT);
167+ }
168+
169+ Vector_2 vec2 = ref2.to_vector ();
170+ Vector_2 ortho = vec2.perpendicular (CLOCKWISE);
171+ ortho = ortho / approximate_sqrt (ortho * ortho);
172+
173+ Segment_2 sc (ref2.source () + ortho * tolerance,
174+ ref2.target () + ortho * tolerance);
175+ Segment_2 scc (ref2.source () - ortho * tolerance,
176+ ref2.target () - ortho * tolerance);
177+
178+ Orientation source_sc = orientation (sc.source (), sc.target (), seg2.source ());
179+ Orientation source_scc = orientation (scc.source (), scc.target (), seg2.source ());
180+ Orientation target_sc = orientation (sc.source (), sc.target (), seg2.target ());
181+ Orientation target_scc = orientation (scc.source (), scc.target (), seg2.target ());
182+
183+ bool source_in = (source_sc != source_scc);
184+ bool target_in = (target_sc != target_scc);
185+
186+ // Whole segment is inside tolerance
187+ if (source_in && target_in)
188+ return std::make_pair (Point_3 (), Result::SEGMENT);
189+
190+
191+ if (!source_in && !target_in)
192+ {
193+ Line_2 lc = sc.supporting_line ();
194+ auto inter = intersection (seg2, lc);
195+ // Segment intersect both borders
196+ if (inter)
197+ return std::make_pair (Point_3 (), Result::SEGMENT);
198+
199+ // Segment does not intersect at all
200+ return std::make_pair (Point_3 (), Result::EMPTY);
201+ }
202+
203+ // One vertex only is in tolerance
204+ if (source_in)
205+ return std::make_pair (seg.source (), Result::POINT);
206+ // else
207+ return std::make_pair (seg.target (), Result::POINT);
208+ };
209+
210+ auto crop0 = coplanar_snapping (s0, s1);
211+ auto crop1 = coplanar_snapping (s1, s0);
212+
213+ if (crop0.second == Result::POINT && crop1.second == Result::POINT)
214+ return Result (crop0.first , crop1.first );
215+
216+ if (crop0.second == Result::POINT && crop1.second == Result::SEGMENT)
217+ return Result (crop0.first , s1.supporting_line ().projection (crop0.first ));
218+
219+ if (crop0.second == Result::SEGMENT && crop1.second == Result::POINT)
220+ return Result (crop1.first , s0.supporting_line ().projection (crop1.first ));
221+
222+ if (crop0.second == Result::SEGMENT && crop1.second == Result::SEGMENT)
223+ {
224+ // Note: there might be very specific cases where these segments should be cropped
225+ return Result (s0, s1);
226+ }
227+
113228 return Result ();
114229 }
115230
231+ // General case (segments are not coplanar/collinear)
232+
116233 Plane_3 plane0 (s0.source (), normal);
117234 Plane_3 plane1 (s1.source (), normal);
118235
@@ -140,7 +257,6 @@ exact_snapping (const typename Exact_kernel::Segment_3& s0,
140257 return Result (*p0, *p1);
141258 }
142259
143- // else coplanar segments
144260 return Result ();
145261}
146262
@@ -154,11 +270,13 @@ class Exact_snapping<Kernel,false>
154270{
155271// typedefs
156272 using Exact_kernel = Exact_predicates_exact_constructions_kernel;
273+ using Exact_FT = typename Exact_kernel::FT;
157274 using Exact_segment_3 = typename Exact_kernel::Segment_3;
158275 using Exact_point_3 = typename Exact_kernel::Point_3;
159276 using Exact_result = Snapping_result<Exact_kernel>;
160277
161278 using Inexact_kernel = Kernel;
279+ using Inexact_FT = typename Inexact_kernel::FT;
162280 using Inexact_segment_3 = typename Inexact_kernel::Segment_3;
163281 using Inexact_point_3 = typename Inexact_kernel::Point_3;
164282 using Inexact_result = Snapping_result<Inexact_kernel>;
@@ -171,9 +289,10 @@ class Exact_snapping<Kernel,false>
171289
172290public:
173291
174- Inexact_result intersection (const Inexact_segment_3& s0, const Inexact_segment_3& s1) const
292+ Inexact_result intersection (const Inexact_segment_3& s0, const Inexact_segment_3& s1,
293+ const Inexact_FT& tolerance) const
175294 {
176- Exact_result result = exact_snapping<Exact_kernel> (to_exact (s0), to_exact (s1));
295+ Exact_result result = exact_snapping<Exact_kernel> (to_exact (s0), to_exact (s1), to_exact (tolerance) );
177296 if (result.type == Exact_result::EMPTY)
178297 return Inexact_result ();
179298 if (result.type == Exact_result::POINT)
@@ -189,15 +308,16 @@ class Exact_snapping<Kernel,false>
189308template <class Kernel >
190309class Exact_snapping <Kernel,true >
191310{
311+ using FT = typename Kernel::FT;
192312 using Segment_3 = typename Kernel::Segment_3;
193313 using Point_3 = typename Kernel::Point_3;
194314 using Result = Snapping_result<Kernel>;
195315
196316public:
197317
198- Result intersection (const Segment_3& s0, const Segment_3& s1) const
318+ Result intersection (const Segment_3& s0, const Segment_3& s1, const FT& tolerance ) const
199319 {
200- return exact_snapping<Kernel>(s0,s1);
320+ return exact_snapping<Kernel>(s0,s1,tolerance );
201321 }
202322};
203323
@@ -293,7 +413,7 @@ void polyline_snapping (const PolylineRange& polylines,
293413
294414 Point_3 new_point;
295415 bool okay;
296- std::tie (new_point, okay) = exact_snapping.intersection (s1, s2);
416+ std::tie (new_point, okay) = exact_snapping.intersection (s1, s2, tolerance );
297417
298418 if (!okay)
299419 return ;
@@ -413,6 +533,8 @@ void polyline_snapping (Graph& graph, PointMap point_map, double tolerance)
413533 using Vertex_position = std::pair<FT, vertex_descriptor>;
414534 std::map<edge_descriptor, std::vector<Vertex_position>> new_vertices;
415535
536+ std::map<Point_3, vertex_descriptor> map_p2v;
537+
416538 // Compute intersections
417539 box_self_intersection_d
418540 (boxes.begin (), boxes.end (),
@@ -437,7 +559,7 @@ void polyline_snapping (Graph& graph, PointMap point_map, double tolerance)
437559 return ;
438560
439561 // Compute exact snapping point
440- Result snapping = exact_snapping.intersection (sa, sb);
562+ Result snapping = exact_snapping.intersection (sa, sb, tolerance );
441563 if (snapping.type == Result::EMPTY)
442564 return ;
443565
@@ -461,6 +583,10 @@ void polyline_snapping (Graph& graph, PointMap point_map, double tolerance)
461583 ma.first ->second .emplace_back (ba, vd);
462584 mb.first ->second .emplace_back (bb, vd);
463585 }
586+ else // snapping.type == Result::SEGMENT)
587+ {
588+ std::cerr << " Warning: coplanar segment snapping not handled" << std::endl;
589+ }
464590 },
465591 cutoff);
466592
@@ -485,52 +611,6 @@ void polyline_snapping (Graph& graph, PointMap point_map, double tolerance)
485611 add_edge (vertices[i].second , vertices[i+1 ].second , graph);
486612 }
487613
488- #if 0
489- // Detect edges smaller than threshold
490- using vedge = std::pair<vertex_descriptor, vertex_descriptor>;
491- std::vector<vedge> vedges;
492- for (edge_descriptor ed : make_range(edges(graph).first, edges(graph).second))
493- {
494- vertex_descriptor vs = source (ed, graph);
495- vertex_descriptor vt = target (ed, graph);
496- vedges.emplace_back (vs, vt);
497- }
498-
499- // Map deleted vertex -> valid vertex
500- std::map<vertex_descriptor, vertex_descriptor> map_v2v;
501- // Map vertex -> barycenter weight
502- std::map<vertex_descriptor, std::size_t> map_v2b;
503- for (vertex_descriptor vd : make_range(vertices(graph).first, vertices(graph).second))
504- {
505- map_v2v.insert (std::make_pair(vd, vd));
506- map_v2b.insert (std::make_pair(vd, 1));
507- }
508-
509- // Collapse edges
510- for (vedge ve : vedges)
511- {
512- vertex_descriptor vs = map_v2v[ve.first];
513- vertex_descriptor vt = map_v2v[ve.second];
514-
515- if (vs == vt)
516- continue;
517-
518- const Point_3& ps = get (point_map, vs);
519- const Point_3& pt = get (point_map, vt);
520-
521- if (squared_distance(ps, ps) > tolerance * tolerance)
522- continue;
523-
524- std::size_t& bs = map_v2b[vs];
525- std::size_t& bt = map_v2b[vt];
526- Point_3 bary = barycenter (ps, bs, pt, bt);
527- bs += bt;
528-
529- put (point_map, vs, bary);
530-
531- map_v2v[vt] = vs;
532- }
533- #endif
534614
535615}
536616
0 commit comments