11#include " mapping.hpp"
2-
2+ #include < algorithm>
3+ #include < iostream>
4+ #include < iomanip>
5+ #include < cmath>
6+ #include < unordered_set>
37
48// Custom hash function for tuple
59struct TupleHash {
@@ -18,13 +22,10 @@ std::tuple<int64_t, int64_t> grid_key(double x, double y, double tolerance) {
1822 };
1923}
2024
21- bool is_same_point (double x1, double y1, double z1,
22- double x2, double y2, double z2,
23- double tol) {
24- double dx = x1 - x2;
25- double dy = y1 - y2;
26- double dz = z1 - z2;
27- return (dx*dx + dy*dy + dz*dz) < (tol*tol);
25+ bool is_same_point (double x1, double y1, double x2, double y2, double tol) {
26+ double dx = x1 - x2;
27+ double dy = y1 - y2;
28+ return (dx*dx + dy*dy) < (tol*tol);
2829}
2930
3031std::vector<std::vector<int >> map_mesh_cropped (
@@ -98,7 +99,7 @@ std::vector<std::vector<int>> map_mesh_cropped(
9899 }
99100
100101 // Update pattern vertex position through barycentric interpolation, comment this out if you keep 2D pattern
101- pattern_v.row (id) = A * L (0 , 0 ) + B * L (0 , 1 ) + C * L (0 , 2 );
102+ // pattern_v.row(id) = A * L(0, 0) + B * L(0, 1) + C * L(0, 2);
102103
103104 // If normals are requested, interpolate them using the same barycentric coordinates
104105 if (compute_normals) {
@@ -166,13 +167,16 @@ bool paths_intersect(const Clipper2Lib::PathsD& paths1, const Clipper2Lib::Paths
166167}
167168
168169
170+
171+
169172std::tuple<compas::RowMatrixXd, std::vector<std::vector<int >>, std::vector<bool >, std::vector<int >> eigen_to_clipper (
170173 Eigen::Ref<const compas::RowMatrixXd> flattned_target_uv,
171174 Eigen::Ref<const compas::RowMatrixXi> target_f,
172175
173176 Eigen::Ref<const compas::RowMatrixXd> pattern_v,
174177 const std::vector<std::vector<int >>& pattern_f,
175178 bool clip_boundaries,
179+ bool simplify_borders,
176180 double tolerance
177181)
178182{
@@ -217,21 +221,18 @@ std::tuple<compas::RowMatrixXd, std::vector<std::vector<int>>, std::vector<bool>
217221 path.emplace_back (pattern_v (point_id, 0 ), pattern_v (point_id, 1 ));
218222 paths.emplace_back (path);
219223
224+ if (!clip_boundaries){
225+ patterns_to_keep.push_back (paths);
226+ continue ;
227+ }
228+
220229 // Get bounds of current pattern polygon
221230 Clipper2Lib::RectD pattern_bounds = Clipper2Lib::GetBounds (paths);
222231
223232 // Check if the pattern bounds intersect with boundary bounds
224233 if (!pattern_bounds.Intersects (boundary_bounds))
225234 continue ;
226-
227- // Check if Elements are inside the boundary
228- // Clipper2Lib::PointD corners[] = {
229- // {pattern_bounds.left, pattern_bounds.top}, // Top-left
230- // {pattern_bounds.right, pattern_bounds.top}, // Top-right
231- // {pattern_bounds.right, pattern_bounds.bottom}, // Bottom-right
232- // {pattern_bounds.left, pattern_bounds.bottom} // Bottom-left
233- // };
234-
235+
235236 bool is_rect_inside_polygon = false ;
236237 size_t num_corners_in_polygon = 0 ;
237238 for (const auto & corner : paths[0 ]) {
@@ -241,8 +242,6 @@ std::tuple<compas::RowMatrixXd, std::vector<std::vector<int>>, std::vector<bool>
241242 is_rect_inside_polygon = true ;
242243 bool has_collision = paths_intersect (paths, boundary);
243244
244-
245-
246245 // check if the point is not in a hole
247246 bool is_rect_outside_polygon_hole = true ;
248247 for (size_t i = 1 ; i < boundary.size (); i++) {
@@ -274,12 +273,10 @@ std::tuple<compas::RowMatrixXd, std::vector<std::vector<int>>, std::vector<bool>
274273 // Fully enclosed polygons are added to the keep list
275274 // Other polygons edges are intersected with the boundary
276275
277- if (num_corners_in_polygon == paths[0 ].size () && !is_hole_in_polygon){
276+ if (num_corners_in_polygon == paths[0 ].size () && !is_hole_in_polygon){ //
278277 patterns_to_keep.push_back (paths);
279- }else if (clip_boundaries){
280- if (paths_intersect (paths, boundary) || is_hole_in_polygon){
281- patterns_to_cut.push_back (paths);
282- }
278+ }else if (num_corners_in_polygon > 0 || paths_intersect (paths, boundary) || is_hole_in_polygon){
279+ patterns_to_cut.push_back (paths);
283280 }
284281
285282
@@ -290,10 +287,56 @@ std::tuple<compas::RowMatrixXd, std::vector<std::vector<int>>, std::vector<bool>
290287 std::vector<Clipper2Lib::PathsD> solutions;
291288 for (const auto &subject : patterns_to_cut){
292289 Clipper2Lib::PathsD solution = Clipper2Lib::Intersect (subject, boundary, Clipper2Lib::FillRule::NonZero, 8 );
293- if (solution.size () == 0 )
290+
291+ if (solution.size () == 0 ){
292+ // If no intersection is found, skip this polygon
294293 continue ;
294+ }else if (solution.size () > 1 || !simplify_borders){
295+ // Highly likely we do not consider polygons with holes, we do not simplify them.
296+ solutions.push_back (solution);
297+ }else {
298+ // After boolean intersection the pattern is merged with boundary polygons. This is often not wanted.
299+ // Simplification is made by comparing polygon after boolean intersection and before. If the points lie on the initial polygon edges we keep them.
300+ // Additionally we add an option to keep arbitrary points from a user given points or boundary valence points.
301+ Clipper2Lib::PathsD simplified_paths{Clipper2Lib::PathD ()};
302+ simplified_paths[0 ].reserve (subject[0 ].size ());
303+
304+ for (const auto &p : solution[0 ]){ // iterate the points of the intersection polygon
305+ // First check if point is close to any vertex (corner)
306+ bool point_added = false ;
307+ for (size_t i = 0 ; i < subject[0 ].size (); i++){
308+ double dx = p.x - subject[0 ][i].x ;
309+ double dy = p.y - subject[0 ][i].y ;
310+ if (dx*dx + dy*dy < tolerance*tolerance){
311+ simplified_paths[0 ].push_back (p);
312+ point_added = true ;
313+ break ;
314+ }
315+ }
316+
317+ // If not close to a vertex, check if close to any edge
318+ if (!point_added) {
319+ for (size_t i = 0 ; i < subject[0 ].size ()-1 ; i++){
320+ if (Clipper2Lib::PerpendicDistFromLineSqrd (p, subject[0 ][i], subject[0 ][i+1 ]) < tolerance*tolerance){
321+ simplified_paths[0 ].push_back (p);
322+ break ;
323+ }
324+ }
325+ // Also check the closing edge between last and first point
326+ if (subject[0 ].size () > 1 ){
327+ size_t last = subject[0 ].size ()-1 ;
328+ if (Clipper2Lib::PerpendicDistFromLineSqrd (p, subject[0 ][last], subject[0 ][0 ]) < tolerance*tolerance){
329+ simplified_paths[0 ].push_back (p);
330+ }
331+ }
332+ }
333+ }
334+
335+ if (simplified_paths[0 ].size () > 2 )
336+ solutions.push_back (simplified_paths);
337+ }
295338
296- solutions. push_back (solution);
339+
297340 }
298341
299342 std::vector<std::array<double , 3 >> unique_points;
@@ -305,6 +348,7 @@ std::tuple<compas::RowMatrixXd, std::vector<std::vector<int>>, std::vector<bool>
305348 groups.reserve (solutions.size () + patterns_to_keep.size ());
306349 is_boundary.reserve (solutions.size () + patterns_to_keep.size ());
307350
351+ // 9 bucket search to avoid rounding wrong rounded grid key, when the distance is close to the tolerance.
308352 auto find_or_add_point = [&](double x, double y) -> int {
309353 double z = 0.0 ;
310354
@@ -323,7 +367,7 @@ std::tuple<compas::RowMatrixXd, std::vector<std::vector<int>>, std::vector<bool>
323367 // Check all points in this cell
324368 for (int idx : grid_it->second ) {
325369 const auto & pt = unique_points[idx];
326- if (is_same_point (pt[0 ], pt[1 ], pt[ 2 ], x, y, z , tolerance))
370+ if (is_same_point (pt[0 ], pt[1 ], x, y, tolerance))
327371 return idx;
328372 }
329373 }
@@ -378,12 +422,15 @@ std::tuple<compas::RowMatrixXd, std::vector<std::vector<int>>, std::vector<bool>
378422
379423}
380424
425+
426+
381427std::tuple<compas::RowMatrixXd, std::vector<std::vector<int >>, compas::RowMatrixXd, std::vector<bool >, std::vector<int >> map_mesh_with_automatic_parameterization (
382428 Eigen::Ref<const compas::RowMatrixXd> target_v,
383429 Eigen::Ref<const compas::RowMatrixXi> target_f,
384430 Eigen::Ref<compas::RowMatrixXd> pattern_v,
385431 const std::vector<std::vector<int >>& pattern_f,
386432 bool clip_boundaries,
433+ bool simplify_borders,
387434 double tolerance)
388435{
389436
@@ -395,6 +442,35 @@ std::tuple<compas::RowMatrixXd, std::vector<std::vector<int>>, compas::RowMatrix
395442 Eigen::VectorXi B;
396443 igl::boundary_loop (target_f, B);
397444
445+ // Get Boundary vertices
446+ std::vector<Eigen::Vector3d> corner_vertex_coords;
447+
448+ std::vector<std::vector<int >> VV;
449+ igl::adjacency_list (target_f, VV);
450+
451+ // Create a set of boundary vertices for faster lookup
452+ std::unordered_set<int > boundary_set;
453+ for (int i = 0 ; i < B.size (); i++) {
454+ boundary_set.insert (B (i));
455+ }
456+
457+ for (int i = 0 ; i < B.size (); i++) {
458+ int v = B (i);
459+ int boundary_valence = 0 ;
460+ for (int neighbor : VV[v])
461+ if (boundary_set.count (neighbor) > 0 )
462+ boundary_valence++;
463+
464+ if (boundary_valence < 2 ) {
465+ Eigen::Vector3d vertex_pos (target_v (v, 0 ), target_v (v, 1 ), target_v (v, 2 ));
466+ corner_vertex_coords.push_back (vertex_pos);
467+ std::cout << " Corner vertex " << v << " at position ("
468+ << target_v (v, 0 ) << " , "
469+ << target_v (v, 1 ) << " , "
470+ << target_v (v, 2 ) << " )" << std::endl;
471+ }
472+ }
473+
398474 // Fix two points on the boundary
399475 Eigen::VectorXi fixed (2 , 1 );
400476 fixed (0 ) = B (0 );
@@ -415,10 +491,10 @@ std::tuple<compas::RowMatrixXd, std::vector<std::vector<int>>, compas::RowMatrix
415491 double scale_factor = 1.0 / std::max (size (0 ), size (1 )); // Scale to fit in a 0-1 box while maintaining aspect ratio
416492 target_uv *= scale_factor;
417493
418- // Clip the pattern
419- auto [clipped_pattern_v, clipped_pattern_f, clipped_pattern_is_boundary, clipped_pattern_groups] = eigen_to_clipper (target_uv, target_f, pattern_v, pattern_f, clip_boundaries, tolerance);
494+
420495
421- // return std::make_tuple(clipped_pattern_v, clipped_pattern_f); // Comment this out to see 2d cropped pattern
496+ // Clip the pattern
497+ auto [clipped_pattern_v, clipped_pattern_f, clipped_pattern_is_boundary, clipped_pattern_groups] = eigen_to_clipper (target_uv, target_f, pattern_v, pattern_f, clip_boundaries, simplify_borders, tolerance);
422498
423499 Eigen::MatrixXd clipped_pattern_uv;
424500 clipped_pattern_uv.setZero ();
@@ -438,15 +514,13 @@ std::tuple<compas::RowMatrixXd, std::vector<std::vector<int>>, compas::RowMatrix
438514 clipped_pattern_uv,
439515 pattern_normals);
440516
441-
442517 return std::make_tuple (clipped_pattern_v, result, pattern_normals, clipped_pattern_is_boundary, clipped_pattern_groups);
443518
444519}
445520
446-
521+ // Define the nanobind module at global scope
447522NB_MODULE (_mapping, m)
448523{
449-
450524 m.def (
451525 " map_mesh_with_automatic_parameterization" ,
452526 &map_mesh_with_automatic_parameterization,
@@ -456,5 +530,7 @@ NB_MODULE(_mapping, m)
456530 " pattern_v" _a,
457531 " pattern_f" _a,
458532 " clip_boundaries" _a,
459- " tolerance" _a);
533+ " simplify_borders" _a,
534+ " tolerance" _a
535+ );
460536}
0 commit comments