From fcf4c2030bba04d2b533e8213541cbda3ecf399a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Mon, 25 Sep 2023 11:22:34 +0200 Subject: [PATCH 001/120] add initial structure for bsurf import into CGAL --- .../Bsurf/locally_shortest_path.h | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h new file mode 100644 index 000000000000..b597b4644461 --- /dev/null +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -0,0 +1,28 @@ +// Copyright (c) 2023 University of Genova (Italy). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial +// +// +// Author(s) : Claudio Mancinelli + +#ifndef CGAL_POLYGON_MESH_PROCESSING_BSURF_LOCALLY_SHORTEST_PATH_H +#define CGAL_POLYGON_MESH_PROCESSING_BSURF_LOCALLY_SHORTEST_PATH_H + +// #include + +#include +#include + +#include + + + +namespace CGAL{ +namespace Polygon_mesh_processing { + +} } // CGAL::Polygon_mesh_processing From 14641426a7c6aae04b20aac1032e3c32034a4164 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Mon, 25 Sep 2023 12:00:56 +0200 Subject: [PATCH 002/120] first example --- .../Polygon_mesh_processing/CMakeLists.txt | 1 + .../locally_shortest_path_sm_example.cpp | 43 +++++++++++++++++++ .../Bsurf/locally_shortest_path.h | 22 +++++++++- 3 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 Polygon_mesh_processing/examples/Polygon_mesh_processing/locally_shortest_path_sm_example.cpp diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt b/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt index c755ecf63e8e..24c3dae6c0bb 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt @@ -51,6 +51,7 @@ create_single_source_cgal_program("match_faces.cpp") create_single_source_cgal_program("cc_compatible_orientations.cpp") create_single_source_cgal_program("hausdorff_distance_remeshing_example.cpp") create_single_source_cgal_program("hausdorff_bounded_error_distance_example.cpp") +create_single_source_cgal_program("locally_shortest_path_sm_example.cpp") find_package(Eigen3 3.2.0 QUIET) #(requires 3.2.0 or greater) include(CGAL_Eigen3_support) diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/locally_shortest_path_sm_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/locally_shortest_path_sm_example.cpp new file mode 100644 index 000000000000..e365a47c4bc1 --- /dev/null +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/locally_shortest_path_sm_example.cpp @@ -0,0 +1,43 @@ +#include +#include +#include + +#include + + +namespace PMP = CGAL::Polygon_mesh_processing; + +typedef CGAL::Exact_predicates_inexact_constructions_kernel K; +typedef CGAL::Surface_mesh Mesh; +typedef PMP::Face_location Face_location; +typedef PMP::Edge_location Edge_location; + + +int main(int argc, char** argv) +{ + std::string filename = (argc > 1) ? std::string(argv[1]) + : CGAL::data_file_path("meshes/elephant.off"); + + Mesh mesh; + if(!CGAL::IO::read_polygon_mesh(filename, mesh) || !CGAL::is_triangle_mesh(mesh)) + { + std::cerr << "Invalid input." << std::endl; + return 1; + } + + std::size_t nb_faces = faces(mesh).size(); + + // take two random faces and pick the centroid + CGAL::Random rnd = CGAL::get_default_random(); + Mesh::Face_index f1 = *std::next(faces(mesh).begin(), rnd.get_int(0, nb_faces)); + Mesh::Face_index f2 = *std::next(faces(mesh).begin(), rnd.get_int(0, nb_faces)); + Face_location src(f1, CGAL::make_array(0.3,0.3,0.4)); + Face_location tgt(f2, CGAL::make_array(0.3,0.3,0.4)); + + + std::vector edge_locations; + + CGAL::Polygon_mesh_processing::locally_shortest_path(src, tgt, mesh, edge_locations); + + +} diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index b597b4644461..380d9d4ebdc1 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -20,9 +20,29 @@ #include - +#include namespace CGAL{ namespace Polygon_mesh_processing { +template +struct Edge_location +{ + typename boost::graph_traits::edge_descriptor ed; + FT alpha; +}; + +template +void +locally_shortest_path(const Face_location& src, + const Face_location& tgt, + const TriangleMesh& tmesh, + EdgeLocationRange& edge_locations) +{ + +} + + } } // CGAL::Polygon_mesh_processing + +#endif From a0a230249cd18ec298e6bc64bb2c6bed0279191c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Mon, 25 Sep 2023 15:02:17 +0200 Subject: [PATCH 003/120] add dijkstra call on the dual graph + fill edges for unfolding --- .../locally_shortest_path_sm_example.cpp | 11 ++- .../Bsurf/locally_shortest_path.h | 88 +++++++++++++++++-- .../CGAL/Polygon_mesh_processing/locate.h | 12 +++ 3 files changed, 103 insertions(+), 8 deletions(-) diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/locally_shortest_path_sm_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/locally_shortest_path_sm_example.cpp index e365a47c4bc1..5cc5a40b453d 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/locally_shortest_path_sm_example.cpp +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/locally_shortest_path_sm_example.cpp @@ -29,15 +29,20 @@ int main(int argc, char** argv) // take two random faces and pick the centroid CGAL::Random rnd = CGAL::get_default_random(); + std::cout << "seed " << rnd.get_seed() << std::endl; Mesh::Face_index f1 = *std::next(faces(mesh).begin(), rnd.get_int(0, nb_faces)); Mesh::Face_index f2 = *std::next(faces(mesh).begin(), rnd.get_int(0, nb_faces)); Face_location src(f1, CGAL::make_array(0.3,0.3,0.4)); Face_location tgt(f2, CGAL::make_array(0.3,0.3,0.4)); - std::vector edge_locations; - CGAL::Polygon_mesh_processing::locally_shortest_path(src, tgt, mesh, edge_locations); - + std::ofstream out("locally_shortest_path.polylines.txt"); + out << edge_locations.size()+2; + out << " " << PMP::construct_point(src, mesh); + for (auto el : edge_locations) + out << " " << PMP::construct_point(el, mesh); + out << " " << PMP::construct_point(tgt, mesh) << "\n"; + return 0; } diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index 380d9d4ebdc1..6c525a62327e 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -17,20 +17,54 @@ #include #include +#include +#include +#include #include -#include + namespace CGAL{ namespace Polygon_mesh_processing { template -struct Edge_location +using Edge_location = std::pair< typename boost::graph_traits::edge_descriptor, std::array >; + +template +#ifdef DOXYGEN_RUNNING +Point +construct_point(const Edge_location& loc, +#else +typename internal::Location_traits::Point +construct_point(const Edge_location & loc, +#endif + const TriangleMesh& tm, + const NamedParameters& np = parameters::default_values()) { - typename boost::graph_traits::edge_descriptor ed; - FT alpha; -}; + typedef typename boost::graph_traits::edge_descriptor edge_descriptor; + typedef typename GetGeomTraits::type Geom_traits; + + typedef typename GetVertexPointMap::const_type VertexPointMap; + typedef typename boost::property_traits::value_type Point; + typedef typename boost::property_traits::reference Point_reference; + + using parameters::get_parameter; + using parameters::choose_parameter; + + CGAL_precondition(CGAL::is_triangle_mesh(tm)); + + VertexPointMap vpm = parameters::choose_parameter(parameters::get_parameter(np, internal_np::vertex_point), + get_const_property_map(boost::vertex_point, tm)); + Geom_traits gt = choose_parameter(get_parameter(np, internal_np::geom_traits)); + + edge_descriptor ed = loc.first; + const Point_reference p0 = get(vpm, source(ed, tm)); + const Point_reference p1 = get(vpm, target(ed, tm)); + + internal::Barycentric_point_constructor bp_constructor; + return bp_constructor(p0, loc.second[0], p1, loc.second[1], gt); +} template void @@ -39,7 +73,51 @@ locally_shortest_path(const Face_location& src, const TriangleMesh& tmesh, EdgeLocationRange& edge_locations) { + typedef typename boost::graph_traits::face_descriptor face_descriptor; + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + + typename boost::property_map >::const_type + predecessor_map = get(CGAL::dynamic_face_property_t(), tmesh); + typename boost::property_map >::const_type + distance_map = get(CGAL::dynamic_face_property_t(), tmesh); + typename boost::property_map >::const_type + weight_map = get(CGAL::dynamic_edge_property_t(), tmesh); + + Dual dual(tmesh); + + // TODO: fill the weight map + + // TODO try stopping dijkstra as soon tgt is out of the queue. + boost::dijkstra_shortest_paths(dual, src.first, + boost::distance_map(distance_map) + .predecessor_map(predecessor_map) + .weight_map(weight_map)); + + std::vector initial_path; + + auto common_halfedge=[](face_descriptor f1, face_descriptor f2, const TriangleMesh& tmesh) + { + halfedge_descriptor h=halfedge(f1, tmesh); + for (int i=0; i<3;++i) + { + if (face(opposite(h, tmesh), tmesh)==f2) + return h; + h = next(h, tmesh); + } + CGAL_assertion(!"faces do no share a common edge"); + return halfedge_descriptor(); + }; + face_descriptor current_face = tgt.first; + while (true) + { + face_descriptor prev = get(predecessor_map, current_face); + halfedge_descriptor h=common_halfedge(current_face, prev, tmesh); + initial_path.push_back(h); + if (prev==src.first) break; + current_face=prev; + } + std::reverse(initial_path.begin(), initial_path.end()); } diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/locate.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/locate.h index ef361b175299..92da3dc20333 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/locate.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/locate.h @@ -340,6 +340,18 @@ struct Barycentric_point_constructor // 3D version return P(x, y, z); } + + P operator()(const P& p, const FT wp, const P& q, const FT wq, + const K& /*k*/) const + { + FT sum = wp + wq; + CGAL_assertion(sum != FT(0)); + FT x = (wp * p.x() + wq * q.x()) / sum; + FT y = (wp * p.y() + wq * q.y()) / sum; + FT z = (wp * p.z() + wq * q.z()) / sum; + + return P(x, y, z); + } }; } // namespace internal From c75619969267e9c8a35214e0ea1c7111df3f7d76 Mon Sep 17 00:00:00 2001 From: ClaudioMancinelli Date: Mon, 25 Sep 2023 18:18:14 +0200 Subject: [PATCH 004/120] first running versione of shortest path (runs but bugged) --- .../Bsurf/locally_shortest_path.h | 533 ++++++++++++++++-- 1 file changed, 482 insertions(+), 51 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index 6c525a62327e..58397470b93f 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -16,47 +16,53 @@ // #include #include -#include -#include #include +#include +#include #include #include - - -namespace CGAL{ +namespace CGAL { namespace Polygon_mesh_processing { template -using Edge_location = std::pair< typename boost::graph_traits::edge_descriptor, std::array >; +using Edge_location = + std::pair::edge_descriptor, + std::array>; -template +template #ifdef DOXYGEN_RUNNING -Point -construct_point(const Edge_location& loc, +Point construct_point( + const Edge_location &loc, #else typename internal::Location_traits::Point -construct_point(const Edge_location & loc, +construct_point(const Edge_location &loc, #endif - const TriangleMesh& tm, - const NamedParameters& np = parameters::default_values()) -{ - typedef typename boost::graph_traits::edge_descriptor edge_descriptor; - typedef typename GetGeomTraits::type Geom_traits; + const TriangleMesh &tm, + const NamedParameters &np = parameters::default_values()) { + typedef typename boost::graph_traits::edge_descriptor + edge_descriptor; + typedef + typename GetGeomTraits::type Geom_traits; - typedef typename GetVertexPointMap::const_type VertexPointMap; - typedef typename boost::property_traits::value_type Point; - typedef typename boost::property_traits::reference Point_reference; + typedef typename GetVertexPointMap::const_type + VertexPointMap; + typedef typename boost::property_traits::value_type Point; + typedef typename boost::property_traits::reference + Point_reference; - using parameters::get_parameter; using parameters::choose_parameter; + using parameters::get_parameter; CGAL_precondition(CGAL::is_triangle_mesh(tm)); - VertexPointMap vpm = parameters::choose_parameter(parameters::get_parameter(np, internal_np::vertex_point), - get_const_property_map(boost::vertex_point, tm)); - Geom_traits gt = choose_parameter(get_parameter(np, internal_np::geom_traits)); + VertexPointMap vpm = parameters::choose_parameter( + parameters::get_parameter(np, internal_np::vertex_point), + get_const_property_map(boost::vertex_point, tm)); + Geom_traits gt = choose_parameter( + get_parameter(np, internal_np::geom_traits)); edge_descriptor ed = loc.first; const Point_reference p0 = get(vpm, source(ed, tm)); @@ -66,41 +72,452 @@ construct_point(const Edge_location & loc, return bp_constructor(p0, loc.second[0], p1, loc.second[1], gt); } +namespace internal { + +template +struct Locally_shortest_path_imp +{ + using face_descriptor = + typename boost::graph_traits::face_descriptor; + using vertex_descriptor = + typename boost::graph_traits::vertex_descriptor; + using halfedge_descriptor = + typename boost::graph_traits::halfedge_descriptor; + + using Point_2 = typename K::Point_2; + using Point_3 = typename K::Point_3; + using Vector_2 = typename K::Vector_2; + using Vector_3 = typename K::Vector_3; + using FT = typename K::FT; + + static + Vector_2 + intersect_circles(const Vector_2 &c2, FT R2, + const Vector_2 &c1, FT R1) + { + auto R = (c2 - c1).squared_length(); + assert(R > 0); + auto invR = FT(1) / R; + Vector_2 result = c1+c2; + + result = result + (c2 - c1) * ((R1 - R2) * invR); + auto A = 2 * (R1 + R2) * invR; + auto B = (R1 - R2) * invR; + auto s = A - B * B - 1; + assert(s >= 0); + result = result + Vector_2(c2.y() - c1.y(), c1.x() - c2.x()) * sqrt(s); + return result / 2.; + } + + static std::array + init_flat_triangles(face_descriptor f, + const VertexPointMap &vpm, const TriangleMesh &mesh, + const Face_location &src) + { + halfedge_descriptor h = halfedge(f, mesh); + std::array triangle_vertices = make_array( + source(h, mesh), target(h, mesh), target(next(h, mesh), mesh)); + + std::array tr2d; + tr2d[0] = Vector_2(0, 0); + tr2d[1] = + Vector_2(0, sqrt(squared_distance(get(vpm, triangle_vertices[0]), + get(vpm, triangle_vertices[1])))); + auto rx = squared_distance(get(vpm, triangle_vertices[0]), + get(vpm, triangle_vertices[2])); + auto ry = squared_distance(get(vpm, triangle_vertices[1]), + get(vpm, triangle_vertices[2])); + tr2d[2] = intersect_circles(tr2d[0], rx, tr2d[1], ry); + + return tr2d; + } + + static std::array + init_source_triangle(halfedge_descriptor hopp, + const VertexPointMap &vpm, + const TriangleMesh &mesh, + Face_location src) + { + halfedge_descriptor h = opposite(hopp, mesh); + std::array triangle_vertices = make_array( + source(h, mesh), target(h, mesh), target(next(h, mesh), mesh)); + + std::array tr2d; + tr2d[0] = Vector_2(0, 0); + tr2d[1] = + Vector_2(0, sqrt(squared_distance(get(vpm, triangle_vertices[0]), + get(vpm, triangle_vertices[1])))); + FT rx = squared_distance(get(vpm, triangle_vertices[0]), + get(vpm, triangle_vertices[2])); + FT ry = squared_distance(get(vpm, triangle_vertices[1]), + get(vpm, triangle_vertices[2])); + tr2d[2] = intersect_circles(tr2d[0], rx, tr2d[1], ry); + + + halfedge_descriptor href = halfedge(src.first, mesh); + if (href!=h) + { + if (href==next(h, mesh)) + { + std::array tmp = CGAL::make_array(src.second[2], src.second[0], src.second[1]); + src.second = tmp; + } + else + { + CGAL_assertion(next(href, mesh)==h); + std::array tmp = CGAL::make_array(src.second[1], src.second[2], src.second[0]); + src.second = tmp; + } + } + + auto point_coords = tr2d[0] * src.second[0] + tr2d[1] * src.second[1] + + tr2d[2] * src.second[2]; + return make_array(tr2d[0] - point_coords, + tr2d[1] - point_coords); + } + + static std::array + init_target_triangle(halfedge_descriptor h, + const std::array& flat_tid, + const VertexPointMap &vpm, const TriangleMesh &mesh, + Face_location tgt) + { + std::array triangle_vertices = make_array( + source(h, mesh), target(h, mesh), target(next(h, mesh), mesh)); + + std::array tr2d; + tr2d[0] = flat_tid[1]; + tr2d[1] = flat_tid[0]; + FT rx = squared_distance(get(vpm, triangle_vertices[0]), + get(vpm, triangle_vertices[2])); + FT ry = squared_distance(get(vpm, triangle_vertices[1]), + get(vpm, triangle_vertices[2])); + tr2d[2] = intersect_circles(tr2d[0], rx, tr2d[1], ry); + + + halfedge_descriptor href = halfedge(tgt.first, mesh); + if (href!=h) + { + if (href==next(h, mesh)) + { + std::array tmp = CGAL::make_array(tgt.second[2], tgt.second[0], tgt.second[1]); + tgt.second = tmp; + } + else + { + CGAL_assertion(next(href, mesh)==h); + std::array tmp = CGAL::make_array(tgt.second[1], tgt.second[2], tgt.second[0]); + tgt.second = tmp; + } + } + + auto point_coords = tr2d[0] * tgt.second[0] + tr2d[1] * tgt.second[1] + + tr2d[2] * tgt.second[2]; + return make_array(point_coords, + point_coords); + } + + // static + // std::array + // unfold_face(halfedge_descriptor h, + // const VertexPointMap &vpm, const TriangleMesh &mesh, + // const std::array& flat_tid) + // { + // halfedge_descriptor h_opp = opposite(h_curr, mesh); + + + + // vertex_descriptor v = target(next(h_curr,mesh),mesh); + // vertex_descriptor a = target(h_curr,mesh); + // vertex_descriptor b = source(h_curr, mesh); + // FT r0 = squared_distance(get(vpm,v), get(vpm,a)); + // FT r1 = squared_distance(get(vpm,v), get(vpm,b)); + + // Vector_2 v2 = intersect_circles(flat_tid[0], r0, flat_tid[1], r1); + + + // std::array res; + // if(next(h_curr, mesh) == h_next_opp) + // { + // res[0]=flat_tid[0]; + // //res[2]=flat_tid[1]; + // res[1]=v2; + // } + // else + // { + // CGAL_assertion(prev(h_curr, mesh) == h_next_opp); + // res[0]=v2; + // res[1]=flat_tid[1]; + // //res[2]=flat_tid[0]; + // } + + // return res; + // } +static + std::array + unfold_face(halfedge_descriptor h_curr, halfedge_descriptor h_next, + const VertexPointMap &vpm, const TriangleMesh &mesh, + const std::array& flat_tid) + { + halfedge_descriptor h_next_opp = opposite(h_next, mesh); + CGAL_assertion(face( h_curr, mesh) == face(h_next_opp, mesh)); + + + vertex_descriptor v = target(next(h_curr,mesh),mesh); + vertex_descriptor a = target(h_curr,mesh); + vertex_descriptor b = source(h_curr, mesh); + FT r0 = squared_distance(get(vpm,v), get(vpm,a)); + FT r1 = squared_distance(get(vpm,v), get(vpm,b)); + + Vector_2 v2 = intersect_circles(flat_tid[0], r0, flat_tid[1], r1); + + + std::array res; + if(next(h_curr, mesh) == h_next_opp) + { + res[0]=flat_tid[0]; + //res[2]=flat_tid[1]; + res[1]=v2; + } + else + { + CGAL_assertion(prev(h_curr, mesh) == h_next_opp); + res[0]=v2; + res[1]=flat_tid[1]; + //res[2]=flat_tid[0]; + } + + return res; + } + static + std::vector< std::array> + unfold_strip(const std::vector& initial_path, + const Face_location& src, + const Face_location& tgt, + const VertexPointMap &vpm, const TriangleMesh &mesh) + { + std::size_t s=initial_path.size(); + std::vector> result(s+1); + result[0]=init_source_triangle(initial_path[0], vpm, mesh, src); + for(std::size_t i=1;i &path) { + // Among vertices around which the path curves, find the vertex + // with maximum angle. We are going to fix that vertex. Actually, max_index is + // the index of the first face containing that vertex. + auto max_index = -1; + auto max_angle = 0.0f; + for (auto i = 1; i < path.size() - 1; ++i) { + Vector_2 pos = path[i].pos; + Vector_2 prev = path[i - 1].pos; + Vector_2 next = path[i + 1].pos; + Vector_2 v0 = pos - prev; + v0 = v0 / sqrt(v0.squared_length()); + Vector_2 v1 = next - pos; + v1 = v1 / sqrt(v1.squared_length()); + FT angle = 1 - scalar_product(v0, v1); + if (angle > max_angle) { + max_index = path[i].face; + max_angle = angle; + } + } + return max_index; + } + + static + std::vector + funnel(const std::vector< std::array>& portals, int& max_index) + { + // Find straight path. + Vector_2 start(NULL_VECTOR); + int apex_index = 0; + int left_index = 0; + int right_index = 0; + Vector_2 apex = start; + Vector_2 left_bound = portals[0][0]; + Vector_2 right_bound = portals[0][1]; + + // Add start point. + std::vector points = std::vector{{apex_index, apex}}; + points.reserve(portals.size()); + + // @Speed: is this slower than an inlined function? + auto area = [](const Vector_2 a, const Vector_2 b, const Vector_2 c) { + return determinant(b - a, c - a); + }; + + for (auto i = 1; i < portals.size(); ++i) { + auto left = portals[i][0], right = portals[i][1]; + // Update right vertex. + if (area(apex, right_bound, right) <= 0) { + if (apex == right_bound || area(apex, left_bound, right) > 0) { + // Tighten the funnel. + right_bound = right; + right_index = i; + } else { + // Right over left, insert left to path and restart scan from + // portal left point. + if (left_bound != apex) { + points.push_back({left_index, left_bound}); + // Make current left the new apex. + apex = left_bound; + apex_index = left_index; + // Reset portal + left_bound = apex; + right_bound = apex; + left_index = apex_index; + right_index = apex_index; + // Restart scan + i = apex_index; + continue; + } + } + } + + // Update left vertex. + if (area(apex, left_bound, left) >= 0) { + if (apex == left_bound || area(apex, right_bound, left) < 0) { + // Tighten the funnel. + left_bound = left; + left_index = i; + } else { + if (right_bound != apex) { + points.push_back({right_index, right_bound}); + // Make current right the new apex. + apex = right_bound; + apex_index = right_index; + // Reset portal + left_bound = apex; + right_bound = apex; + left_index = apex_index; + right_index = apex_index; + // Restart scan + i = apex_index; + continue; + } + } + } + } + + // This happens when we got an apex on the last edge of the strip + if (points.back().pos != portals.back()[0]) { + points.push_back({(int)portals.size() - 1, portals.back()[0]}); + } + assert(points.back().pos == portals.back()[0]); + assert(points.back().pos == portals.back()[1]); + + std::vector lerps; + lerps.reserve(portals.size()); + for (auto i = 0; i < points.size() - 1; i++) { + auto a = points[i].pos; + auto b = points[i + 1].pos; + for (auto k = points[i].face; k < points[i + 1].face; ++k) { + auto portal = portals[k]; + // assert(cross(b - a, portal.second - portal.first) > 0); + FT s = intersect_segments(a, b, portal[0], portal[1]); + lerps.push_back(std::clamp(s, 0.0, 1.0)); + } + } + + auto index = 1; + for (auto i = 1; i < portals.size(); ++i) { + if ((portals[i][0] == points[index].pos) || + (portals[i][1] == points[index].pos)) { + points[index].face = i; + index += 1; + } + } + max_index = max_curvature_point(points); + // assert(lerps.size() == portals.size() - 1); + return lerps; + } +}; + +} // namespace internal + template -void -locally_shortest_path(const Face_location& src, - const Face_location& tgt, - const TriangleMesh& tmesh, - EdgeLocationRange& edge_locations) +void locally_shortest_path(const Face_location &src, + const Face_location &tgt, + const TriangleMesh &tmesh, + EdgeLocationRange &edge_locations) { - typedef typename boost::graph_traits::face_descriptor face_descriptor; - typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + typedef typename boost::graph_traits::face_descriptor + face_descriptor; + typedef typename boost::graph_traits::halfedge_descriptor + halfedge_descriptor; + typedef typename boost::graph_traits::edge_descriptor + edge_descriptor; + + using VPM = typename boost::property_map::const_type; + using K = typename Kernel_traits::value_type>::type; + using Impl = internal::Locally_shortest_path_imp; + VPM vpm = get(CGAL::vertex_point, tmesh); - typename boost::property_map >::const_type - predecessor_map = get(CGAL::dynamic_face_property_t(), tmesh); - typename boost::property_map >::const_type - distance_map = get(CGAL::dynamic_face_property_t(), tmesh); - typename boost::property_map >::const_type - weight_map = get(CGAL::dynamic_edge_property_t(), tmesh); + typename boost::property_map< + TriangleMesh, CGAL::dynamic_face_property_t>::const_type + predecessor_map = + get(CGAL::dynamic_face_property_t(), tmesh); + typename boost::property_map>::const_type + distance_map = get(CGAL::dynamic_face_property_t(), tmesh); + typename boost::property_map< + TriangleMesh, CGAL::dynamic_edge_property_t>::const_type weight_map = + get(CGAL::dynamic_edge_property_t(), tmesh); + +//TODO: handle boundary edges Dual dual(tmesh); - // TODO: fill the weight map + // TODO: fill the weight map using something better than euclidean distance + for (edge_descriptor ed : edges(tmesh)) + { + halfedge_descriptor h=halfedge(ed, tmesh), hopp=opposite(h, tmesh); + put(weight_map, ed, + sqrt(squared_distance( + centroid(get(vpm, source(h, tmesh)), get(vpm, target(h, tmesh)), get(vpm, target(next(h, tmesh), tmesh))), + centroid(get(vpm, source(hopp, tmesh)), get(vpm, target(hopp, tmesh)), get(vpm, target(next(hopp, tmesh), tmesh))) + ))); + } // TODO try stopping dijkstra as soon tgt is out of the queue. boost::dijkstra_shortest_paths(dual, src.first, boost::distance_map(distance_map) - .predecessor_map(predecessor_map) - .weight_map(weight_map)); + .predecessor_map(predecessor_map) + .weight_map(weight_map)); std::vector initial_path; - auto common_halfedge=[](face_descriptor f1, face_descriptor f2, const TriangleMesh& tmesh) - { - halfedge_descriptor h=halfedge(f1, tmesh); - for (int i=0; i<3;++i) - { - if (face(opposite(h, tmesh), tmesh)==f2) + auto common_halfedge = [](face_descriptor f1, face_descriptor f2, + const TriangleMesh &tmesh) { + halfedge_descriptor h = halfedge(f1, tmesh); + for (int i = 0; i < 3; ++i) { + if (face(opposite(h, tmesh), tmesh) == f2) return h; h = next(h, tmesh); } @@ -109,18 +526,32 @@ locally_shortest_path(const Face_location& src, }; face_descriptor current_face = tgt.first; - while (true) - { + while (true) { face_descriptor prev = get(predecessor_map, current_face); - halfedge_descriptor h=common_halfedge(current_face, prev, tmesh); + halfedge_descriptor h = common_halfedge(current_face, prev, tmesh); initial_path.push_back(h); - if (prev==src.first) break; - current_face=prev; + if (prev == src.first) + break; + current_face = prev; } std::reverse(initial_path.begin(), initial_path.end()); -} +//TODO replace with named parameter + std::vector< std::array> portals=Impl::unfold_strip(initial_path,src,tgt,vpm,tmesh); + int max_index=0; + std::vector lerps=Impl::funnel(portals,max_index); + + CGAL_assertion(lerps.size()==initial_path.size()); + + //TODO: tmp for testing + edge_locations.reserve(initial_path.size()); + for(std::size_t i=0; i Date: Tue, 26 Sep 2023 08:47:08 +0200 Subject: [PATCH 005/120] fix order for the intersection since vertices should be CCW oriented --- .../locally_shortest_path_sm_example.cpp | 3 + .../Bsurf/locally_shortest_path.h | 69 +++++++++++++------ 2 files changed, 51 insertions(+), 21 deletions(-) diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/locally_shortest_path_sm_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/locally_shortest_path_sm_example.cpp index 5cc5a40b453d..513e2e18a8cc 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/locally_shortest_path_sm_example.cpp +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/locally_shortest_path_sm_example.cpp @@ -32,6 +32,9 @@ int main(int argc, char** argv) std::cout << "seed " << rnd.get_seed() << std::endl; Mesh::Face_index f1 = *std::next(faces(mesh).begin(), rnd.get_int(0, nb_faces)); Mesh::Face_index f2 = *std::next(faces(mesh).begin(), rnd.get_int(0, nb_faces)); + // or pick specific faces + //Mesh::Face_index f1( 5090 ); + //Mesh::Face_index f2( 617 ); Face_location src(f1, CGAL::make_array(0.3,0.3,0.4)); Face_location tgt(f2, CGAL::make_array(0.3,0.3,0.4)); diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index 58397470b93f..c9909726fa49 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -75,7 +75,7 @@ construct_point(const Edge_location &loc, namespace internal { template -struct Locally_shortest_path_imp +struct Locally_shortest_path_imp { using face_descriptor = typename boost::graph_traits::face_descriptor; @@ -90,10 +90,11 @@ struct Locally_shortest_path_imp using Vector_3 = typename K::Vector_3; using FT = typename K::FT; + // TODO: recode using CGAL code? static Vector_2 intersect_circles(const Vector_2 &c2, FT R2, - const Vector_2 &c1, FT R1) + const Vector_2 &c1, FT R1) { auto R = (c2 - c1).squared_length(); assert(R > 0); @@ -153,7 +154,7 @@ struct Locally_shortest_path_imp get(vpm, triangle_vertices[2])); tr2d[2] = intersect_circles(tr2d[0], rx, tr2d[1], ry); - + halfedge_descriptor href = halfedge(src.first, mesh); if (href!=h) { @@ -172,6 +173,12 @@ struct Locally_shortest_path_imp auto point_coords = tr2d[0] * src.second[0] + tr2d[1] * src.second[1] + tr2d[2] * src.second[2]; +#ifdef CGAL_DEBUG_BSURF + std::cout << "4 " << tr2d[0] - point_coords << " 0 " + << tr2d[1] - point_coords << " 0 " + << tr2d[2] - point_coords << " 0 " + << tr2d[0] - point_coords << " 0\n"; +#endif return make_array(tr2d[0] - point_coords, tr2d[1] - point_coords); } @@ -194,7 +201,7 @@ struct Locally_shortest_path_imp get(vpm, triangle_vertices[2])); tr2d[2] = intersect_circles(tr2d[0], rx, tr2d[1], ry); - + halfedge_descriptor href = halfedge(tgt.first, mesh); if (href!=h) { @@ -213,6 +220,14 @@ struct Locally_shortest_path_imp auto point_coords = tr2d[0] * tgt.second[0] + tr2d[1] * tgt.second[1] + tr2d[2] * tgt.second[2]; + +#ifdef CGAL_DEBUG_BSURF + std::cout << "4 " << tr2d[0] << " 0 " + << tr2d[1] << " 0 " + << tr2d[2] << " 0 " + << tr2d[0] << " 0\n"; +#endif + return make_array(point_coords, point_coords); } @@ -221,18 +236,18 @@ struct Locally_shortest_path_imp // std::array // unfold_face(halfedge_descriptor h, // const VertexPointMap &vpm, const TriangleMesh &mesh, - // const std::array& flat_tid) + // const std::array& flat_tid) // { // halfedge_descriptor h_opp = opposite(h_curr, mesh); - - - + + + // vertex_descriptor v = target(next(h_curr,mesh),mesh); // vertex_descriptor a = target(h_curr,mesh); // vertex_descriptor b = source(h_curr, mesh); // FT r0 = squared_distance(get(vpm,v), get(vpm,a)); // FT r1 = squared_distance(get(vpm,v), get(vpm,b)); - + // Vector_2 v2 = intersect_circles(flat_tid[0], r0, flat_tid[1], r1); @@ -257,19 +272,19 @@ static std::array unfold_face(halfedge_descriptor h_curr, halfedge_descriptor h_next, const VertexPointMap &vpm, const TriangleMesh &mesh, - const std::array& flat_tid) + const std::array& flat_tid) { halfedge_descriptor h_next_opp = opposite(h_next, mesh); CGAL_assertion(face( h_curr, mesh) == face(h_next_opp, mesh)); - - + + vertex_descriptor v = target(next(h_curr,mesh),mesh); vertex_descriptor a = target(h_curr,mesh); vertex_descriptor b = source(h_curr, mesh); FT r0 = squared_distance(get(vpm,v), get(vpm,a)); FT r1 = squared_distance(get(vpm,v), get(vpm,b)); - - Vector_2 v2 = intersect_circles(flat_tid[0], r0, flat_tid[1], r1); + + Vector_2 v2 = intersect_circles(flat_tid[1], r1, flat_tid[0], r0); std::array res; @@ -278,6 +293,12 @@ static res[0]=flat_tid[0]; //res[2]=flat_tid[1]; res[1]=v2; +#ifdef CGAL_DEBUG_BSURF + std::cout << "4 " << res[0] << " 0 " + << res[1] << " 0 " + << flat_tid[1] << " 0 " + << res[0] << " 0\n"; +#endif } else { @@ -285,24 +306,30 @@ static res[0]=v2; res[1]=flat_tid[1]; //res[2]=flat_tid[0]; +#ifdef CGAL_DEBUG_BSURF + std::cout << "4 " << res[0] << " 0 " + << res[1] << " 0 " + << flat_tid[0] << " 0 " + << res[0] << " 0\n"; +#endif } return res; } - static + static std::vector< std::array> - unfold_strip(const std::vector& initial_path, + unfold_strip(const std::vector& initial_path, const Face_location& src, const Face_location& tgt, const VertexPointMap &vpm, const TriangleMesh &mesh) - { + { std::size_t s=initial_path.size(); std::vector> result(s+1); result[0]=init_source_triangle(initial_path[0], vpm, mesh, src); for(std::size_t i=1;i &path) { // Among vertices around which the path curves, find the vertex // with maximum angle. We are going to fix that vertex. Actually, max_index is @@ -352,7 +379,7 @@ static static std::vector - funnel(const std::vector< std::array>& portals, int& max_index) + funnel(const std::vector< std::array>& portals, int& max_index) { // Find straight path. Vector_2 start(NULL_VECTOR); @@ -498,7 +525,7 @@ void locally_shortest_path(const Face_location &src, for (edge_descriptor ed : edges(tmesh)) { halfedge_descriptor h=halfedge(ed, tmesh), hopp=opposite(h, tmesh); - put(weight_map, ed, + put(weight_map, ed, sqrt(squared_distance( centroid(get(vpm, source(h, tmesh)), get(vpm, target(h, tmesh)), get(vpm, target(next(h, tmesh), tmesh))), centroid(get(vpm, source(hopp, tmesh)), get(vpm, target(hopp, tmesh)), get(vpm, target(next(hopp, tmesh), tmesh))) From 9e3c1f4b1382dfdb7cdca3ae9805ff9a63e68baa Mon Sep 17 00:00:00 2001 From: ClaudioMancinelli Date: Tue, 26 Sep 2023 12:23:29 +0200 Subject: [PATCH 006/120] WIP: straightening path --- .../locally_shortest_path_sm_example.cpp | 8 +- .../Bsurf/locally_shortest_path.h | 122 +++++++++++++++++- 2 files changed, 126 insertions(+), 4 deletions(-) diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/locally_shortest_path_sm_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/locally_shortest_path_sm_example.cpp index 513e2e18a8cc..25dd70f5ad05 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/locally_shortest_path_sm_example.cpp +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/locally_shortest_path_sm_example.cpp @@ -28,7 +28,8 @@ int main(int argc, char** argv) std::size_t nb_faces = faces(mesh).size(); // take two random faces and pick the centroid - CGAL::Random rnd = CGAL::get_default_random(); + // CGAL::Random rnd = CGAL::get_default_random(); + CGAL::Random rnd(1695720148); std::cout << "seed " << rnd.get_seed() << std::endl; Mesh::Face_index f1 = *std::next(faces(mesh).begin(), rnd.get_int(0, nb_faces)); Mesh::Face_index f2 = *std::next(faces(mesh).begin(), rnd.get_int(0, nb_faces)); @@ -38,9 +39,12 @@ int main(int argc, char** argv) Face_location src(f1, CGAL::make_array(0.3,0.3,0.4)); Face_location tgt(f2, CGAL::make_array(0.3,0.3,0.4)); + std::cout << "src = " << PMP::construct_point(src, mesh) << "\n"; + std::cout << "tgt = " << PMP::construct_point(tgt, mesh) << "\n"; + std::vector edge_locations; CGAL::Polygon_mesh_processing::locally_shortest_path(src, tgt, mesh, edge_locations); - + std::ofstream out("locally_shortest_path.polylines.txt"); out << edge_locations.size()+2; out << " " << PMP::construct_point(src, mesh); diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index c9909726fa49..95aa60e61ad4 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -90,6 +90,23 @@ struct Locally_shortest_path_imp using Vector_3 = typename K::Vector_3; using FT = typename K::FT; +// #ifdef CGAL_DEBUG_BSURF + static + void dump_path(const std::vector& path, + const std::vector& lerps, + const Face_location& src, + const Face_location& tgt, + const TriangleMesh& mesh) + { + static int i = -1; + std::ofstream out("path_"+std::to_string(++i)+".polylines.txt"); + out << path.size()+2 << " " << construct_point(src, mesh); + for(std::size_t i=0; i(path[i], make_array(lerps[i], 1.-lerps[i])), mesh); + out << " " << construct_point(tgt, mesh) << "\n"; + } +// #endif + // TODO: recode using CGAL code? static Vector_2 @@ -484,6 +501,107 @@ static // assert(lerps.size() == portals.size() - 1); return lerps; } + static + void straighten_path(std::vector< std::array>& portals, + std::vector& lerps, + std::vector& path, + const Face_location& src, + const Face_location& tgt, + const VertexPointMap &vpm, const TriangleMesh &mesh,int index) + { + dump_path(path, lerps, src, tgt, mesh); + vertex_descriptor vertex=boost::graph_traits::null_vertex(); + + // TODO: use a while loop breaking when no apex vertices not already visited are available + for (auto i = 0; i < portals.size() * 2 && index != -1; i++) + { + std::cout << "improve path\n"; + + vertex_descriptor new_vertex=boost::graph_traits::null_vertex(); + halfedge_descriptor h_curr = path[index]; + halfedge_descriptor h_next = path[index + 1]; + auto flank_left = false; + bool is_target = false; + if (lerps[index] == 0) { + new_vertex = target(h_curr,mesh); + flank_left = false; + is_target = true; + } else if (lerps[index] == 1) { + new_vertex = source(h_curr,mesh); + flank_left = true; + } + if (new_vertex == vertex) break; + vertex = new_vertex; + +// std::cout << "OLD: " << get(vpm, new_vertex) << "\n"; +// for (auto h : path) +// { +// std::cout << "4 " << get(vpm, source(h, mesh)) +// << " " << get(vpm, target(h, mesh)) +// << " " << get(vpm, target(next(h, mesh), mesh)) +// << " " << get(vpm, source(h, mesh)) << "\n"; + +// } + + + // if I hit the source vertex v of h_curr, then h_next has v as source, thus we turn ccw around v in path + // Similarly, if I hit the target vertex v of h_curr, then h_next has v as target, thus we turn cw around v in path + CGAL_assertion(!is_target || opposite(next(h_curr, mesh), mesh)==h_next); + CGAL_assertion(is_target || opposite(prev(h_curr, mesh), mesh)==h_next); + + std::size_t curr_index = index+1; + + //TODO check that curr_index does not go out of bound + std::vector new_hedges; + if (is_target) + { + while (target(path[curr_index], mesh) == new_vertex) ++curr_index; + halfedge_descriptor h_loop=next(h_curr, mesh); + CGAL_assertion(face(path[index-1], mesh)==face(opposite(h_loop, mesh), mesh)); + do { + new_hedges.push_back(h_loop); + h_loop=opposite(next(h_loop,mesh), mesh); + } + while(face(opposite(path[curr_index], mesh), mesh)!=face(h_loop, mesh)); + new_hedges.push_back(h_loop); + } + else + { + while (source(path[curr_index], mesh) == new_vertex) ++curr_index; + halfedge_descriptor h_loop=opposite(next(opposite(h_curr, mesh), mesh), mesh); // skip the face before h_curr (as we won't remove it from path) + CGAL_assertion(face(path[index-1], mesh)==face(opposite(h_loop, mesh), mesh)); + do { + new_hedges.push_back(h_loop); + h_loop=opposite(next(h_loop,mesh), mesh); + } + while(face(opposite(path[curr_index], mesh), mesh)!=face(h_loop, mesh)); + new_hedges.push_back(h_loop); + } + + // replace the halfedges incident to the apex vertex with the opposite part of the ring + std::vector new_path(path.begin(),path.begin()+index); + new_path.insert(new_path.end(), new_hedges.begin(), new_hedges.end()); + new_path.insert(new_path.end(), path.begin()+curr_index, path.end()); + path.swap(new_path); + + +// std::cout << "NEW\n"; +// for (auto h : path) +// { +// std::cout << "4 " << get(vpm, source(h, mesh)) +// << " " << get(vpm, target(h, mesh)) +// << " " << get(vpm, target(next(h, mesh), mesh)) +// << " " << get(vpm, source(h, mesh)) << "\n"; + +// } + + portals=unfold_strip(path,src,tgt,vpm,mesh); + lerps=funnel(portals,index); + + dump_path(path, lerps, src, tgt, mesh); + } + + } }; } // namespace internal @@ -501,6 +619,7 @@ void locally_shortest_path(const Face_location &src, typedef typename boost::graph_traits::edge_descriptor edge_descriptor; + //TODO replace with named parameter using VPM = typename boost::property_map::const_type; using K = typename Kernel_traits::value_type>::type; using Impl = internal::Locally_shortest_path_imp; @@ -563,11 +682,10 @@ void locally_shortest_path(const Face_location &src, } std::reverse(initial_path.begin(), initial_path.end()); -//TODO replace with named parameter std::vector< std::array> portals=Impl::unfold_strip(initial_path,src,tgt,vpm,tmesh); int max_index=0; std::vector lerps=Impl::funnel(portals,max_index); - + Impl::straighten_path(portals,lerps,initial_path,src,tgt,vpm,tmesh,max_index); CGAL_assertion(lerps.size()==initial_path.size()); //TODO: tmp for testing From 11991e62474863eed5111d02c1ae4eea15a3d42d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Tue, 26 Sep 2023 09:42:30 +0200 Subject: [PATCH 007/120] copy edit plugin/item as a base for editable shortest path --- .../Polyhedron/Plugins/Bsurf/CMakeLists.txt | 6 + .../Bsurf/Locally_shortest_path_plugin.cpp | 155 ++ .../Plugins/Bsurf/Scene_edit_path_item.cpp | 1309 +++++++++++++++++ .../Plugins/Bsurf/Scene_edit_path_item.h | 66 + .../Bsurf/Scene_edit_path_item_config.h | 10 + 5 files changed, 1546 insertions(+) create mode 100644 Polyhedron/demo/Polyhedron/Plugins/Bsurf/CMakeLists.txt create mode 100644 Polyhedron/demo/Polyhedron/Plugins/Bsurf/Locally_shortest_path_plugin.cpp create mode 100644 Polyhedron/demo/Polyhedron/Plugins/Bsurf/Scene_edit_path_item.cpp create mode 100644 Polyhedron/demo/Polyhedron/Plugins/Bsurf/Scene_edit_path_item.h create mode 100644 Polyhedron/demo/Polyhedron/Plugins/Bsurf/Scene_edit_path_item_config.h diff --git a/Polyhedron/demo/Polyhedron/Plugins/Bsurf/CMakeLists.txt b/Polyhedron/demo/Polyhedron/Plugins/Bsurf/CMakeLists.txt new file mode 100644 index 000000000000..925904c3d238 --- /dev/null +++ b/Polyhedron/demo/Polyhedron/Plugins/Bsurf/CMakeLists.txt @@ -0,0 +1,6 @@ +include(polyhedron_demo_macros) + +polyhedron_demo_plugin(locally_shortest_path_plugin Locally_shortest_path_plugin) +target_link_libraries(locally_shortest_path_plugin PUBLIC scene_edit_path_item + scene_surface_mesh_item) + diff --git a/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Locally_shortest_path_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Locally_shortest_path_plugin.cpp new file mode 100644 index 000000000000..424762f7ddfd --- /dev/null +++ b/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Locally_shortest_path_plugin.cpp @@ -0,0 +1,155 @@ +#include + +#include +#include +#include "Scene_edit_path_item.h" +#include "Scene_surface_mesh_item.h" +#include +#include +#include +#include +#include +#include + + +#include +#include +using namespace CGAL::Three; +class Locally_shortest_path_plugin : + public QObject, + public Polyhedron_demo_plugin_interface +{ + Q_OBJECT + Q_INTERFACES(CGAL::Three::Polyhedron_demo_plugin_interface) + Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.PluginInterface/1.0") + +public: + void init(QMainWindow* mainWindow, CGAL::Three::Scene_interface* scene_interface, Messages_interface*); + QList actions() const { + return QList() << actionBbox + << actionExport; + } + + bool applicable(QAction* a) const { + if(a==actionBbox &&scene->numberOfEntries() > 0) + return true; + else if(a==actionExport ) + { + for(int i = 0, end = scene->numberOfEntries(); + i < end; ++i) + { + if(qobject_cast(scene->item(i))) + { + return true; + } + } + } + return false;} +public Q_SLOTS: + + void bbox(); + void enableAction(); + void exportToPoly(); + void connectNewViewer(QObject* o) + { + for(int i=0; inumberOfEntries(); ++i) + { + Scene_edit_path_item* item = qobject_cast( + scene->item(i)); + if(item) + o->installEventFilter(item); + } + } + +private: + CGAL::Three::Scene_interface* scene; + QMainWindow* mw; + QAction* actionBbox; + QAction* actionExport; + + +}; // end Locally_shortest_path_plugin + +void Locally_shortest_path_plugin::init(QMainWindow* mainWindow, CGAL::Three::Scene_interface* scene_interface, Messages_interface*) +{ + scene = scene_interface; + mw = mainWindow; + actionBbox = new QAction(tr("Create Locally Shortest Path"), mainWindow); + connect(actionBbox, SIGNAL(triggered()), + this, SLOT(bbox())); + actionExport = new QAction(tr("PIPO"), mainWindow); + connect(actionExport, SIGNAL(triggered()), + this, SLOT(exportToPoly())); + connect(mw, SIGNAL(newViewerCreated(QObject*)), + this, SLOT(connectNewViewer(QObject*))); +} + +void Locally_shortest_path_plugin::bbox() +{ + for(int i = 0, end = scene->numberOfEntries(); + i < end; ++i) + { + if(qobject_cast(scene->item(i))) + return; + } + QApplication::setOverrideCursor(Qt::WaitCursor); + Scene_edit_path_item* item = new Scene_edit_path_item(scene); + connect(item, SIGNAL(destroyed()), + this, SLOT(enableAction())); + item->setName("Edit box"); + item->setRenderingMode(FlatPlusEdges); + Q_FOREACH(CGAL::QGLViewer* viewer, CGAL::QGLViewer::QGLViewerPool()) + viewer->installEventFilter(item); + + scene->addItem(item); + actionBbox->setEnabled(false); + + QApplication::restoreOverrideCursor(); +} + +void Locally_shortest_path_plugin::enableAction() { + actionBbox->setEnabled(true); +} + +void Locally_shortest_path_plugin::exportToPoly() +{ + int id =0; + const CGAL::qglviewer::Vec v_offset = Three::mainViewer()->offset(); + EPICK::Vector_3 offset(v_offset.x, v_offset.y, v_offset.z); + Scene_edit_path_item* item = nullptr; + for(int i = 0, end = scene->numberOfEntries(); + i < end; ++i) + { + item = qobject_cast(scene->item(i)); + if(item) + { + id = i; + break; + } + } + + EPICK::Point_3 points[8]; + for(int i=0; i<8; ++i) + { + points[i] = EPICK::Point_3(item->point(i,0),item->point(i,1), item->point(i,2))-offset; + } + + Scene_surface_mesh_item* poly_item = new Scene_surface_mesh_item(); + CGAL::make_hexahedron(points[0], + points[3], + points[2], + points[1], + points[5], + points[4], + points[7], + points[6], + *poly_item->polyhedron()); + CGAL::Polygon_mesh_processing::triangulate_faces(*poly_item->polyhedron()); + poly_item->setName("Edit box"); + poly_item->setRenderingMode(FlatPlusEdges); + poly_item->invalidateOpenGLBuffers(); + scene->replaceItem(id, poly_item, true); + item->deleteLater(); + actionBbox->setEnabled(true); +} +#include "Locally_shortest_path_plugin.moc" diff --git a/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Scene_edit_path_item.cpp b/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Scene_edit_path_item.cpp new file mode 100644 index 000000000000..2eaa561a90f0 --- /dev/null +++ b/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Scene_edit_path_item.cpp @@ -0,0 +1,1309 @@ +#include "Scene_edit_path_item.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace CGAL::Three; +typedef Viewer_interface Vi; +typedef Triangle_container Tc; +typedef Edge_container Ec; +typedef Scene_edit_path_item_priv Priv; + +struct Scene_edit_path_item::vertex{ + int id; + double *x; + double *y; + double *z; + + CGAL::Point_3 position()const + { + return CGAL::Point_3(*x,*y,*z); + } + double operator[](int i) + { + switch(i) + { + case 0: + return *x; + case 1: + return *y; + case 2: + return *z; + default: + return 0; + } + } +}; +struct Scene_edit_path_item::edge{ + vertex* source; + vertex* target; +}; +struct Scene_edit_path_item::face{ + vertex* vertices[4]; +}; + +struct Scene_edit_path_item_priv{ + typedef CGAL::Simple_cartesian Kernel; + enum Face_containers{ + Faces = 0, + S_Faces, + Spheres, + S_Spheres, + P_Spheres, + P_Faces, + Nbf + }; + + enum Line_containers{ + Edges = 0, + S_Edges, + P_Edges, + Nbe + }; + + enum HL_Primitive{ + VERTEX=0, + EDGE, + FACE, + NO_TYPE + }; + + Scene_edit_path_item_priv(const Scene_interface *scene_interface, Scene_edit_path_item* ebi) + { + const CGAL::qglviewer::Vec offset = static_cast(CGAL::QGLViewer::QGLViewerPool().first())->offset(); + ready_to_hl = true; + scene = scene_interface; + item = ebi; + selection_on = false; + Scene_item::Bbox bb = scene->bbox(); + double x=(bb.xmin()+bb.xmax())/2; + double y=(bb.ymin()+bb.ymax())/2; + double z=(bb.zmin()+bb.zmax())/2; + center_ = CGAL::qglviewer::Vec(x,y,z); + relative_center_ = CGAL::qglviewer::Vec(0,0,0); + remodel_frame = new Scene_item::ManipulatedFrame(); + remodel_frame->setTranslationSensitivity(1.0); + frame = new Scene_item::ManipulatedFrame(); + frame->setPosition(center_+offset); + frame->setSpinningSensitivity(100.0); //forbid spinning + constraint.setRotationConstraintType(CGAL::qglviewer::AxisPlaneConstraint::AXIS); + constraint.setTranslationConstraintType(CGAL::qglviewer::AxisPlaneConstraint::FREE); + constraint.setRotationConstraintDirection(CGAL::qglviewer::Vec(.0,.0,.1)); + frame->setConstraint(&constraint); + //create the sphere model + pool[0] = bb.xmin(); pool[3] = bb.xmax(); + pool[1] = bb.ymin(); pool[4] = bb.ymax(); + pool[2] = bb.zmin(); pool[5] = bb.zmax(); + + vertex_spheres.resize(0); + normal_spheres.resize(0); + create_flat_sphere(1.0f, vertex_spheres, normal_spheres,10); + // 5-----6 + // . | . | + // 4------7 | + // | | | | + // | 1-|---2 + // | . |. + // 0------3 + + //vertices + for( int i = 0; i< 8; ++i) + { + + vertices[i].x = ((i/2)%2==0)? &pool[0]:&pool[3]; + vertices[i].y = (i/4==0)? &pool[1]:&pool[4]; + vertices[i].z = (((i+1)/2)%2==1)? &pool[2]:&pool[5]; + vertices[i].id = i; + } + + // .--5--. + // 4 | 6 | + // .--7-1-. 2 + // | | | | + // 0 .-39--. + // | 8 |10 + // .--11--. + + //edges + for( int i=0; i<12; ++i) + { + if(i<4) + { + edges[i].source = &vertices[i]; + edges[i].target = &vertices[i+4]; + } + else if(i<8) + { + edges[i].source = &vertices[i]; + edges[i].target = &vertices[(i+1)%4 +4]; + } + else + { + edges[i].source = &vertices[i%4]; + edges[i].target = &vertices[(i+1) %4]; + } + vertex_edges.resize(0); + } + + // + // 5------6 + // | | + // .-----. | 2 | + // . |5 . | | | + // .------.2 | 5------1------2------6------5 + // | 1 | | | | | | | | + // | 4.-|-3-. | 1 | 0 | 3 | 5 | + // | . 0 |. | | | | | + // .------. 4------0------3------7------4 + // | | + // | 4 | + // | | + // 4------7 + + + //faces + for( int i=0; i<4; ++i) + { + faces[0].vertices[i] = &vertices[i]; + } + + for( int i=1; i<4; ++i) + { + faces[i].vertices[0] = &vertices[i]; + faces[i].vertices[1] = &vertices[(i-1)]; + faces[i].vertices[2] = &vertices[i+3]; + faces[i].vertices[3] = &vertices[i+4]; + } + + faces[4].vertices[0] = &vertices[0]; + faces[4].vertices[1] = &vertices[3]; + faces[4].vertices[2] = &vertices[7]; + faces[4].vertices[3] = &vertices[4]; + + for( int i=0; i<4; ++i) + { + faces[5].vertices[i] = &vertices[i+4]; + } + + vertex_faces.resize(0); + normal_faces.resize(0); + + for(int i=0; i<8; ++i) + for(int j=0; j<3; ++j) + last_pool[i][j] = vertices[i][j]; + reset_selection(); + last_picked_id = -1; + last_picked_type = -1; + QPixmap pix(":/cgal/cursors/resources/rotate_around_cursor.png"); + rotate_cursor = QCursor(pix); + } + ~Scene_edit_path_item_priv(){ + delete frame; + delete remodel_frame; + } + mutable std::vector vertex_edges; + mutable std::vector color_edges; + mutable std::vector vertex_spheres; + mutable std::vector normal_spheres; + mutable std::vector center_spheres; + mutable std::vector color_spheres; + mutable std::vector vertex_faces; + mutable std::vector normal_faces; + mutable std::vector color_faces; + mutable std::vector hl_vertex; + mutable std::vector hl_normal; + + double pool[6]; + //id|coord + double last_pool[8][3]; + bool ready_to_hl; + + + CGAL::qglviewer::ManipulatedFrame* frame; + CGAL::qglviewer::ManipulatedFrame* remodel_frame; + CGAL::qglviewer::Vec rf_last_pos; + CGAL::qglviewer::LocalConstraint constraint; + CGAL::qglviewer::Vec center_; + CGAL::qglviewer::Vec relative_center_; + + mutable Scene_edit_path_item::vertex vertices[8]; + mutable Scene_edit_path_item::edge edges[12]; + mutable Scene_edit_path_item::face faces[6]; + + std::vector selected_vertices; + void reset_selection(); + bool selection_on; + void picking(int& type, int& id, Viewer_interface *viewer); + + void initializeBuffers(Viewer_interface *viewer)const; + + void computeElements() const; + void draw_picking(Viewer_interface*); + void remodel_box(const QVector3D &dir); + double applyX(int id, double x, double dirx); + double applyY(int id, double y, double diry); + double applyZ(int id, double z, double dirz); + const Scene_interface* scene; + Scene_edit_path_item* item; + QPoint picked_pixel; + HL_Primitive hl_type; + int last_picked_id; + int last_picked_type; + QCursor rotate_cursor; + +}; + +Scene_edit_path_item::Scene_edit_path_item() +{ + d = nullptr; +} +Scene_edit_path_item::Scene_edit_path_item(const Scene_interface *scene_interface) +{ + d = new Scene_edit_path_item_priv(scene_interface, this); + + are_buffers_filled = false; + + Q_FOREACH(CGAL::QGLViewer* v, CGAL::QGLViewer::QGLViewerPool()) + { + v->setMouseTracking(true); + } + connect(Three::mainWindow(), SIGNAL(newViewerCreated(QObject*)), + this, SLOT(connectNewViewer(QObject*))); + + setTriangleContainer(Priv::P_Faces , new Tc(Vi::PROGRAM_NO_SELECTION, false)); + setTriangleContainer(Priv::Faces , new Tc(Vi::PROGRAM_WITH_LIGHT, false)); + setTriangleContainer(Priv::S_Faces , new Tc(Vi::PROGRAM_WITH_LIGHT, false)); + setTriangleContainer(Priv::Spheres , new Tc(Vi::PROGRAM_SPHERES, false)); + setTriangleContainer(Priv::S_Spheres, new Tc(Vi::PROGRAM_SPHERES, false)); + setTriangleContainer(Priv::P_Spheres, new Tc(Vi::PROGRAM_DARK_SPHERES, false)); + + + for(int i=Priv::Nbe-1; i>=0; --i) + { + setEdgeContainer(i, + new Ec(Three::mainViewer()->isOpenGL_4_3() + ? Vi::PROGRAM_SOLID_WIREFRAME + : Vi::PROGRAM_NO_SELECTION, + false)); + } +} +QString Scene_edit_path_item::toolTip() const { + + return QString(); +} + +void Scene_edit_path_item::drawSpheres(Viewer_interface *viewer, const QMatrix4x4 f_matrix ) const +{ + GLdouble d_mat[16]; + QMatrix4x4 mv_mat; + viewer->camera()->getModelViewMatrix(d_mat); + for (int i=0; i<16; ++i) + mv_mat.data()[i] = GLfloat(d_mat[i]); + mv_mat = mv_mat*f_matrix; + double radius =std::sqrt( + (point(6,0) - point(0,0)) * (point(6,0) - point(0,0)) + + (point(6,1) - point(0,1)) * (point(6,1) - point(0,1)) + + (point(6,2) - point(0,2)) * (point(6,2) - point(0,2))) *0.02 ; + + Tc* tc = getTriangleContainer(Priv::Spheres); + tc->setFrameMatrix(f_matrix); + tc->setMvMatrix(mv_mat); + tc->setClipping(false); + tc->getVao(viewer)->bind(); + tc->getVao(viewer)->program->setAttributeValue("radius",radius); + tc->getVao(viewer)->release(); + tc->setColor(QColor(Qt::red)); + tc->draw(viewer, true); +} + +void Scene_edit_path_item::draw(Viewer_interface *viewer) const +{ + if(!isInit(viewer)) + initGL(viewer); + if ( getBuffersFilled() && + ! getBuffersInit(viewer)) + { + initializeBuffers(viewer); + setBuffersInit(viewer, true); + } + if(!getBuffersFilled()) + { + computeElements(); + initializeBuffers(viewer); + } + QMatrix4x4 f_matrix; + for (int i=0; i<16; ++i){ + f_matrix.data()[i] = (float)d->frame->matrix()[i]; + } + + drawSpheres(viewer, f_matrix); + +} + +void Scene_edit_path_item::drawEdges(Viewer_interface* viewer) const +{ + if(!isInit(viewer)) + initGL(viewer); + if ( getBuffersFilled() && + ! getBuffersInit(viewer)) + { + initializeBuffers(viewer); + setBuffersInit(viewer, true); + } + if(!getBuffersFilled()) + { + computeElements(); + initializeBuffers(viewer); + } + QMatrix4x4 f_matrix; + for (int i=0; i<16; ++i){ + f_matrix.data()[i] = (float)d->frame->matrix()[i]; + } + Ec* ec = getEdgeContainer(Priv::Edges); + if(viewer->isOpenGL_4_3()) + { + QVector2D vp(viewer->width(), viewer->height()); + ec->setViewport(vp); + ec->setWidth(6.0f); + } + ec->setClipping(false); + ec->setFrameMatrix(f_matrix); + ec->setColor(QColor(Qt::black)); + ec->draw(viewer, true); + + if(renderingMode() == Wireframe) + { + drawSpheres(viewer, f_matrix); + } + drawHl(viewer); + drawTransparent(viewer); +} + +void Scene_edit_path_item::compute_bbox() const +{ + const CGAL::qglviewer::Vec offset = static_cast(CGAL::QGLViewer::QGLViewerPool().first())->offset(); + + + QVector3D vmin(d->pool[0], d->pool[1], d->pool[2]); + QVector3D vmax(d->pool[3], d->pool[4], d->pool[5]); + + for(int i=0; i< 3; ++i) + { + vmin[i] += d->frame->translation()[i]-d->center_[i]-offset[i]; + vmax[i] += d->frame->translation()[i]-d->center_[i]-offset[i]; + } + + setBbox(Scene_item::Bbox(vmin.x(),vmin.y(),vmin.z(),vmax.x(),vmax.y(),vmax.z())); +} + + + + +void push_xyz(std::vector &v, + const Scene_edit_path_item::Kernel::Point_3& p, + CGAL::qglviewer::Vec center_ = CGAL::qglviewer::Vec(0,0,0)) +{ + v.push_back(p.x()-center_.x); + v.push_back(p.y()-center_.y); + v.push_back(p.z()-center_.z); +} + +void push_normal(std::vector &v, int id) +{ + switch(id) + { + case 0: + v.push_back(0); + v.push_back(-1); + v.push_back(0); + break; + case 1: + v.push_back(-1); + v.push_back(0); + v.push_back(0); + break; + case 2: + v.push_back(0); + v.push_back(0); + v.push_back(-1); + break; + case 3: + v.push_back(1); + v.push_back(0); + v.push_back(0); + break; + case 4: + v.push_back(0); + v.push_back(0); + v.push_back(1); + break; + case 5: + v.push_back(0); + v.push_back(1); + v.push_back(0); + break; + default: + break; + } +} + +void Scene_edit_path_item_priv::computeElements() const +{ + vertex_edges.clear(); + vertex_faces.clear(); + normal_faces.clear(); + center_spheres.clear(); + color_edges.clear(); + color_faces.clear(); + color_spheres.clear(); + + //edges + for( int i=0; i<12; ++i) + { + if(i<4) + { + vertex_edges.push_back(vertices[i].position().x()-center_.x); + vertex_edges.push_back(vertices[i].position().y()-center_.y); + vertex_edges.push_back(vertices[i].position().z()-center_.z); + + center_spheres.push_back(vertices[i].position().x()-center_.x); + center_spheres.push_back(vertices[i].position().y()-center_.y); + center_spheres.push_back(vertices[i].position().z()-center_.z); + + vertex_edges.push_back(vertices[i+4].position().x()-center_.x); + vertex_edges.push_back(vertices[i+4].position().y()-center_.y); + vertex_edges.push_back(vertices[i+4].position().z()-center_.z); + + } + else if(i<8) + { + vertex_edges.push_back(vertices[i].position().x()-center_.x); + vertex_edges.push_back(vertices[i].position().y()-center_.y); + vertex_edges.push_back(vertices[i].position().z()-center_.z); + + center_spheres.push_back(vertices[i].position().x()-center_.x); + center_spheres.push_back(vertices[i].position().y()-center_.y); + center_spheres.push_back(vertices[i].position().z()-center_.z); + + vertex_edges.push_back(vertices[(i+1)%4 +4].position().x()-center_.x); + vertex_edges.push_back(vertices[(i+1)%4 +4].position().y()-center_.y); + vertex_edges.push_back(vertices[(i+1)%4 +4].position().z()-center_.z); + } + else + { + vertex_edges.push_back(vertices[i%4].position().x()-center_.x); + vertex_edges.push_back(vertices[i%4].position().y()-center_.y); + vertex_edges.push_back(vertices[i%4].position().z()-center_.z); + + vertex_edges.push_back(vertices[(i+1) %4].position().x()-center_.x); + vertex_edges.push_back(vertices[(i+1) %4].position().y()-center_.y); + vertex_edges.push_back(vertices[(i+1) %4].position().z()-center_.z); + } + color_edges.push_back(0); + color_edges.push_back((20.0*i+10)/255); + color_edges.push_back(0); + + color_edges.push_back(0); + color_edges.push_back((20.0*i+10)/255); + color_edges.push_back(0); + + } + //faces + for( int i=0; i<6; ++i) + { + push_xyz(vertex_faces, faces[i].vertices[0]->position(), center_); + push_xyz(vertex_faces, faces[i].vertices[3]->position(), center_); + push_xyz(vertex_faces, faces[i].vertices[2]->position(), center_); + + push_xyz(vertex_faces, faces[i].vertices[0]->position(), center_); + push_xyz(vertex_faces, faces[i].vertices[2]->position(), center_); + push_xyz(vertex_faces, faces[i].vertices[1]->position(), center_); + + for( int j=0; j<6; ++j) + { + push_normal(normal_faces, i); + + color_faces.push_back(0); + color_faces.push_back(0); + color_faces.push_back((20.0*i+10)/255); + } + } + + //spheres + for( int i=0; i<8; ++i) + { + color_spheres.push_back((20.0*i+10)/255); + color_spheres.push_back(0); + color_spheres.push_back(0); + } +} + +Scene_edit_path_item::~Scene_edit_path_item() +{ + CGAL::QGLViewer* viewer = *CGAL::QGLViewer::QGLViewerPool().begin(); + viewer->setMouseTracking(false); + + delete d; +} + +// Indicate if rendering mode is supported +bool Scene_edit_path_item::supportsRenderingMode(RenderingMode m) const { + return (m==Wireframe || m==FlatPlusEdges); +} + +Scene_item::ManipulatedFrame* +Scene_edit_path_item::manipulatedFrame() +{ + return d->frame; +} + +double Scene_edit_path_item::point(short i, short j) const +{ + CGAL::qglviewer::Vec pos(d->vertices[i].position().x()-d->center_.x, + d->vertices[i].position().y()-d->center_.y, + d->vertices[i].position().z()-d->center_.z); + return (d->frame->inverseCoordinatesOf(pos))[j]; +} + +void Scene_edit_path_item::highlight(Viewer_interface *viewer) +{ + d->ready_to_hl = true; + viewer->makeCurrent(); + int type = -1, id = -1; + //pick + if(!d->selection_on) + { + d->picking(type, id, viewer); + d->last_picked_id = id; + d->last_picked_type = type; + } + //highlight + d->hl_normal.clear(); + d->hl_vertex.clear(); + if(type !=-1) + { + switch(d->last_picked_type) + { + case 0: + { + //compute + d->hl_vertex.push_back(d->vertices[d->last_picked_id].position().x()-d->center_.x); + d->hl_vertex.push_back(d->vertices[d->last_picked_id].position().y()-d->center_.y); + d->hl_vertex.push_back(d->vertices[d->last_picked_id].position().z()-d->center_.z); + //fill buffers + Tc* tc = getTriangleContainer(Priv::S_Spheres); + tc->reset_vbos(ALL); + tc->allocate( + Tc::Flat_vertices, + d->vertex_spheres.data(), + static_cast(d->vertex_spheres.size()*sizeof(float))); + + tc->allocate( + Tc::Flat_normals, + d->normal_spheres.data(), + static_cast(d->normal_spheres.size()*sizeof(float))); + + tc->allocate( + Tc::Facet_centers, + d->hl_vertex.data(), + static_cast(d->hl_vertex.size()*sizeof(float))); + + tc->setFlatDataSize(d->vertex_spheres.size()); + tc->setCenterSize(d->hl_vertex.size()); + //draw + d->hl_type = Scene_edit_path_item_priv::VERTEX; + Q_FOREACH(CGAL::QGLViewer* v, CGAL::QGLViewer::QGLViewerPool()) + { + CGAL::Three::Viewer_interface* viewer = + static_cast(v); + tc->initializeBuffers(viewer); + } + break; + } + case 1: + { + //compute + d->hl_vertex.push_back(d->edges[d->last_picked_id].source->position().x()-d->center_.x); + d->hl_vertex.push_back(d->edges[d->last_picked_id].source->position().y()-d->center_.y); + d->hl_vertex.push_back(d->edges[d->last_picked_id].source->position().z()-d->center_.z); + + d->hl_vertex.push_back(d->edges[d->last_picked_id].target->position().x()-d->center_.x); + d->hl_vertex.push_back(d->edges[d->last_picked_id].target->position().y()-d->center_.y); + d->hl_vertex.push_back(d->edges[d->last_picked_id].target->position().z()-d->center_.z); + + //fill buffers + Ec* ec = getEdgeContainer(Priv::S_Edges); + ec->reset_vbos(ALL); + ec->allocate( + Ec::Vertices, + d->hl_vertex.data(), + static_cast(d->hl_vertex.size()*sizeof(float))); + ec->setFlatDataSize(d->hl_vertex.size()); + //draw + d->hl_type = Scene_edit_path_item_priv::EDGE; + Q_FOREACH(CGAL::QGLViewer* v, CGAL::QGLViewer::QGLViewerPool()) + { + CGAL::Three::Viewer_interface* viewer = + static_cast(v); + ec->initializeBuffers(viewer); + } + break; + } + case 2: + { + //compute + push_xyz(d->hl_vertex, d->faces[d->last_picked_id].vertices[0]->position(), d->center_); + push_xyz(d->hl_vertex, d->faces[d->last_picked_id].vertices[3]->position(), d->center_); + push_xyz(d->hl_vertex, d->faces[d->last_picked_id].vertices[2]->position(), d->center_); + + push_xyz(d->hl_vertex, d->faces[d->last_picked_id].vertices[0]->position(), d->center_); + push_xyz(d->hl_vertex, d->faces[d->last_picked_id].vertices[2]->position(), d->center_); + push_xyz(d->hl_vertex, d->faces[d->last_picked_id].vertices[1]->position(), d->center_); + + for( int j=0; j<6; ++j) + { + push_normal(d->hl_normal, d->last_picked_id); + } + //fill buffers + Tc* tc = getTriangleContainer(Priv::S_Faces); + tc->reset_vbos(ALL); + tc->allocate( + Tc::Flat_vertices, + d->hl_vertex.data(), + static_cast(d->hl_vertex.size()*sizeof(float))); + + tc->allocate( + Tc::Flat_normals, + d->hl_normal.data(), + static_cast(d->hl_normal.size()*sizeof(float))); + tc->setFlatDataSize(d->hl_vertex.size()); + //draw + d->hl_type = Scene_edit_path_item_priv::FACE; + Q_FOREACH(CGAL::QGLViewer* v, CGAL::QGLViewer::QGLViewerPool()) + { + CGAL::Three::Viewer_interface* viewer = + static_cast(v); + tc->initializeBuffers(viewer); + } + break; + } + default: + d->hl_type = Scene_edit_path_item_priv::NO_TYPE; + break; + } + } + else + clearHL(); + redraw(); + + d->ready_to_hl = false; +} + +void Scene_edit_path_item::clearHL() +{ + Viewer_interface* viewer = dynamic_cast(*CGAL::QGLViewer::QGLViewerPool().begin()); + viewer->makeCurrent(); + d->hl_normal.clear(); + d->hl_vertex.clear(); + + Tc* tc = getTriangleContainer(Priv::S_Spheres); + tc->reset_vbos(ALL); + tc->allocate(Tc::Flat_vertices, d->vertex_spheres.data(), + static_cast(d->vertex_spheres.size()*sizeof(float))); + tc->allocate(Tc::Flat_normals, + d->normal_spheres.data(), + static_cast(d->normal_spheres.size()*sizeof(float))); + + tc->allocate(Tc::Facet_centers, nullptr, 0); + + tc->initializeBuffers(viewer); + tc->setFlatDataSize(0); + tc->setCenterSize(0); + //draw + Ec* ec = getEdgeContainer(Priv::S_Edges); + ec->reset_vbos(ALL); + ec->allocate(Ec::Vertices, nullptr, 0); + ec->initializeBuffers(viewer); + ec->setFlatDataSize(0); + + tc = getTriangleContainer(Priv::S_Faces); + tc->reset_vbos(ALL); + tc->allocate(Tc::Flat_vertices, nullptr, 0); + tc->allocate(Tc::Flat_normals, nullptr, 0); + tc->initializeBuffers(viewer); + tc->setFlatDataSize(0); + d->hl_type = Scene_edit_path_item_priv::NO_TYPE; + + itemChanged(); + +} +void Scene_edit_path_item_priv::reset_selection() +{ + selection_on = false; + CGAL::QGLViewer* viewer = *CGAL::QGLViewer::QGLViewerPool().begin(); + viewer->setManipulatedFrame(frame); + viewer->setMouseBinding(Qt::ShiftModifier, Qt::LeftButton, CGAL::qglviewer::SELECT); + constraint.setTranslationConstraintType(CGAL::qglviewer::AxisPlaneConstraint::FREE); + selected_vertices.clear(); +} + +//intercept events for picking +bool Scene_edit_path_item::eventFilter(QObject *obj, QEvent *event) +{ + if(!visible()) + return false; + Viewer_interface* viewer = qobject_cast(obj); + if(!viewer) + return false; + if(event->type() == QEvent::MouseButtonPress) + { + QMouseEvent* e = static_cast(event); + if(e->modifiers() == Qt::NoModifier) + { + //pick + int type, picked; + d->picked_pixel = e->pos(); + d->picking(type, picked, viewer); + viewer->makeCurrent(); + if(type !=-1) + { + bool found = false; + QApplication::setOverrideCursor(Qt::DragMoveCursor); + CGAL::qglviewer::Vec pos = viewer->camera()->pointUnderPixel(d->picked_pixel, found); + if(found) + { + d->rf_last_pos = pos; + d->remodel_frame->setPosition(pos); + } + for(int i=0; i<8; ++i) + for(int j=0; j<3; ++j) + d->last_pool[i][j] = d->vertices[i][j]; + d->selection_on = true; + if(type == 0) + { + d->selected_vertices.push_back(&d->vertices[picked]); + d->constraint.setTranslationConstraintType(CGAL::qglviewer::AxisPlaneConstraint::FREE); + d->remodel_frame->setConstraint(&d->constraint); + } + else if(type == 1) + { + d->selected_vertices.push_back(d->edges[picked].source); + d->selected_vertices.push_back(d->edges[picked].target); + Kernel::Point_3 s(d->edges[picked].source->position()), t(d->edges[picked].target->position()); + + CGAL::qglviewer::Vec normal(t.x()-s.x(), t.y()-s.y(), t.z()-s.z()); + d->constraint.setTranslationConstraintType(CGAL::qglviewer::AxisPlaneConstraint::PLANE); + d->constraint.setTranslationConstraintDirection(normal); + d->remodel_frame->setConstraint(&d->constraint); + } + else if(type == 2) + { + for(int i=0; i<4; ++i) + d->selected_vertices.push_back(d->faces[picked].vertices[i]); + Kernel::Point_3 a1(d->faces[picked].vertices[1]->position()), a0(d->faces[picked].vertices[0]->position()) + ,a3(d->faces[picked].vertices[3]->position()); + Kernel::Vector_3 a = a1 - a0, + b = a3 - a0; + Kernel::Vector_3 n = CGAL::cross_product(a,b); + + d->remodel_frame->setConstraint(&d->constraint); + d->constraint.setTranslationConstraintType(CGAL::qglviewer::AxisPlaneConstraint::AXIS); + d->constraint.setTranslationConstraintDirection(CGAL::qglviewer::Vec(n.x(), n.y(), n.z())); + + } + + viewer->setManipulatedFrame(d->remodel_frame); + viewer->setMouseBinding( + Qt::NoModifier, + Qt::LeftButton, + CGAL::qglviewer::FRAME, + CGAL::qglviewer::TRANSLATE); + } + else + { + d->reset_selection(); + } + } + return false; + } + else if(event->type() == QEvent::MouseMove) + { + QMouseEvent* e = static_cast(event); + if(e->modifiers() == Qt::NoModifier) + { + if(d->selection_on) + { + d->remodel_frame->setOrientation(d->frame->orientation()); + CGAL::qglviewer::Vec td(d->remodel_frame->transformOf(d->remodel_frame->position() + - d->rf_last_pos)); + QVector3D dir(td.x, td.y, td.z); + d->remodel_box(dir); + } + d->ready_to_hl= true; + d->picked_pixel = e->pos(); + QTimer::singleShot(0, this, + [this, viewer](){ + highlight(viewer); + }); + } + else if(e->modifiers() == Qt::ControlModifier && + e->buttons() == Qt::LeftButton) + { + QApplication::setOverrideCursor(d->rotate_cursor); + } + else if(d->selection_on) + { + d->reset_selection(); + } + d->picked_pixel = e->pos(); + return false; + } + else if(event->type() == QEvent::MouseButtonRelease) + { + d->reset_selection(); + QApplication::setOverrideCursor(QCursor()); + viewer->setMouseBinding( + Qt::NoModifier, + Qt::LeftButton, + CGAL::qglviewer::CAMERA, + CGAL::qglviewer::ROTATE); + } + else if(event->type() == QEvent::KeyRelease) + { + QKeyEvent* e = static_cast(event); + if(e->key() == Qt::Key_Control) + { + QApplication::setOverrideCursor(QCursor()); + } + } + return false; +} + +void Scene_edit_path_item_priv::draw_picking(Viewer_interface* viewer) +{ + + QMatrix4x4 f_matrix; + for (int i=0; i<16; ++i){ + f_matrix.data()[i] = (float)frame->matrix()[i]; + } + GLdouble d_mat[16]; + QMatrix4x4 mv_mat; + viewer->camera()->getModelViewMatrix(d_mat); + for (int i=0; i<16; ++i) + mv_mat.data()[i] = GLfloat(d_mat[i]); + mv_mat = mv_mat*f_matrix; + + + if(item->renderingMode() == FlatPlusEdges) + { + Tc* tc = item->getTriangleContainer(P_Faces); + tc->setFrameMatrix(f_matrix); + tc->setClipping(false); + tc->draw(viewer, false); + } + double radius =std::sqrt( + (item->point(6,0) - item->point(0,0)) * (item->point(6,0) - item->point(0,0)) + + (item->point(6,1) - item->point(0,1)) * (item->point(6,1) - item->point(0,1)) + + (item->point(6,2) - item->point(0,2)) * (item->point(6,2) - item->point(0,2))) *0.02 ; + Tc* tc = item->getTriangleContainer(P_Spheres); + tc->setFrameMatrix(f_matrix); + tc->setClipping(false); + tc->getVao(viewer)->bind(); + tc->getVao(viewer)->program->setAttributeValue("radius", (float)radius); + tc->getVao(viewer)->release(); + tc->draw(viewer, false); + + Ec* ec = item->getEdgeContainer(P_Edges); + if(viewer->isOpenGL_4_3()) + { + + QVector2D vp(viewer->width(), viewer->height()); + ec->setViewport(vp); + ec->setWidth(6.0f); + } + ec->setFrameMatrix(f_matrix); + ec->setClipping(false); + ec->draw(viewer, false); +} + +void Scene_edit_path_item_priv::remodel_box(const QVector3D &dir) +{ + CGAL::qglviewer::AxisPlaneConstraint::Type prev_cons = constraint.translationConstraintType(); + constraint.setTranslationConstraintType(CGAL::qglviewer::AxisPlaneConstraint::FREE); + Q_FOREACH(Scene_edit_path_item::vertex* selected_vertex, selected_vertices ) + { + int id = selected_vertex->id; + CGAL_assume(id<8); + *selected_vertex->x = applyX(id, last_pool[id][0], dir.x()); + *selected_vertex->y = applyY(id, last_pool[id][1], dir.y()); + *selected_vertex->z = applyZ(id, last_pool[id][2], dir.z()); + for( int i=0; i<3; ++i) + relative_center_[i] =(pool[i]+pool[i+3])/2 - center_[i]; + for( int i=0; i<3; ++i) + center_[i] =(pool[i]+pool[i+3])/2; + frame->translate(frame->inverseTransformOf(relative_center_)); + } + item->invalidateOpenGLBuffers(); + constraint.setTranslationConstraintType(prev_cons); +} + +double Scene_edit_path_item_priv::applyX(int id, double x, double dirx) +{ + switch(id) + { + case 0: + case 1: + case 4: + case 5: + if(x+dirx < pool[3]) + return x+dirx; + else + return pool[3]; + case 2: + case 3: + case 6: + case 7: + if(x+dirx > pool[0]) + return x+dirx; + else + return pool[0]; + default: + return 0; + } + return 0; +} + +double Scene_edit_path_item_priv::applyY(int id, double y, double diry) +{ + switch(id) + { + case 0: + case 1: + case 2: + case 3: + if(y+diry < pool[4]) + return y+diry; + else + return pool[4]; + case 4: + case 5: + case 6: + case 7: + if(y+diry > pool[1]) + return y+diry; + else + return pool[1]; + default: + return 0; + } + return 0; +} + +double Scene_edit_path_item_priv::applyZ(int id, double z, double dirz) +{ + switch(id) + { + case 1: + case 2: + case 5: + case 6: + if(z+dirz < pool[5]) + return z+dirz; + else + return pool[5]; + case 0: + case 3: + case 4: + case 7: + if(z+dirz > pool[2]) + return z+dirz; + else + return pool[2]; + default: + return 0; + } + return 0; +} + +//type : 0 = vertex, 1 = edge, 2 = face +void Scene_edit_path_item_priv::picking(int& type, int& id, Viewer_interface *viewer) +{ + viewer->makeCurrent(); + type = -1; + id = -1; + int deviceWidth = viewer->camera()->screenWidth(); + int deviceHeight = viewer->camera()->screenHeight(); + QOpenGLFramebufferObject* fbo = new QOpenGLFramebufferObject(deviceWidth, deviceHeight,QOpenGLFramebufferObject::Depth); + fbo->bind(); + viewer->glEnable(GL_DEPTH_TEST); + viewer->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + QColor bgColor(viewer->backgroundColor()); + //draws the image in the fbo + viewer->setBackgroundColor(::Qt::white); + draw_picking(viewer); + + const auto buffer = read_pixel_as_ubyte_rgba(picked_pixel, viewer, viewer->camera()); + //decode ID and pick (don't forget the case nothing is picked + if(!(buffer[0]==buffer[1] && buffer[1]==buffer[2])) + { + int r(std::ceil((buffer[0]-10)/20)), g(std::ceil((buffer[1]-10)/20)), b(std::ceil((buffer[2]-10)/20)); + id = (std::max)(r,g); + id = (std::max)(id,b); + if(buffer[0] > 0) + { + if(id <8) + type = 0 ; + } + else if(buffer[1] > 0) + { + if(id <12) + { + type = 1; + } + } + else if(buffer[2] > 0) + { + if(id <6) + { + type = 2; + } + } + } + viewer->setBackgroundColor(bgColor); + fbo->release(); + delete fbo; +} + +void Scene_edit_path_item::drawHl(Viewer_interface* viewer)const +{ + GLfloat offset_factor; + GLfloat offset_units; + QMatrix4x4 f_matrix; + for (int i=0; i<16; ++i){ + f_matrix.data()[i] = (float)d->frame->matrix()[i]; + } + GLdouble d_mat[16]; + QMatrix4x4 mv_mat; + viewer->camera()->getModelViewMatrix(d_mat); + for (int i=0; i<16; ++i) + mv_mat.data()[i] = GLfloat(d_mat[i]); + mv_mat = mv_mat*f_matrix; + + if(d->hl_type == Scene_edit_path_item_priv::VERTEX) + { + Tc* tc = getTriangleContainer(Priv::S_Spheres); + + tc->setFrameMatrix(f_matrix); + tc->setMvMatrix(mv_mat); + tc->setColor(QColor(Qt::yellow)); + + double radius =std::sqrt( + (point(6,0) - point(0,0)) * (point(6,0) - point(0,0)) + + (point(6,1) - point(0,1)) * (point(6,1) - point(0,1)) + + (point(6,2) - point(0,2)) * (point(6,2) - point(0,2))) *0.02 ; + tc->setClipping(false); + tc->getVao(viewer)->bind(); + tc->getVao(viewer)->program->setUniformValue("radius", (float)radius); + tc->getVao(viewer)->release(); + tc->draw(viewer, true); + } + else if(d->hl_type == Scene_edit_path_item_priv::EDGE) + { + Ec* ec = getEdgeContainer(Priv::S_Edges); + if(viewer->isOpenGL_4_3()) + { + QVector2D vp(viewer->width(), viewer->height()); + ec->setViewport(vp); + ec->setWidth(6.0f); + } + ec->setClipping(false); + ec->setFrameMatrix(f_matrix); + ec->setColor(QColor(Qt::yellow)); + ec->draw(viewer, true); + + } + else if(d->hl_type == Scene_edit_path_item_priv::FACE) + { + viewer->glGetFloatv(GL_POLYGON_OFFSET_FACTOR, &offset_factor); + viewer->glGetFloatv(GL_POLYGON_OFFSET_UNITS, &offset_units); + viewer->glEnable(GL_BLEND); + viewer->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + Tc* tc = getTriangleContainer(Priv::S_Faces); + tc->setMvMatrix(mv_mat); + tc->setFrameMatrix(f_matrix); + tc->setClipping(false); + + tc->setColor(QColor(Qt::yellow)); + tc->setAlpha(0.5); + tc->draw(viewer, true); + viewer->glPolygonOffset(offset_factor, offset_units); + viewer->glDisable(GL_BLEND); + } +} +void Scene_edit_path_item::drawTransparent(CGAL::Three::Viewer_interface*viewer)const +{ + if(renderingMode() != FlatPlusEdges) + return; + if(!isInit(viewer)) + initGL(viewer); + if ( getBuffersFilled() && + ! getBuffersInit(viewer)) + { + initializeBuffers(viewer); + setBuffersInit(viewer, true); + } + if(!getBuffersFilled()) + { + computeElements(); + initializeBuffers(viewer); + } + QMatrix4x4 f_matrix; + for (int i=0; i<16; ++i){ + f_matrix.data()[i] = (float)d->frame->matrix()[i]; + } + + GLdouble d_mat[16]; + QMatrix4x4 mv_mat; + viewer->camera()->getModelViewMatrix(d_mat); + for (int i=0; i<16; ++i) + mv_mat.data()[i] = GLfloat(d_mat[i]); + mv_mat = mv_mat*f_matrix; + + viewer->glEnable(GL_BLEND); + viewer->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + Tc* tc = getTriangleContainer(Priv::Faces); + tc->setMvMatrix(mv_mat); + tc->setFrameMatrix(f_matrix); + tc->setClipping(false); + tc->setColor(QColor(128,128,128,128)); + tc->setAlpha(0.5); + tc->draw(viewer, true); + viewer->glDisable(GL_BLEND); +} + +void Scene_edit_path_item::invalidateOpenGLBuffers() +{ + compute_bbox(); + setBuffersFilled(false); + getTriangleContainer(Priv::Faces)->reset_vbos(ALL); + getTriangleContainer(Priv::P_Faces)->reset_vbos(ALL); + getTriangleContainer(Priv::Spheres)->reset_vbos(ALL); + getTriangleContainer(Priv::P_Spheres)->reset_vbos(ALL); + getEdgeContainer(Priv::Edges)->reset_vbos(ALL); + getEdgeContainer(Priv::P_Edges)->reset_vbos(ALL); +} + +void Scene_edit_path_item::computeElements() const +{ + d->computeElements(); + getEdgeContainer(Priv::Edges)->allocate( + Ec::Vertices, + d->vertex_edges.data(), + static_cast(d->vertex_edges.size()*sizeof(float))); + Ec* ec = getEdgeContainer(Priv::P_Edges); + ec->allocate( + Ec::Vertices, + d->vertex_edges.data(), + static_cast(d->vertex_edges.size()*sizeof(float))); + + ec->allocate( + Ec::Colors, + d->color_edges.data(), + static_cast(d->color_edges.size()*sizeof(float))); + + + Tc* tc = getTriangleContainer(Priv::Spheres); + tc->allocate( + Tc::Flat_vertices, + d->vertex_spheres.data(), + static_cast(d->vertex_spheres.size()*sizeof(float))); + + tc->allocate( + Tc::Flat_normals, + d->normal_spheres.data(), + static_cast(d->normal_spheres.size()*sizeof(float))); + tc->allocate( + Tc::Facet_centers, + d->center_spheres.data(), + static_cast(d->center_spheres.size()*sizeof(float))); + + tc = getTriangleContainer(Priv::P_Spheres); + tc->allocate( + Tc::Flat_vertices, + d->vertex_spheres.data(), + static_cast(d->vertex_spheres.size()*sizeof(float))); + + tc->allocate( + Tc::Facet_centers, + d->center_spheres.data(), + static_cast(d->center_spheres.size()*sizeof(float))); + + tc->allocate( + Tc::FColors, + d->color_spheres.data(), + static_cast(d->color_spheres.size()*sizeof(float))); + + tc = getTriangleContainer(Priv::Faces); + tc->allocate( + Tc::Flat_vertices, + d->vertex_faces.data(), + static_cast(d->vertex_faces.size()*sizeof(float))); + + tc->allocate( + Tc::Flat_normals, + d->normal_faces.data(), + static_cast(d->normal_faces.size()*sizeof(float))); + tc = getTriangleContainer(Priv::P_Faces); + tc->allocate( + Tc::Flat_vertices, + d->vertex_faces.data(), + static_cast(d->vertex_faces.size()*sizeof(float))); + tc->allocate( + Tc::FColors, + d->color_faces.data(), + static_cast(d->color_faces.size()*sizeof(float))); + setBuffersFilled(true); +} + +void Scene_edit_path_item::initializeBuffers(Viewer_interface *v) const +{ + + getTriangleContainer(Priv::Faces)->initializeBuffers(v); + getTriangleContainer(Priv::P_Faces)->initializeBuffers(v); + + getTriangleContainer(Priv::Spheres)->initializeBuffers(v); + getTriangleContainer(Priv::Spheres)->initializeBuffers(v); + getTriangleContainer(Priv::P_Spheres)->initializeBuffers(v); + getTriangleContainer(Priv::P_Spheres)->initializeBuffers(v); + + getEdgeContainer(Priv::Edges)->initializeBuffers(v); + getEdgeContainer(Priv::P_Edges)->initializeBuffers(v); + + + getTriangleContainer(Priv::Faces)->setFlatDataSize(d->vertex_faces.size()); + getTriangleContainer(Priv::P_Faces)->setFlatDataSize(d->vertex_faces.size()); + + getTriangleContainer(Priv::Spheres)->setFlatDataSize(d->vertex_spheres.size()); + getTriangleContainer(Priv::Spheres)->setCenterSize(d->center_spheres.size()); + getTriangleContainer(Priv::P_Spheres)->setFlatDataSize(d->vertex_spheres.size()); + getTriangleContainer(Priv::P_Spheres)->setCenterSize(d->center_spheres.size()); + + getEdgeContainer(Priv::Edges)->setFlatDataSize(d->vertex_edges.size()); + getEdgeContainer(Priv::P_Edges)->setFlatDataSize(d->vertex_edges.size()); +} + +void Scene_edit_path_item::connectNewViewer(QObject *o) +{ + Vi* viewer = qobject_cast(o); + if(!viewer) + return; + viewer->setMouseTracking(true); +} diff --git a/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Scene_edit_path_item.h b/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Scene_edit_path_item.h new file mode 100644 index 000000000000..f410c4c15e21 --- /dev/null +++ b/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Scene_edit_path_item.h @@ -0,0 +1,66 @@ +#ifndef SCENE_EDIT_PATH_ITEM_H +#define SCENE_EDIT_PATH_ITEM_H + +#include +#include +#include +#include "create_sphere.h" +#include "Scene_edit_path_item_config.h" +struct Scene_edit_path_item_priv; +class SCENE_EDIT_PATH_ITEM_EXPORT Scene_edit_path_item: + public CGAL::Three::Scene_item_rendering_helper, + public CGAL::Three::Scene_transparent_interface +{ + Q_OBJECT + Q_INTERFACES(CGAL::Three::Scene_transparent_interface) + Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.TransparentInterface/1.0") + public: + typedef CGAL::Simple_cartesian Kernel; + struct vertex; + struct edge; + struct face; + Scene_edit_path_item(); + Scene_edit_path_item(const CGAL::Three::Scene_interface* scene_interface); + ~Scene_edit_path_item(); + bool isFinite() const { return true; } + bool isEmpty() const { return false; } + void compute_bbox() const; + + bool manipulatable() const { return true; } + ManipulatedFrame* manipulatedFrame(); + Scene_edit_path_item* clone() const { + return nullptr; + } + + QString toolTip() const; + + bool eventFilter(QObject *, QEvent *); + // Indicate if rendering mode is supported + bool supportsRenderingMode(RenderingMode m) const; + void draw(CGAL::Three::Viewer_interface *) const; + void drawTransparent(CGAL::Three::Viewer_interface*)const; + void drawHl(CGAL::Three::Viewer_interface *) const; + void drawEdges(CGAL::Three::Viewer_interface* viewer) const; + void drawSpheres(CGAL::Three::Viewer_interface* viewer, const QMatrix4x4 f_matrix) const; + void invalidateOpenGLBuffers(); + + // 5-----6 + // . | . | + // 4------7 | + // | | | | + // | 1-|---2 + // | . |. + // 0------3 + + double point(short i, short j) const; + void initializeBuffers(CGAL::Three::Viewer_interface *) const; + void computeElements() const; +public Q_SLOTS: + void highlight(CGAL::Three::Viewer_interface* viewer); + void clearHL(); + void connectNewViewer(QObject* o); +protected: + friend struct Scene_edit_path_item_priv; + Scene_edit_path_item_priv* d; +}; +#endif // SCENE_EDIT_PATH_ITEM_H diff --git a/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Scene_edit_path_item_config.h b/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Scene_edit_path_item_config.h new file mode 100644 index 000000000000..43a77157719d --- /dev/null +++ b/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Scene_edit_path_item_config.h @@ -0,0 +1,10 @@ +#ifndef SCENE_EDIT_PATH_ITEM_CONFIG_H +#define SCENE_EDIT_PATH_ITEM_CONFIG_H + +#ifdef scene_edit_path_item_EXPORTS +# define SCENE_EDIT_PATH_ITEM_EXPORT Q_DECL_EXPORT +#else +# define SCENE_EDIT_PATH_ITEM_EXPORT Q_DECL_IMPORT +#endif + +#endif // SCENE_EDIT_PATH_ITEM_CONFIG_H From 8fa860fe646b88764265217d9a954ebd100b5835 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Tue, 26 Sep 2023 16:08:23 +0200 Subject: [PATCH 008/120] bug fixes, first working implementation still using euclidean weigths in the input --- .../locally_shortest_path_sm_example.cpp | 29 ++++- .../Bsurf/locally_shortest_path.h | 123 ++++++++++-------- 2 files changed, 96 insertions(+), 56 deletions(-) diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/locally_shortest_path_sm_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/locally_shortest_path_sm_example.cpp index 25dd70f5ad05..ebbc2159578c 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/locally_shortest_path_sm_example.cpp +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/locally_shortest_path_sm_example.cpp @@ -28,14 +28,33 @@ int main(int argc, char** argv) std::size_t nb_faces = faces(mesh).size(); // take two random faces and pick the centroid - // CGAL::Random rnd = CGAL::get_default_random(); - CGAL::Random rnd(1695720148); + CGAL::Random rnd = CGAL::get_default_random(); + // CGAL::Random rnd(1695720148); + // CGAL::Random rnd(1695724381); + std::cout << "seed " << rnd.get_seed() << std::endl; Mesh::Face_index f1 = *std::next(faces(mesh).begin(), rnd.get_int(0, nb_faces)); Mesh::Face_index f2 = *std::next(faces(mesh).begin(), rnd.get_int(0, nb_faces)); // or pick specific faces - //Mesh::Face_index f1( 5090 ); - //Mesh::Face_index f2( 617 ); + + // TODO: add in testsuite: special case + // Mesh::Face_index f1( 3268 ); + // Mesh::Face_index f2( 3014 ); + + // TODO: add in testsuite: special case + // Mesh::Face_index f2( 3265 ); + // Mesh::Face_index f1( 3014 ); + + // TODO: add in testsuite: special case + // Mesh::Face_index f2( 3268 ); + // Mesh::Face_index f1( 3014 ); + + // TODO: add in testsuite: special case + // Mesh::Face_index f1( 3265 ); + // Mesh::Face_index f2( 3014 ); + + + Face_location src(f1, CGAL::make_array(0.3,0.3,0.4)); Face_location tgt(f2, CGAL::make_array(0.3,0.3,0.4)); @@ -44,7 +63,7 @@ int main(int argc, char** argv) std::vector edge_locations; CGAL::Polygon_mesh_processing::locally_shortest_path(src, tgt, mesh, edge_locations); - + std::ofstream out("locally_shortest_path.polylines.txt"); out << edge_locations.size()+2; out << " " << PMP::construct_point(src, mesh); diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index 95aa60e61ad4..a8ef88b8cdaf 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -90,7 +90,7 @@ struct Locally_shortest_path_imp using Vector_3 = typename K::Vector_3; using FT = typename K::FT; -// #ifdef CGAL_DEBUG_BSURF +#ifdef CGAL_DEBUG_BSURF static void dump_path(const std::vector& path, const std::vector& lerps, @@ -105,7 +105,7 @@ struct Locally_shortest_path_imp out << " " << construct_point(Edge_location(path[i], make_array(lerps[i], 1.-lerps[i])), mesh); out << " " << construct_point(tgt, mesh) << "\n"; } -// #endif +#endif // TODO: recode using CGAL code? static @@ -375,9 +375,9 @@ static // Among vertices around which the path curves, find the vertex // with maximum angle. We are going to fix that vertex. Actually, max_index is // the index of the first face containing that vertex. - auto max_index = -1; - auto max_angle = 0.0f; - for (auto i = 1; i < path.size() - 1; ++i) { + std::size_t max_index = -1; + FT max_angle = 0.; + for (std::size_t i = 1; i < path.size()-1; ++i) { Vector_2 pos = path[i].pos; Vector_2 prev = path[i - 1].pos; Vector_2 next = path[i + 1].pos; @@ -396,7 +396,7 @@ static static std::vector - funnel(const std::vector< std::array>& portals, int& max_index) + funnel(const std::vector< std::array>& portals, std::size_t& max_index) { // Find straight path. Vector_2 start(NULL_VECTOR); @@ -410,13 +410,12 @@ static // Add start point. std::vector points = std::vector{{apex_index, apex}}; points.reserve(portals.size()); - // @Speed: is this slower than an inlined function? auto area = [](const Vector_2 a, const Vector_2 b, const Vector_2 c) { return determinant(b - a, c - a); }; - for (auto i = 1; i < portals.size(); ++i) { + for (std::size_t i = 0; i < portals.size(); ++i) { auto left = portals[i][0], right = portals[i][1]; // Update right vertex. if (area(apex, right_bound, right) <= 0) { @@ -478,7 +477,7 @@ static std::vector lerps; lerps.reserve(portals.size()); - for (auto i = 0; i < points.size() - 1; i++) { + for (std::size_t i = 0; i < points.size() - 1; i++) { auto a = points[i].pos; auto b = points[i + 1].pos; for (auto k = points[i].face; k < points[i + 1].face; ++k) { @@ -490,7 +489,7 @@ static } auto index = 1; - for (auto i = 1; i < portals.size(); ++i) { + for (std::size_t i = 0; i < portals.size(); ++i) { if ((portals[i][0] == points[index].pos) || (portals[i][1] == points[index].pos)) { points[index].face = i; @@ -504,45 +503,47 @@ static static void straighten_path(std::vector< std::array>& portals, std::vector& lerps, - std::vector& path, + std::vector& path, const Face_location& src, const Face_location& tgt, - const VertexPointMap &vpm, const TriangleMesh &mesh,int index) - { + const VertexPointMap &vpm, const TriangleMesh &mesh, std::size_t index) + { +#ifdef CGAL_DEBUG_BSURF dump_path(path, lerps, src, tgt, mesh); +#endif vertex_descriptor vertex=boost::graph_traits::null_vertex(); // TODO: use a while loop breaking when no apex vertices not already visited are available - for (auto i = 0; i < portals.size() * 2 && index != -1; i++) + for (std::size_t i = 0; i < portals.size() * 2 && index != std::size_t(-1); i++) { - std::cout << "improve path\n"; +#ifdef CGAL_DEBUG_BSURF + std::cout << "Improving path " << path.size() << " hedges\n"; +#endif vertex_descriptor new_vertex=boost::graph_traits::null_vertex(); halfedge_descriptor h_curr = path[index]; halfedge_descriptor h_next = path[index + 1]; - auto flank_left = false; bool is_target = false; if (lerps[index] == 0) { new_vertex = target(h_curr,mesh); - flank_left = false; is_target = true; } else if (lerps[index] == 1) { new_vertex = source(h_curr,mesh); - flank_left = true; } if (new_vertex == vertex) break; vertex = new_vertex; - -// std::cout << "OLD: " << get(vpm, new_vertex) << "\n"; -// for (auto h : path) -// { -// std::cout << "4 " << get(vpm, source(h, mesh)) -// << " " << get(vpm, target(h, mesh)) -// << " " << get(vpm, target(next(h, mesh), mesh)) -// << " " << get(vpm, source(h, mesh)) << "\n"; -// } +#ifdef CGAL_DEBUG_BSURF + std::cout << " Current strip with Apex: " << get(vpm, new_vertex) << "\n"; + for (auto h : path) + { + std::cout << " 4 " << get(vpm, source(h, mesh)) + << " " << get(vpm, target(h, mesh)) + << " " << get(vpm, target(next(h, mesh), mesh)) + << " " << get(vpm, source(h, mesh)) << std::endl; + } +#endif // if I hit the source vertex v of h_curr, then h_next has v as source, thus we turn ccw around v in path // Similarly, if I hit the target vertex v of h_curr, then h_next has v as target, thus we turn cw around v in path @@ -550,31 +551,49 @@ static CGAL_assertion(is_target || opposite(prev(h_curr, mesh), mesh)==h_next); std::size_t curr_index = index+1; - - //TODO check that curr_index does not go out of bound std::vector new_hedges; if (is_target) { - while (target(path[curr_index], mesh) == new_vertex) ++curr_index; - halfedge_descriptor h_loop=next(h_curr, mesh); - CGAL_assertion(face(path[index-1], mesh)==face(opposite(h_loop, mesh), mesh)); + face_descriptor target_face; + if(index+1==path.size()-1) + { + target_face=tgt.first; + curr_index=index+2; + } + else + { + while (target(path[curr_index], mesh) == new_vertex) ++curr_index; + target_face = face(opposite(path[curr_index], mesh), mesh); + } + + halfedge_descriptor h_loop=opposite(prev(opposite(h_curr, mesh), mesh), mesh); do { new_hedges.push_back(h_loop); - h_loop=opposite(next(h_loop,mesh), mesh); + h_loop=opposite(prev(h_loop,mesh), mesh); } - while(face(opposite(path[curr_index], mesh), mesh)!=face(h_loop, mesh)); + while(target_face!=face(h_loop, mesh)); new_hedges.push_back(h_loop); } else { - while (source(path[curr_index], mesh) == new_vertex) ++curr_index; - halfedge_descriptor h_loop=opposite(next(opposite(h_curr, mesh), mesh), mesh); // skip the face before h_curr (as we won't remove it from path) - CGAL_assertion(face(path[index-1], mesh)==face(opposite(h_loop, mesh), mesh)); + face_descriptor target_face; + if (index+1==path.size()-1) + { + target_face=tgt.first; + curr_index=index+2; + } + else + { + while (source(path[curr_index], mesh) == new_vertex) ++curr_index; + target_face=face(opposite(path[curr_index], mesh), mesh); + } + + halfedge_descriptor h_loop=opposite(next(opposite(h_curr, mesh), mesh), mesh); // skip the face before h_curr (as we won't remove it from path) do { new_hedges.push_back(h_loop); h_loop=opposite(next(h_loop,mesh), mesh); } - while(face(opposite(path[curr_index], mesh), mesh)!=face(h_loop, mesh)); + while(target_face!=face(h_loop, mesh)); new_hedges.push_back(h_loop); } @@ -583,24 +602,26 @@ static new_path.insert(new_path.end(), new_hedges.begin(), new_hedges.end()); new_path.insert(new_path.end(), path.begin()+curr_index, path.end()); path.swap(new_path); - - -// std::cout << "NEW\n"; -// for (auto h : path) -// { -// std::cout << "4 " << get(vpm, source(h, mesh)) -// << " " << get(vpm, target(h, mesh)) -// << " " << get(vpm, target(next(h, mesh), mesh)) -// << " " << get(vpm, source(h, mesh)) << "\n"; - -// } portals=unfold_strip(path,src,tgt,vpm,mesh); lerps=funnel(portals,index); - +#ifdef CGAL_DEBUG_BSURF dump_path(path, lerps, src, tgt, mesh); +#endif } +#ifdef CGAL_DEBUG_BSURF + std::cout << " Final strip\n"; + for (auto h : path) + { + std::cout << " 4 " << get(vpm, source(h, mesh)) + << " " << get(vpm, target(h, mesh)) + << " " << get(vpm, target(next(h, mesh), mesh)) + << " " << get(vpm, source(h, mesh)) << "\n"; + + } +#endif + } }; @@ -683,7 +704,7 @@ void locally_shortest_path(const Face_location &src, std::reverse(initial_path.begin(), initial_path.end()); std::vector< std::array> portals=Impl::unfold_strip(initial_path,src,tgt,vpm,tmesh); - int max_index=0; + std::size_t max_index=0; std::vector lerps=Impl::funnel(portals,max_index); Impl::straighten_path(portals,lerps,initial_path,src,tgt,vpm,tmesh,max_index); CGAL_assertion(lerps.size()==initial_path.size()); From 768f6b8ef2104bbf4c588a611647e5fd0ad91515 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Tue, 26 Sep 2023 16:13:04 +0200 Subject: [PATCH 009/120] add missing entry --- Polyhedron/demo/Polyhedron/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Polyhedron/demo/Polyhedron/CMakeLists.txt b/Polyhedron/demo/Polyhedron/CMakeLists.txt index 325a670e8096..7b1725d718ab 100644 --- a/Polyhedron/demo/Polyhedron/CMakeLists.txt +++ b/Polyhedron/demo/Polyhedron/CMakeLists.txt @@ -264,6 +264,8 @@ if(CGAL_Qt5_FOUND AND Qt5_FOUND) add_item(scene_edit_box_item Plugins/PCA/Scene_edit_box_item.cpp) + add_item(scene_edit_path_item Plugins/Bsurf/Scene_edit_path_item.cpp) + add_item(scene_image_item Scene_image_item.cpp) add_item(scene_surface_mesh_item Scene_surface_mesh_item.cpp) From bd3b5138fb56a5dba7bd8d54bd565be15ca1e249 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 27 Sep 2023 08:36:11 +0200 Subject: [PATCH 010/120] add code for recursive de Casteljau (compiles, runs but crashes) --- .../Polygon_mesh_processing/CMakeLists.txt | 1 + .../trace_bezier_segment_sm_example.cpp | 58 +++++ .../Bsurf/locally_shortest_path.h | 209 ++++++++++++++++++ 3 files changed, 268 insertions(+) create mode 100644 Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_bezier_segment_sm_example.cpp diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt b/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt index 24c3dae6c0bb..0dfd0e65547e 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt @@ -52,6 +52,7 @@ create_single_source_cgal_program("cc_compatible_orientations.cpp") create_single_source_cgal_program("hausdorff_distance_remeshing_example.cpp") create_single_source_cgal_program("hausdorff_bounded_error_distance_example.cpp") create_single_source_cgal_program("locally_shortest_path_sm_example.cpp") +create_single_source_cgal_program("trace_bezier_segment_sm_example.cpp") find_package(Eigen3 3.2.0 QUIET) #(requires 3.2.0 or greater) include(CGAL_Eigen3_support) diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_bezier_segment_sm_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_bezier_segment_sm_example.cpp new file mode 100644 index 000000000000..f8a2dd722174 --- /dev/null +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_bezier_segment_sm_example.cpp @@ -0,0 +1,58 @@ +#include +#include +#include + +#include + + +namespace PMP = CGAL::Polygon_mesh_processing; + +typedef CGAL::Exact_predicates_inexact_constructions_kernel K; +typedef CGAL::Surface_mesh Mesh; +typedef PMP::Face_location Face_location; +typedef PMP::Edge_location Edge_location; + + +int main(int argc, char** argv) +{ + std::string filename = (argc > 1) ? std::string(argv[1]) + : CGAL::data_file_path("meshes/elephant.off"); + + Mesh mesh; + if(!CGAL::IO::read_polygon_mesh(filename, mesh) || !CGAL::is_triangle_mesh(mesh)) + { + std::cerr << "Invalid input." << std::endl; + return 1; + } + + std::size_t nb_faces = faces(mesh).size(); + + // take two random faces and pick the centroid + // CGAL::Random rnd = CGAL::get_default_random(); + CGAL::Random rnd(1695795785); + + std::cout << "seed " << rnd.get_seed() << std::endl; + Mesh::Face_index f1 = *std::next(faces(mesh).begin(), rnd.get_int(0, nb_faces)); + Mesh::Face_index f2 = *std::next(faces(mesh).begin(), rnd.get_int(0, nb_faces)); + Mesh::Face_index f3 = *std::next(faces(mesh).begin(), rnd.get_int(0, nb_faces)); + Mesh::Face_index f4 = *std::next(faces(mesh).begin(), rnd.get_int(0, nb_faces)); + + + Face_location a(f1, CGAL::make_array(0.3,0.3,0.4)), + b(f2, CGAL::make_array(0.3,0.3,0.4)), + c(f3, CGAL::make_array(0.3,0.3,0.4)), + d(f4, CGAL::make_array(0.3,0.3,0.4)); + PMP::Bezier_segment control_points=CGAL::make_array(a, b, c, d); + + + std::vector face_locations = + PMP::recursive_de_Casteljau(mesh, control_points, 10); + + + std::ofstream out("bezier.polylines.txt"); + out << face_locations.size(); + for (auto fl : face_locations) + out << " " << PMP::construct_point(fl, mesh); // TODO: we should connect points with geodesics and not segments + + return 0; +} diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index a8ef88b8cdaf..175284a766fe 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -26,11 +26,21 @@ namespace CGAL { namespace Polygon_mesh_processing { +template +void locally_shortest_path(const Face_location &src, + const Face_location &tgt, + const TriangleMesh &tmesh, + EdgeLocationRange &edge_locations); + template using Edge_location = std::pair::edge_descriptor, std::array>; +template +using Bezier_segment = std::array, 4>; + + template #ifdef DOXYGEN_RUNNING @@ -623,6 +633,173 @@ static #endif } + +// TODO: starting from here, we can move that to a de Casteljau Impl struct + + template + static + std::vector + get_positions(const EdgeLocationRange& edge_locations, + const TriangleMesh& mesh, + const Face_location& src, + const Face_location& tgt) + { + std::vector result; + result.reserve(edge_locations.size()+2); + result.push_back(construct_point(src,mesh)); + for(auto& e: edge_locations) + result.push_back(construct_point(e,mesh)); + + result.push_back(construct_point(tgt,mesh)); +//TODO: we must guarantee that result is sorted and unique (rounding issue?) + return result; + } + + template + static + std::vector + path_parameters(const EdgeLocationRange& edge_locations, + const TriangleMesh& mesh, + const Face_location& src, + const Face_location& tgt) + { + std::vector pos=get_positions(edge_locations,mesh,src,tgt); + FT L=0.; + std::vector result(pos.size()); + for(std::size_t i=0;i + static + Face_location + eval_point_on_geodesic(const EdgeLocationRange& edge_locations, + const TriangleMesh& mesh, + const Face_location& src, + const Face_location& tgt, + const std::vector& parameters,/// edge length parameterization of the path from src to tgt through edge_locations + const FT& t) + { + if (t==0) return src; + if (t==1) return tgt; + + if(src.first==tgt.first) + { + std::array bary; + bary[0]=(1-t)*src.second[0]+t*tgt.second[0]; + bary[1]=(1-t)*src.second[1]+t*tgt.second[1]; + bary[2]=(1-t)*src.second[2]+t*tgt.second[2]; + return {src.first,bary}; + } + + std::size_t i = 0; + for (; i < parameters.size() - 1; i++) + { + if (parameters[i + 1] >= t) break; + } + FT t_low = parameters[i]; + FT t_high = parameters[i + 1]; + CGAL_assertion(t_high!=t_low); + FT alpha = (t - t_low) / (t_high - t_low); + std::array bary_low; + std::array bary_high; + + face_descriptor curr_tid = i==0?src.first:face(halfedge(edge_locations[i].first,mesh),mesh); + halfedge_descriptor h_face = halfedge(curr_tid, mesh); + auto edge_barycentric_coordinate = + [&mesh, h_face](halfedge_descriptor h_edge, + const std::array& bary_edge) + { + std::array bary_edge_in_face; + if (h_face!=h_edge) + { + if (h_face==next(h_edge, mesh)) + { + bary_edge_in_face[0]=bary_edge[1]; + bary_edge_in_face[1]=0; + bary_edge_in_face[2]=bary_edge[0]; + } + else + { + bary_edge_in_face[0]=0; + bary_edge_in_face[1]=bary_edge[0]; + bary_edge_in_face[2]=bary_edge[1]; + } + } + else + { + bary_edge_in_face[0]=bary_edge[0]; + bary_edge_in_face[1]=bary_edge[1]; + bary_edge_in_face[2]=0; + } + + return bary_edge_in_face; + }; + + if(i==0) + bary_low=src.second; + else + { + halfedge_descriptor h_low = halfedge(edge_locations[i].first, mesh); + bary_low = edge_barycentric_coordinate(h_low, edge_locations[i].second); + } + + if(i==parameters.size()-2) + bary_high=tgt.second; + else + { + halfedge_descriptor h_high = opposite(halfedge(edge_locations[i+1].first, mesh), mesh); + CGAL_assertion(face(h_high,mesh)==curr_tid); + std::array edge_bary_high=edge_locations[i+1].second; + std::swap(edge_bary_high[0],edge_bary_high[1]); + bary_high = edge_barycentric_coordinate(h_high, edge_bary_high); + } + + std::array bary; + bary[0]=(1-alpha)*bary_low[0]+alpha*bary_high[0]; + bary[1]=(1-alpha)*bary_low[1]+alpha*bary_high[1]; + bary[2]=(1-alpha)*bary_low[2]+alpha*bary_high[2]; + + return {curr_tid,bary}; + } + + static + Face_location + geodesic_lerp(const TriangleMesh &mesh, + const Face_location& src, + const Face_location& tgt,const FT& t) + { + std::vector> edge_locations; + locally_shortest_path(src,tgt,mesh, edge_locations); + std::vector parameters=path_parameters(edge_locations,mesh,src,tgt); + Face_location point = + eval_point_on_geodesic(edge_locations,mesh,src,tgt,parameters,t); + return point; + } + + + static + std::pair,Bezier_segment> + subdivide_bezier_polygon(const TriangleMesh& mesh, + const Bezier_segment& polygon, + const FT& t) + { + Face_location Q0 = geodesic_lerp(mesh, polygon[0], polygon[1], t); + Face_location Q1 = geodesic_lerp(mesh, polygon[1], polygon[2], t); + Face_location Q2 = geodesic_lerp(mesh, polygon[2], polygon[3], t); + Face_location R0 = geodesic_lerp(mesh, Q0, Q1, t); + Face_location R1 = geodesic_lerp(mesh, Q1, Q2, t); + Face_location S = geodesic_lerp(mesh, R0, R1, t); + + return {{polygon[0], Q0, R0, S}, {S, R1, Q2, polygon[3]}}; + } }; } // namespace internal @@ -717,6 +894,38 @@ void locally_shortest_path(const Face_location &src, } } +template +std::vector> +recursive_de_Casteljau(const TriangleMesh &mesh, + const Bezier_segment& control_points, + const int num_subdiv) +{ + //TODO replace with named parameter + using VPM = typename boost::property_map::const_type; + using K = typename Kernel_traits::value_type>::type; + using Impl = internal::Locally_shortest_path_imp; + + std::vector> segments(1,control_points); + std::vector> result; + for (auto subdivision = 0; subdivision < num_subdiv; subdivision++) + { + result.clear(); + result.reserve(segments.size() * 2); + for (std::size_t i = 0; i < segments.size(); ++i) + { + auto [split0, split1] = Impl::subdivide_bezier_polygon(mesh, segments[i], 0.5); + result.push_back(split0); + result.push_back(split1); + } + std::swap(segments, result); + } + + // nasty trick to build the vector from a pair of iterators + // using the fact that data in array and vector are contiguous + return {(Face_location*)segments.data(), + (Face_location*)segments.data() + segments.size() * 4}; +} + } // namespace Polygon_mesh_processing } // namespace CGAL From 176e4c79c8350f8c3902e4a62fc51237d13c7f8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 27 Sep 2023 09:59:23 +0200 Subject: [PATCH 011/120] WIP debug --- .../Bsurf/locally_shortest_path.h | 106 ++++++++++++------ 1 file changed, 74 insertions(+), 32 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index 175284a766fe..093bd0011af3 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -100,7 +100,7 @@ struct Locally_shortest_path_imp using Vector_3 = typename K::Vector_3; using FT = typename K::FT; -#ifdef CGAL_DEBUG_BSURF +// #ifdef CGAL_DEBUG_BSURF static void dump_path(const std::vector& path, const std::vector& lerps, @@ -109,13 +109,14 @@ struct Locally_shortest_path_imp const TriangleMesh& mesh) { static int i = -1; + std::cout << "dump current path in path_"+std::to_string(i)+".polylines.txt\n"; std::ofstream out("path_"+std::to_string(++i)+".polylines.txt"); out << path.size()+2 << " " << construct_point(src, mesh); for(std::size_t i=0; i(path[i], make_array(lerps[i], 1.-lerps[i])), mesh); out << " " << construct_point(tgt, mesh) << "\n"; } -#endif +// #endif // TODO: recode using CGAL code? static @@ -401,6 +402,12 @@ static max_angle = angle; } } + +std::cout << "funnels ("<< max_index << ")"; +for (auto f : path) + std::cout << " " << f.pos << " |"; +std::cout << "\n"; + return max_index; } @@ -425,7 +432,8 @@ static return determinant(b - a, c - a); }; - for (std::size_t i = 0; i < portals.size(); ++i) { + for (std::size_t i = 0; i < portals.size(); ++i) + { auto left = portals[i][0], right = portals[i][1]; // Update right vertex. if (area(apex, right_bound, right) <= 0) { @@ -493,15 +501,29 @@ static for (auto k = points[i].face; k < points[i + 1].face; ++k) { auto portal = portals[k]; // assert(cross(b - a, portal.second - portal.first) > 0); + +std::cout << "i=" << i << "\n"; +std::cout << "a=" << a << " b=" << b << " portal[0]=" << portal[0] << " portal[1]=" << portal[1] << "\n"; + FT s = intersect_segments(a, b, portal[0], portal[1]); + +std::cout << "s=" << s << "\n"; + lerps.push_back(std::clamp(s, 0.0, 1.0)); } } auto index = 1; - for (std::size_t i = 0; i < portals.size(); ++i) { + std::cout << "setting funnel_point indices\n"; + for (std::size_t i = 0; i < portals.size(); ++i) + { + std::cout << " i=" << i << " index = " << index << "\n"; + std::cout << " portals[i][0]=" << portals[i][0] << " portals[i][1]=" << portals[i][1] << "\n"; + std::cout << " points[index].pos = " <& tgt, const VertexPointMap &vpm, const TriangleMesh &mesh, std::size_t index) { -#ifdef CGAL_DEBUG_BSURF +// #ifdef CGAL_DEBUG_BSURF dump_path(path, lerps, src, tgt, mesh); -#endif +// #endif + vertex_descriptor vertex=boost::graph_traits::null_vertex(); // TODO: use a while loop breaking when no apex vertices not already visited are available for (std::size_t i = 0; i < portals.size() * 2 && index != std::size_t(-1); i++) { -#ifdef CGAL_DEBUG_BSURF +// #ifdef CGAL_DEBUG_BSURF std::cout << "Improving path " << path.size() << " hedges\n"; -#endif + std::cout << "src = " << construct_point(src, mesh) << "\n"; + std::cout << "tgt = " << construct_point(tgt, mesh) << "\n"; +// #endif vertex_descriptor new_vertex=boost::graph_traits::null_vertex(); halfedge_descriptor h_curr = path[index]; @@ -543,7 +568,7 @@ static if (new_vertex == vertex) break; vertex = new_vertex; -#ifdef CGAL_DEBUG_BSURF +// #ifdef CGAL_DEBUG_BSURF std::cout << " Current strip with Apex: " << get(vpm, new_vertex) << "\n"; for (auto h : path) { @@ -553,10 +578,14 @@ static << " " << get(vpm, source(h, mesh)) << std::endl; } -#endif +// #endif // if I hit the source vertex v of h_curr, then h_next has v as source, thus we turn ccw around v in path // Similarly, if I hit the target vertex v of h_curr, then h_next has v as target, thus we turn cw around v in path + + if ( !(!is_target || opposite(next(h_curr, mesh), mesh)==h_next) ) + std::cout << edge(h_curr, mesh) << " | " << edge(opposite(next(h_curr, mesh), mesh), mesh) << " vs " << edge(h_next, mesh) << "\n"; + CGAL_assertion(!is_target || opposite(next(h_curr, mesh), mesh)==h_next); CGAL_assertion(is_target || opposite(prev(h_curr, mesh), mesh)==h_next); @@ -565,16 +594,19 @@ static if (is_target) { face_descriptor target_face; - if(index+1==path.size()-1) + + while (target(path[curr_index], mesh) == new_vertex) { - target_face=tgt.first; - curr_index=index+2; + if(curr_index==path.size()-1) + { + target_face=tgt.first; + curr_index=path.size(); + break; + } + ++curr_index; } - else - { - while (target(path[curr_index], mesh) == new_vertex) ++curr_index; + if (curr_index != path.size()) target_face = face(opposite(path[curr_index], mesh), mesh); - } halfedge_descriptor h_loop=opposite(prev(opposite(h_curr, mesh), mesh), mesh); do { @@ -587,16 +619,22 @@ static else { face_descriptor target_face; - if (index+1==path.size()-1) + +std::cout << "index = " << index << "\n"; +std::cout << "path.size() = " << path.size() << "\n"; + + while (source(path[curr_index], mesh) == new_vertex) { - target_face=tgt.first; - curr_index=index+2; + if(curr_index==path.size()-1) + { + target_face=tgt.first; + curr_index=path.size(); + break; + } + ++curr_index; } - else - { - while (source(path[curr_index], mesh) == new_vertex) ++curr_index; + if (curr_index != path.size()) target_face=face(opposite(path[curr_index], mesh), mesh); - } halfedge_descriptor h_loop=opposite(next(opposite(h_curr, mesh), mesh), mesh); // skip the face before h_curr (as we won't remove it from path) do { @@ -615,9 +653,9 @@ static portals=unfold_strip(path,src,tgt,vpm,mesh); lerps=funnel(portals,index); -#ifdef CGAL_DEBUG_BSURF +// #ifdef CGAL_DEBUG_BSURF dump_path(path, lerps, src, tgt, mesh); -#endif +// #endif } #ifdef CGAL_DEBUG_BSURF @@ -711,7 +749,9 @@ static std::array bary_low; std::array bary_high; - face_descriptor curr_tid = i==0?src.first:face(halfedge(edge_locations[i].first,mesh),mesh); + // warning there is an offset of the index: parameters contains one extra element (src) at 0 + // while edge_locations does not + face_descriptor curr_tid = i==0?src.first:face(halfedge(edge_locations[i-1].first,mesh),mesh); halfedge_descriptor h_face = halfedge(curr_tid, mesh); auto edge_barycentric_coordinate = [&mesh, h_face](halfedge_descriptor h_edge, @@ -747,17 +787,17 @@ static bary_low=src.second; else { - halfedge_descriptor h_low = halfedge(edge_locations[i].first, mesh); - bary_low = edge_barycentric_coordinate(h_low, edge_locations[i].second); + halfedge_descriptor h_low = halfedge(edge_locations[i-1].first, mesh); + bary_low = edge_barycentric_coordinate(h_low, edge_locations[i-1].second); } if(i==parameters.size()-2) bary_high=tgt.second; else { - halfedge_descriptor h_high = opposite(halfedge(edge_locations[i+1].first, mesh), mesh); + halfedge_descriptor h_high = opposite(halfedge(edge_locations[i].first, mesh), mesh); CGAL_assertion(face(h_high,mesh)==curr_tid); - std::array edge_bary_high=edge_locations[i+1].second; + std::array edge_bary_high=edge_locations[i].second; std::swap(edge_bary_high[0],edge_bary_high[1]); bary_high = edge_barycentric_coordinate(h_high, edge_bary_high); } @@ -881,6 +921,8 @@ void locally_shortest_path(const Face_location &src, std::reverse(initial_path.begin(), initial_path.end()); std::vector< std::array> portals=Impl::unfold_strip(initial_path,src,tgt,vpm,tmesh); + std::cout << "portals.size() " << portals.size() << "\n"; + std::cout << "initial_path.size() " << initial_path.size() << "\n"; std::size_t max_index=0; std::vector lerps=Impl::funnel(portals,max_index); Impl::straighten_path(portals,lerps,initial_path,src,tgt,vpm,tmesh,max_index); From 0f5db6daae4dd4dfa90a0e5df13d427203e96f17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 27 Sep 2023 12:08:22 +0200 Subject: [PATCH 012/120] fix tracing bezier! --- .../locally_shortest_path_sm_example.cpp | 4 +- .../trace_bezier_segment_sm_example.cpp | 12 ++- .../Bsurf/locally_shortest_path.h | 79 +++++++++++-------- 3 files changed, 60 insertions(+), 35 deletions(-) diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/locally_shortest_path_sm_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/locally_shortest_path_sm_example.cpp index ebbc2159578c..8340c9cbc1e7 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/locally_shortest_path_sm_example.cpp +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/locally_shortest_path_sm_example.cpp @@ -53,7 +53,9 @@ int main(int argc, char** argv) // Mesh::Face_index f1( 3265 ); // Mesh::Face_index f2( 3014 ); - + // TODO: add in testsuite: special case + // Mesh::Face_index f1( 3543 ); + // Mesh::Face_index f2( 4356 ); Face_location src(f1, CGAL::make_array(0.3,0.3,0.4)); Face_location tgt(f2, CGAL::make_array(0.3,0.3,0.4)); diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_bezier_segment_sm_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_bezier_segment_sm_example.cpp index f8a2dd722174..1a64cb285a12 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_bezier_segment_sm_example.cpp +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_bezier_segment_sm_example.cpp @@ -28,8 +28,8 @@ int main(int argc, char** argv) std::size_t nb_faces = faces(mesh).size(); // take two random faces and pick the centroid - // CGAL::Random rnd = CGAL::get_default_random(); - CGAL::Random rnd(1695795785); + CGAL::Random rnd = CGAL::get_default_random(); + // CGAL::Random rnd(1695795785); std::cout << "seed " << rnd.get_seed() << std::endl; Mesh::Face_index f1 = *std::next(faces(mesh).begin(), rnd.get_int(0, nb_faces)); @@ -44,15 +44,21 @@ int main(int argc, char** argv) d(f4, CGAL::make_array(0.3,0.3,0.4)); PMP::Bezier_segment control_points=CGAL::make_array(a, b, c, d); + std::cout << "Using the following control points:\n"; + std::cout << PMP::construct_point(a, mesh) << "\n"; + std::cout << PMP::construct_point(b, mesh) << "\n"; + std::cout << PMP::construct_point(c, mesh) << "\n"; + std::cout << PMP::construct_point(d, mesh) << "\n"; std::vector face_locations = - PMP::recursive_de_Casteljau(mesh, control_points, 10); + PMP::recursive_de_Casteljau(mesh, control_points, 8); std::ofstream out("bezier.polylines.txt"); out << face_locations.size(); for (auto fl : face_locations) out << " " << PMP::construct_point(fl, mesh); // TODO: we should connect points with geodesics and not segments + out << "\n"; return 0; } diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index 093bd0011af3..ea913985f721 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -100,7 +100,7 @@ struct Locally_shortest_path_imp using Vector_3 = typename K::Vector_3; using FT = typename K::FT; -// #ifdef CGAL_DEBUG_BSURF +#ifdef CGAL_DEBUG_BSURF static void dump_path(const std::vector& path, const std::vector& lerps, @@ -116,7 +116,7 @@ struct Locally_shortest_path_imp out << " " << construct_point(Edge_location(path[i], make_array(lerps[i], 1.-lerps[i])), mesh); out << " " << construct_point(tgt, mesh) << "\n"; } -// #endif +#endif // TODO: recode using CGAL code? static @@ -296,7 +296,7 @@ struct Locally_shortest_path_imp // return res; // } -static + static std::array unfold_face(halfedge_descriptor h_curr, halfedge_descriptor h_next, const VertexPointMap &vpm, const TriangleMesh &mesh, @@ -354,10 +354,16 @@ static std::size_t s=initial_path.size(); std::vector> result(s+1); result[0]=init_source_triangle(initial_path[0], vpm, mesh, src); +#ifdef CGAL_DEBUG_BSURF + std::cout << "unfolding faces\n"; +#endif for(std::size_t i=1;i 0); -std::cout << "i=" << i << "\n"; -std::cout << "a=" << a << " b=" << b << " portal[0]=" << portal[0] << " portal[1]=" << portal[1] << "\n"; - +#ifdef CGAL_DEBUG_BSURF + std::cout << "i=" << i << "\n"; + std::cout << "a=" << a << " b=" << b << " portal[0]=" << portal[0] << " portal[1]=" << portal[1] << "\n"; +#endif FT s = intersect_segments(a, b, portal[0], portal[1]); -std::cout << "s=" << s << "\n"; - +#ifdef CGAL_DEBUG_BSURF + std::cout << "s=" << s << "\n"; +#endif lerps.push_back(std::clamp(s, 0.0, 1.0)); } } auto index = 1; +#ifdef CGAL_DEBUG_BSURF std::cout << "setting funnel_point indices\n"; - for (std::size_t i = 0; i < portals.size(); ++i) +#endif + for (std::size_t i = 0; i < portals.size(); ++i) { +#ifdef CGAL_DEBUG_BSURF std::cout << " i=" << i << " index = " << index << "\n"; std::cout << " portals[i][0]=" << portals[i][0] << " portals[i][1]=" << portals[i][1] << "\n"; std::cout << " points[index].pos = " <& tgt, const VertexPointMap &vpm, const TriangleMesh &mesh, std::size_t index) { -// #ifdef CGAL_DEBUG_BSURF +#ifdef CGAL_DEBUG_BSURF dump_path(path, lerps, src, tgt, mesh); -// #endif +#endif vertex_descriptor vertex=boost::graph_traits::null_vertex(); // TODO: use a while loop breaking when no apex vertices not already visited are available for (std::size_t i = 0; i < portals.size() * 2 && index != std::size_t(-1); i++) { -// #ifdef CGAL_DEBUG_BSURF +#ifdef CGAL_DEBUG_BSURF std::cout << "Improving path " << path.size() << " hedges\n"; std::cout << "src = " << construct_point(src, mesh) << "\n"; std::cout << "tgt = " << construct_point(tgt, mesh) << "\n"; -// #endif +#endif vertex_descriptor new_vertex=boost::graph_traits::null_vertex(); halfedge_descriptor h_curr = path[index]; @@ -568,7 +588,7 @@ std::cout << "s=" << s << "\n"; if (new_vertex == vertex) break; vertex = new_vertex; -// #ifdef CGAL_DEBUG_BSURF +#ifdef CGAL_DEBUG_BSURF std::cout << " Current strip with Apex: " << get(vpm, new_vertex) << "\n"; for (auto h : path) { @@ -578,7 +598,7 @@ std::cout << "s=" << s << "\n"; << " " << get(vpm, source(h, mesh)) << std::endl; } -// #endif +#endif // if I hit the source vertex v of h_curr, then h_next has v as source, thus we turn ccw around v in path // Similarly, if I hit the target vertex v of h_curr, then h_next has v as target, thus we turn cw around v in path @@ -620,9 +640,6 @@ std::cout << "s=" << s << "\n"; { face_descriptor target_face; -std::cout << "index = " << index << "\n"; -std::cout << "path.size() = " << path.size() << "\n"; - while (source(path[curr_index], mesh) == new_vertex) { if(curr_index==path.size()-1) @@ -653,9 +670,9 @@ std::cout << "path.size() = " << path.size() << "\n"; portals=unfold_strip(path,src,tgt,vpm,mesh); lerps=funnel(portals,index); -// #ifdef CGAL_DEBUG_BSURF +#ifdef CGAL_DEBUG_BSURF dump_path(path, lerps, src, tgt, mesh); -// #endif +#endif } #ifdef CGAL_DEBUG_BSURF @@ -850,6 +867,8 @@ void locally_shortest_path(const Face_location &src, const TriangleMesh &tmesh, EdgeLocationRange &edge_locations) { + if (src.first == tgt.first) return; + typedef typename boost::graph_traits::face_descriptor face_descriptor; typedef typename boost::graph_traits::halfedge_descriptor @@ -879,6 +898,7 @@ void locally_shortest_path(const Face_location &src, Dual dual(tmesh); // TODO: fill the weight map using something better than euclidean distance + // TODO: the edge map could be precomputed if we know that several queries will be done for (edge_descriptor ed : edges(tmesh)) { halfedge_descriptor h=halfedge(ed, tmesh), hopp=opposite(h, tmesh); @@ -921,14 +941,11 @@ void locally_shortest_path(const Face_location &src, std::reverse(initial_path.begin(), initial_path.end()); std::vector< std::array> portals=Impl::unfold_strip(initial_path,src,tgt,vpm,tmesh); - std::cout << "portals.size() " << portals.size() << "\n"; - std::cout << "initial_path.size() " << initial_path.size() << "\n"; std::size_t max_index=0; std::vector lerps=Impl::funnel(portals,max_index); Impl::straighten_path(portals,lerps,initial_path,src,tgt,vpm,tmesh,max_index); CGAL_assertion(lerps.size()==initial_path.size()); - //TODO: tmp for testing edge_locations.reserve(initial_path.size()); for(std::size_t i=0; i Date: Wed, 27 Sep 2023 13:15:32 +0200 Subject: [PATCH 013/120] intrinsic dual_weights in graph for Dijkstra --- .../Bsurf/locally_shortest_path.h | 116 +++++++++++------- 1 file changed, 70 insertions(+), 46 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index ea913985f721..cfdc5900223a 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -139,11 +139,9 @@ struct Locally_shortest_path_imp } static std::array - init_flat_triangles(face_descriptor f, - const VertexPointMap &vpm, const TriangleMesh &mesh, - const Face_location &src) + init_flat_triangle( const halfedge_descriptor& h, + const VertexPointMap &vpm, const TriangleMesh &mesh) { - halfedge_descriptor h = halfedge(f, mesh); std::array triangle_vertices = make_array( source(h, mesh), target(h, mesh), target(next(h, mesh), mesh)); @@ -160,7 +158,7 @@ struct Locally_shortest_path_imp return tr2d; } - + static std::array init_source_triangle(halfedge_descriptor hopp, const VertexPointMap &vpm, @@ -260,42 +258,35 @@ struct Locally_shortest_path_imp point_coords); } - // static - // std::array - // unfold_face(halfedge_descriptor h, - // const VertexPointMap &vpm, const TriangleMesh &mesh, - // const std::array& flat_tid) - // { - // halfedge_descriptor h_opp = opposite(h_curr, mesh); - - - - // vertex_descriptor v = target(next(h_curr,mesh),mesh); - // vertex_descriptor a = target(h_curr,mesh); - // vertex_descriptor b = source(h_curr, mesh); - // FT r0 = squared_distance(get(vpm,v), get(vpm,a)); - // FT r1 = squared_distance(get(vpm,v), get(vpm,b)); - - // Vector_2 v2 = intersect_circles(flat_tid[0], r0, flat_tid[1], r1); - - - // std::array res; - // if(next(h_curr, mesh) == h_next_opp) - // { - // res[0]=flat_tid[0]; - // //res[2]=flat_tid[1]; - // res[1]=v2; - // } - // else - // { - // CGAL_assertion(prev(h_curr, mesh) == h_next_opp); - // res[0]=v2; - // res[1]=flat_tid[1]; - // //res[2]=flat_tid[0]; - // } - - // return res; - // } + static + std::array + unfold_face(halfedge_descriptor h, + const VertexPointMap &vpm, const TriangleMesh &mesh, + const std::array& flat_tid) + { + halfedge_descriptor h_opp = opposite(h, mesh); + + + + vertex_descriptor v = target(next(h_opp,mesh),mesh); + vertex_descriptor a = target(h_opp,mesh);//consistent with init_flat_triangle + vertex_descriptor b = source(h_opp, mesh); + FT r0 = squared_distance(get(vpm,v), get(vpm,a)); + FT r1 = squared_distance(get(vpm,v), get(vpm,b)); + + Vector_2 v2 = intersect_circles(flat_tid[0], r0, flat_tid[1], r1); + + + std::array res; + + res[0]=flat_tid[0]; + res[1]=flat_tid[1]; + res[2]=v2; + + + return res; + } + static std::array unfold_face(halfedge_descriptor h_curr, halfedge_descriptor h_next, @@ -688,6 +679,27 @@ struct Locally_shortest_path_imp #endif } + //:::::::::::::::::::::Straightest Geodesic:::::::::::::::::::::::::::: + std::vector get_3D_basis_at_point(const Face_location& p, + const TriangleMesh& mesh, + const VertexPointMap &vpm) + { + halfedge_descriptor h=halfedge(p.first,mesh); + + Point_3 p0=get(vpm, source(h, mesh)); + Point_3 p1=get(vpm, target(h, mesh)); + Point_3 p2= get(vpm,target(next(h, mesh), mesh)); + + Vector_3 e=p1-p0; + e/=sqrt(e.squared_length()); + Vector_3 n= triangle_normal(p0,p1,p2); + Vector_3 e1= cross_product(n,e); + + return {e,e1,n}; + + + } + // TODO: starting from here, we can move that to a de Casteljau Impl struct @@ -893,20 +905,32 @@ void locally_shortest_path(const Face_location &src, TriangleMesh, CGAL::dynamic_edge_property_t>::const_type weight_map = get(CGAL::dynamic_edge_property_t(), tmesh); + auto compute_dual_weights=[&tmesh,&vpm](const halfedge_descriptor& h) + { + auto flat_tid= Impl::init_flat_triangle(h,vpm,tmesh); + auto flat_nei= Impl::unfold_face(h,vpm,tmesh,flat_tid); + Vector_2 c0=0.33*(flat_tid[0]+flat_tid[1]+flat_tid[2]); + Vector_2 c1=0.33*(flat_nei[0]+flat_nei[1]+flat_nei[2]); + return sqrt((c1 - c0).squared_length()); + }; //TODO: handle boundary edges Dual dual(tmesh); // TODO: fill the weight map using something better than euclidean distance // TODO: the edge map could be precomputed if we know that several queries will be done + // TODO: construct the dual graph once at the beginning and then use Dijkstra to + // to navigate it at every query for (edge_descriptor ed : edges(tmesh)) { halfedge_descriptor h=halfedge(ed, tmesh), hopp=opposite(h, tmesh); - put(weight_map, ed, - sqrt(squared_distance( - centroid(get(vpm, source(h, tmesh)), get(vpm, target(h, tmesh)), get(vpm, target(next(h, tmesh), tmesh))), - centroid(get(vpm, source(hopp, tmesh)), get(vpm, target(hopp, tmesh)), get(vpm, target(next(hopp, tmesh), tmesh))) - ))); + + put(weight_map, ed, compute_dual_weights(h)); + // put(weight_map, ed, + // sqrt(squared_distance( + // centroid(get(vpm, source(h, tmesh)), get(vpm, target(h, tmesh)), get(vpm, target(next(h, tmesh), tmesh))), + // centroid(get(vpm, source(hopp, tmesh)), get(vpm, target(hopp, tmesh)), get(vpm, target(next(hopp, tmesh), tmesh))) + // ))); } // TODO try stopping dijkstra as soon tgt is out of the queue. From 197627bed78c066c6233d6281ac63d6baee58c3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 27 Sep 2023 14:26:36 +0200 Subject: [PATCH 014/120] fix intersection point computation --> fixes weights --- .../locally_shortest_path_sm_example.cpp | 1 + .../Bsurf/locally_shortest_path.h | 25 +++++++++++-------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/locally_shortest_path_sm_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/locally_shortest_path_sm_example.cpp index 8340c9cbc1e7..7b8c397f076e 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/locally_shortest_path_sm_example.cpp +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/locally_shortest_path_sm_example.cpp @@ -31,6 +31,7 @@ int main(int argc, char** argv) CGAL::Random rnd = CGAL::get_default_random(); // CGAL::Random rnd(1695720148); // CGAL::Random rnd(1695724381); + // CGAL::Random rnd(1695813638); std::cout << "seed " << rnd.get_seed() << std::endl; Mesh::Face_index f1 = *std::next(faces(mesh).begin(), rnd.get_int(0, nb_faces)); diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index cfdc5900223a..f8b7284cbe5f 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -158,7 +158,7 @@ struct Locally_shortest_path_imp return tr2d; } - + static std::array init_source_triangle(halfedge_descriptor hopp, const VertexPointMap &vpm, @@ -274,7 +274,7 @@ struct Locally_shortest_path_imp FT r0 = squared_distance(get(vpm,v), get(vpm,a)); FT r1 = squared_distance(get(vpm,v), get(vpm,b)); - Vector_2 v2 = intersect_circles(flat_tid[0], r0, flat_tid[1], r1); + Vector_2 v2 = intersect_circles(flat_tid[1], r1, flat_tid[0], r0); std::array res; @@ -282,7 +282,7 @@ struct Locally_shortest_path_imp res[0]=flat_tid[0]; res[1]=flat_tid[1]; res[2]=v2; - + return res; } @@ -699,7 +699,7 @@ struct Locally_shortest_path_imp } - + // TODO: starting from here, we can move that to a de Casteljau Impl struct @@ -909,6 +909,7 @@ void locally_shortest_path(const Face_location &src, { auto flat_tid= Impl::init_flat_triangle(h,vpm,tmesh); auto flat_nei= Impl::unfold_face(h,vpm,tmesh,flat_tid); + Vector_2 c0=0.33*(flat_tid[0]+flat_tid[1]+flat_tid[2]); Vector_2 c1=0.33*(flat_nei[0]+flat_nei[1]+flat_nei[2]); @@ -919,18 +920,20 @@ void locally_shortest_path(const Face_location &src, // TODO: fill the weight map using something better than euclidean distance // TODO: the edge map could be precomputed if we know that several queries will be done - // TODO: construct the dual graph once at the beginning and then use Dijkstra to + // TODO: construct the dual graph once at the beginning and then use Dijkstra to // to navigate it at every query for (edge_descriptor ed : edges(tmesh)) { halfedge_descriptor h=halfedge(ed, tmesh), hopp=opposite(h, tmesh); - - put(weight_map, ed, compute_dual_weights(h)); + + // distance between centroids of unfolded triangles + put(weight_map, ed, compute_dual_weights(h)); + // Euclidean distance between centroids // put(weight_map, ed, - // sqrt(squared_distance( - // centroid(get(vpm, source(h, tmesh)), get(vpm, target(h, tmesh)), get(vpm, target(next(h, tmesh), tmesh))), - // centroid(get(vpm, source(hopp, tmesh)), get(vpm, target(hopp, tmesh)), get(vpm, target(next(hopp, tmesh), tmesh))) - // ))); + // sqrt(squared_distance( + // centroid(get(vpm, source(h, tmesh)), get(vpm, target(h, tmesh)), get(vpm, target(next(h, tmesh), tmesh))), + // centroid(get(vpm, source(hopp, tmesh)), get(vpm, target(hopp, tmesh)), get(vpm, target(next(hopp, tmesh), tmesh))) + // ))); } // TODO try stopping dijkstra as soon tgt is out of the queue. From e7b3e9a51be12037e9a8f269c0e468c68129213d Mon Sep 17 00:00:00 2001 From: ClaudioMancinelli Date: Thu, 28 Sep 2023 12:32:09 +0200 Subject: [PATCH 015/120] primal and dual graph for geodesic queries --- .../Bsurf/locally_shortest_path.h | 1033 ++++++++++++++++- 1 file changed, 1027 insertions(+), 6 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index f8b7284cbe5f..5b048f381f00 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -679,10 +679,16 @@ struct Locally_shortest_path_imp #endif } + + + + #if 0 //:::::::::::::::::::::Straightest Geodesic:::::::::::::::::::::::::::: - std::vector get_3D_basis_at_point(const Face_location& p, - const TriangleMesh& mesh, - const VertexPointMap &vpm) + static + std::vector + get_3D_basis_at_point(const Face_location& p, + const TriangleMesh& mesh, + const VertexPointMap &vpm) { halfedge_descriptor h=halfedge(p.first,mesh); @@ -696,12 +702,407 @@ struct Locally_shortest_path_imp Vector_3 e1= cross_product(n,e); return {e,e1,n}; + } + + + static + std::tuple + point_is_edge(const Face_location& p, + const FT& tol=1e-5) + { + auto bary=p.second; + if (bary[0] > tol && bary[1] > tol && bary[2] <= tol) + return {true, 0}; + if (bary[1] > tol && bary[2] > tol && bary[0] <= tol) + return {true, 1}; + if (bary[2] > tol && bary[0] > tol && bary[1] <= tol) + return {true, 2}; + + return {false, -1}; + } + + static + //https://rootllama.wordpress.com/2014/06/20/ray-line-segment-intersection-test-in-2d/ + std::pair + intersect(const Vector_2 &direction, const Vector_2 &left, + const Vector_2 &right,const Vector_2& origin) + { + auto v1 = origin-left; + auto v2 = right - left; + auto v3 = vec2f{-direction.y, direction.x}; + auto t0 = cross(v2, v1) / dot(v2, v3); + auto t1 = -dot(left, v3) / dot(v2, v3); + return std::make_pair(t0, t1); + }; + + static + std::tuple + segment_in_tri(const Vector_2& p, + const std::array& tri, + const Vector_2& dir, + const int& k) + { + for (auto i = 0; i < 3; ++i) + { + auto [t0, t1] = intersect(dir, tri[(k+i)%3], tri[(k + 1+i) % 3],p); + + //TODO: replace intersection with CGAL code + if (t0 > 0 && t1 >= -1e-4 && t1 <= 1 + 1e-4) + { + auto result_k =(k+i)%3; + return {result_k, clamp(t1, 0.f, 1.f)}; + } + } + CGAL_assertion(false); + return {-1,-1}; + } + + FT get_total_angle(const vertex_descriptor& vid, + const TriangleMesh& mesh, + const VertexPointMap &vpm) + { + halfedge_descriptor h_ref= halfedge(vid,mesh); + halfedge_descriptor h_start=h_ref + halfedge_descriptor h_next = next(h_start,mesh); + + FT theta=angle(get(vpm,source(h_start,mesh))-get(vpm,target(h_start,mesh)), + get(vpm,target(h_next,mesh))-get(vpm,target(h_start,mesh))); + h_start=opposite(h_next,mesh); + h_next=next(h_start,mesh); + while(h_start!=h_ref) + { + theta+=angle(get(vpm,source(h_start,mesh))-get(vpm,target(h_start,mesh)), + get(vpm,target(h_next,mesh))-get(vpm,target(h_start,mesh))); + h_start=opposite(h_next,mesh); + h_next=next(h_start,mesh); + } + + return theta; + } + + static + std::tuple, halfedge_descriptor> + polthier_condition_at_vert(const TriangleMesh& mesh, + const VertexPointMap &vpm + const vertex_descriptor& vid, + const face_descriptor& tid, + const int kv, + const Vector_3 &dir) + { + FT total_angle=get_total_angle(vid,mesh,vpm); + FT theta = 0.5 * total_angle; + halfedge_descriptor h=halfedge(tid,mesh); + while(target(h,mesh)!=vid) + h=next(h,mesh); + + Point_3 vert = get(vpm,vid); + Point_3 vert_adj=get(vpm,source(h,mesh)); + Vector_3 v = vert - vert_adj; + + FT acc = angle(v, dir); + FT prev_angle = acc; + face_descriptor curr_tid = tid; + + while (acc < theta) { + h=prev(opposite(h.mesh),mesh); + prev_angle = acc; + Point_3 next_vert_adj=get(vpm,source(h,mesh)); + acc += angle(vert_adj - vert,next_vert_adj - vert); + vert_adj=next_vert_adj; + } + auto offset = theta - prev; + Point_3 prev_vert_adj=get(vpm,target(next(h,mesh,mesh))); + + FT l = sqrt(squared_distance(prev_vert_adj,vert)); + FT phi = angle(vert - prev_vert_adj, vert_adj - prev_vert_adj); + FT x = l * std::sin(offset) / std::sin(M_PI - phi - offset); + FT alpha = x / sqrt(squared_distance(vert_adj - prev_vert_adj)); + halfedge_descriptor prev_h=prev(h,mesh); + std::array flat_tid = init_flat_triangle(prev_h,vpm,mesh); + + Vector_2 q = (1 - alpha) * flat_tid[0] + alpha * flat_tid[1]; + + Vector_2 new_dir = q - flat_tid[2]; + Face_location new_p; + new_p.first=face(h,mesh); + halfedge_descriptor h_face=halfedge(new_p.first,mesh); + Vector_3 bary; + Vector_2 bary_edge=Vector_2{1-alpha,alpha}; + if (h_face!=prev_h) + { + if (h_face==next(prev_h, mesh)) + { + new_p.second[0]=bary_edge[1]; + new_p.second[1]=0; + new_p.second[2]=bary_edge[0]; + } + else + { + new_p.second[0]=0; + new_p.second[1]=bary_edge[0]; + new_p.second[2]=bary_edge[1]; + } + } + else + { + new_p.second[0]=bary_edge[0]; + new_p.second[1]=bary_edge[1]; + new_p.second[2]=0; + } + + return {new_dir, point_from_vert(triangles, vid, face(prev_h,mesh)), prev_h}; } + static + std::vector> + straightest_goedesic(const Face_location& p, + const TriangleMesh& mesh, + const VertexPointMap &vpm, + const Vector_2& dir,const FT& len) + { + auto get_vid=[&mesh](const int k,const face_descriptor& tid) + { + halfedge_descriptor h=halfedge(tid,mesh); + if(k==0) + return source(h,mesh); + if(k==1) + return target(h,mesh); + if(k==2) + return target(next(h,mesh),mesh); + + }; + + std::vector> result; + FT accumulated=0.; + face_descriptor curr_tid=p.first; + std::array curr_flat_tid=init_flat_triangle(halfedge(curr_tid,mesh),vpm,mesh); + Vector_2 flat_p= p.second[0]*curr_flat_tid[0]+p.second[1]*curr_flat_tid[1]+p.second[2]*curr_flat_tid[2]; + Face_location curr_p=p; + Face_location prev_p; + Vector_2 curr_dir=dir; + + result.push_back(p); + + int k_start=-1; + + auto [is_vert, kv] = point_is_vert(p); + auto [is_edge, ke] = point_is_edge(p); + if (is_vert) + k_start = kv; + else if (is_edge) + k_start = ke; + + + while (accumulated < len) + { + auto [k, t1] = segment_in_tri(flat_p, curr_flat_tid, curr_dir,k_start); + + Vector_3 new_bary=Vector_3{0,0,0}; + Face_location point_on_edge; + //TODO: we assume k is always different from -1, check this! + + new_bary[k] = 1 - t1; + new_bary[(k + 1) % 3] = t1; + point_on_edge.first=curr_tid; + point_on_edge.second=new_bary; + + + std::tie(is_vert, kv) = point_is_vert(point_on_edge); + + if (is_vert) + { + auto vid = get_vertex(kv,curr_tid); + + accumulated += + sqrt(squared_distance(construct_point(curr_p,mesh) - get(vpm,vid))); + prev_p = curr_p; + std::tie(curr_dir, curr_p, k_start) = + polthier_condition_at_vert(triangles, positions, adjacencies, + total_angles, vid, curr_tid, dir3d); + curr_tid = curr_p.face; + if (curr_tid == -1) + return result; + + } + else + { + auto adj = adjacencies[curr_tid][k]; + if (adj == -1) + return result; + auto h = find(adjacencies[adj], curr_tid); + + new_bary = zero3f; + new_bary[h] = t1; + new_bary[(h + 1) % 3] = 1 - t1; + + prev_p = curr_p; + curr_p = mesh_point{adj, vec2f{new_bary.y, new_bary.z}}; + accumulated += length(eval_position(triangles, positions, curr_p) - + eval_position(triangles, positions, prev_p)); -// TODO: starting from here, we can move that to a de Casteljau Impl struct + auto T = switch_reference_frame(triangles, positions, adj, curr_tid); + curr_dir = switch_reference_frame_vector(T, curr_dir); + + curr_tid = adj; + k_start = h; + } + + result.push_back(curr_p); + } + + auto excess = accumulated - len; + auto prev_pos = eval_position(triangles, positions, result.rbegin()[1]); + auto last_pos = eval_position(triangles, positions, result.back()); + auto alpha = excess / length(last_pos - prev_pos); + auto pos = alpha * prev_pos + (1 - alpha) * last_pos; + + auto [inside, bary] = + point_in_triangle(triangles, positions, prev_p.face, pos); + if (!inside) + std::cout << "error!This point should be in the triangle" << std::endl; + + result.pop_back(); + result.push_back(mesh_point{prev_p.face, bary}); + + return result; + } + + static + std::vector + polthier_straightest_geodesic(const vector &triangles, const vector &positions, + const vector &adjacencies, const vector> &v2t, + const vector> &angles, const vector &total_angles, + const mesh_point &p, const vec2f &dir, const float &len) + { + auto result = vector{}; + auto accumulated = 0.f; + auto curr_tid = p.face; + auto curr_p = p; + auto prev_p = mesh_point{}; + auto curr_dir = dir; + + result.push_back(p); + + auto k_start = 0; + auto [is_vert, kv] = point_is_vert(p); + auto [is_edge, ke] = point_is_edge(p); + if (is_vert) + k_start = kv; + else if (is_edge) + k_start = ke; + + auto count = 0; + while (accumulated < len) + { + ++count; + auto [k, t1] = straightest_path_in_tri(triangles, positions, curr_p, + curr_dir, k_start); + + auto new_bary = zero3f; + auto point_on_edge = mesh_point{}; + if (k != -1) { + new_bary[k] = 1 - t1; + new_bary[(k + 1) % 3] = t1; + point_on_edge = mesh_point{curr_tid, vec2f{new_bary.y, new_bary.z}}; + } else { + std::tie(is_edge, ke) = point_is_edge(curr_p, 5e-3); + std::tie(is_vert, kv) = point_is_vert(curr_p, 5e-3); + auto bary = get_bary(curr_p.uv); + if (is_edge) { + k = ke; + t1 = bary[(k + 1) % 3]; + point_on_edge = curr_p; + } else if (is_vert) { + auto bary3 = zero3f; + bary3[kv] = 1; + point_on_edge = {curr_p.face, {bary3.y, bary3.z}}; + } else { + std::cout << "Error!This should not happen" << std::endl; + return result; + } + } + + std::tie(is_vert, kv) = point_is_vert(point_on_edge); + + if (is_vert) { + auto vid = triangles[curr_tid][kv]; + if (angles[vid].size() == 0) + return result; + + accumulated += + length(eval_position(triangles, positions, curr_p) - positions[vid]); + auto dir3d = normalize(eval_position(triangles, positions, curr_p) - + positions[vid]); + prev_p = curr_p; + std::tie(curr_dir, curr_p, k_start) = + polthier_condition_at_vert(triangles, positions, adjacencies, + total_angles, vid, curr_tid, dir3d); + curr_tid = curr_p.face; + if (curr_tid == -1) + return result; + + } else { + auto adj = adjacencies[curr_tid][k]; + if (adj == -1) + return result; + auto h = find(adjacencies[adj], curr_tid); + + new_bary = zero3f; + new_bary[h] = t1; + new_bary[(h + 1) % 3] = 1 - t1; + + prev_p = curr_p; + curr_p = mesh_point{adj, vec2f{new_bary.y, new_bary.z}}; + accumulated += length(eval_position(triangles, positions, curr_p) - + eval_position(triangles, positions, prev_p)); + + auto T = switch_reference_frame(triangles, positions, adj, curr_tid); + curr_dir = switch_reference_frame_vector(T, curr_dir); + + curr_tid = adj; + k_start = h; + } + + result.push_back(curr_p); + } + + auto excess = accumulated - len; + auto prev_pos = eval_position(triangles, positions, result.rbegin()[1]); + auto last_pos = eval_position(triangles, positions, result.back()); + auto alpha = excess / length(last_pos - prev_pos); + auto pos = alpha * prev_pos + (1 - alpha) * last_pos; + + auto [inside, bary] = + point_in_triangle(triangles, positions, prev_p.face, pos); + if (!inside) + std::cout << "error!This point should be in the triangle" << std::endl; + + result.pop_back(); + result.push_back(mesh_point{prev_p.face, bary}); + + return result; + } +#endif + +}; + +template +struct Bezier_tracing_impl +{ + using face_descriptor = + typename boost::graph_traits::face_descriptor; + using vertex_descriptor = + typename boost::graph_traits::vertex_descriptor; + using halfedge_descriptor = + typename boost::graph_traits::halfedge_descriptor; + + using Point_2 = typename K::Point_2; + using Point_3 = typename K::Point_3; + using Vector_2 = typename K::Vector_2; + using Vector_3 = typename K::Vector_3; + using FT = typename K::FT; template static @@ -869,6 +1270,579 @@ struct Locally_shortest_path_imp return {{polygon[0], Q0, R0, S}, {S, R1, Q2, polygon[3]}}; } + +}; + + +template +struct Geodesic_circle_impl +{ + using face_descriptor = + typename boost::graph_traits::face_descriptor; + using vertex_descriptor = + typename boost::graph_traits::vertex_descriptor; + using halfedge_descriptor = + typename boost::graph_traits::halfedge_descriptor; + + using Point_2 = typename K::Point_2; + using Point_3 = typename K::Point_3; + using Vector_2 = typename K::Vector_2; + using Vector_3 = typename K::Vector_3; + using FT = typename K::FT; + + struct geodesic_solver { + struct graph_edge { + int node = -1; + double length=DBL_MAX; + }; + std::vector> graph; + }; + struct dual_geodesic_solver { + struct edge { + int node = -1; + double length = DBL_MAX; + }; + std::vector> graph = {}; + }; + + static + std::tuple + point_is_vert(const Face_location& p,const FT& tol=1e-5) + { + auto bary=p.second; + if (bary[0] > tol && bary[1] <= tol && bary[2] <= tol) + return {true, 0}; + if (bary[1] > tol && bary[0] <= tol && bary[2] <= tol) + return {true, 1}; + if (bary[2] > tol && bary[0] <= tol && bary[1] <= tol) + return {true, 2}; + + return {false, -1}; + } + + static + void connect_nodes(geodesic_solver &solver, + const vertex_descriptor& a, + const vertex_descriptor& b, + const VertexPointMap &vpm, + const VertexIndexMap& vidmap, const FT& length) + { + solver.graph[get(vidmap,a)].push_back({get(vidmap,b), length}); + solver.graph[get(vidmap,b)].push_back({get(vidmap,a), length}); + } + + static + double opposite_nodes_arc_length(const VertexPointMap &vpm, + const TriangleMesh &mesh, + const vertex_descriptor& a, + const vertex_descriptor& b, + const vertex_descriptor& v0, + const vertex_descriptor& v1) + { + // Triangles (a, b, v0) and (a, b, v1) are connected by (a, b) edge + // Nodes a and c must be connected. + + //NOOO it is the opposite + + Vector_3 ba = get(vpm,a) - get(vpm,b); + Vector_3 bv0 = get(vpm,v0) - get(vpm,b); + Vector_3 bv1 = get(vpm,v1) - get(vpm,b); + + double cos_alpha = ba/sqrt(ba.squared_lenght())*bv1/sqrt(bv1.squared_lenght()); + double cos_beta = bv0/sqrt(bv0.squared_lenght())*bv1/sqrt(bv1.squared_lenght()); + double sin_alpha = sqrt(std::max(0.0, 1 - cos_alpha * cos_alpha)); + double sin_beta = sqrt(std::max(0.0, 1 - cos_beta * cos_beta)); + + // cos(alpha + beta) + double cos_alpha_beta = cos_alpha * cos_beta - sin_alpha * sin_beta; + if (cos_alpha_beta <= -1) return DBL_MAX; + + // law of cosines (generalized Pythagorean theorem) + double len = dot(ba, ba) + dot(bv0, bv0) - + length(ba) * length(bv0) * 2 * cos_alpha_beta; + + if (len <= 0) + return DBL_MAX; + else + return sqrt(len); + } + + static + void connect_opposite_nodes(geodesic_solver& solver, + const VertexPointMap &vpm, + const TriangleMesh &mesh, + const VertexIndexMap& vidmap, + const vertex_descriptor& a, + const vertex_descriptor& b, + const halfedge_descriptor& h) + { + vertex_descriptor v0 =target(next(h,mesh),mesh); + vertex_descriptor v1= target(next(opposite(h,mesh),mesh),mesh); + auto length = opposite_nodes_arc_length(vpm, mesh, vidmap, a,b,v0,v1); + connect_nodes(solver, v0, v1, length); + } + + static + geodesic_solver + make_geodesic_solver(const VertexPointMap &vpm, + const VertexIndexMap& vidmap, + const TriangleMesh &mesh) + { + geodesic_solver solver; + solver.graph.resize(vertices(mesh).size()); + for (auto& f : faces(mesh)) { + halfedge_descriptor h=halfedge(f,mesh); + for(auto i=0;i<3;++i) + { + vertex_descriptor a=source(h,mesh); + vertex_descriptor b=target(h,mesh); + if(a; + std::array flat_tid= Impl::init_flat_triangle(h,vpm,mesh); + std::array flat_nei= Impl::unfold_face(h,vpm,mesh,flat_tid); + + Vector_2 c0=0.33*(flat_tid[0]+flat_tid[1]+flat_tid[2]); + Vector_2 c1=0.33*(flat_nei[0]+flat_nei[1]+flat_nei[2]); + + return sqrt((c1 - c0).squared_length()); + }; + + dual_geodesic_solver solver; + solver.graph.resize(faces(mesh).size()); + for (auto f : faces(mesh)) { + halfedge_descriptor h=halfedge(f,mesh); + int entry=get(tidmap,f); + for (auto i = 0; i < 3; ++i) { + solver.graph[entry][i].node = get(tidmap,face(opposite(h,mesh),mesh)); + solver.graph[entry][i].length = compute_dual_weights(h); + } + h=next(h,mesh); + } + return solver; + } + + // `update` is a function that is executed during expansion, every time a node + // is put into queue. `exit` is a function that tells whether to expand the + // current node or perform early exit. + template + static + void visit_geodesic_graph(std::vector &field, const geodesic_solver &solver, + const std::vector &sources, Update &&update, + Stop &&stop, Exit &&exit) + { + /* + This algortithm uses the heuristic Small Label Fisrt and Large Label Last + https://en.wikipedia.org/wiki/Shortest_Path_Faster_Algorithm + + Large Label Last (LLL): When extracting nodes from the queue, pick the + front one. If it weights more than the average weight of the queue, put + on the back and check the next node. Continue this way. + Sometimes average_weight is less than every value due to floating point + errors (doesn't happen with double precision). + + Small Label First (SLF): When adding a new node to queue, instead of + always pushing it to the end of the queue, if it weights less than the + front node of the queue, it is put on front. Otherwise the node is put at + the end of the queue. + */ + + auto in_queue = std::vector(solver.graph.size(), false); + + // Cumulative weights of elements in queue. Used to keep track of the + // average weight of the queue. + auto cumulative_weight = 0.0; + + // setup queue + auto queue = std::deque(); + for (auto source : sources) { + in_queue[source] = true; + cumulative_weight += field[source]; + queue.push_back(source); + } + + while (!queue.empty()) { + auto node = queue.front(); + auto average_weight = (cumulative_weight / queue.size()); + + // Large Label Last (see comment at the beginning) + for (auto tries = 0; tries < queue.size() + 1; tries++) { + if (field[node] <= average_weight) + break; + queue.pop_front(); + queue.push_back(node); + node = queue.front(); + } + + // Remove node from queue. + queue.pop_front(); + in_queue[node] = false; + cumulative_weight -= field[node]; + + // Check early exit condition. + if (exit(node)) + break; + if (stop(node)) + continue; + + for (auto i = 0; i < (int)solver.graph[node].size(); i++) { + // Distance of neighbor through this node + auto new_distance = field[node] + solver.graph[node][i].length; + auto neighbor = solver.graph[node][i].node; + + auto old_distance = field[neighbor]; + if (new_distance >= old_distance) + continue; + + if (in_queue[neighbor]) { + // If neighbor already in queue, don't add it. + // Just update cumulative weight. + cumulative_weight += new_distance - old_distance; + } else { + // If neighbor not in queue, add node to queue using Small Label + // First (see comment at the beginning). + if (queue.empty() || (new_distance < field[queue.front()])) + queue.push_front(neighbor); + else + queue.push_back(neighbor); + + // Update queue information. + in_queue[neighbor] = true; + cumulative_weight += new_distance; + } + + // Update distance of neighbor. + field[neighbor] = new_distance; + update(node, neighbor, new_distance); + } + } + } + + template + static + void visit_dual_geodesic_graph(std::vector &field, + const dual_geodesic_solver &solver, + const std::vector &sources, + Update &&update, + Stop &&stop, + Exit &&exit) + { + /* + This algortithm uses the heuristic Small Label Fisrt and Large Label Last + https://en.wikipedia.org/wiki/Shortest_Path_Faster_Algorithm + + Large Label Last (LLL): When extracting nodes from the queue, pick the + front one. If it weights more than the average weight of the queue, put + on the back and check the next node. Continue this way. + Sometimes average_weight is less than every value due to floating point + errors (doesn't happen with double precision). + + Small Label First (SLF): When adding a new node to queue, instead of + always pushing it to the end of the queue, if it weights less than the + front node of the queue, it is put on front. Otherwise the node is put at + the end of the queue. + */ + + auto in_queue = std::vector(solver.graph.size(), false); + + // Cumulative weights of elements in queue. Used to keep track of the + // average weight of the queue. + auto cumulative_weight = 0.0; + + // setup queue + auto queue = std::deque(); + for (auto source : sources) { + in_queue[source] = true; + cumulative_weight += field[source]; + queue.push_back(source); + } + + while (!queue.empty()) { + auto node = queue.front(); + auto average_weight = (cumulative_weight / queue.size()); + + // Large Label Last (see comment at the beginning) + for (auto tries = 0; tries < queue.size() + 1; tries++) { + if (field[node] <= average_weight) + break; + queue.pop_front(); + queue.push_back(node); + node = queue.front(); + } + + // Remove node from queue. + queue.pop_front(); + in_queue[node] = false; + cumulative_weight -= field[node]; + + // Check early exit condition. + if (exit(node)) + break; + if (stop(node)) + continue; + + for (auto i = 0; i < (int)solver.graph[node].size(); i++) { + // Distance of neighbor through this node + auto new_distance = field[node] + solver.graph[node][i].length; + auto neighbor = solver.graph[node][i].node; + + auto old_distance = field[neighbor]; + if (new_distance >= old_distance) + continue; + + if (in_queue[neighbor]) { + // If neighbor already in queue, don't add it. + // Just update cumulative weight. + cumulative_weight += new_distance - old_distance; + } else { + // If neighbor not in queue, add node to queue using Small Label + // First (see comment at the beginning). + if (queue.empty() || (new_distance < field[queue.front()])) + queue.push_front(neighbor); + else + queue.push_back(neighbor); + + // Update queue information. + in_queue[neighbor] = true; + cumulative_weight += new_distance; + } + + // Update distance of neighbor. + field[neighbor] = new_distance; + update(node, neighbor, new_distance); + } + } + } + + static + std::vector + compute_geodesic_distances(const geodesic_solver &solver, + const VertexIndexMap& vidmap, + const std::vector> &sources_and_dist) + { + auto update = [](int node, int neighbor, double new_distance) {}; + auto stop = [](int node) { return false; }; + auto exit = [](int node) { return false; }; + + auto distances = std::vector{}; + distances.assign(solver.graph.size(), DBL_MAX); + std::vectorsources_id((sources_and_dist.size())); + for (auto i = 0; i < sources_and_dist.size(); ++i) { + sources_id[i] = get(vidmap,sources_and_dist[i].first); + distances[sources_id[i]] = sources_and_dist[i].second; + } + visit_geodesic_graph(distances, solver, sources_id, update, stop, exit); + + return distances; + } + + static + std::vector solve_with_targets(const geodesic_solver& solver, + const VertexIndexMap& vidmap, + const std::vector> &sources_and_dist, + const std::vector> &targets_and_dist) + { + auto update = [](int node, int neighbor, float new_distance) {}; + auto stop = [](int node) { return false; }; + double max_distance = DBL_MAX; + std::vector exit_verts(targets_and_dist.size()); + for (auto i = 0; i < targets_and_dist.size(); ++i) { + exit_verts[i]=get(vidmap,targets_and_dist[i].first); + } + auto exit = [&exit_verts](int node) { + auto it = find(exit_verts.begin(), exit_verts.end(), node); + if (it != exit_verts.end()) + exit_verts.erase(it); + + if (exit_verts.empty()) + return true; + + return false; + }; + + auto distances = vector(solver.graph.size(), DBL_MAX); + std::vectorsources_id((sources_and_dist.size())); + for (auto i = 0; i < sources_and_dist.size(); ++i) { + sources_id[i] = get(vidmap,sources_and_dist[i].first); + distances[sources_id[i]] = sources_and_dist[i].second; + } + + visit_graph(distances, solver, sources_id, update, stop, exit); + return distances; + } + + //compute the length between two opposite vertices by flattening + //TODO: handle concave configurations + static + double length_by_flattening(const VertexPointMap &vpm, + const TriangleMesh &mesh, + const halfedge_descriptor& h) + { + std::array flat_tid=init_flat_triangle(h,vpm,mesh); + std::array flat_nei=unfold_face(h,vpm,mesh,flat_tid); + return sqrt(squared_distance(flat_tid[2],flat_nei[2])); + } + + static + Point_3 eval_position(const VertexPointMap &vpm, + const TriangleMesh &mesh, + const Face_location& p) + { + halfedge_descriptor h=halfedge(p.first,mesh); + return p.second[0]*source(h,mesh)+p.second[1]*target(h,mesh)+p.second[2]*target(next(h,mesh),mesh); + } + + // compute the distance between a point p and some vertices around him + // TODO: consider to take more vertices (increase accuracy) + // TODO: handle concave configurations + static + std::vector> + nodes_around_point(const VertexPointMap &vpm, + const TriangleMesh &mesh, + const Face_location& p) + { + auto get_vid=[&mesh](const int k,const face_descriptor& tid) + { + halfedge_descriptor h=halfedge(tid,mesh); + if(k==0) + return source(h,mesh); + if(k==1) + return target(h,mesh); + if(k==2) + return target(next(h,mesh),mesh); + + }; + std::vector> nodes; + nodes.reserve(6); + auto [is_vert,offset]=point_is_vert(p); + if (is_vert) { + vertex_descriptor vid = get_vid(offset,p.first); + nodes.push_back({vid, 0}); + } else { + face_descriptor tid = p.first; + Point_3 pos = eval_position(vpm, mesh, p); + halfedge_descriptor h=halfedge(tid,mesh); + for (auto i = 0; i < 3; ++i) { + vertex_descriptor p0 = source(h,mesh); + //connect to current vertex + double d = sqrt(squared_distances(get(vpm,p0),pos)); + nodes.push_back(std::make_pair(p0, d)); + + //connecting to opposite vertex w.r.t to current halfedge + vertex_descriptor opp = target(next(opposite(h,mesh),mesh),mesh); + double l = + length_by_flattening(vpm,mesh,h); + nodes.push_back(std::make_pair(opp, l)); + h=next(h,mesh); + } + } + + return nodes; + } + + //compute geodesic distance field from p + //TODO: can be easiliy extended to more than one source + static + std::vector + compute_geodesic_distances(const geodesic_solver &solver, + const VertexPointMap &vpm, + const TriangleMesh &mesh, + const Face_location& p) + { + std::vector> source_nodes=nodes_around_point(vpm,mesh,p); + + return compute_geodesic_distances(solver, source_nodes); + } + + //compute the geodesic distance field from src, and stop the propagation + //once tgt is reached + static + std::vector + compute_pruned_geodesic_distances(const geodesic_solver &solver, + const VertexPointMap &vpm, + const VertexIndexMap& vidmap, + const TriangleMesh &mesh, + const Face_location& src, + const Face_location& tgt) + { + std::vector> + source_nodes = nodes_around_point(vpm,mesh,src); + + std::vector> + target_nodes = nodes_around_point(vpm,mesh,tgt); + + return solve_with_targets(solver, source_nodes, target_nodes); + } + + static + std::vector + strip_on_dual_graph(const dual_geodesic_solver &solver, + const TriangleMesh &mesh, + const int src, + const int tgt) + { + if (src == tgt) + return {}; + + auto common_halfedge = [&mesh](face_descriptor f1, face_descriptor f2) { + halfedge_descriptor h = halfedge(f1, mesh); + for (int i = 0; i < 3; ++i) { + if (face(opposite(h, mesh), mesh) == f2) + return h; + h = next(h, mesh); + } + CGAL_assertion(!"faces do no share a common edge"); + return halfedge_descriptor(); + }; + // initialize once for all and sparsely cleanup at the end of every solve + std::vector parents(solver.graph.size(), -1); + std::vector field(solver.graph.size(), DBL_MAX); + std::vector id_to_face_map(faces(mesh).begin(), faces(mesh).end()); + + field[src]=0.0; + std::vector sources = std::vector{src}; + auto update = [&parents](int node, int neighbor, double new_distance) { + parents[neighbor] = node; + }; + auto stop = [](int node) { return false; }; + auto exit = [tgt](int node) { return node==tgt; }; + + visit_dual_geodesic_graph(field,solver, std::vector{src}, update, stop, exit); + + // extract_strip + std::vector strip; + int node = tgt; + CGAL_assertion(parents[tgt] != -1); + //update the result using id_to_face_map + strip.reserve((int)std::sqrt(parents.size())); + while (node != -1) { + strip.push_back(common_halfedge(id_to_face_map[node],id_to_face_map[parents[node]])); + node = parents[node]; + } + std::reverse(strip.begin(),strip.end()); + return strip; + } }; } // namespace internal @@ -894,6 +1868,7 @@ void locally_shortest_path(const Face_location &src, using Impl = internal::Locally_shortest_path_imp; VPM vpm = get(CGAL::vertex_point, tmesh); +#if CGAL_BSURF_USE_DIJKSTRA_SP typename boost::property_map< TriangleMesh, CGAL::dynamic_face_property_t>::const_type predecessor_map = @@ -966,6 +1941,18 @@ void locally_shortest_path(const Face_location &src, current_face = prev; } std::reverse(initial_path.begin(), initial_path.end()); +#else + //TODO: VIM is not needed here + typedef typename GetInitializedVertexIndexMap::const_type VIM; + typedef typename GetInitializedFaceIndexMap::const_type FIM; + const FIM fim = get_initialized_face_index_map(tmesh, parameters::default_values()); + + using Impl2 = typename internal::Geodesic_circle_impl; + + typename Impl2::dual_geodesic_solver solver = Impl2::make_dual_geodesic_solver(vpm, fim, tmesh); + std::vector initial_path = + Impl2::strip_on_dual_graph(solver, tmesh, get(fim, src.first), get(fim,tgt.first)); +#endif std::vector< std::array> portals=Impl::unfold_strip(initial_path,src,tgt,vpm,tmesh); std::size_t max_index=0; @@ -982,14 +1969,15 @@ void locally_shortest_path(const Face_location &src, template std::vector> -recursive_de_Casteljau(const TriangleMesh &mesh, +recursive_de_Casteljau(const TriangleMesh& mesh, const Bezier_segment& control_points, const int num_subdiv) { //TODO replace with named parameter using VPM = typename boost::property_map::const_type; using K = typename Kernel_traits::value_type>::type; - using Impl = internal::Locally_shortest_path_imp; + using Impl = internal::Bezier_tracing_impl; + std::vector> segments(1,control_points); std::vector> result; @@ -1012,6 +2000,39 @@ recursive_de_Casteljau(const TriangleMesh &mesh, (Face_location*)segments.data() + segments.size() * 4}; } +template +void approximate_geodesic_distance_field(const Face_location& center, + VertexDistanceMap distance_map, + const TriangleMesh& tmesh) +{ + // TODO: the solver could be init once and used several times for different centers + // in particular, it can be tweaked to compute the Voronoi diagram of the initial centers + // or geodesic furthest point sampling. + // TODO: add a parameter for the link size to increase to precision of the approximation of the distance + // TODO: concave flattening should be handled to improve the approximation of the distance + // (shortest path is not a line in that case) + + //TODO replace with named parameter + using VPM = typename boost::property_map::const_type; + VPM vpm = get(CGAL::vertex_point, tmesh); + using K = typename Kernel_traits::value_type>::type; + + typedef typename GetInitializedVertexIndexMap::const_type VIM; + const VIM vim = get_initialized_vertex_index_map(tmesh, parameters::default_values()); + typedef typename GetInitializedFaceIndexMap::const_type FIM; + const FIM fim = get_initialized_face_index_map(tmesh, parameters::default_values()); + + using Impl = typename internal::Geodesic_circle_impl; + + typename Impl::geodesic_solver solver = Impl::make_geodesic_solver(vpm, vim,tmesh); + std::vector distances = Impl::compute_geodesic_distances(solver, vpm, tmesh, center); + + for (typename Impl::vertex_descriptor v : vertices(tmesh)) + { + put(distance_map, v, distances[get(vim,v)]); + } +} + } // namespace Polygon_mesh_processing } // namespace CGAL From defa982d2032c3ab60c78fb94eb679593888d1a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Thu, 28 Sep 2023 12:40:58 +0200 Subject: [PATCH 016/120] fix warnings --- .../Bsurf/locally_shortest_path.h | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index 5b048f381f00..868cbbff8fa3 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -1583,7 +1583,7 @@ struct Geodesic_circle_impl auto average_weight = (cumulative_weight / queue.size()); // Large Label Last (see comment at the beginning) - for (auto tries = 0; tries < queue.size() + 1; tries++) { + for (std::size_t tries = 0; tries < queue.size() + 1; tries++) { if (field[node] <= average_weight) break; queue.pop_front(); @@ -1681,7 +1681,7 @@ struct Geodesic_circle_impl return false; }; - auto distances = vector(solver.graph.size(), DBL_MAX); + auto distances = std::vector(solver.graph.size(), DBL_MAX); std::vectorsources_id((sources_and_dist.size())); for (auto i = 0; i < sources_and_dist.size(); ++i) { sources_id[i] = get(vidmap,sources_and_dist[i].first); @@ -1822,10 +1822,10 @@ struct Geodesic_circle_impl field[src]=0.0; std::vector sources = std::vector{src}; - auto update = [&parents](int node, int neighbor, double new_distance) { + auto update = [&parents](int node, int neighbor, double) { parents[neighbor] = node; }; - auto stop = [](int node) { return false; }; + auto stop = [](int) { return false; }; auto exit = [tgt](int node) { return node==tgt; }; visit_dual_geodesic_graph(field,solver, std::vector{src}, update, stop, exit); @@ -1855,12 +1855,9 @@ void locally_shortest_path(const Face_location &src, { if (src.first == tgt.first) return; - typedef typename boost::graph_traits::face_descriptor - face_descriptor; + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; - typedef typename boost::graph_traits::edge_descriptor - edge_descriptor; //TODO replace with named parameter using VPM = typename boost::property_map::const_type; @@ -1869,6 +1866,11 @@ void locally_shortest_path(const Face_location &src, VPM vpm = get(CGAL::vertex_point, tmesh); #if CGAL_BSURF_USE_DIJKSTRA_SP + typedef typename boost::graph_traits::face_descriptor + face_descriptor; + typedef typename boost::graph_traits::edge_descriptor + edge_descriptor; + typename boost::property_map< TriangleMesh, CGAL::dynamic_face_property_t>::const_type predecessor_map = From 3a581857ab291d576de79593db30252141c4b8a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Thu, 28 Sep 2023 13:53:41 +0200 Subject: [PATCH 017/120] bug fix --- .../Polygon_mesh_processing/Bsurf/locally_shortest_path.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index 868cbbff8fa3..e078ecfd077c 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -1437,8 +1437,8 @@ struct Geodesic_circle_impl for (auto i = 0; i < 3; ++i) { solver.graph[entry][i].node = get(tidmap,face(opposite(h,mesh),mesh)); solver.graph[entry][i].length = compute_dual_weights(h); + h=next(h,mesh); } - h=next(h,mesh); } return solver; } @@ -1821,14 +1821,14 @@ struct Geodesic_circle_impl std::vector id_to_face_map(faces(mesh).begin(), faces(mesh).end()); field[src]=0.0; - std::vector sources = std::vector{src}; + std::vector sources = {src}; auto update = [&parents](int node, int neighbor, double) { parents[neighbor] = node; }; auto stop = [](int) { return false; }; auto exit = [tgt](int node) { return node==tgt; }; - visit_dual_geodesic_graph(field,solver, std::vector{src}, update, stop, exit); + visit_dual_geodesic_graph(field,solver, sources, update, stop, exit); // extract_strip std::vector strip; @@ -1865,6 +1865,8 @@ void locally_shortest_path(const Face_location &src, using Impl = internal::Locally_shortest_path_imp; VPM vpm = get(CGAL::vertex_point, tmesh); + +//TODO: handle cases of src and tgt not in the same connected component (assert?) #if CGAL_BSURF_USE_DIJKSTRA_SP typedef typename boost::graph_traits::face_descriptor face_descriptor; From 58adc66f2f825832fb74d45b6eae754a4feffd05 Mon Sep 17 00:00:00 2001 From: ClaudioMancinelli Date: Thu, 28 Sep 2023 14:03:48 +0200 Subject: [PATCH 018/120] bug fix strip on dual graph --- .../CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index e078ecfd077c..2a6fe62523bf 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -1836,7 +1836,7 @@ struct Geodesic_circle_impl CGAL_assertion(parents[tgt] != -1); //update the result using id_to_face_map strip.reserve((int)std::sqrt(parents.size())); - while (node != -1) { + while (parents[node] != -1) { strip.push_back(common_halfedge(id_to_face_map[node],id_to_face_map[parents[node]])); node = parents[node]; } From 85e649bc6af1cddd2feaa6df06e6db0b61dde1fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Thu, 28 Sep 2023 16:30:46 +0200 Subject: [PATCH 019/120] fix macro --- .../CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index 2a6fe62523bf..86400f37e190 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -1867,7 +1867,7 @@ void locally_shortest_path(const Face_location &src, //TODO: handle cases of src and tgt not in the same connected component (assert?) -#if CGAL_BSURF_USE_DIJKSTRA_SP +#ifdef CGAL_BSURF_USE_DIJKSTRA_SP typedef typename boost::graph_traits::face_descriptor face_descriptor; typedef typename boost::graph_traits::edge_descriptor From 40f741bdee1c38d6bb7c497cd8ee0d865a342089 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Thu, 28 Sep 2023 17:19:07 +0200 Subject: [PATCH 020/120] add example for geodesic distance computation + fix compilation and warnings --- .../Polygon_mesh_processing/CMakeLists.txt | 1 + .../geodesic_circles_sm_example.cpp | 74 +++++++++++++++++++ .../Bsurf/locally_shortest_path.h | 74 ++++++++++--------- 3 files changed, 114 insertions(+), 35 deletions(-) create mode 100644 Polygon_mesh_processing/examples/Polygon_mesh_processing/geodesic_circles_sm_example.cpp diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt b/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt index 0dfd0e65547e..61afb9accd99 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt @@ -53,6 +53,7 @@ create_single_source_cgal_program("hausdorff_distance_remeshing_example.cpp") create_single_source_cgal_program("hausdorff_bounded_error_distance_example.cpp") create_single_source_cgal_program("locally_shortest_path_sm_example.cpp") create_single_source_cgal_program("trace_bezier_segment_sm_example.cpp") +create_single_source_cgal_program("geodesic_circles_sm_example.cpp") find_package(Eigen3 3.2.0 QUIET) #(requires 3.2.0 or greater) include(CGAL_Eigen3_support) diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/geodesic_circles_sm_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/geodesic_circles_sm_example.cpp new file mode 100644 index 000000000000..540420174513 --- /dev/null +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/geodesic_circles_sm_example.cpp @@ -0,0 +1,74 @@ +#include +#include +#include + +#include + + +namespace PMP = CGAL::Polygon_mesh_processing; + +typedef CGAL::Exact_predicates_inexact_constructions_kernel K; +typedef CGAL::Surface_mesh Mesh; +typedef PMP::Face_location Face_location; +typedef PMP::Edge_location Edge_location; + + +int main(int argc, char** argv) +{ + std::string filename = (argc > 1) ? std::string(argv[1]) + : CGAL::data_file_path("meshes/elephant.off"); + + Mesh mesh; + if(!CGAL::IO::read_polygon_mesh(filename, mesh) || !CGAL::is_triangle_mesh(mesh)) + { + std::cerr << "Invalid input." << std::endl; + return 1; + } + + std::size_t nb_faces = faces(mesh).size(); + + // take two random faces and pick the centroid + CGAL::Random rnd = CGAL::get_default_random(); + // CGAL::Random rnd(1695720148); + // CGAL::Random rnd(1695724381); + // CGAL::Random rnd(1695813638); + + std::cout << "seed " << rnd.get_seed() << std::endl; + Mesh::Face_index f = *std::next(faces(mesh).begin(), rnd.get_int(0, nb_faces)); + // or pick specific faces + + Face_location src(f, CGAL::make_array(0.3,0.3,0.4)); + std::vector radii = {0.001, 0.01, 0.05, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1, 10}; + + std::cout << "src = " << PMP::construct_point(src, mesh) << "\n"; + + auto distance_map = mesh.add_property_map("v:dstmap").first; + std::vector edge_locations; + CGAL::Polygon_mesh_processing::approximate_geodesic_distance_field(src, distance_map, mesh); + + std::ofstream out("circles.polylines.txt"); + + for (double r : radii) + { + for (Mesh::Face_index f : faces(mesh)) + { + std::vector pts; + Mesh::Halfedge_index h = halfedge(f, mesh); + for (int i=0; i<3; ++i) + { + Mesh::Vertex_index src = source(h, mesh), tgt = target(h, mesh); + double ds = get(distance_map, src); + double dt = get(distance_map, tgt); + if ((ds < r) != (dt < r)) + { + double alpha = (r - dt) / (ds - dt); + pts.push_back( CGAL::barycenter(mesh.point(src), alpha, mesh.point(tgt), 1-alpha) ); + } + } + if (pts.size()==2) + out << "2 " << pts[0] << " " << pts[1] << "\n"; + } + } + + return 0; +} diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index 86400f37e190..af154e171f8d 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -1324,16 +1324,16 @@ struct Geodesic_circle_impl void connect_nodes(geodesic_solver &solver, const vertex_descriptor& a, const vertex_descriptor& b, - const VertexPointMap &vpm, - const VertexIndexMap& vidmap, const FT& length) + const VertexIndexMap& vidmap, + const FT& length) { - solver.graph[get(vidmap,a)].push_back({get(vidmap,b), length}); - solver.graph[get(vidmap,b)].push_back({get(vidmap,a), length}); + // TODO: avoid cast + solver.graph[get(vidmap,a)].push_back({static_cast(get(vidmap,b), length)}); + solver.graph[get(vidmap,b)].push_back({static_cast(get(vidmap,a), length)}); } static double opposite_nodes_arc_length(const VertexPointMap &vpm, - const TriangleMesh &mesh, const vertex_descriptor& a, const vertex_descriptor& b, const vertex_descriptor& v0, @@ -1348,8 +1348,8 @@ struct Geodesic_circle_impl Vector_3 bv0 = get(vpm,v0) - get(vpm,b); Vector_3 bv1 = get(vpm,v1) - get(vpm,b); - double cos_alpha = ba/sqrt(ba.squared_lenght())*bv1/sqrt(bv1.squared_lenght()); - double cos_beta = bv0/sqrt(bv0.squared_lenght())*bv1/sqrt(bv1.squared_lenght()); + double cos_alpha = ba/sqrt(ba.squared_length())*bv1/sqrt(bv1.squared_length()); + double cos_beta = bv0/sqrt(bv0.squared_length())*bv1/sqrt(bv1.squared_length()); double sin_alpha = sqrt(std::max(0.0, 1 - cos_alpha * cos_alpha)); double sin_beta = sqrt(std::max(0.0, 1 - cos_beta * cos_beta)); @@ -1358,8 +1358,8 @@ struct Geodesic_circle_impl if (cos_alpha_beta <= -1) return DBL_MAX; // law of cosines (generalized Pythagorean theorem) - double len = dot(ba, ba) + dot(bv0, bv0) - - length(ba) * length(bv0) * 2 * cos_alpha_beta; + double len = ba.squared_length() + bv0.squared_length() - + sqrt(ba.squared_length()) * sqrt(bv0.squared_length()) * 2 * cos_alpha_beta; if (len <= 0) return DBL_MAX; @@ -1378,8 +1378,8 @@ struct Geodesic_circle_impl { vertex_descriptor v0 =target(next(h,mesh),mesh); vertex_descriptor v1= target(next(opposite(h,mesh),mesh),mesh); - auto length = opposite_nodes_arc_length(vpm, mesh, vidmap, a,b,v0,v1); - connect_nodes(solver, v0, v1, length); + auto length = opposite_nodes_arc_length(vpm, a,b,v0,v1); + connect_nodes(solver, v0, v1, vidmap, length); } static @@ -1390,7 +1390,7 @@ struct Geodesic_circle_impl { geodesic_solver solver; solver.graph.resize(vertices(mesh).size()); - for (auto& f : faces(mesh)) { + for (face_descriptor f : faces(mesh)) { halfedge_descriptor h=halfedge(f,mesh); for(auto i=0;i<3;++i) { @@ -1399,11 +1399,11 @@ struct Geodesic_circle_impl if(a> &sources_and_dist) { - auto update = [](int node, int neighbor, double new_distance) {}; - auto stop = [](int node) { return false; }; - auto exit = [](int node) { return false; }; + auto update = [](int /* node */, int /* neighbor */, double /* new_distance */) {}; + auto stop = [](int /* node */) { return false; }; + auto exit = [](int /* node */) { return false; }; auto distances = std::vector{}; distances.assign(solver.graph.size(), DBL_MAX); std::vectorsources_id((sources_and_dist.size())); - for (auto i = 0; i < sources_and_dist.size(); ++i) { + for (std::size_t i = 0; i < sources_and_dist.size(); ++i) { sources_id[i] = get(vidmap,sources_and_dist[i].first); distances[sources_id[i]] = sources_and_dist[i].second; } @@ -1699,9 +1699,10 @@ struct Geodesic_circle_impl const TriangleMesh &mesh, const halfedge_descriptor& h) { - std::array flat_tid=init_flat_triangle(h,vpm,mesh); - std::array flat_nei=unfold_face(h,vpm,mesh,flat_tid); - return sqrt(squared_distance(flat_tid[2],flat_nei[2])); + using Impl = Locally_shortest_path_imp; + std::array flat_tid=Impl::init_flat_triangle(h,vpm,mesh); + std::array flat_nei=Impl::unfold_face(h,vpm,mesh,flat_tid); + return sqrt((flat_tid[2]-flat_nei[2]).squared_length()); } static @@ -1710,7 +1711,7 @@ struct Geodesic_circle_impl const Face_location& p) { halfedge_descriptor h=halfedge(p.first,mesh); - return p.second[0]*source(h,mesh)+p.second[1]*target(h,mesh)+p.second[2]*target(next(h,mesh),mesh); + return CGAL::barycenter(get(vpm, source(h,mesh)), p.second[0], get(vpm, target(h,mesh)), p.second[1], get(vpm, target(next(h,mesh),mesh)), p.second[2]); } // compute the distance between a point p and some vertices around him @@ -1723,17 +1724,20 @@ struct Geodesic_circle_impl const Face_location& p) { auto get_vid=[&mesh](const int k,const face_descriptor& tid) + { + halfedge_descriptor h=halfedge(tid,mesh); + switch(k) { - halfedge_descriptor h=halfedge(tid,mesh); - if(k==0) + case 0: return source(h,mesh); - if(k==1) + case 1: return target(h,mesh); - if(k==2) + default: return target(next(h,mesh),mesh); + } + }; - }; - std::vector> nodes; + std::vector> nodes; nodes.reserve(6); auto [is_vert,offset]=point_is_vert(p); if (is_vert) { @@ -1746,7 +1750,7 @@ struct Geodesic_circle_impl for (auto i = 0; i < 3; ++i) { vertex_descriptor p0 = source(h,mesh); //connect to current vertex - double d = sqrt(squared_distances(get(vpm,p0),pos)); + double d = sqrt(squared_distance(get(vpm,p0),pos)); nodes.push_back(std::make_pair(p0, d)); //connecting to opposite vertex w.r.t to current halfedge @@ -1765,14 +1769,15 @@ struct Geodesic_circle_impl //TODO: can be easiliy extended to more than one source static std::vector - compute_geodesic_distances(const geodesic_solver &solver, - const VertexPointMap &vpm, + compute_geodesic_distances(const geodesic_solver& solver, + const VertexPointMap& vpm, + const VertexIndexMap& vim, const TriangleMesh &mesh, const Face_location& p) { std::vector> source_nodes=nodes_around_point(vpm,mesh,p); - return compute_geodesic_distances(solver, source_nodes); + return compute_geodesic_distances(solver, vim, source_nodes); } //compute the geodesic distance field from src, and stop the propagation @@ -2024,12 +2029,11 @@ void approximate_geodesic_distance_field(const Face_location& typedef typename GetInitializedVertexIndexMap::const_type VIM; const VIM vim = get_initialized_vertex_index_map(tmesh, parameters::default_values()); typedef typename GetInitializedFaceIndexMap::const_type FIM; - const FIM fim = get_initialized_face_index_map(tmesh, parameters::default_values()); using Impl = typename internal::Geodesic_circle_impl; typename Impl::geodesic_solver solver = Impl::make_geodesic_solver(vpm, vim,tmesh); - std::vector distances = Impl::compute_geodesic_distances(solver, vpm, tmesh, center); + std::vector distances = Impl::compute_geodesic_distances(solver, vpm, vim, tmesh, center); for (typename Impl::vertex_descriptor v : vertices(tmesh)) { From c5236db0a202246f5b3a32460823e1f9aa067cd6 Mon Sep 17 00:00:00 2001 From: ClaudioMancinelli Date: Fri, 29 Sep 2023 09:43:00 +0200 Subject: [PATCH 021/120] bug fix (still bugged) Dijkstra --- .../Bsurf/locally_shortest_path.h | 934 ++++++++++-------- 1 file changed, 522 insertions(+), 412 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index af154e171f8d..5cccb5f6c199 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -682,408 +682,488 @@ struct Locally_shortest_path_imp - #if 0 - //:::::::::::::::::::::Straightest Geodesic:::::::::::::::::::::::::::: + #if 1 + //:::::::::::::::::::::Parallel Transport:::::::::::::::::::::::::::: static - std::vector - get_3D_basis_at_point(const Face_location& p, - const TriangleMesh& mesh, - const VertexPointMap &vpm) - { - halfedge_descriptor h=halfedge(p.first,mesh); - - Point_3 p0=get(vpm, source(h, mesh)); - Point_3 p1=get(vpm, target(h, mesh)); - Point_3 p2= get(vpm,target(next(h, mesh), mesh)); - - Vector_3 e=p1-p0; - e/=sqrt(e.squared_length()); - Vector_3 n= triangle_normal(p0,p1,p2); - Vector_3 e1= cross_product(n,e); - - return {e,e1,n}; - } - - - static - std::tuple - point_is_edge(const Face_location& p, - const FT& tol=1e-5) - { - auto bary=p.second; - if (bary[0] > tol && bary[1] > tol && bary[2] <= tol) - return {true, 0}; - if (bary[1] > tol && bary[2] > tol && bary[0] <= tol) - return {true, 1}; - if (bary[2] > tol && bary[0] > tol && bary[1] <= tol) - return {true, 2}; - - return {false, -1}; - } - - static - //https://rootllama.wordpress.com/2014/06/20/ray-line-segment-intersection-test-in-2d/ - std::pair - intersect(const Vector_2 &direction, const Vector_2 &left, - const Vector_2 &right,const Vector_2& origin) - { - auto v1 = origin-left; - auto v2 = right - left; - auto v3 = vec2f{-direction.y, direction.x}; - auto t0 = cross(v2, v1) / dot(v2, v3); - auto t1 = -dot(left, v3) / dot(v2, v3); - return std::make_pair(t0, t1); - }; - - static - std::tuple - segment_in_tri(const Vector_2& p, - const std::array& tri, - const Vector_2& dir, - const int& k) - { - for (auto i = 0; i < 3; ++i) - { - auto [t0, t1] = intersect(dir, tri[(k+i)%3], tri[(k + 1+i) % 3],p); - - //TODO: replace intersection with CGAL code - if (t0 > 0 && t1 >= -1e-4 && t1 <= 1 + 1e-4) - { - auto result_k =(k+i)%3; - return {result_k, clamp(t1, 0.f, 1.f)}; - } - } - CGAL_assertion(false); - return {-1,-1}; - } - - FT get_total_angle(const vertex_descriptor& vid, - const TriangleMesh& mesh, - const VertexPointMap &vpm) - { - halfedge_descriptor h_ref= halfedge(vid,mesh); - halfedge_descriptor h_start=h_ref - halfedge_descriptor h_next = next(h_start,mesh); - - FT theta=angle(get(vpm,source(h_start,mesh))-get(vpm,target(h_start,mesh)), - get(vpm,target(h_next,mesh))-get(vpm,target(h_start,mesh))); - h_start=opposite(h_next,mesh); - h_next=next(h_start,mesh); - while(h_start!=h_ref) - { - theta+=angle(get(vpm,source(h_start,mesh))-get(vpm,target(h_start,mesh)), - get(vpm,target(h_next,mesh))-get(vpm,target(h_start,mesh))); - h_start=opposite(h_next,mesh); - h_next=next(h_start,mesh); - } - - return theta; + Vector_3 face_normal(const VertexPointMap &vpm, + const TriangleMesh &mesh, + const face_descriptor& f) + { halfedge_descriptor h=halfedge(f,mesh); + Point_3 p0=get(vpm,source(h,mesh)); + Point_3 p1=get(vpm,target(h,mesh)); + Point_3 p2=get(vpm,target(next(h,mesh),mesh)); + Vector_3 n = triangle_normal(p0,p1,p2); + n=n/sqrt(n.squared_length()); + + return n; } - static - std::tuple, halfedge_descriptor> - polthier_condition_at_vert(const TriangleMesh& mesh, - const VertexPointMap &vpm - const vertex_descriptor& vid, - const face_descriptor& tid, - const int kv, - const Vector_3 &dir) + std::vector polar_basis(const VertexPointMap &vpm, + const TriangleMesh &mesh, + const face_descriptor& f, + const Vector_3& normal) { - FT total_angle=get_total_angle(vid,mesh,vpm); - FT theta = 0.5 * total_angle; - halfedge_descriptor h=halfedge(tid,mesh); - while(target(h,mesh)!=vid) - h=next(h,mesh); - - Point_3 vert = get(vpm,vid); - Point_3 vert_adj=get(vpm,source(h,mesh)); - Vector_3 v = vert - vert_adj; - - FT acc = angle(v, dir); - FT prev_angle = acc; - face_descriptor curr_tid = tid; - - while (acc < theta) { - h=prev(opposite(h.mesh),mesh); - prev_angle = acc; - Point_3 next_vert_adj=get(vpm,source(h,mesh)); - acc += angle(vert_adj - vert,next_vert_adj - vert); - vert_adj=next_vert_adj; - } - auto offset = theta - prev; - Point_3 prev_vert_adj=get(vpm,target(next(h,mesh,mesh))); - - FT l = sqrt(squared_distance(prev_vert_adj,vert)); - FT phi = angle(vert - prev_vert_adj, vert_adj - prev_vert_adj); - FT x = l * std::sin(offset) / std::sin(M_PI - phi - offset); - FT alpha = x / sqrt(squared_distance(vert_adj - prev_vert_adj)); - halfedge_descriptor prev_h=prev(h,mesh); - std::array flat_tid = init_flat_triangle(prev_h,vpm,mesh); - - Vector_2 q = (1 - alpha) * flat_tid[0] + alpha * flat_tid[1]; - - Vector_2 new_dir = q - flat_tid[2]; + halfedge_descriptor h=halfedge(f,mesh); + Point_3 p0=get(vpm,source(h,mesh)); + Point_3 p1=get(vpm,target(h,mesh)); + Vector_3 e=p1-p0; + e=e/sqrt(e.squared_length()); + Vector_3 e_perp=cross_product(normal,e); - Face_location new_p; - new_p.first=face(h,mesh); - halfedge_descriptor h_face=halfedge(new_p.first,mesh); - Vector_3 bary; - Vector_2 bary_edge=Vector_2{1-alpha,alpha}; - - if (h_face!=prev_h) - { - if (h_face==next(prev_h, mesh)) - { - new_p.second[0]=bary_edge[1]; - new_p.second[1]=0; - new_p.second[2]=bary_edge[0]; - } - else - { - new_p.second[0]=0; - new_p.second[1]=bary_edge[0]; - new_p.second[2]=bary_edge[1]; - } - } - else - { - new_p.second[0]=bary_edge[0]; - new_p.second[1]=bary_edge[1]; - new_p.second[2]=0; - } + return std::vector{e,e_perp,normal}; - return {new_dir, point_from_vert(triangles, vid, face(prev_h,mesh)), prev_h}; } - static - std::vector> - straightest_goedesic(const Face_location& p, - const TriangleMesh& mesh, - const VertexPointMap &vpm, - const Vector_2& dir,const FT& len) - { - auto get_vid=[&mesh](const int k,const face_descriptor& tid) - { - halfedge_descriptor h=halfedge(tid,mesh); - if(k==0) - return source(h,mesh); - if(k==1) - return target(h,mesh); - if(k==2) - return target(next(h,mesh),mesh); - - }; - - std::vector> result; - FT accumulated=0.; - face_descriptor curr_tid=p.first; - std::array curr_flat_tid=init_flat_triangle(halfedge(curr_tid,mesh),vpm,mesh); - Vector_2 flat_p= p.second[0]*curr_flat_tid[0]+p.second[1]*curr_flat_tid[1]+p.second[2]*curr_flat_tid[2]; - Face_location curr_p=p; - Face_location prev_p; - Vector_2 curr_dir=dir; - - result.push_back(p); - int k_start=-1; - - auto [is_vert, kv] = point_is_vert(p); - auto [is_edge, ke] = point_is_edge(p); - if (is_vert) - k_start = kv; - else if (is_edge) - k_start = ke; - - - while (accumulated < len) - { - auto [k, t1] = segment_in_tri(flat_p, curr_flat_tid, curr_dir,k_start); - - Vector_3 new_bary=Vector_3{0,0,0}; - Face_location point_on_edge; - //TODO: we assume k is always different from -1, check this! - - new_bary[k] = 1 - t1; - new_bary[(k + 1) % 3] = t1; - point_on_edge.first=curr_tid; - point_on_edge.second=new_bary; - - - std::tie(is_vert, kv) = point_is_vert(point_on_edge); - - if (is_vert) - { - auto vid = get_vertex(kv,curr_tid); - - accumulated += - sqrt(squared_distance(construct_point(curr_p,mesh) - get(vpm,vid))); - prev_p = curr_p; - std::tie(curr_dir, curr_p, k_start) = - polthier_condition_at_vert(triangles, positions, adjacencies, - total_angles, vid, curr_tid, dir3d); - curr_tid = curr_p.face; - if (curr_tid == -1) - return result; - - } - else - { - auto adj = adjacencies[curr_tid][k]; - if (adj == -1) - return result; - auto h = find(adjacencies[adj], curr_tid); - - new_bary = zero3f; - new_bary[h] = t1; - new_bary[(h + 1) % 3] = 1 - t1; - - prev_p = curr_p; - curr_p = mesh_point{adj, vec2f{new_bary.y, new_bary.z}}; - accumulated += length(eval_position(triangles, positions, curr_p) - - eval_position(triangles, positions, prev_p)); - - auto T = switch_reference_frame(triangles, positions, adj, curr_tid); - curr_dir = switch_reference_frame_vector(T, curr_dir); - - curr_tid = adj; - k_start = h; - } - - result.push_back(curr_p); - } - - auto excess = accumulated - len; - auto prev_pos = eval_position(triangles, positions, result.rbegin()[1]); - auto last_pos = eval_position(triangles, positions, result.back()); - auto alpha = excess / length(last_pos - prev_pos); - auto pos = alpha * prev_pos + (1 - alpha) * last_pos; - - auto [inside, bary] = - point_in_triangle(triangles, positions, prev_p.face, pos); - if (!inside) - std::cout << "error!This point should be in the triangle" << std::endl; - - result.pop_back(); - result.push_back(mesh_point{prev_p.face, bary}); - - return result; - } - - static - std::vector - polthier_straightest_geodesic(const vector &triangles, const vector &positions, - const vector &adjacencies, const vector> &v2t, - const vector> &angles, const vector &total_angles, - const mesh_point &p, const vec2f &dir, const float &len) - { - auto result = vector{}; - auto accumulated = 0.f; - auto curr_tid = p.face; - auto curr_p = p; - auto prev_p = mesh_point{}; - auto curr_dir = dir; - - result.push_back(p); - - auto k_start = 0; - auto [is_vert, kv] = point_is_vert(p); - auto [is_edge, ke] = point_is_edge(p); - if (is_vert) - k_start = kv; - else if (is_edge) - k_start = ke; - - auto count = 0; - while (accumulated < len) - { - ++count; - auto [k, t1] = straightest_path_in_tri(triangles, positions, curr_p, - curr_dir, k_start); - - auto new_bary = zero3f; - auto point_on_edge = mesh_point{}; - if (k != -1) { - new_bary[k] = 1 - t1; - new_bary[(k + 1) % 3] = t1; - point_on_edge = mesh_point{curr_tid, vec2f{new_bary.y, new_bary.z}}; - } else { - std::tie(is_edge, ke) = point_is_edge(curr_p, 5e-3); - std::tie(is_vert, kv) = point_is_vert(curr_p, 5e-3); - auto bary = get_bary(curr_p.uv); - if (is_edge) { - k = ke; - t1 = bary[(k + 1) % 3]; - point_on_edge = curr_p; - } else if (is_vert) { - auto bary3 = zero3f; - bary3[kv] = 1; - point_on_edge = {curr_p.face, {bary3.y, bary3.z}}; - } else { - std::cout << "Error!This should not happen" << std::endl; - return result; - } - } - - std::tie(is_vert, kv) = point_is_vert(point_on_edge); - - if (is_vert) { - auto vid = triangles[curr_tid][kv]; - if (angles[vid].size() == 0) - return result; - - accumulated += - length(eval_position(triangles, positions, curr_p) - positions[vid]); - auto dir3d = normalize(eval_position(triangles, positions, curr_p) - - positions[vid]); - prev_p = curr_p; - std::tie(curr_dir, curr_p, k_start) = - polthier_condition_at_vert(triangles, positions, adjacencies, - total_angles, vid, curr_tid, dir3d); - curr_tid = curr_p.face; - if (curr_tid == -1) - return result; - - } else { - auto adj = adjacencies[curr_tid][k]; - if (adj == -1) - return result; - auto h = find(adjacencies[adj], curr_tid); - - new_bary = zero3f; - new_bary[h] = t1; - new_bary[(h + 1) % 3] = 1 - t1; - - prev_p = curr_p; - curr_p = mesh_point{adj, vec2f{new_bary.y, new_bary.z}}; - accumulated += length(eval_position(triangles, positions, curr_p) - - eval_position(triangles, positions, prev_p)); - - auto T = switch_reference_frame(triangles, positions, adj, curr_tid); - curr_dir = switch_reference_frame_vector(T, curr_dir); - - curr_tid = adj; - k_start = h; - } - - result.push_back(curr_p); - } - - auto excess = accumulated - len; - auto prev_pos = eval_position(triangles, positions, result.rbegin()[1]); - auto last_pos = eval_position(triangles, positions, result.back()); - auto alpha = excess / length(last_pos - prev_pos); - auto pos = alpha * prev_pos + (1 - alpha) * last_pos; - - auto [inside, bary] = - point_in_triangle(triangles, positions, prev_p.face, pos); - if (!inside) - std::cout << "error!This point should be in the triangle" << std::endl; - - result.pop_back(); - result.push_back(mesh_point{prev_p.face, bary}); - - return result; - } + // //:::::::::::::::::::::Straightest Geodesic:::::::::::::::::::::::::::: + // static + // void parallel_transport_through_flattening(Vector_3& v, + // const VertexPointMap &vpm, + // const TriangleMesh &mesh, + // const face_descriptor& from, + // const face_descriptor& to) + // { + // halfedge_descriptor h_ref = halfedge(from, mesh); + // halfedge_descriptor h=h_ref; + // for (int i = 0; i < 3; ++i) { + // if (face(opposite(h_ref, mesh), mesh) == to) + // { + // h=h_ref; + // break; + // } + // h_ref = next(h_ref, mesh); + // } + + // std::array flat_from = init_flat_triangle(h,vpm,mesh); + // std::array flat_to = unfold_face(h,vpm,mesh,flat_from); + // Vector_2 bary = Vector_2{0.333, 0.333}; + // Vector_2 c0 = 0.33*(flat_from[0]+flat_from[1]+flat_from[2]); + // Vector_2 c1 = 0.33*(flat_t0[0]+flat_t0[1]+flat_t0[2]); + // Vector_2 e0 = flat_from[0] - c0; + // Vector_2 e1 = flat_to[0] - c1; + + // Vector_2 w = c1 - c0; + // FT phi_ij = angle(e0, w); + // if (e0.x()*w.y()-e0.y()*w.x() < 0) + // phi_ij = 2 * M_PI - phi_ij; + // w *= -1; + // FT phi_ji = angle(e1, w); + + // if (e1.x()*w.y()-e1.y()*w.x() < 0) + // phi_ji = 2 * M_PI - phi_ji; + + // Vector_3 n_from= face_normal(vpm, mesh, from); + // std::vector e_from = polar_basis(vpm, mesh, from); + // double teta = angle(e_from, v); + // if (cross_product(e_from, v)*n_from < 0) + // teta = 2 * M_PI - teta; + + // std::vector e_to = polar_basis(vpm, mesh, from to); + // Vector_3 n_to = face_normal(vpm, mesh, to); + // double rot = teta + phi_ji + M_PI - phi_ij; + // e_to =e_to*sqrt(v.length_squared()); + // v = rot_vect(e_to, n_to, rot); + // } + // static + // std::vector + // get_3D_basis_at_point(const Face_location& p, + // const TriangleMesh& mesh, + // const VertexPointMap &vpm) + // { + // halfedge_descriptor h=halfedge(p.first,mesh); + + // Point_3 p0=get(vpm, source(h, mesh)); + // Point_3 p1=get(vpm, target(h, mesh)); + // Point_3 p2= get(vpm,target(next(h, mesh), mesh)); + + // Vector_3 e=p1-p0; + // e/=sqrt(e.squared_length()); + // Vector_3 n= triangle_normal(p0,p1,p2); + // Vector_3 e1= cross_product(n,e); + + // return {e,e1,n}; + // } + + + // static + // std::tuple + // point_is_edge(const Face_location& p, + // const FT& tol=1e-5) + // { + // auto bary=p.second; + // if (bary[0] > tol && bary[1] > tol && bary[2] <= tol) + // return {true, 0}; + // if (bary[1] > tol && bary[2] > tol && bary[0] <= tol) + // return {true, 1}; + // if (bary[2] > tol && bary[0] > tol && bary[1] <= tol) + // return {true, 2}; + + // return {false, -1}; + // } + + // static + // //https://rootllama.wordpress.com/2014/06/20/ray-line-segment-intersection-test-in-2d/ + // std::pair + // intersect(const Vector_2 &direction, const Vector_2 &left, + // const Vector_2 &right,const Vector_2& origin) + // { + // auto v1 = origin-left; + // auto v2 = right - left; + // auto v3 = vec2f{-direction.y, direction.x}; + // auto t0 = cross(v2, v1) / dot(v2, v3); + // auto t1 = -dot(left, v3) / dot(v2, v3); + // return std::make_pair(t0, t1); + // }; + + // static + // std::tuple + // segment_in_tri(const Vector_2& p, + // const std::array& tri, + // const Vector_2& dir, + // const int& k) + // { + // for (auto i = 0; i < 3; ++i) + // { + // auto [t0, t1] = intersect(dir, tri[(k+i)%3], tri[(k + 1+i) % 3],p); + + // //TODO: replace intersection with CGAL code + // if (t0 > 0 && t1 >= -1e-4 && t1 <= 1 + 1e-4) + // { + // auto result_k =(k+i)%3; + // return {result_k, clamp(t1, 0.f, 1.f)}; + // } + // } + // CGAL_assertion(false); + // return {-1,-1}; + // } + + // FT get_total_angle(const vertex_descriptor& vid, + // const TriangleMesh& mesh, + // const VertexPointMap &vpm) + // { + // halfedge_descriptor h_ref= halfedge(vid,mesh); + // halfedge_descriptor h_start=h_ref + // halfedge_descriptor h_next = next(h_start,mesh); + + // FT theta=angle(get(vpm,source(h_start,mesh))-get(vpm,target(h_start,mesh)), + // get(vpm,target(h_next,mesh))-get(vpm,target(h_start,mesh))); + // h_start=opposite(h_next,mesh); + // h_next=next(h_start,mesh); + // while(h_start!=h_ref) + // { + // theta+=angle(get(vpm,source(h_start,mesh))-get(vpm,target(h_start,mesh)), + // get(vpm,target(h_next,mesh))-get(vpm,target(h_start,mesh))); + // h_start=opposite(h_next,mesh); + // h_next=next(h_start,mesh); + // } + + // return theta; + // } + + // static + // std::tuple, halfedge_descriptor> + // polthier_condition_at_vert(const TriangleMesh& mesh, + // const VertexPointMap &vpm + // const vertex_descriptor& vid, + // const face_descriptor& tid, + // const int kv, + // const Vector_3 &dir) + // { + // FT total_angle=get_total_angle(vid,mesh,vpm); + // FT theta = 0.5 * total_angle; + // halfedge_descriptor h=halfedge(tid,mesh); + // while(target(h,mesh)!=vid) + // h=next(h,mesh); + + // Point_3 vert = get(vpm,vid); + // Point_3 vert_adj=get(vpm,source(h,mesh)); + // Vector_3 v = vert - vert_adj; + + // FT acc = angle(v, dir); + // FT prev_angle = acc; + // face_descriptor curr_tid = tid; + + // while (acc < theta) { + // h=prev(opposite(h.mesh),mesh); + // prev_angle = acc; + // Point_3 next_vert_adj=get(vpm,source(h,mesh)); + // acc += angle(vert_adj - vert,next_vert_adj - vert); + // vert_adj=next_vert_adj; + // } + // auto offset = theta - prev; + // Point_3 prev_vert_adj=get(vpm,target(next(h,mesh,mesh))); + + // FT l = sqrt(squared_distance(prev_vert_adj,vert)); + // FT phi = angle(vert - prev_vert_adj, vert_adj - prev_vert_adj); + // FT x = l * std::sin(offset) / std::sin(M_PI - phi - offset); + // FT alpha = x / sqrt(squared_distance(vert_adj - prev_vert_adj)); + // halfedge_descriptor prev_h=prev(h,mesh); + // std::array flat_tid = init_flat_triangle(prev_h,vpm,mesh); + + // Vector_2 q = (1 - alpha) * flat_tid[0] + alpha * flat_tid[1]; + + // Vector_2 new_dir = q - flat_tid[2]; + + // Face_location new_p; + // new_p.first=face(h,mesh); + // halfedge_descriptor h_face=halfedge(new_p.first,mesh); + // Vector_3 bary; + // Vector_2 bary_edge=Vector_2{1-alpha,alpha}; + + // if (h_face!=prev_h) + // { + // if (h_face==next(prev_h, mesh)) + // { + // new_p.second[0]=bary_edge[1]; + // new_p.second[1]=0; + // new_p.second[2]=bary_edge[0]; + // } + // else + // { + // new_p.second[0]=0; + // new_p.second[1]=bary_edge[0]; + // new_p.second[2]=bary_edge[1]; + // } + // } + // else + // { + // new_p.second[0]=bary_edge[0]; + // new_p.second[1]=bary_edge[1]; + // new_p.second[2]=0; + // } + + // return {new_dir, point_from_vert(triangles, vid, face(prev_h,mesh)), prev_h}; + // } + + // static + // std::vector> + // straightest_goedesic(const Face_location& p, + // const TriangleMesh& mesh, + // const VertexPointMap &vpm, + // const Vector_2& dir,const FT& len) + // { + // auto get_vid=[&mesh](const int k,const face_descriptor& tid) + // { + // halfedge_descriptor h=halfedge(tid,mesh); + // if(k==0) + // return source(h,mesh); + // if(k==1) + // return target(h,mesh); + // if(k==2) + // return target(next(h,mesh),mesh); + + // }; + + // std::vector> result; + // FT accumulated=0.; + // face_descriptor curr_tid=p.first; + // std::array curr_flat_tid=init_flat_triangle(halfedge(curr_tid,mesh),vpm,mesh); + // Vector_2 flat_p= p.second[0]*curr_flat_tid[0]+p.second[1]*curr_flat_tid[1]+p.second[2]*curr_flat_tid[2]; + // Face_location curr_p=p; + // Face_location prev_p; + // Vector_2 curr_dir=dir; + + // result.push_back(p); + + // int k_start=-1; + + // auto [is_vert, kv] = point_is_vert(p); + // auto [is_edge, ke] = point_is_edge(p); + // if (is_vert) + // k_start = kv; + // else if (is_edge) + // k_start = ke; + + + // while (accumulated < len) + // { + // auto [k, t1] = segment_in_tri(flat_p, curr_flat_tid, curr_dir,k_start); + // CGAL_assertion(k!=-1); + // Vector_3 new_bary=Vector_3{0,0,0}; + // Face_location point_on_edge; + // //TODO: we assume k is always different from -1, check this! + + // new_bary[k] = 1 - t1; + // new_bary[(k + 1) % 3] = t1; + // point_on_edge.first=curr_tid; + // point_on_edge.second=new_bary; + + + // std::tie(is_vert, kv) = point_is_vert(point_on_edge); + + // if (is_vert) + // { + // auto vid = get_vertex(kv,curr_tid); + + // accumulated += + // sqrt(squared_distance(construct_point(curr_p,mesh) - get(vpm,vid))); + // prev_p = curr_p; + // std::tie(curr_dir, curr_p, k_start) = + // polthier_condition_at_vert(triangles, positions, adjacencies, + // total_angles, vid, curr_tid, dir3d); + // curr_tid = curr_p.face; + // if (curr_tid == -1) + // return result; + + // } + // else + // { + // auto adj = adjacencies[curr_tid][k]; + // if (adj == -1) + // return result; + // auto h = find(adjacencies[adj], curr_tid); + + // new_bary = zero3f; + // new_bary[h] = t1; + // new_bary[(h + 1) % 3] = 1 - t1; + + // prev_p = curr_p; + // curr_p = mesh_point{adj, vec2f{new_bary.y, new_bary.z}}; + // accumulated += length(eval_position(triangles, positions, curr_p) - + // eval_position(triangles, positions, prev_p)); + + // auto T = switch_reference_frame(triangles, positions, adj, curr_tid); + // curr_dir = switch_reference_frame_vector(T, curr_dir); + + // curr_tid = adj; + // k_start = h; + // } + + // result.push_back(curr_p); + // } + + // auto excess = accumulated - len; + // auto prev_pos = eval_position(triangles, positions, result.rbegin()[1]); + // auto last_pos = eval_position(triangles, positions, result.back()); + // auto alpha = excess / length(last_pos - prev_pos); + // auto pos = alpha * prev_pos + (1 - alpha) * last_pos; + + // auto [inside, bary] = + // point_in_triangle(triangles, positions, prev_p.face, pos); + // if (!inside) + // std::cout << "error!This point should be in the triangle" << std::endl; + + // result.pop_back(); + // result.push_back(mesh_point{prev_p.face, bary}); + + // return result; + // } + + // static + // std::vector + // polthier_straightest_geodesic(const vector &triangles, const vector &positions, + // const vector &adjacencies, const vector> &v2t, + // const vector> &angles, const vector &total_angles, + // const mesh_point &p, const vec2f &dir, const float &len) + // { + // auto result = vector{}; + // auto accumulated = 0.f; + // auto curr_tid = p.face; + // auto curr_p = p; + // auto prev_p = mesh_point{}; + // auto curr_dir = dir; + + // result.push_back(p); + + // auto k_start = 0; + // auto [is_vert, kv] = point_is_vert(p); + // auto [is_edge, ke] = point_is_edge(p); + // if (is_vert) + // k_start = kv; + // else if (is_edge) + // k_start = ke; + + // auto count = 0; + // while (accumulated < len) + // { + // ++count; + // auto [k, t1] = straightest_path_in_tri(triangles, positions, curr_p, + // curr_dir, k_start); + + // auto new_bary = zero3f; + // auto point_on_edge = mesh_point{}; + // if (k != -1) { + // new_bary[k] = 1 - t1; + // new_bary[(k + 1) % 3] = t1; + // point_on_edge = mesh_point{curr_tid, vec2f{new_bary.y, new_bary.z}}; + // } else { + // std::tie(is_edge, ke) = point_is_edge(curr_p, 5e-3); + // std::tie(is_vert, kv) = point_is_vert(curr_p, 5e-3); + // auto bary = get_bary(curr_p.uv); + // if (is_edge) { + // k = ke; + // t1 = bary[(k + 1) % 3]; + // point_on_edge = curr_p; + // } else if (is_vert) { + // auto bary3 = zero3f; + // bary3[kv] = 1; + // point_on_edge = {curr_p.face, {bary3.y, bary3.z}}; + // } else { + // std::cout << "Error!This should not happen" << std::endl; + // return result; + // } + // } + + // std::tie(is_vert, kv) = point_is_vert(point_on_edge); + + // if (is_vert) { + // auto vid = triangles[curr_tid][kv]; + // if (angles[vid].size() == 0) + // return result; + + // accumulated += + // length(eval_position(triangles, positions, curr_p) - positions[vid]); + // auto dir3d = normalize(eval_position(triangles, positions, curr_p) - + // positions[vid]); + // prev_p = curr_p; + // std::tie(curr_dir, curr_p, k_start) = + // polthier_condition_at_vert(triangles, positions, adjacencies, + // total_angles, vid, curr_tid, dir3d); + // curr_tid = curr_p.face; + // if (curr_tid == -1) + // return result; + + // } else { + // auto adj = adjacencies[curr_tid][k]; + // if (adj == -1) + // return result; + // auto h = find(adjacencies[adj], curr_tid); + + // new_bary = zero3f; + // new_bary[h] = t1; + // new_bary[(h + 1) % 3] = 1 - t1; + + // prev_p = curr_p; + // curr_p = mesh_point{adj, vec2f{new_bary.y, new_bary.z}}; + // accumulated += length(eval_position(triangles, positions, curr_p) - + // eval_position(triangles, positions, prev_p)); + + // auto T = switch_reference_frame(triangles, positions, adj, curr_tid); + // curr_dir = switch_reference_frame_vector(T, curr_dir); + + // curr_tid = adj; + // k_start = h; + // } + + // result.push_back(curr_p); + // } + + // auto excess = accumulated - len; + // auto prev_pos = eval_position(triangles, positions, result.rbegin()[1]); + // auto last_pos = eval_position(triangles, positions, result.back()); + // auto alpha = excess / length(last_pos - prev_pos); + // auto pos = alpha * prev_pos + (1 - alpha) * last_pos; + + // auto [inside, bary] = + // point_in_triangle(triangles, positions, prev_p.face, pos); + // if (!inside) + // std::cout << "error!This point should be in the triangle" << std::endl; + + // result.pop_back(); + // result.push_back(mesh_point{prev_p.face, bary}); + + // return result; + // } #endif }; @@ -1325,42 +1405,50 @@ struct Geodesic_circle_impl const vertex_descriptor& a, const vertex_descriptor& b, const VertexIndexMap& vidmap, - const FT& length) + const FT& len) { // TODO: avoid cast - solver.graph[get(vidmap,a)].push_back({static_cast(get(vidmap,b), length)}); - solver.graph[get(vidmap,b)].push_back({static_cast(get(vidmap,a), length)}); + uint vida=get(vidmap,a); + uint vidb=get(vidmap,b); + solver.graph[vida].push_back({static_cast(vidb), len}); + solver.graph[vidb].push_back({static_cast(vida), len}); + } static double opposite_nodes_arc_length(const VertexPointMap &vpm, const vertex_descriptor& a, + const vertex_descriptor& c, const vertex_descriptor& b, - const vertex_descriptor& v0, - const vertex_descriptor& v1) + const vertex_descriptor& d) { - // Triangles (a, b, v0) and (a, b, v1) are connected by (a, b) edge + // Triangles (a, b, d) and (b, d, c) are connected by (b, d) edge // Nodes a and c must be connected. - //NOOO it is the opposite - Vector_3 ba = get(vpm,a) - get(vpm,b); - Vector_3 bv0 = get(vpm,v0) - get(vpm,b); - Vector_3 bv1 = get(vpm,v1) - get(vpm,b); + Vector_3 bc = get(vpm,c) - get(vpm,b); + Vector_3 bd = get(vpm,d) - get(vpm,b); - double cos_alpha = ba/sqrt(ba.squared_length())*bv1/sqrt(bv1.squared_length()); - double cos_beta = bv0/sqrt(bv0.squared_length())*bv1/sqrt(bv1.squared_length()); + Vector_3 ba_norm=ba/sqrt(ba.squared_length()); + Vector_3 bd_norm=bd/sqrt(bd.squared_length()); + Vector_3 bc_norm=bc/sqrt(bc.squared_length()); + + double cos_alpha = ba_norm * bd_norm; + double cos_beta = bc_norm * bd_norm; double sin_alpha = sqrt(std::max(0.0, 1 - cos_alpha * cos_alpha)); double sin_beta = sqrt(std::max(0.0, 1 - cos_beta * cos_beta)); + // cos(alpha + beta) double cos_alpha_beta = cos_alpha * cos_beta - sin_alpha * sin_beta; + CGAL_assertion(cos_alpha_beta>-1); if (cos_alpha_beta <= -1) return DBL_MAX; // law of cosines (generalized Pythagorean theorem) - double len = ba.squared_length() + bv0.squared_length() - - sqrt(ba.squared_length()) * sqrt(bv0.squared_length()) * 2 * cos_alpha_beta; + double len = ba.squared_length() + bc.squared_length() - + sqrt(ba.squared_length()) * sqrt(bc.squared_length()) * 2 * cos_alpha_beta; + CGAL_assertion(len>0); if (len <= 0) return DBL_MAX; else @@ -1378,8 +1466,8 @@ struct Geodesic_circle_impl { vertex_descriptor v0 =target(next(h,mesh),mesh); vertex_descriptor v1= target(next(opposite(h,mesh),mesh),mesh); - auto length = opposite_nodes_arc_length(vpm, a,b,v0,v1); - connect_nodes(solver, v0, v1, vidmap, length); + auto len = opposite_nodes_arc_length(vpm, v0,v1,a,b); + connect_nodes(solver, v0, v1, vidmap, len); } static @@ -1392,13 +1480,17 @@ struct Geodesic_circle_impl solver.graph.resize(vertices(mesh).size()); for (face_descriptor f : faces(mesh)) { halfedge_descriptor h=halfedge(f,mesh); - for(auto i=0;i<3;++i) + for(std::size_t i=0;i<3;++i) { vertex_descriptor a=source(h,mesh); vertex_descriptor b=target(h,mesh); + if(a Date: Fri, 29 Sep 2023 10:22:44 +0200 Subject: [PATCH 022/120] add early quit for Dijkstra --- .../Bsurf/locally_shortest_path.h | 56 +++++++++++++++++-- 1 file changed, 52 insertions(+), 4 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index 5cccb5f6c199..b4adb29f2aba 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -1960,6 +1960,43 @@ struct Geodesic_circle_impl } }; +#ifdef CGAL_BSURF_USE_DIJKSTRA_SP + class Dijkstra_end_exception : public std::exception + { + const char* what() const throw () + { + return "Dijkstra shortest path: reached the target vertex."; + } + }; + + template + class Stop_at_target_Dijkstra_visitor : boost::default_dijkstra_visitor + { + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + typedef typename boost::graph_traits::edge_descriptor edge_descriptor; + + vertex_descriptor destination_vd; + + public: + Stop_at_target_Dijkstra_visitor(vertex_descriptor destination_vd) + : destination_vd(destination_vd) + { } + + void initialize_vertex(const vertex_descriptor& /*s*/, const Graph& /*mesh*/) const { } + void examine_vertex(const vertex_descriptor& /*s*/, const Graph& /*mesh*/) const { } + void examine_edge(const edge_descriptor& /*e*/, const Graph& /*mesh*/) const { } + void edge_relaxed(const edge_descriptor& /*e*/, const Graph& /*mesh*/) const { } + void discover_vertex(const vertex_descriptor& /*s*/, const Graph& /*mesh*/) const { } + void edge_not_relaxed(const edge_descriptor& /*e*/, const Graph& /*mesh*/) const { } + void finish_vertex(const vertex_descriptor &vd, const Graph& /* mesh*/) const + { + if(vd == destination_vd) + throw Dijkstra_end_exception(); + } + }; +#endif + + } // namespace internal template @@ -2030,11 +2067,20 @@ void locally_shortest_path(const Face_location &src, // ))); } + internal::Stop_at_target_Dijkstra_visitor> vis(tgt.first); + + try{ + boost::dijkstra_shortest_paths(dual, src.first, + boost::distance_map(distance_map) + .predecessor_map(predecessor_map) + .weight_map(weight_map) + .visitor(vis)); + } + catch(const internal::Dijkstra_end_exception&) + {} + // TODO try stopping dijkstra as soon tgt is out of the queue. - boost::dijkstra_shortest_paths(dual, src.first, - boost::distance_map(distance_map) - .predecessor_map(predecessor_map) - .weight_map(weight_map)); + std::vector initial_path; @@ -2076,6 +2122,8 @@ void locally_shortest_path(const Face_location &src, std::vector< std::array> portals=Impl::unfold_strip(initial_path,src,tgt,vpm,tmesh); std::size_t max_index=0; std::vector lerps=Impl::funnel(portals,max_index); + // TODO: if you comment this if you don't want to shorten the path (option?). + // but this part is really fast so maybe does not make sense. Impl::straighten_path(portals,lerps,initial_path,src,tgt,vpm,tmesh,max_index); CGAL_assertion(lerps.size()==initial_path.size()); From 32704eb29f7f9a7717236da4bc35670c6ed2bbc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 29 Sep 2023 10:51:46 +0200 Subject: [PATCH 023/120] add missing next + TODOs --- .../Polygon_mesh_processing/geodesic_circles_sm_example.cpp | 1 + .../Polygon_mesh_processing/Bsurf/locally_shortest_path.h | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/geodesic_circles_sm_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/geodesic_circles_sm_example.cpp index 540420174513..2f833f31d161 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/geodesic_circles_sm_example.cpp +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/geodesic_circles_sm_example.cpp @@ -64,6 +64,7 @@ int main(int argc, char** argv) double alpha = (r - dt) / (ds - dt); pts.push_back( CGAL::barycenter(mesh.point(src), alpha, mesh.point(tgt), 1-alpha) ); } + h=next(h, mesh); } if (pts.size()==2) out << "2 " << pts[0] << " " << pts[1] << "\n"; diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index b4adb29f2aba..3489ac36093b 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -2176,6 +2176,10 @@ void approximate_geodesic_distance_field(const Face_location& // in particular, it can be tweaked to compute the Voronoi diagram of the initial centers // or geodesic furthest point sampling. // TODO: add a parameter for the link size to increase to precision of the approximation of the distance + // that is you increase the size of the neighborhood of each vertex and you connect in the graph each vertex to its neighbors + // with weight being the geodesic shortest path. + // the more neighbors you have, the better is the approximation + // TODO: graph construction can be done in parallel // TODO: concave flattening should be handled to improve the approximation of the distance // (shortest path is not a line in that case) From ffa46ec8ad943d8593a058da456001d613f5f53f Mon Sep 17 00:00:00 2001 From: ClaudioMancinelli Date: Fri, 29 Sep 2023 11:59:57 +0200 Subject: [PATCH 024/120] straightest geodesic (WIP) --- .../Polygon_mesh_processing/CMakeLists.txt | 7 +- .../geodesic_circles_sm_example.cpp | 1 + .../Bsurf/locally_shortest_path.h | 461 ++++++++++++------ 3 files changed, 321 insertions(+), 148 deletions(-) diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt b/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt index 61afb9accd99..6aab9d704111 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt @@ -51,9 +51,6 @@ create_single_source_cgal_program("match_faces.cpp") create_single_source_cgal_program("cc_compatible_orientations.cpp") create_single_source_cgal_program("hausdorff_distance_remeshing_example.cpp") create_single_source_cgal_program("hausdorff_bounded_error_distance_example.cpp") -create_single_source_cgal_program("locally_shortest_path_sm_example.cpp") -create_single_source_cgal_program("trace_bezier_segment_sm_example.cpp") -create_single_source_cgal_program("geodesic_circles_sm_example.cpp") find_package(Eigen3 3.2.0 QUIET) #(requires 3.2.0 or greater) include(CGAL_Eigen3_support) @@ -76,6 +73,10 @@ if(TARGET CGAL::Eigen3_support) target_link_libraries(delaunay_remeshing_example PUBLIC CGAL::Eigen3_support) create_single_source_cgal_program("remesh_almost_planar_patches.cpp") target_link_libraries(remesh_almost_planar_patches PUBLIC CGAL::Eigen3_support) + create_single_source_cgal_program("locally_shortest_path_sm_example.cpp") + target_link_libraries(locally_shortest_path_sm_example PUBLIC CGAL::Eigen3_support) + create_single_source_cgal_program("trace_bezier_segment_sm_example.cpp") + create_single_source_cgal_program("geodesic_circles_sm_example.cpp") else() message(STATUS "NOTICE: Examples that use Eigen will not be compiled.") endif() diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/geodesic_circles_sm_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/geodesic_circles_sm_example.cpp index 2f833f31d161..413561efc7b3 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/geodesic_circles_sm_example.cpp +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/geodesic_circles_sm_example.cpp @@ -48,6 +48,7 @@ int main(int argc, char** argv) std::ofstream out("circles.polylines.txt"); + for(auto v: vertices(mesh)) std::cout << get(distance_map, v) << "\n"; for (double r : radii) { for (Mesh::Face_index f : faces(mesh)) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index 3489ac36093b..1921ab16dcfd 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -21,6 +21,8 @@ #include #include +//TODO: split to avoid redundant linking +#include #include namespace CGAL { @@ -716,150 +718,281 @@ struct Locally_shortest_path_imp // //:::::::::::::::::::::Straightest Geodesic:::::::::::::::::::::::::::: - // static - // void parallel_transport_through_flattening(Vector_3& v, - // const VertexPointMap &vpm, - // const TriangleMesh &mesh, - // const face_descriptor& from, - // const face_descriptor& to) - // { - // halfedge_descriptor h_ref = halfedge(from, mesh); - // halfedge_descriptor h=h_ref; - // for (int i = 0; i < 3; ++i) { - // if (face(opposite(h_ref, mesh), mesh) == to) - // { - // h=h_ref; - // break; - // } - // h_ref = next(h_ref, mesh); - // } + static + Eigen::Matrix3d rot_matrix (const FT& angle, const Vector_3& axis) + { + Eigen::Matrix3d result; + double c=std::cos(angle); + double s=std::sin(angle); + result < flat_from = init_flat_triangle(h,vpm,mesh); - // std::array flat_to = unfold_face(h,vpm,mesh,flat_from); - // Vector_2 bary = Vector_2{0.333, 0.333}; - // Vector_2 c0 = 0.33*(flat_from[0]+flat_from[1]+flat_from[2]); - // Vector_2 c1 = 0.33*(flat_t0[0]+flat_t0[1]+flat_t0[2]); - // Vector_2 e0 = flat_from[0] - c0; - // Vector_2 e1 = flat_to[0] - c1; - - // Vector_2 w = c1 - c0; - // FT phi_ij = angle(e0, w); - // if (e0.x()*w.y()-e0.y()*w.x() < 0) - // phi_ij = 2 * M_PI - phi_ij; - // w *= -1; - // FT phi_ji = angle(e1, w); - - // if (e1.x()*w.y()-e1.y()*w.x() < 0) - // phi_ji = 2 * M_PI - phi_ji; - - // Vector_3 n_from= face_normal(vpm, mesh, from); - // std::vector e_from = polar_basis(vpm, mesh, from); - // double teta = angle(e_from, v); - // if (cross_product(e_from, v)*n_from < 0) - // teta = 2 * M_PI - teta; - - // std::vector e_to = polar_basis(vpm, mesh, from to); - // Vector_3 n_to = face_normal(vpm, mesh, to); - // double rot = teta + phi_ji + M_PI - phi_ij; - // e_to =e_to*sqrt(v.length_squared()); - // v = rot_vect(e_to, n_to, rot); - // } - // static - // std::vector - // get_3D_basis_at_point(const Face_location& p, - // const TriangleMesh& mesh, - // const VertexPointMap &vpm) - // { - // halfedge_descriptor h=halfedge(p.first,mesh); + return result; + } + static + Vector_3 rotate_vector(const Vector_3& v,const Vector_3& axis, const FT& angle) + { + Eigen::Matrix3d rot=rot_matrix(angle,axis); + Eigen::Vector3d wrap_v; + wrap_v< flat_from = init_flat_triangle(h,vpm,mesh); + std::array flat_to = unfold_face(h,vpm,mesh,flat_from); + Vector_2 bary = Vector_2{0.333, 0.333}; + Vector_2 c0 = 0.33*(flat_from[0]+flat_from[1]+flat_from[2]); + Vector_2 c1 = 0.33*(flat_to[0]+flat_to[1]+flat_to[2]); + Vector_2 e0 = flat_from[0] - c0; + Vector_2 e1 = flat_to[0] - c1; + + Vector_2 w = c1 - c0; + FT phi_ij = angle(e0, w); + if (e0.x()*w.y()-e0.y()*w.x() < 0) + phi_ij = 2 * M_PI - phi_ij; + w *= -1; + FT phi_ji = angle(e1, w); + + if (e1.x()*w.y()-e1.y()*w.x() < 0) + phi_ji = 2 * M_PI - phi_ji; + + Vector_3 n_from= face_normal(vpm, mesh, from); + std::vector e_from = polar_basis(vpm, mesh, from); + double teta = angle(e_from, v); + if (cross_product(e_from, v)*n_from < 0) + teta = 2 * M_PI - teta; + + std::vector e_to = polar_basis(vpm, mesh, from ,to); + Vector_3 n_to = face_normal(vpm, mesh, to); + double rot = teta + phi_ji + M_PI - phi_ij; + e_to =e_to*sqrt(v.length_squared()); + v = rotate_vector(e_to, n_to, rot); - // return {e,e1,n}; - // } + } + static + void parallel_transport_through_flattening(Vector_2& v, + const VertexPointMap &vpm, + const TriangleMesh &mesh, + const face_descriptor& from, + const face_descriptor& to) + { + halfedge_descriptor h_ref = halfedge(from, mesh); + halfedge_descriptor h=h_ref; + for (int i = 0; i < 3; ++i) { + if (face(opposite(h_ref, mesh), mesh) == to) + { + h=h_ref; + break; + } + h_ref = next(h_ref, mesh); + } + std::array flat_from = init_flat_triangle(h,vpm,mesh); + std::array flat_to = unfold_face(h,vpm,mesh,flat_from); + Vector_2 bary = Vector_2{0.333, 0.333}; + Vector_2 c0 = 0.33*(flat_from[0]+flat_from[1]+flat_from[2]); + Vector_2 c1 = 0.33*(flat_to[0]+flat_to[1]+flat_to[2]); + Vector_2 e0 = flat_from[0] - c0; + Vector_2 e1 = flat_to[0] - c1; - // static - // std::tuple - // point_is_edge(const Face_location& p, - // const FT& tol=1e-5) - // { - // auto bary=p.second; - // if (bary[0] > tol && bary[1] > tol && bary[2] <= tol) - // return {true, 0}; - // if (bary[1] > tol && bary[2] > tol && bary[0] <= tol) - // return {true, 1}; - // if (bary[2] > tol && bary[0] > tol && bary[1] <= tol) - // return {true, 2}; - - // return {false, -1}; - // } + Vector_2 w = c1 - c0; + FT phi_ij = angle(e0, w); + if (e0.x()*w.y()-e0.y()*w.x() < 0) + phi_ij = 2 * M_PI - phi_ij; + w *= -1; + FT phi_ji = angle(e1, w); - // static - // //https://rootllama.wordpress.com/2014/06/20/ray-line-segment-intersection-test-in-2d/ - // std::pair - // intersect(const Vector_2 &direction, const Vector_2 &left, - // const Vector_2 &right,const Vector_2& origin) - // { - // auto v1 = origin-left; - // auto v2 = right - left; - // auto v3 = vec2f{-direction.y, direction.x}; - // auto t0 = cross(v2, v1) / dot(v2, v3); - // auto t1 = -dot(left, v3) / dot(v2, v3); - // return std::make_pair(t0, t1); - // }; + if (e1.x()*w.y()-e1.y()*w.x() < 0) + phi_ji = 2 * M_PI - phi_ji; + + + double teta = angle(e0, v); + if (e0.x()*v.y()-e0.y()*v.x() < 0) + teta = 2 * M_PI - teta; + + double rot = teta + phi_ji + M_PI - phi_ij; + e1 =e1/sqrt(e1.length_squared())*sqrt(v.length_squared()); + + Vector_3 v_3d = rotate_vector(Vector_3{e1.x(),e1.y(),0}, Vector_3{0,0,1}, rot); + + v=Vector_2{v_3d.x(),v_3d.y()}; + } + Vector_3 parallel_transport_along_path(const std::vector>& edge_locations, + const VertexPointMap &vpm, + const TriangleMesh& mesh, + const Face_location& src, + const Face_location& tgt, + const Vector_3& v) + { + if(edge_locations.size()==0) + return v; + Vector_3 w=v; + face_descriptor f_start=src.first; + for(std::size_t i=0;i - // segment_in_tri(const Vector_2& p, - // const std::array& tri, - // const Vector_2& dir, - // const int& k) + // std::tuple + // polthier_condition_at_vert(const TriangleMesh& mesh, + // const VertexPointMap &vpm + // const vertex_descriptor& vid, + // const face_descriptor& tid, + // const int kv, + // const Vector_3 &dir) // { - // for (auto i = 0; i < 3; ++i) - // { - // auto [t0, t1] = intersect(dir, tri[(k+i)%3], tri[(k + 1+i) % 3],p); + // FT total_angle=get_total_angle(vid,mesh,vpm); + // FT theta = 0.5 * total_angle; + // halfedge_descriptor h=halfedge(tid,mesh); + // while(target(h,mesh)!=vid) + // h=next(h,mesh); - // //TODO: replace intersection with CGAL code - // if (t0 > 0 && t1 >= -1e-4 && t1 <= 1 + 1e-4) - // { - // auto result_k =(k+i)%3; - // return {result_k, clamp(t1, 0.f, 1.f)}; - // } - // } - // CGAL_assertion(false); - // return {-1,-1}; - // } + // Point_3 vert = get(vpm,vid); + // Point_3 vert_adj=get(vpm,source(h,mesh)); + // Vector_3 v = vert - vert_adj; - // FT get_total_angle(const vertex_descriptor& vid, - // const TriangleMesh& mesh, - // const VertexPointMap &vpm) - // { - // halfedge_descriptor h_ref= halfedge(vid,mesh); - // halfedge_descriptor h_start=h_ref - // halfedge_descriptor h_next = next(h_start,mesh); - - // FT theta=angle(get(vpm,source(h_start,mesh))-get(vpm,target(h_start,mesh)), - // get(vpm,target(h_next,mesh))-get(vpm,target(h_start,mesh))); - // h_start=opposite(h_next,mesh); - // h_next=next(h_start,mesh); - // while(h_start!=h_ref) - // { - // theta+=angle(get(vpm,source(h_start,mesh))-get(vpm,target(h_start,mesh)), - // get(vpm,target(h_next,mesh))-get(vpm,target(h_start,mesh))); - // h_start=opposite(h_next,mesh); - // h_next=next(h_start,mesh); + // FT acc = angle(v, dir); + // FT prev_angle = acc; + // face_descriptor curr_tid = tid; + + // while (acc < theta) { + // h=prev(opposite(h.mesh),mesh); + // prev_angle = acc; + // Point_3 next_vert_adj=get(vpm,source(h,mesh)); + // acc += angle(vert_adj - vert,next_vert_adj - vert); + // vert_adj=next_vert_adj; // } + // auto offset = theta - prev; + // Point_3 prev_vert_adj=get(vpm,target(next(h,mesh,mesh))); - // return theta; + // Vector_3 result=Vector_3{prev_vert_adj.x()-vert.x(),prev_vert_adj.y()-vert.y(),prev_vert_adj.z()-vert.z()}; + // face_descriptor f=face(h,mesh); + // Vector_3 n=face_normal(vpm,mesh,f); + // result=rotate_vector(result,n,offset); + // return {result,f}; // } + + static + std::vector + get_3D_basis_at_point(const Face_location& p, + const TriangleMesh& mesh, + const VertexPointMap &vpm) + { + halfedge_descriptor h=halfedge(p.first,mesh); + + Point_3 p0=get(vpm, source(h, mesh)); + Point_3 p1=get(vpm, target(h, mesh)); + Point_3 p2= get(vpm,target(next(h, mesh), mesh)); + + Vector_3 e=p1-p0; + e/=sqrt(e.squared_length()); + Vector_3 n= triangle_normal(p0,p1,p2); + Vector_3 e1= cross_product(n,e); + + return {e,e1,n}; + } + + + static + std::tuple + point_is_edge(const Face_location& p, + const FT& tol=1e-5) + { + auto bary=p.second; + if (bary[0] > tol && bary[1] > tol && bary[2] <= tol) + return {true, 0}; + if (bary[1] > tol && bary[2] > tol && bary[0] <= tol) + return {true, 1}; + if (bary[2] > tol && bary[0] > tol && bary[1] <= tol) + return {true, 2}; + + return {false, -1}; + } + + static + //https://rootllama.wordpress.com/2014/06/20/ray-line-segment-intersection-test-in-2d/ + std::pair + intersect(const Vector_2 &direction, const Vector_2 &left, + const Vector_2 &right,const Vector_2& origin) + { + Vector_2 v1 = origin-left; + Vector_2 v2 = right - left; + Vector_2 v3 = Vector_2{-direction.y, direction.x}; + double t0 = (v2.x()*v1.y()-v2.y()*v1.x())/ v2*v3; + double t1 = -left*v3/ v2*v3; + return std::make_pair(t0, t1); + }; + + static + std::tuple + segment_in_tri(const Vector_2& p, + const std::array& tri, + const Vector_2& dir, + const int& k) + { + for (auto i = 0; i < 3; ++i) + { + auto [t0, t1] = intersect(dir, tri[(k+i)%3], tri[(k + 1+i) % 3],p); + + //TODO: replace intersection with CGAL code + if (t0 > 0 && t1 >= -1e-4 && t1 <= 1 + 1e-4) + { + auto result_k =(k+i)%3; + return {result_k, clamp(t1, 0.f, 1.f)}; + } + } + CGAL_assertion(false); + return {-1,-1}; + } + + FT get_total_angle(const vertex_descriptor& vid, + const TriangleMesh& mesh, + const VertexPointMap &vpm) + { + halfedge_descriptor h_ref= halfedge(vid,mesh); + halfedge_descriptor h_start=h_ref; + halfedge_descriptor h_next = next(h_start,mesh); + + FT theta=angle(get(vpm,source(h_start,mesh))-get(vpm,target(h_start,mesh)), + get(vpm,target(h_next,mesh))-get(vpm,target(h_start,mesh))); + h_start=opposite(h_next,mesh); + h_next=next(h_start,mesh); + while(h_start!=h_ref) + { + theta+=angle(get(vpm,source(h_start,mesh))-get(vpm,target(h_start,mesh)), + get(vpm,target(h_next,mesh))-get(vpm,target(h_start,mesh))); + h_start=opposite(h_next,mesh); + h_next=next(h_start,mesh); + } + + return theta; + } + // static // std::tuple, halfedge_descriptor> // polthier_condition_at_vert(const TriangleMesh& mesh, @@ -954,6 +1087,48 @@ struct Locally_shortest_path_imp // }; + // auto get_halfedge=[&mesh](const int k,const face_descriptor& tid) + // { + // halfedge_descriptor h=halfedge(tid,mesh); + // if(k==0) + // return h; + // if(k==1) + // return next(h,mesh); + // if(k==2) + // return next(next(h,mesh),mesh); + + // }; + + // auto edge_barycentric_coordinate = + // [&mesh, h_face](halfedge_descriptor h_edge, + // const std::array& bary_edge) + // { + // std::array bary_edge_in_face; + // if (h_face!=h_edge) + // { + // if (h_face==next(h_edge, mesh)) + // { + // bary_edge_in_face[0]=bary_edge[1]; + // bary_edge_in_face[1]=0; + // bary_edge_in_face[2]=bary_edge[0]; + // } + // else + // { + // bary_edge_in_face[0]=0; + // bary_edge_in_face[1]=bary_edge[0]; + // bary_edge_in_face[2]=bary_edge[1]; + // } + // } + // else + // { + // bary_edge_in_face[0]=bary_edge[0]; + // bary_edge_in_face[1]=bary_edge[1]; + // bary_edge_in_face[2]=0; + // } + + // return bary_edge_in_face; + // }; + // std::vector> result; // FT accumulated=0.; // face_descriptor curr_tid=p.first; @@ -993,7 +1168,7 @@ struct Locally_shortest_path_imp // if (is_vert) // { - // auto vid = get_vertex(kv,curr_tid); + // uint vid = get_vertex(kv,curr_tid); // accumulated += // sqrt(squared_distance(construct_point(curr_p,mesh) - get(vpm,vid))); @@ -1001,17 +1176,13 @@ struct Locally_shortest_path_imp // std::tie(curr_dir, curr_p, k_start) = // polthier_condition_at_vert(triangles, positions, adjacencies, // total_angles, vid, curr_tid, dir3d); - // curr_tid = curr_p.face; - // if (curr_tid == -1) - // return result; + // curr_tid = curr_p.first; // } // else // { - // auto adj = adjacencies[curr_tid][k]; - // if (adj == -1) - // return result; - // auto h = find(adjacencies[adj], curr_tid); + + // face_descriptor adj = face(get_halfedge(curr_tid,k),mesh); // new_bary = zero3f; // new_bary[h] = t1; @@ -1373,14 +1544,14 @@ struct Geodesic_circle_impl struct geodesic_solver { struct graph_edge { int node = -1; - double length=DBL_MAX; + double len=DBL_MAX; }; std::vector> graph; }; struct dual_geodesic_solver { struct edge { int node = -1; - double length = DBL_MAX; + double len = DBL_MAX; }; std::vector> graph = {}; }; @@ -1467,6 +1638,7 @@ struct Geodesic_circle_impl vertex_descriptor v0 =target(next(h,mesh),mesh); vertex_descriptor v1= target(next(opposite(h,mesh),mesh),mesh); auto len = opposite_nodes_arc_length(vpm, v0,v1,a,b); + //std::cout<0); connect_nodes(solver,a,b,vidmap,len); } face_descriptor nei=face(opposite(h,mesh),mesh); @@ -1545,7 +1716,7 @@ struct Geodesic_circle_impl int entry=get(tidmap,f); for (auto i = 0; i < 3; ++i) { solver.graph[entry][i].node = get(tidmap,face(opposite(h,mesh),mesh)); - solver.graph[entry][i].length = compute_dual_weights(h); + solver.graph[entry][i].len = compute_dual_weights(h); h=next(h,mesh); } } @@ -1617,7 +1788,7 @@ struct Geodesic_circle_impl for (auto i = 0; i < (int)solver.graph[node].size(); i++) { // Distance of neighbor through this node - double new_distance = field[node] + solver.graph[node][i].length; + double new_distance = field[node] + solver.graph[node][i].len; auto neighbor = solver.graph[node][i].node; @@ -1714,7 +1885,7 @@ struct Geodesic_circle_impl for (auto i = 0; i < (int)solver.graph[node].size(); i++) { // Distance of neighbor through this node - auto new_distance = field[node] + solver.graph[node][i].length; + auto new_distance = field[node] + solver.graph[node][i].len; auto neighbor = solver.graph[node][i].node; auto old_distance = field[neighbor]; @@ -1798,7 +1969,7 @@ struct Geodesic_circle_impl distances[sources_id[i]] = sources_and_dist[i].second; } - visit_graph(distances, solver, sources_id, update, stop, exit); + visit_geodesic_graph(distances, solver, sources_id, update, stop, exit); return distances; } From d79a0bd5841c5edc298901858b3d8fdf73bf6a2a Mon Sep 17 00:00:00 2001 From: ClaudioMancinelli Date: Fri, 29 Sep 2023 15:11:23 +0200 Subject: [PATCH 025/120] straightest geodesic (compile) --- .../Bsurf/locally_shortest_path.h | 434 ++++++++++-------- 1 file changed, 237 insertions(+), 197 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index 1921ab16dcfd..1302262cbf48 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -718,6 +718,41 @@ struct Locally_shortest_path_imp // //:::::::::::::::::::::Straightest Geodesic:::::::::::::::::::::::::::: + static + std::tuple> point_in_triangle(const VertexPointMap &vpm, + const TriangleMesh &mesh, + const face_descriptor& face, + const Point_3 point, + float tol=1e-5) { + // http://www.r-5.org/files/books/computers/algo-list/realtime-3d/Christer_Ericson-Real-Time_Collision_Detection-EN.pdf + // pag.48 + std::array b = make_array(0.,0.,0.); + halfedge_descriptor h=halfedge(face,mesh); + Point_3 v0 = get(vpm,source(h,mesh)); + Point_3 v1 = get(vpm,target(h,mesh)); + Point_3 v2 = get(vpm,target(next(h,mesh),mesh)); + + Vector_3 u = v1 - v0, v = v2 - v0, w = point - v0; + double d00 = u.length_squared(), d01 = u*v, d11 = v.length_squared(), d20 = w*u, + d21 = w*v, d = d00 * d11 - d01 * d01; + + if (d == 0) + return {false, make_array(0.,0.,0.)}; + + b[2] = (d00 * d21 - d01 * d20) / d; + assert(!isnan(b[2])); + b[1] = (d11 * d20 - d01 * d21) / d; + assert(!isnan(b[1])); + b[0] = 1 - b[1] - b[2]; + assert(!isnan(b[0])); + + for (auto i = 0; i < 3; ++i) { + if (b[i] < -tol || b[i] > 1.0 + tol) + return {false, make_array(0.,0.,0.)}; + } + + return {true, b}; +} static Eigen::Matrix3d rot_matrix (const FT& angle, const Vector_3& axis) { @@ -952,24 +987,28 @@ struct Locally_shortest_path_imp std::tuple segment_in_tri(const Vector_2& p, const std::array& tri, - const Vector_2& dir, - const int& k) + const Vector_2& dir,const halfedge_descriptor& h_curr,const int offset) { + //rotated the triangle in order to test intersection at meaningful edges before + std::array rotated_tri=tri; + for(size_t k=0;k 0 && t1 >= -1e-4 && t1 <= 1 + 1e-4) { - auto result_k =(k+i)%3; - return {result_k, clamp(t1, 0.f, 1.f)}; + return {((i+1)%3 + offset)%3, clamp(t1, 0.f, 1.f)}; //return the offset w.r.t h_ref } } CGAL_assertion(false); return {-1,-1}; } - + static FT get_total_angle(const vertex_descriptor& vid, const TriangleMesh& mesh, const VertexPointMap &vpm) @@ -993,232 +1032,233 @@ struct Locally_shortest_path_imp return theta; } - // static - // std::tuple, halfedge_descriptor> - // polthier_condition_at_vert(const TriangleMesh& mesh, - // const VertexPointMap &vpm - // const vertex_descriptor& vid, - // const face_descriptor& tid, - // const int kv, - // const Vector_3 &dir) - // { - // FT total_angle=get_total_angle(vid,mesh,vpm); - // FT theta = 0.5 * total_angle; - // halfedge_descriptor h=halfedge(tid,mesh); - // while(target(h,mesh)!=vid) - // h=next(h,mesh); + static + std::tuple + polthier_condition_at_vert(const TriangleMesh& mesh, + const VertexPointMap &vpm, + const vertex_descriptor& vid, + const face_descriptor& tid, + const int kv, + const Vector_2 &dir) + { + FT total_angle=get_total_angle(vid,mesh,vpm); + FT theta = 0.5 * total_angle; + halfedge_descriptor h=halfedge(tid,mesh); + + while(target(h,mesh)!=vid) + h=next(h,mesh); + + Point_3 vert = get(vpm,vid); + Point_3 vert_adj=get(vpm,source(h,mesh)); + Vector_3 v = vert - vert_adj; + + FT acc = angle(v, dir); + FT prev_angle = acc; + face_descriptor curr_tid = tid; + + while (acc < theta) { + h=prev(opposite(h,mesh),mesh); + prev_angle = acc; + Point_3 next_vert_adj=get(vpm,source(h,mesh)); + acc += angle(vert_adj - vert,next_vert_adj - vert); + vert_adj=next_vert_adj; + } + auto offset = theta - prev; + Point_3 prev_vert_adj=get(vpm,target(next(h,mesh,mesh))); - // Point_3 vert = get(vpm,vid); - // Point_3 vert_adj=get(vpm,source(h,mesh)); - // Vector_3 v = vert - vert_adj; + FT l = sqrt(squared_distance(prev_vert_adj,vert)); + FT phi = angle(vert - prev_vert_adj, vert_adj - prev_vert_adj); + FT x = l * std::sin(offset) / std::sin(M_PI - phi - offset); + FT alpha = x / sqrt(squared_distance(vert_adj - prev_vert_adj)); + halfedge_descriptor prev_h=prev(h,mesh); + std::array flat_tid = init_flat_triangle(prev_h,vpm,mesh); - // FT acc = angle(v, dir); - // FT prev_angle = acc; - // face_descriptor curr_tid = tid; + Vector_2 q = (1 - alpha) * flat_tid[0] + alpha * flat_tid[1]; - // while (acc < theta) { - // h=prev(opposite(h.mesh),mesh); - // prev_angle = acc; - // Point_3 next_vert_adj=get(vpm,source(h,mesh)); - // acc += angle(vert_adj - vert,next_vert_adj - vert); - // vert_adj=next_vert_adj; - // } - // auto offset = theta - prev; - // Point_3 prev_vert_adj=get(vpm,target(next(h,mesh,mesh))); + Vector_2 new_dir = q - flat_tid[2]; - // FT l = sqrt(squared_distance(prev_vert_adj,vert)); - // FT phi = angle(vert - prev_vert_adj, vert_adj - prev_vert_adj); - // FT x = l * std::sin(offset) / std::sin(M_PI - phi - offset); - // FT alpha = x / sqrt(squared_distance(vert_adj - prev_vert_adj)); - // halfedge_descriptor prev_h=prev(h,mesh); - // std::array flat_tid = init_flat_triangle(prev_h,vpm,mesh); + return {new_dir,face(prev_h,mesh), h}; + } - // Vector_2 q = (1 - alpha) * flat_tid[0] + alpha * flat_tid[1]; + static + std::vector> + straightest_goedesic(const Face_location& p, + const TriangleMesh& mesh, + const VertexPointMap &vpm, + const Vector_2& dir,const FT& len) + { + auto get_halfedge_offset=[&mesh](const halfedge_descriptor& h_ref,const halfedge_descriptor& h_curr) + { + if(source(h_ref,mesh)==source(h_curr,mesh)) + return 0; - // Vector_2 new_dir = q - flat_tid[2]; + if(source(next(h_ref,mesh),mesh)==source(h_curr,mesh)) + return 1; - // Face_location new_p; - // new_p.first=face(h,mesh); - // halfedge_descriptor h_face=halfedge(new_p.first,mesh); - // Vector_3 bary; - // Vector_2 bary_edge=Vector_2{1-alpha,alpha}; + if(source(prev(h_ref,mesh),mesh)==source(h_curr,mesh)) + return 2; - // if (h_face!=prev_h) - // { - // if (h_face==next(prev_h, mesh)) - // { - // new_p.second[0]=bary_edge[1]; - // new_p.second[1]=0; - // new_p.second[2]=bary_edge[0]; - // } - // else - // { - // new_p.second[0]=0; - // new_p.second[1]=bary_edge[0]; - // new_p.second[2]=bary_edge[1]; - // } - // } - // else - // { - // new_p.second[0]=bary_edge[0]; - // new_p.second[1]=bary_edge[1]; - // new_p.second[2]=0; - // } + std::cout<<"Error! Halfedges are in different faces"<> - // straightest_goedesic(const Face_location& p, - // const TriangleMesh& mesh, - // const VertexPointMap &vpm, - // const Vector_2& dir,const FT& len) - // { - // auto get_vid=[&mesh](const int k,const face_descriptor& tid) - // { - // halfedge_descriptor h=halfedge(tid,mesh); - // if(k==0) - // return source(h,mesh); - // if(k==1) - // return target(h,mesh); - // if(k==2) - // return target(next(h,mesh),mesh); + auto get_vid_offset=[&mesh](const halfedge_descriptor& h_ref,const vertex_descriptor& vid) + { + if(source(h_ref,mesh)==vid) + return 0; - // }; + if(target(h_ref,mesh)==vid) + return 1; - // auto get_halfedge=[&mesh](const int k,const face_descriptor& tid) - // { - // halfedge_descriptor h=halfedge(tid,mesh); - // if(k==0) - // return h; - // if(k==1) - // return next(h,mesh); - // if(k==2) - // return next(next(h,mesh),mesh); - - // }; - - // auto edge_barycentric_coordinate = - // [&mesh, h_face](halfedge_descriptor h_edge, - // const std::array& bary_edge) - // { - // std::array bary_edge_in_face; - // if (h_face!=h_edge) - // { - // if (h_face==next(h_edge, mesh)) - // { - // bary_edge_in_face[0]=bary_edge[1]; - // bary_edge_in_face[1]=0; - // bary_edge_in_face[2]=bary_edge[0]; - // } - // else - // { - // bary_edge_in_face[0]=0; - // bary_edge_in_face[1]=bary_edge[0]; - // bary_edge_in_face[2]=bary_edge[1]; - // } - // } - // else - // { - // bary_edge_in_face[0]=bary_edge[0]; - // bary_edge_in_face[1]=bary_edge[1]; - // bary_edge_in_face[2]=0; - // } + if(target(next(h_ref,mesh),mesh)==vid) + return 2; - // return bary_edge_in_face; - // }; + std::cout<<"Error! Halfedges are in different faces"<> result; - // FT accumulated=0.; - // face_descriptor curr_tid=p.first; - // std::array curr_flat_tid=init_flat_triangle(halfedge(curr_tid,mesh),vpm,mesh); - // Vector_2 flat_p= p.second[0]*curr_flat_tid[0]+p.second[1]*curr_flat_tid[1]+p.second[2]*curr_flat_tid[2]; - // Face_location curr_p=p; - // Face_location prev_p; - // Vector_2 curr_dir=dir; + CGAL_assertion(false); - // result.push_back(p); + }; + auto get_halfedge=[&mesh](const int k,const halfedge_descriptor& h_ref) + { + switch(k) + { + case 0: + return h_ref; - // int k_start=-1; + case 1: + return next(h_ref,mesh); - // auto [is_vert, kv] = point_is_vert(p); - // auto [is_edge, ke] = point_is_edge(p); - // if (is_vert) - // k_start = kv; - // else if (is_edge) - // k_start = ke; + default: + return prev(h_ref,mesh); + } + }; + auto edge_barycentric_coordinate = + [&mesh](const halfedge_descriptor h_edge, + const halfedge_descriptor h_face, + const std::array& bary_edge) + { + std::array bary_edge_in_face=make_array(0,0,0); + if (h_face!=h_edge) + { + if (h_face==next(h_edge, mesh)) + { + bary_edge_in_face[0]=bary_edge[1]; + bary_edge_in_face[1]=0; + bary_edge_in_face[2]=bary_edge[0]; + } + else + { + bary_edge_in_face[0]=0; + bary_edge_in_face[1]=bary_edge[0]; + bary_edge_in_face[2]=bary_edge[1]; + } + } + else + { + bary_edge_in_face[0]=bary_edge[0]; + bary_edge_in_face[1]=bary_edge[1]; + bary_edge_in_face[2]=0; + } + return bary_edge_in_face; + }; - // while (accumulated < len) - // { - // auto [k, t1] = segment_in_tri(flat_p, curr_flat_tid, curr_dir,k_start); - // CGAL_assertion(k!=-1); - // Vector_3 new_bary=Vector_3{0,0,0}; - // Face_location point_on_edge; - // //TODO: we assume k is always different from -1, check this! + std::vector> result; + FT accumulated=0.; + face_descriptor curr_tid=p.first; + std::array curr_flat_tid=init_flat_triangle(halfedge(curr_tid,mesh),vpm,mesh); + Vector_2 flat_p= p.second[0]*curr_flat_tid[0]+p.second[1]*curr_flat_tid[1]+p.second[2]*curr_flat_tid[2]; + Face_location curr_p=p; + Face_location prev_p; + Vector_2 curr_dir=dir; + halfedge_descriptor h_ref=halfedge(curr_tid,mesh); + halfedge_descriptor h_curr=h_ref; - // new_bary[k] = 1 - t1; - // new_bary[(k + 1) % 3] = t1; - // point_on_edge.first=curr_tid; - // point_on_edge.second=new_bary; + result.push_back(p); - // std::tie(is_vert, kv) = point_is_vert(point_on_edge); + auto [is_vert, kv] = point_is_vert(p); + auto [is_edge, ke] = point_is_edge(p); + if (is_vert) + h_curr=get_halfedge(kv,h_ref); + else if (is_edge) + h_curr=get_halfedge(ke,h_ref); - // if (is_vert) - // { - // uint vid = get_vertex(kv,curr_tid); - // accumulated += - // sqrt(squared_distance(construct_point(curr_p,mesh) - get(vpm,vid))); - // prev_p = curr_p; - // std::tie(curr_dir, curr_p, k_start) = - // polthier_condition_at_vert(triangles, positions, adjacencies, - // total_angles, vid, curr_tid, dir3d); - // curr_tid = curr_p.first; - // } - // else - // { + while (accumulated < len) + { + int curr_offset=get_halfedge_offset(h_ref,h_curr); - // face_descriptor adj = face(get_halfedge(curr_tid,k),mesh); + auto [k, t1] = segment_in_tri(flat_p, curr_flat_tid, curr_dir,h_curr,curr_offset); - // new_bary = zero3f; - // new_bary[h] = t1; - // new_bary[(h + 1) % 3] = 1 - t1; + CGAL_assertion(k!=-1); + std::array new_bary=make_array(0.,0.,0.); + Face_location point_on_edge; - // prev_p = curr_p; - // curr_p = mesh_point{adj, vec2f{new_bary.y, new_bary.z}}; - // accumulated += length(eval_position(triangles, positions, curr_p) - - // eval_position(triangles, positions, prev_p)); + new_bary[k] = 1 - t1; + new_bary[(k + 1) % 3] = t1; + point_on_edge.first=curr_tid; + point_on_edge.second=new_bary; + std::tie(is_vert, kv) = point_is_vert(point_on_edge); - // auto T = switch_reference_frame(triangles, positions, adj, curr_tid); - // curr_dir = switch_reference_frame_vector(T, curr_dir); + if (is_vert) + { + uint vid = get_vertex(kv,curr_tid); - // curr_tid = adj; - // k_start = h; - // } + accumulated += + sqrt(squared_distance(construct_point(curr_p,mesh) - get(vpm,vid))); - // result.push_back(curr_p); - // } + std::tie(curr_dir, curr_tid, h_curr) = + polthier_condition_at_vert(mesh,vpm,vid,curr_tid,kv,curr_dir); - // auto excess = accumulated - len; - // auto prev_pos = eval_position(triangles, positions, result.rbegin()[1]); - // auto last_pos = eval_position(triangles, positions, result.back()); - // auto alpha = excess / length(last_pos - prev_pos); - // auto pos = alpha * prev_pos + (1 - alpha) * last_pos; + h_ref=halfedge(curr_tid,mesh); + curr_flat_tid=init_flat_triangle(h_ref,vpm,mesh); + prev_p = curr_p; + curr_p=point_on_edge; + int k=get_vid_offset(h_ref,target(h_curr,mesh)); + flat_p=curr_flat_tid[k]; + } + else + { + h_curr=opposite(get_halfedge(curr_tid,k),mesh); + face_descriptor adj = face(h_curr,mesh); + std::array curr_alpha=make_array(t1,1-t1); //reversed because will switch face + new_bary=edge_barycentric_coordinate(h_curr,halfedge(adj,mesh),curr_alpha); + prev_p = curr_p; + curr_p.first=adj; + curr_p.second= new_bary; + accumulated += sqrt(squared_distance(construct_point(curr_p,mesh) - construct_point(prev_p,mesh))); + curr_dir=parallel_transport_through_flattening(curr_dir,vpm,mesh,curr_tid,adj); + + curr_tid = adj; + h_ref=halfedge(curr_tid,mesh); + curr_flat_tid=init_flat_triangle(h_ref,vpm,mesh); + flat_p= curr_p.second[0]*curr_flat_tid[0]+curr_p.second[1]*curr_flat_tid[1]+curr_p.second[2]*curr_flat_tid[2]; - // auto [inside, bary] = - // point_in_triangle(triangles, positions, prev_p.face, pos); - // if (!inside) - // std::cout << "error!This point should be in the triangle" << std::endl; + } - // result.pop_back(); - // result.push_back(mesh_point{prev_p.face, bary}); + result.push_back(curr_p); + } - // return result; - // } + double excess = accumulated - len; + Vector_3 prev_pos = construct_point(result.rbegin()[1],mesh); + Vector_3 last_pos = construct_point(result.back(),mesh); + double alpha = excess / sqrt((last_pos - prev_pos).length_squared()); + Point_3 pos = alpha * prev_pos + (1 - alpha) * last_pos; + + auto [inside, bary] = + point_in_triangle(vpm,mesh,prev_p.first,pos); + if (!inside) + std::cout << "error!This point should be in the triangle" << std::endl; + + result.pop_back(); + prev_p.second=bary; + result.push_back(prev_p); + + return result; + } // static // std::vector From 76e0f7a001410a9191ee927c420e512085b625f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 29 Sep 2023 12:09:59 +0200 Subject: [PATCH 026/120] raw import of file version from CB:AUS:marking_sample@a80cd964d0 --- .../Polygon_mesh_processing/CMakeLists.txt | 1 + .../Bsurf/locally_shortest_path.h | 4 + .../walk_in_polygon_mesh.h | 565 +++++++++ .../Polygon_mesh_processing/walk_to_select.h | 1033 +++++++++++++++++ .../include/CGAL/Polyhedron_simplex_type.h | 33 + 5 files changed, 1636 insertions(+) create mode 100644 Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/walk_in_polygon_mesh.h create mode 100644 Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/walk_to_select.h create mode 100644 Polygon_mesh_processing/include/CGAL/Polyhedron_simplex_type.h diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt b/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt index 6aab9d704111..9fc1422d0515 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt @@ -77,6 +77,7 @@ if(TARGET CGAL::Eigen3_support) target_link_libraries(locally_shortest_path_sm_example PUBLIC CGAL::Eigen3_support) create_single_source_cgal_program("trace_bezier_segment_sm_example.cpp") create_single_source_cgal_program("geodesic_circles_sm_example.cpp") + create_single_source_cgal_program("straightest_geodesic_sm_example.cpp") else() message(STATUS "NOTICE: Examples that use Eigen will not be compiled.") endif() diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index 1302262cbf48..03171c598936 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -2332,6 +2332,10 @@ void locally_shortest_path(const Face_location &src, std::vector< std::array> portals=Impl::unfold_strip(initial_path,src,tgt,vpm,tmesh); std::size_t max_index=0; + + //TODO: if src and/or target are vertices are edges, the selected face should matter to avoid cycling + // around them. ==> after dijkstra, clean the strip if the halfedge is incident to the source/target + std::vector lerps=Impl::funnel(portals,max_index); // TODO: if you comment this if you don't want to shorten the path (option?). // but this part is really fast so maybe does not make sense. diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/walk_in_polygon_mesh.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/walk_in_polygon_mesh.h new file mode 100644 index 000000000000..179577dc1d01 --- /dev/null +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/walk_in_polygon_mesh.h @@ -0,0 +1,565 @@ +// Copyright (c) 2014 GeometryFactory (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// You can redistribute it and/or modify it under the terms of the GNU +// General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. +// +// Licensees holding a valid commercial license may use this file in +// accordance with the commercial license agreement provided with the software. +// +// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: GPL-3.0+ +// +// Author(s) : Sébastien Loriot + +#ifndef CGAL_WALK_IN_POLYGON_MESH_H +#define CGAL_WALK_IN_POLYGON_MESH_H + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef CGAL_DEBUG_WALK_IN_POLYGON_MESH +#include +#define CGAL_WALKER_TRACE(X) std::cout << X; +#else +#define CGAL_WALKER_TRACE(X) +#endif + +namespace CGAL{ + +struct Walk_is_cycling_exception : std::logic_error +{ + Walk_is_cycling_exception() + : std::logic_error("Loop detected during the walk") + {} +}; + +namespace walker_impl{ + + /// \todo I think this was used in the clipping-snapping branch + struct Walk_on_polyhedron_border_crossing_exception : std::logic_error + { + Walk_on_polyhedron_border_crossing_exception() + : std::logic_error("Trying to walking outside of the polyhedron") + {} + }; + + /// get the set of faces incident to a given simplex + template + void get_incident_faces(Polyhedron_simplex_type simplex_type, + typename boost::graph_traits::halfedge_descriptor h, + std::set::face_descriptor>& incident_faces, + const PM& pm) + { + switch(simplex_type) + { + case POLYHEDRON_FACET: + incident_faces.insert(face(h, pm)); + break; + case POLYHEDRON_EDGE: + if (!is_border(h, pm)) incident_faces.insert(face(h, pm)); + if (!is_border(opposite(h, pm), pm)) incident_faces.insert(face(opposite(h, pm), pm)); + break; + case POLYHEDRON_VERTEX: + { + BOOST_FOREACH(typename boost::graph_traits::halfedge_descriptor hd, + halfedges_around_target(h, pm)) + { + if (!is_border(hd, pm)) + incident_faces.insert(face(hd, pm)); + } + } + break; + default: + break; + } + } + + template + typename boost::graph_traits::halfedge_descriptor + canonical_hedge(typename boost::graph_traits::halfedge_descriptor h, + const PM& pm) + { + return h + typename boost::graph_traits::halfedge_descriptor + common_edge( typename boost::graph_traits::face_descriptor f1, + typename boost::graph_traits::face_descriptor f2, + const PM& pm) + { + CGAL_assertion(f1!=boost::graph_traits::null_face()); + CGAL_assertion(f2!=boost::graph_traits::null_face()); + BOOST_FOREACH(typename boost::graph_traits::halfedge_descriptor h, + halfedges_around_face(halfedge(f1, pm), pm)) + { + if ( face(opposite(h, pm), pm)==f2 ) + return h; + } + return boost::graph_traits::null_halfedge(); + } +} // end of internal namespace + +/// \todo document me +/// We walk from (src, source_type) to (tgt, target_type) intersecting +/// all edges encountered. The predicate inside_edge_pred is responsible to indicate the correct +/// direction to move. +/// \param source_type is different from POLYHEDRON_NONE +template< class PolygonMesh, + class EdgeIntersectionPredicate, + class EdgeIntersectionVisitor > +bool walk_in_polygon_mesh(const PolygonMesh& pm, + typename boost::graph_traits::halfedge_descriptor src, + Polyhedron_simplex_type source_type, + typename boost::graph_traits::halfedge_descriptor tgt, + Polyhedron_simplex_type target_type, + const EdgeIntersectionPredicate& inside_edge_pred, + EdgeIntersectionVisitor& visitor) +{ + typedef boost::graph_traits graph_traits; + typedef typename graph_traits::vertex_descriptor vertex_descriptor; + typedef typename graph_traits::halfedge_descriptor halfedge_descriptor; + typedef typename graph_traits::face_descriptor face_descriptor; + + using namespace walker_impl; + + /// info returned by the predicate when an edge of the input triangle mesh + /// is intersected in its interior + typedef typename EdgeIntersectionPredicate::Barycentric_NT Barycentric_NT; + +/// First check that the projected vertices do not fall in a common simplex + halfedge_descriptor common_face = graph_traits::null_halfedge(); + bool new_edge_to_add = true; + switch( source_type ) + { + case POLYHEDRON_FACET: + CGAL_WALKER_TRACE("\n1-POLYHEDRON_FACET\n") + switch(target_type) + { + case POLYHEDRON_FACET: + CGAL_WALKER_TRACE("2-POLYHEDRON_FACET\n") + if (face(src, pm) == face(tgt, pm)) + common_face=src; + break; + case POLYHEDRON_EDGE: + CGAL_WALKER_TRACE("2-POLYHEDRON_EDGE\n") + if ( face(src, pm)==face(tgt, pm) || + face(src, pm)==face(opposite(tgt, pm), pm) ) + common_face=src; + break; + case POLYHEDRON_VERTEX: + { + CGAL_WALKER_TRACE("2-POLYHEDRON_VERTEX\n") + BOOST_FOREACH(halfedge_descriptor hd, halfedges_around_target(tgt, pm)) + if (face(hd, pm)==face(src, pm)) + { + common_face=src; + break; + } + } + break; + case POLYHEDRON_NONE: + CGAL_WALKER_TRACE("2-POLYHEDRON_NONE\n") + break; + } + break; + case POLYHEDRON_EDGE: + CGAL_WALKER_TRACE("\n1-POLYHEDRON_EDGE\n") + switch(target_type) + { + case POLYHEDRON_FACET: + CGAL_WALKER_TRACE("2-POLYHEDRON_FACET\n") + if ( face(tgt, pm)==face(src, pm) || + face(tgt, pm)==face(opposite(src, pm), pm) ) + common_face=tgt; + break; + case POLYHEDRON_EDGE: + CGAL_WALKER_TRACE("2-POLYHEDRON_EDGE\n") + if (canonical_hedge(src, pm)==canonical_hedge(tgt, pm)) + { + /// we explicitly do not update common_face + /// so that the visitor knows src and tgt are completly + /// inside an edge + new_edge_to_add=false; + } + else + { + if ( !is_border(src, pm) && + ( face(src, pm) == face(tgt, pm) || + face(src, pm) == face(opposite(tgt, pm), pm) ) ) + common_face=src; + else{ + if (!is_border(opposite(src, pm), pm)){ + if ( face(tgt, pm) == face(opposite(src, pm), pm) ) + common_face=tgt; + else + if ( face(opposite(tgt, pm), pm) == face(opposite(src, pm), pm) ) + common_face=opposite(tgt, pm); + } + } + } + break; + case POLYHEDRON_VERTEX: + CGAL_WALKER_TRACE("2-POLYHEDRON_VERTEX\n") + if ( target(src, pm)==target(tgt, pm) || + source(src, pm)==target(tgt, pm) ) + { + common_face = target(src, pm)==target(tgt, pm)?src:opposite(src, pm); + new_edge_to_add=false; + } + else + { + if (!is_border(src, pm) && + target(next(src, pm), pm) == target(tgt, pm)) + common_face=src; + else + if (!is_border(opposite(src, pm), pm) && + target(next(opposite(src, pm), pm), pm) == target(tgt, pm)) + common_face=opposite(src, pm); + } + break; + case POLYHEDRON_NONE: + CGAL_WALKER_TRACE("2-POLYHEDRON_NONE\n") + break; + } + break; + case POLYHEDRON_VERTEX: + CGAL_WALKER_TRACE("\n1-POLYHEDRON_VERTEX\n") + switch(target_type) + { + case POLYHEDRON_FACET: + { + CGAL_WALKER_TRACE("2-POLYHEDRON_FACET\n") + BOOST_FOREACH(halfedge_descriptor hd, halfedges_around_target(src, pm)) + if (face(hd, pm)==face(tgt, pm)) + { + common_face=tgt; + break; + } + } + break; + case POLYHEDRON_EDGE: + CGAL_WALKER_TRACE("2-POLYHEDRON_EDGE\n") + if ( target(tgt, pm)==target(src, pm) || + source(tgt, pm)==target(src, pm) ) + { + common_face=target(tgt, pm)==target(src, pm)? + opposite(tgt, pm):tgt; + new_edge_to_add=false; + } + else + { + if (!is_border(tgt, pm) && + target(next(tgt, pm), pm) == target(src, pm)) + common_face=tgt; + else + if (!is_border(opposite(tgt, pm), pm) && + target(next(opposite(tgt, pm), pm), pm) == target(src, pm)) + common_face=opposite(tgt, pm); + } + break; + case POLYHEDRON_VERTEX: + { + /// @TODO Handle the case of when src and tgt are the same vertices + // in case of identical vertices, it might indicate that we want to walk along a cycle. + // If not, the predicate should return false for all candidates + if (target(src, pm) == target(tgt, pm) ) break; + + CGAL_WALKER_TRACE("2-POLYHEDRON_VERTEX\n") + BOOST_FOREACH(halfedge_descriptor hd, halfedges_around_target(src, pm)) + if (source(hd, pm)==target(tgt, pm)) + { + common_face=opposite(hd, pm); + new_edge_to_add=false; + break; + } + } + break; + case POLYHEDRON_NONE: + CGAL_WALKER_TRACE("2-POLYHEDRON_NONE\n") + break; + } + break; + case POLYHEDRON_NONE: + CGAL_assertion("Should never be here\n"); + } + + if (!new_edge_to_add) + { + // In this case, the edge to add is already on an edge of the input mesh + visitor.on_walk_end_on_edge(common_face); + return true; + } + if ( common_face != graph_traits::null_halfedge() ) + { + CGAL_assertion( !is_border(common_face, pm) ); + visitor.on_walk_end(face(common_face, pm)); + return true; + } + + +/// We now do the walk in the triangulation and find edge-edge intersection points + std::set target_faces; /// \todo try using a boost::flat_set instead + get_incident_faces(target_type, tgt, target_faces, pm); + std::set faces_already_visited; +// halfedge_descriptor first_halfedge_intersected; + + while( true ) + { + std::vector hedges_to_test; + switch( source_type ) + { + case POLYHEDRON_FACET: + CGAL_WALKER_TRACE(" i-POLYHEDRON_FACET\n") + hedges_to_test.push_back( src ); + hedges_to_test.push_back( next(src, pm) ); + hedges_to_test.push_back( prev(src, pm) ); + break; + case POLYHEDRON_EDGE: + CGAL_WALKER_TRACE(" i-POLYHEDRON_EDGE\n") + // if the v_src is on an edge, we don't know in which direction to go + // This is only true the first time of the loop + if ( faces_already_visited.empty() && + !is_border(opposite(src, pm), pm) ) + { + hedges_to_test.push_back( next(opposite(src, pm), pm) ); + hedges_to_test.push_back( prev(opposite(src, pm), pm) ); + } + /// @TODO what if src is a border edge? + hedges_to_test.push_back( next(src, pm) ); + hedges_to_test.push_back( prev(src, pm) ); + break; + case POLYHEDRON_VERTEX: + { + CGAL_WALKER_TRACE(" i-POLYHEDRON_VERTEX\n") + BOOST_FOREACH(halfedge_descriptor hd, halfedges_around_target(src, pm)) + { + if ( faces_already_visited.count(face(hd, pm))==0 //we don't want to go back, so we filter faces + && !is_border(hd, pm)){ + hedges_to_test.push_back( prev(hd, pm) ); + } + } + } + break; + case POLYHEDRON_NONE: + CGAL_assertion("Should never be here\n"); + } + + halfedge_descriptor intersected_edge = graph_traits::null_halfedge(); + boost::optional< std::pair > > inter_res; + + CGAL_WALKER_TRACE(" Looking for intersections hedges_to_test.size()="<< hedges_to_test.size() << "\n") + std::size_t k=0; + for (;kfirst); + CGAL_WALKER_TRACE(" intersection found\n") +// if (first_halfedge_intersected==graph_traits::null_halfedge()) +// first_halfedge_intersected=canonical_hedge(intersected_edge, pm); +// else +// if (first_halfedge_intersected==canonical_hedge(intersected_edge, pm)) +// { +// CGAL_WALKER_TRACE(" ERROR: loop detected. Abording.\n"); +// throw Walk_is_cycling_exception(); +// } +#if 0 + if ( is_border_edge(intersected_edge, pm) ) + throw Walk_on_polyhedron_border_crossing_exception(); + // \todo use the boolean in the API about incomplete overlay +#endif + break; + } + } + + if ( k==hedges_to_test.size() ) + { + // this handles the case when the src is on the border + // and the walk directly goes out + if (target_type==POLYHEDRON_NONE) break; + // this handle the case when the src is on the border + // and the tgt is inside or on the boundary of the supporting mesh + // but the path is going out + CGAL_WALKER_TRACE("WARNING: Halting but tgt not reached!\n") + return false; + + + CGAL_WALKER_TRACE("ERROR DID NOT FIND THE INTERSECTION!!!!!!!!!!!!!!!\n") + CGAL_assertion(!"NO INTERSECTION FOUND"); + break; + } + + std::set current_incident_facets; + get_incident_faces(source_type, src, current_incident_facets, pm); + faces_already_visited.clear(); + + const Barycentric_NT* barycentric_coord = + boost::get( &(inter_res->second) ); + if (!barycentric_coord) + { + // we reached a vertex + halfedge_descriptor hedge=canonical_hedge(intersected_edge, pm); + bool is_target = boost::get( inter_res->second ); + if (!is_target) hedge=opposite(hedge, pm); + vertex_descriptor vh=target(hedge, pm); + + Polyhedron_simplex_type prev_type=source_type; + halfedge_descriptor prev_source=src; + source_type=POLYHEDRON_VERTEX; + src=hedge; + + std::vector common_faces; + + bool did_break=false; + + BOOST_FOREACH(halfedge_descriptor hd, halfedges_around_target(hedge, pm)) + { + if ( !is_border(hd, pm) ){ //note that target_faces and current_incident_facets do not contain any NULL face + if ( !did_break && target_faces.count(face(hd, pm))!=0 ) + did_break=true; + if ( current_incident_facets.count( face(hd, pm) )!=0 ) + { + common_faces.push_back(hd); + faces_already_visited.insert(face(hd, pm)); + } + } + } + + //insert current edge + if (common_faces.size()==1) + { + // vertex-vertex case (on the mesh border) + if (prev_type==POLYHEDRON_VERTEX){ + CGAL_assertion(target(common_faces[0], pm)==vh); + visitor.on_input_edge( + source(common_faces[0], pm)==target(prev_source, pm) + ? common_faces[0] + : opposite(next(common_faces[0], pm), pm)); + } + else{ + // edge->vertex case (on the mesh border) + if (prev_type==POLYHEDRON_EDGE && (target(prev_source, pm)==vh || source(prev_source, pm)==vh)) + visitor.on_input_edge(target(prev_source, pm)==vh + ? prev_source:opposite(prev_source, pm)); + else + // we reached a vertex opposite to the edge or in a face + visitor.found_vertex_intersection(common_faces[0]); + } + } + else + { + CGAL_assertion(common_faces.size()==2); + //this is a vertex-vertex case or a edge-vertex + halfedge_descriptor edge = + common_edge(face(common_faces[0], pm), face(common_faces[1], pm), pm); + visitor.on_input_edge(target(edge, pm)==vh?edge:opposite(edge, pm)); + } + + if ( did_break ) + { + CGAL_assertion((hedge==canonical_hedge(intersected_edge, pm)) == is_target); + common_faces.clear(); + + BOOST_FOREACH(halfedge_descriptor hd, halfedges_around_target(hedge, pm)) + { + if ( !is_border(hd, pm) && target_faces.count( face(hd, pm) )!=0 ) + common_faces.push_back(hd); + } + + //insert the last edge + if (common_faces.size()==1){ + // the last visited edge is a complete border edge + if (target_type==POLYHEDRON_VERTEX){ + CGAL_assertion(target(common_faces[0], pm)==vh); + visitor.on_walk_end_on_edge( + source(common_faces[0], pm)==target(tgt, pm) + ? opposite(common_faces[0], pm) + : next(common_faces[0], pm) ); + } + else + { + // the walk ends on a border edge + if(target_type==POLYHEDRON_EDGE && + (target(tgt, pm)==vh || source(tgt, pm)==vh) ) + { + visitor.on_walk_end_on_edge(target(tgt, pm)==vh + ? opposite(tgt, pm):tgt); + } + else + visitor.on_walk_end(face(common_faces[0], pm)); + } + } + else + { + CGAL_assertion(common_faces.size()==2); + //this is a vertex-vertex case or a edge-vertex + halfedge_descriptor edge = + common_edge(face(common_faces[0], pm), face(common_faces[1], pm), pm); + visitor.on_walk_end_on_edge(target(edge, pm)==vh?opposite(edge, pm):edge); + } + break; + } + } + else{ + // we crossed the interior of an edge + faces_already_visited.insert(face(src, pm)); + source_type=POLYHEDRON_EDGE; + src=opposite(intersected_edge, pm); + // barycentric_coord must have been computed using `canonical_hedge(intersected_edge, pm)` + visitor.found_edge_intersection( intersected_edge, *barycentric_coord ); + + if ( target_faces.count( face(src, pm) )!=0 ) + { + CGAL_assertion ( target_type != POLYHEDRON_VERTEX || + ( target(src, pm) != target(tgt, pm) && + source(src, pm) != target(tgt, pm)) ); + //insert the last edge + if ( !is_border(opposite(intersected_edge, pm), pm) ) + visitor.on_walk_end(face(opposite(intersected_edge, pm), pm)); + break; + } + } + if ( is_border_edge(intersected_edge, pm) ){ + if (target_type!=POLYHEDRON_NONE){ + CGAL_WALKER_TRACE("WARNING: Halting but tgt not reached!\n") + return false; + } + break; // the walk reached the boundary of the domain + } + + if(visitor.do_break()) + break; + + } + return true; +} + +} + +#undef CGAL_WALKER_TRACE + +#endif // CGAL_WALK_IN_POLYGON_MESH_H diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/walk_to_select.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/walk_to_select.h new file mode 100644 index 000000000000..6c52c94522e3 --- /dev/null +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/walk_to_select.h @@ -0,0 +1,1033 @@ +// Copyright (c) 2014-2019 GeometryFactory (France). All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// You can redistribute it and/or modify it under the terms of the GNU +// General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. +// +// Licensees holding a valid commercial license may use this file in +// accordance with the commercial license agreement provided with the software. +// +// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: GPL-3.0+ +// +// Author(s) : Maxime Gimeno +// + +#ifndef CGAL_POLYGON_MESH_PROCESSING_WALK_TO_SELECT_H +#define CGAL_POLYGON_MESH_PROCESSING_WALK_TO_SELECT_H + +// #include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include + +//todo: if no point is between the planes, nothing is selected. +namespace CGAL { + +namespace Polygon_mesh_processing { + +namespace walker_internal{ +template +class Edge_intersection_with_plane +{ + typedef typename Kernel_traits< + typename boost::property_traits::value_type + >::Kernel K; + + typedef typename K::Point_3 Point_3; + typedef typename K::Vector_3 Vector_3; + typedef typename K::FT FT; + + typedef boost::variant Variant; + typedef boost::optional< std::pair > result_type; + + Point_3 m_point; + Vector_3 m_normal; + const PolygonMesh* m_pm_ptr; + VertexPointMap m_vpm; + Vector_3 m_other_normal; + mutable FT res_scalar; + mutable int step; + + typedef typename boost::graph_traits::halfedge_descriptor + halfedge_descriptor; + +public: + + typedef FT Barycentric_NT; + + Edge_intersection_with_plane(const Point_3& pt, + const Vector_3& n, + const Vector_3& other_n, + const PolygonMesh& pm, + const VertexPointMap& vpm) + : m_point(pt) + , m_normal(n) + , m_pm_ptr(&pm) + , m_vpm(vpm) + , m_other_normal(other_n) + , res_scalar(1) + , step(0) + {} + + result_type operator()(halfedge_descriptor h) const + { + typename K::Oriented_side_3 oriented_side; + Point_3 b0 = get(m_vpm, source(h, *m_pm_ptr) ); + Point_3 b1 = get(m_vpm, target(h, *m_pm_ptr) ); + + Oriented_side s0 = oriented_side(m_point, m_normal, b0); + Oriented_side s1 = oriented_side(m_point, m_normal, b1); + + //the path is jagged, so we take the biggest projection of 2 steps to + //get the walking direction + if(step == 0){ + res_scalar = scalar_prod(h); + } + if(step == 1){ + FT new_dir= scalar_prod(h); + if(std::abs(new_dir) > std::abs(res_scalar)) + res_scalar = new_dir; + + } + if (s0 == ON_ORIENTED_BOUNDARY){return std::make_pair(0.5, Variant(false));} + if (s1 == ON_ORIENTED_BOUNDARY){return std::make_pair(0.5, Variant(true)); } + if(s0==s1) return boost::none; + FT num = ( (m_point - b1) * m_normal ); + FT denum = ((b0-b1) * m_normal); + + step++; + // the edge is almost in the plane. This is an arbitrary choice + if (denum==0) + return std::make_pair(0.5, Variant( 0.5 )); + + FT alpha = num / denum; + + if (alpha<=0) std::make_pair(0.5, Variant(false)); + if (alpha>=1) std::make_pair(0.5, Variant(true)); + return std::make_pair(0.5, Variant( alpha )); + } + + FT scalar_prod(const halfedge_descriptor& input) const + { + Point_3 b0 = get(m_vpm, source(input, *m_pm_ptr) ); + Point_3 b1 = get(m_vpm, target(input, *m_pm_ptr) ); + Vector_3 dir(b0, b1); + return dir*m_other_normal; + } + + bool wrong_direction() const + { + return res_scalar <= 0; + } + + void reset() + { + res_scalar = 1; + step = 0; + } + + bool do_break() const + { + return step > 1 && wrong_direction(); + } +}; + +template +class Intersection_info +{ + // typedefs + typedef typename Kernel::FT FT; + typedef typename Kernel::Point_3 Point_3; + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + + // data members + bool m_is_vertex; + halfedge_descriptor m_hedge; + Point_3 m_point; + + // internal function + template + Point_3 + compute_point(const FT& alpha, const PolygonMesh& pm, VertexPointMap vpm) + { + CGAL_assertion(!m_is_vertex); + CGAL_assertion(alpha<1 && alpha >0); + // get canonical_edge + halfedge_descriptor hedge = m_hedge < opposite(m_hedge, pm) ? + m_hedge : opposite(m_hedge, pm); + + const Point_3 b0 = get(vpm, source(hedge, pm)); + const Point_3 b1 = get(vpm, target(hedge, pm)); + + return b1+alpha*(b0-b1); + } + +public: + + Intersection_info() + : m_is_vertex(true) + {} + + template + Intersection_info( halfedge_descriptor h, + const PolygonMesh& pm, + VertexPointMap vpm) + : m_is_vertex(true) + , m_hedge(h) + , m_point(get(vpm, target(h, pm))) + {} + + template + Intersection_info( halfedge_descriptor h, + const FT& alpha, + const PolygonMesh& pm, + VertexPointMap vpm) + : m_is_vertex(false) + , m_hedge(h) + , m_point(compute_point(alpha, pm, vpm)) + {} + + + Intersection_info( halfedge_descriptor h, + const Point_3& p) + : m_is_vertex(false) + , m_hedge(h) + , m_point(p) + {} + + const Point_3& point() const + { + return m_point; + } + + bool is_vertex() const + { + return m_is_vertex; + } + + halfedge_descriptor halfedge() const + { + return m_hedge; + } + +}; + +template +class Walk_in_polygon_mesh_visitor { + typedef boost::graph_traits BGT; + typedef typename BGT::vertex_descriptor vertex_descriptor; + typedef typename BGT::halfedge_descriptor halfedge_descriptor; + typedef typename BGT::face_descriptor face_descriptor; + + typedef typename boost::property_traits::value_type Point_3; + typedef typename Kernel_traits::Kernel K; + typedef typename K::FT FT; + + typedef Intersection_info Inter_info; + // to collect the target vertices of the polyline during the walk (but the last one) + std::vector target_vertices; + // the set of halfedges showing the walk + std::vector< halfedge_descriptor > m_walked_halfedges; + // the set of faces modified + std::vector< face_descriptor > m_split_faces; + // the set of faces created + std::vector< face_descriptor > m_new_faces; + // the face where the walk ended (if not on an edge) + face_descriptor m_last_face; + // The triangle mesh to be refined + const TriangleMesh* m_tm_ptr; + //The point property map + VertexPointMap m_vpm; + //the distance to reach whiPoint_set5le walking + FT m_dist; + //the distance currently reached + FT cur_dist; + //the last value of cur_dist (used to find the end_point) + FT last_dist; + //the initial center + Point_3 text_center; + +public: + + Walk_in_polygon_mesh_visitor(const TriangleMesh& tm, + VertexPointMap vpm, + FT dist, + const Point_3& center) + : m_tm_ptr(&tm) + , m_vpm(vpm) + , m_dist(dist) + , cur_dist(0) + , last_dist(0) + , text_center(center) + {} + + // intersected_edge points in the face into which the edge should be added + void found_edge_intersection(halfedge_descriptor intersected_edge, + const FT& barycentric_coord ) + { + m_walked_halfedges.push_back( intersected_edge ); + target_vertices.push_back( Inter_info(intersected_edge, barycentric_coord, *m_tm_ptr, m_vpm) ); + Point_3 new_p = target_vertices.back().point(); + + //update distance + if(target_vertices.size() == 1) + { + cur_dist += CGAL::sqrt(CGAL::squared_distance(text_center,new_p)); + } + else { + Point_3 last_point = target_vertices[target_vertices.size() -2].point(); + last_dist = cur_dist; + cur_dist += CGAL::sqrt(CGAL::squared_distance(last_point,new_p)); + } + } + + void found_vertex_intersection(halfedge_descriptor h) + { + m_walked_halfedges.push_back( h ); + target_vertices.push_back( Inter_info(h, *m_tm_ptr, m_vpm) ); + Point_3 new_p = target_vertices.back().point(); + //update distance + if(target_vertices.size() == 1) + { + cur_dist += CGAL::sqrt(CGAL::squared_distance(text_center,new_p)); + } + else { + Point_3 last_point = target_vertices[target_vertices.size() -2].point(); + last_dist = cur_dist; + cur_dist += CGAL::sqrt(CGAL::squared_distance(last_point,new_p)); + } + } + + void register_initial_intersection(const Inter_info& i) + { + target_vertices.push_back(i); + Point_3 new_p = target_vertices.back().point(); + //update distance + CGAL_assertion(target_vertices.size() == 1); + cur_dist = CGAL::sqrt(CGAL::squared_distance(text_center,new_p)); + } + + void on_walk_end(face_descriptor f) + { + m_last_face = f; + } + + template + void set_beta(T) + {} + + //called when the walk is over but the last segment is on an existing edge + void on_walk_end_on_edge(halfedge_descriptor) {} + + /// indicates that hp1 is on m_hp2, with the same orientation. + /// if hp1 is not initialized, it indicates that m_source and m_target + /// or both on the edge and a geometric predicates need to be used + /// to get a correct hp1 + void on_input_edge(halfedge_descriptor h) + { + m_walked_halfedges.push_back(h); + target_vertices.push_back(Inter_info()); + } + + const std::vector& + walked_halfedges() const + { + return m_walked_halfedges; + } + + bool do_break() + { +// if (cur_dist >= m_dist) +// std::cout << "stop at " << cur_dist << " (vs. " << m_dist << ")\n"; + return cur_dist >= m_dist; + } + + Point_3 end_point() + { + FT alpha = 0; + if(is_wrapping()) + { + alpha = 1; + } + else + { + FT wanted_dist = m_dist - last_dist; + FT gotten_dist = cur_dist - last_dist; + alpha = wanted_dist / gotten_dist; + } + + CGAL_assertion(alpha<=1 && alpha >=0); + Point_3 b0; + if(target_vertices.size() <2) + b0 = text_center; + else + b0 = target_vertices[target_vertices.size() - 2].point(); + Point_3 b1; + if(target_vertices.size() == 0) + b1 = text_center; + else + b1= target_vertices.back().point(); + return b0+alpha*(b1-b0); + } + + bool is_wrapping() + { + return (cur_dist > 0 && (m_dist > cur_dist)); + } + + template + OutputIterator get_intersection_points(OutputIterator out) const + { + for (const Inter_info& i : target_vertices) + *out++=i.point(); + return out; + } +}; + +} // end of Remove_caps_impl + + +// 0: negative side of axis_2 and 1: positive side of axis_2 +template +boost::optional< std::array::halfedge_descriptor, 2> > +get_sorted_intersected_halfedges(typename boost::graph_traits::face_descriptor start_face, + const TriangleMesh& tm, + const Vpm& vpm, + const typename K::Plane_3& axis_1, + const typename K::Plane_3& axis_2) +{ + typedef typename boost::graph_traits BGT; + typedef typename BGT::halfedge_descriptor halfedge_descriptor; + + halfedge_descriptor h = halfedge(start_face, tm); + std::array triangle_points = { + get(vpm, source(h, tm)), + get(vpm, target(h, tm)), + get(vpm, target(next(h, tm), tm)) + }; + std::array oriented_sides; + for (int i=0; i<3; ++i) + { + oriented_sides[i] = axis_1.oriented_side( triangle_points[i] ); + // TODO: handle when the plane goes through a vertex/edge + if (oriented_sides[i] == ON_ORIENTED_BOUNDARY) return boost::none; + } + + std::array int_edges; + int k=-1; + if (oriented_sides[0]!=oriented_sides[1]) int_edges[++k]=h; + if (oriented_sides[1]!=oriented_sides[2]) int_edges[++k]=next(h, tm); + if (oriented_sides[2]!=oriented_sides[0]) int_edges[++k]=prev(h, tm); + + if (k!=1) return boost::none; + + // TODO: avoid the constructions + boost::optional > + inter_res = intersection( typename K::Segment_3(get(vpm, source(int_edges[0], tm)), + get(vpm, target(int_edges[0], tm))), + axis_1 ); + CGAL_assertion( inter_res != boost::none ); + const typename K::Point_3* pt = boost::get(&(*inter_res)); + CGAL_assertion( pt!=NULL ); + + if (axis_2.oriented_side( *pt )!= ON_NEGATIVE_SIDE) + std::swap(int_edges[0], int_edges[1]); + return int_edges; +} + +template +std::size_t +two_side_walk_and_intersection_point_collection(const TriangleMesh& tm, + const typename K::Point_3& text_center, + typename boost::graph_traits::face_descriptor start_face, + const typename K::Plane_3& axis_1, + const typename K::Plane_3& axis_2, + double width, + std::vector& inter_points, + const NamedParameters& np) +{ + typedef boost::graph_traits BGT; + typedef typename BGT::halfedge_descriptor halfedge_descriptor; + using parameters::choose_parameter; + using parameters::get_parameter; + + // Vertex point maps + typedef typename GetVertexPointMap::const_type Vpm; + Vpm vpm = choose_parameter(get_parameter(np, internal_np::vertex_point), + get_const_property_map(boost::vertex_point, tm)); + + typedef walker_internal::Edge_intersection_with_plane Predicate; + typedef walker_internal::Walk_in_polygon_mesh_visitor Visitor; + + boost::optional< std::array > opt_int_edges = + get_sorted_intersected_halfedges(start_face, tm, vpm, axis_1, axis_2); + + if (opt_int_edges == boost::none) + return 0; + + // 1) walk width/2 to the left + // first compute the out point from the center triangle (TODO: make more robust) + boost::optional > + inter_res = intersection( typename K::Segment_3(get(vpm, source((*opt_int_edges)[0], tm)), + get(vpm, target((*opt_int_edges)[0], tm))), + axis_1 ); + if ( inter_res == boost::none ) + { + CGAL_warning(!"Intersection 1 not found"); + return 0; + } + const typename K::Point_3* pt = boost::get(&(*inter_res)); + if ( pt == nullptr ) + { + CGAL_warning(!"Intersection 1 is not a point"); + return 0; + } + + // walk only if needed + if ( CGAL::compare_squared_distance(text_center, *pt, width*width / 4.0) == SMALLER ) + { + Predicate predicate(text_center, axis_1.orthogonal_vector(), + axis_2.orthogonal_vector(), tm, vpm); + Visitor visitor = Visitor(tm, vpm, width/2.0, text_center); + visitor.register_initial_intersection({(*opt_int_edges)[0], *pt}); + walk_in_polygon_mesh(tm, + (*opt_int_edges)[0], CGAL::POLYHEDRON_EDGE, + BGT::null_halfedge(), CGAL::POLYHEDRON_NONE, + predicate, visitor); + visitor.get_intersection_points(std::back_inserter(inter_points)); + inter_points.back() = visitor.end_point(); // replace the last point by the real endpoint of the walk + // reverse sequence + std::reverse(inter_points.begin(), inter_points.end()); + } + else + inter_points.push_back(*pt); + + // add the center + save its position + std::size_t center_pos = inter_points.size(); + if (inter_points.back()!=text_center) + inter_points.push_back(text_center); + else + --center_pos; + + //2)walk width/2 to the right + // first compute the out point from the center triangle, other direction (TODO: make more robust) + inter_res = intersection( typename K::Segment_3(get(vpm, source((*opt_int_edges)[1], tm)), + get(vpm, target((*opt_int_edges)[1], tm))), + axis_1 ); + if ( inter_res == boost::none ) + { + CGAL_warning(!"Intersection 2 not found"); + return 0; + } + pt = boost::get(&(*inter_res)); + if ( pt == nullptr ) + { + CGAL_warning(!"Intersection 2 is not a point"); + return 0; + } + + // walk only if needed + if ( CGAL::compare_squared_distance(text_center, *pt, width*width / 4.0) == SMALLER ) + { + Predicate predicate(text_center, axis_1.orthogonal_vector(), + -axis_2.orthogonal_vector(), tm, vpm); + Visitor visitor(tm, vpm, width/2.0, text_center); + visitor.register_initial_intersection({(*opt_int_edges)[1], *pt}); + + walk_in_polygon_mesh(tm, + (*opt_int_edges)[1], CGAL::POLYHEDRON_EDGE, + BGT::null_halfedge(), CGAL::POLYHEDRON_NONE, + predicate, visitor); + visitor.get_intersection_points(std::back_inserter(inter_points)); + inter_points.back() = visitor.end_point(); // replace the last point by the real endpoint of the walk + } + else + inter_points.push_back(*pt); + + return center_pos; +} + +template +struct Weight_map{ + const TriangleMesh& tm; + const ECM& ecm; + typedef double value_type; + typedef double reference; + typedef boost::readable_property_map_tag category; + typedef typename boost::graph_traits::edge_descriptor key_type; + + Weight_map(const TriangleMesh& tm, const ECM& ecm) + : tm(tm) + , ecm(ecm) + {} + + friend + double get(const Weight_map& wm, key_type ed) + { + if (get(wm.ecm, ed)) return std::numeric_limits::max(); // do not use constrained edges (no walk back); + return std::sqrt( squared_distance(wm.tm.point(source(ed, wm.tm)), wm.tm.point(target(ed, wm.tm))) ); + } +}; + +template +OutputIterator +select_to_walk( const TriangleMesh& tm, + const Tree& aabb_tree, + typename K::Point_3 text_center, + typename K::Plane_3 axis_1, + typename K::Plane_3 axis_2, + double width, + double height, + OutputIterator out, + const NamedParameters& np) +{ + typedef boost::graph_traits BGT; + typedef typename BGT::halfedge_descriptor halfedge_descriptor; + typedef typename BGT::face_descriptor face_descriptor; + typedef typename BGT::vertex_descriptor vertex_descriptor; + typedef typename BGT::edge_descriptor edge_descriptor; + using boost::choose_param; + using boost::get_param; + + // Vertex point maps + typedef typename GetVertexPointMap::const_type Vpm; + Vpm vpm = choose_param(get_param(np, internal_np::vertex_point), + get_const_property_map(boost::vertex_point, tm)); + + typedef walker_internal::Edge_intersection_with_plane Predicate; + typedef walker_internal::Walk_in_polygon_mesh_visitor Visitor; + + face_descriptor start_face = aabb_tree.closest_point_and_primitive(text_center).second; + + // code use to get the top/bottom, left/right points surrounding the letter + // on the input mesh +#if 0 + typedef typename K::Plane_3 Plane_3; + typedef typename K::Point_3 Point_3; + + std::array int_edges = + get_sorted_intersected_halfedges(start_face, tm, vpm, axis_1, axis_2); + + // 1) walk width/2 to the right + Predicate predicate(text_center, axis_1.orthogonal_vector(), + axis_2.orthogonal_vector(), tm, vpm); + Visitor visitor = Visitor(tm, vpm, width/2.0, text_center); + Point_3 new_p; + walk_in_polygon_mesh(tm, + int_edges[1], CGAL::POLYHEDRON_EDGE, + BGT::null_halfedge(), CGAL::POLYHEDRON_NONE, + predicate, visitor); + new_p= visitor.end_point(); + std::cout << new_p << "\n"; + Plane_3 r_plane(new_p, -axis_2.orthogonal_vector()); + //2)walk width/2 to the left + predicate = Predicate(text_center, axis_1.orthogonal_vector(), + -axis_2.orthogonal_vector(), tm, vpm); + visitor = Visitor(tm, vpm, width/2.0, text_center); + //get last point orientation to know where to walk next. + walk_in_polygon_mesh(tm, + int_edges[0], CGAL::POLYHEDRON_EDGE, + BGT::null_halfedge(), CGAL::POLYHEDRON_NONE, + predicate, visitor); + + new_p= visitor.end_point(); + std::cout << new_p << "\n"; + //bool is_l_wrapping = visitor.is_wrapping(); + Plane_3 l_plane(new_p, axis_2.orthogonal_vector()); + + int_edges = get_sorted_intersected_halfedges(start_face, tm, vpm, axis_2, axis_1); + //3)walk height /2 to the top + predicate = Predicate(text_center, axis_2.orthogonal_vector(), + axis_1.orthogonal_vector(),tm, vpm); + visitor = Visitor(tm, vpm, height/2.0, text_center); + walk_in_polygon_mesh(tm, + int_edges[1], CGAL::POLYHEDRON_EDGE, + BGT::null_halfedge(), CGAL::POLYHEDRON_NONE, + predicate, visitor); + new_p= visitor.end_point(); + std::cout << new_p << "\n"; + //bool is_t_wrapping = visitor.is_wrapping(); + Plane_3 t_plane(new_p, -axis_1.orthogonal_vector()); + //4)walk height /2 to the bottom + predicate = Predicate(text_center, axis_2.orthogonal_vector(), + -axis_1.orthogonal_vector(), tm, vpm); + visitor = Visitor(tm, vpm, height/2.0, text_center); + walk_in_polygon_mesh(tm, + int_edges[0], CGAL::POLYHEDRON_EDGE, + BGT::null_halfedge(), CGAL::POLYHEDRON_NONE, + predicate, visitor); + new_p= visitor.end_point(); + std::cout << new_p << "\n"; + //bool is_b_wrapping = visitor.is_wrapping(); + Plane_3 b_plane(new_p, axis_1.orthogonal_vector()); + CGAL_assertion( l_plane.has_on_positive_side(r_plane.point()) && + t_plane.has_on_positive_side(b_plane.point()) ); +#endif + + // walk in the input mesh to get the 4 corners of the bbox of the letter + // Then using, these corner we extract the next halfedge that would have + // been intersected if the walk wasn't ended. Using these edges, we are + // trying to use Dijkstra to close the selection area. + // The result is not satisfactory on meshes with low triangle resolution + std::vector targets(4), sources(4); + + typename K::Vector_3 tmp_axis_1 = axis_1.orthogonal_vector(), + tmp_axis_2 = axis_2.orthogonal_vector(); + tmp_axis_1 /= std::sqrt(tmp_axis_1*tmp_axis_1); + tmp_axis_2 /= std::sqrt(tmp_axis_2*tmp_axis_2); + + typename K::Vector_3 v1 = tmp_axis_1 + tmp_axis_2; + typename K::Vector_3 v2 = -tmp_axis_1 + tmp_axis_2; + + std::array int_edges = + get_sorted_intersected_halfedges(start_face, tm, vpm, typename K::Plane_3(text_center,v1), typename K::Plane_3(text_center,v2)); + + double D = std::sqrt( ( width * width + height * height )/ 4. ); + + halfedge_descriptor hedge = boost::graph_traits::null_halfedge(); + + typedef typename boost::property_map >::const_type ECM; + ECM ecm = get(dynamic_edge_property_t(), tm); + for (edge_descriptor ed : edges(tm)) + put(ecm, ed, false); + + // 1) walk to the corner 0 + Predicate predicate(text_center, v1, + v2, tm, vpm); + Visitor visitor = Visitor(tm, vpm, D, text_center); + walk_in_polygon_mesh(tm, + int_edges[1], CGAL::POLYHEDRON_EDGE, + BGT::null_halfedge(), CGAL::POLYHEDRON_NONE, + predicate, visitor); + hedge = visitor.walked_halfedges().back(); + put(ecm, edge(hedge, tm), true); + targets[0] = target(hedge, tm); + sources[3] = source(hedge, tm); +#ifndef NO_DEBUG + std::cout << "* " << visitor.end_point() << "\n"; + std::cout << "2 " << tm.point(source(hedge, tm)) << " " << tm.point(target(hedge, tm)) << "\n"; +#endif + //2)walk to the corner 2 + predicate = Predicate(text_center, v1, + -v2, tm, vpm); + visitor = Visitor(tm, vpm, D, text_center); + walk_in_polygon_mesh(tm, + int_edges[0], CGAL::POLYHEDRON_EDGE, + BGT::null_halfedge(), CGAL::POLYHEDRON_NONE, + predicate, visitor); + hedge = visitor.walked_halfedges().back(); + put(ecm, edge(hedge, tm), true); + targets[2] = target(hedge, tm); + sources[1] = source(hedge, tm); +#ifndef NO_DEBUG + std::cout << "* " << visitor.end_point() << "\n"; + std::cout << "2 " << tm.point(source(hedge, tm)) << " " << tm.point(target(hedge, tm)) << "\n"; +#endif + int_edges = get_sorted_intersected_halfedges(start_face, tm, vpm, typename K::Plane_3(text_center,v2), typename K::Plane_3(text_center,v1)); + //3)walk to the corner 1 + predicate = Predicate(text_center, v2, + v1,tm, vpm); + visitor = Visitor(tm, vpm, D, text_center); + walk_in_polygon_mesh(tm, + int_edges[1], CGAL::POLYHEDRON_EDGE, + BGT::null_halfedge(), CGAL::POLYHEDRON_NONE, + predicate, visitor); + hedge = visitor.walked_halfedges().back(); + put(ecm, edge(hedge, tm), true); + targets[1] = target(hedge, tm); + sources[0] = source(hedge, tm); +#ifndef NO_DEBUG + std::cout << "* " << visitor.end_point() << "\n"; + std::cout << "2 " << tm.point(source(hedge, tm)) << " " << tm.point(target(hedge, tm)) << "\n"; +#endif + //4)walk to the corner 3 + predicate = Predicate(text_center, v2, + -v1, tm, vpm); + visitor = Visitor(tm, vpm, D, text_center); + walk_in_polygon_mesh(tm, + int_edges[0], CGAL::POLYHEDRON_EDGE, + BGT::null_halfedge(), CGAL::POLYHEDRON_NONE, + predicate, visitor); + hedge = visitor.walked_halfedges().back(); + put(ecm, edge(hedge, tm), true); + targets[3] = target(hedge, tm); + sources[2] = source(hedge, tm); +#ifndef NO_DEBUG + std::cout << "* " << visitor.end_point() << "\n"; + std::cout << "2 " << tm.point(source(hedge, tm)) << " " << tm.point(target(hedge, tm)) << "\n"; +#endif + + class Dijkstra_end_exception : public std::exception + { + const char* what() const throw () + { + return "Dijkstra shortest path: reached the target vertex."; + } + }; + + class Stop_at_target_Dijkstra_visitor : boost::default_dijkstra_visitor + { + vertex_descriptor destination_vd; + + public: + Stop_at_target_Dijkstra_visitor(vertex_descriptor destination_vd) + : destination_vd(destination_vd) + { } + + void initialize_vertex(const vertex_descriptor& /*s*/, const TriangleMesh& /*mesh*/) const { } + void examine_vertex(const vertex_descriptor& /*s*/, const TriangleMesh& /*mesh*/) const { } + void examine_edge(const edge_descriptor& /*e*/, const TriangleMesh& /*mesh*/) const { } + void edge_relaxed(const edge_descriptor& /*e*/, const TriangleMesh& /*mesh*/) const { } + void discover_vertex(const vertex_descriptor& /*s*/, const TriangleMesh& /*mesh*/) const { } + void edge_not_relaxed(const edge_descriptor& /*e*/, const TriangleMesh& /*mesh*/) const { } + void finish_vertex(const vertex_descriptor &vd, const TriangleMesh& /* mesh*/) const + { + if(vd == destination_vd) + throw Dijkstra_end_exception(); + } + }; + + Weight_map wm(tm, ecm); + +// close the loop + for (std::size_t i=0; i<4; ++i) + { + typedef boost::unordered_map Pred_umap; + typedef boost::associative_property_map Pred_pmap; + vertex_descriptor src = sources[i], tgt=targets[i]; + if (src==tgt) continue; // TODO: another case to handle is when hedge was the same for 2 consecutive corners + Pred_umap pred_umap; + Pred_pmap pred_pmap(pred_umap); + Stop_at_target_Dijkstra_visitor vis(tgt); + + try{ + boost::dijkstra_shortest_paths(tm, src, + boost::predecessor_map(pred_pmap).visitor(vis).weight_map(wm)); + } + catch(const Dijkstra_end_exception&) + {} + + vertex_descriptor prev = tgt; + do{ + CGAL_assertion( pred_umap.count(prev) == 1 ); + vertex_descriptor vd = pred_umap[prev]; + halfedge_descriptor hd = halfedge(vd, prev, tm).first; + CGAL_assertion( hd != boost::graph_traits::null_halfedge() ); + put(ecm, edge(hd, tm), true); +#ifndef NO_DEBUG + std::cout << "2 " << tm.point(prev) << " " << tm.point(vd) << "\n"; +#endif + prev = vd; + } + while(prev != src); + std::cout << "--\n"; + } + +#ifndef NO_DEBUG + for (edge_descriptor ed : edges(tm)) + if (get(ecm, ed)) + std::cout << "2 " << tm.point(source(ed, tm)) << " " << tm.point(target(ed, tm)) << "\n"; +#endif + connected_component(start_face, tm, out, parameters::edge_is_constrained_map(ecm)); + +#if 1 + +#if 0 + + +// select face by expension from the center face, cross an edge if one of its endpoint is inside the selection + //TODO: use dynamic maps + + std::array planes = { &r_plane, &l_plane, &t_plane, &b_plane }; + + std::vector visited_faces(num_faces(tm),false), + visited_vertices(num_vertices(tm), false); + std::vector < std::bitset<4> > + vertices_pos(vertices(tm).size()); + + std::vector queue; + visited_faces[start_face]=true; + *out++=start_face; + queue.push_back( halfedge(start_face, tm) ); + queue.push_back( next(queue.back(), tm) ); + queue.push_back( next(queue.back(), tm) ); + + while(!queue.empty()) + { + halfedge_descriptor h = opposite(queue.back(), tm); + queue.pop_back(); + face_descriptor f = face(h, tm); + if (f==BGT::null_face()) continue; + if (visited_faces[f]) continue; + visited_faces[f] = true; + + std::array, 3> f_v_selected; + for (int i=0; i<3; ++i) + { + vertex_descriptor v = source(h, tm); + if ( !visited_vertices[v] ) + { + for (int k=0;k<4;++k) + vertices_pos[v][k] = !planes[k]->has_on_negative_side(get(vpm,v)); + visited_vertices[v] = true; + } + f_v_selected[i] = vertices_pos[v]; + h = next(h, tm); + } + *out++=f; + + // cross any edge having a point in the plane intersection + // or if it is intersected by at least two consecutive planes + std::bitset<4> bs = f_v_selected[1] ^ f_v_selected[2]; + if ( f_v_selected[1].all() || f_v_selected[2].all() | + (bs[0] && bs[2]) || (bs[0] && bs[3]) || (bs[1] && bs[2]) || (bs[1] && bs[3]) ) + { + queue.push_back(next(h,tm)); + } + bs = f_v_selected[0] ^ f_v_selected[2]; + if ( f_v_selected[0].all() || f_v_selected[2].all() || + (bs[0] && bs[2]) || (bs[0] && bs[3]) || (bs[1] && bs[2]) || (bs[1] && bs[3]) ) + { + queue.push_back(prev(h,tm)); + } + } +#endif +#else + bool w_planes_are_reversed = l_plane.has_on_negative_side(r_plane.point()); + bool h_planes_are_reversed = t_plane.has_on_negative_side(b_plane.point()); + + //if a walk on 1 direction made a whole turn of a cc (e.g. wrapping a sphere ), + //disable the corresponding planes test, as they are combined + bool w_wrapping = false; + bool h_wrapping = false; + + std::vector all_selected_faces; + CGAL::Triangle_from_face_descriptor_map triangulator(&tm, vpm) ; + //first pass : all inside 4 planes + for(auto fd : faces(tm)) + { + if (fd==start_face) + { + all_selected_faces.push_back(fd); + continue; + } + bool do_select = false; + //get faces with at least 1 point inside planes + for(auto vd : CGAL::vertices_around_face(halfedge(fd, tm), tm)) + { + Point_3 p = get(vpm, vd); + if( + (w_wrapping || + (w_planes_are_reversed + ? (r_plane.has_on_negative_side(p) && l_plane.has_on_negative_side(p)) + : (r_plane.has_on_positive_side(p) && l_plane.has_on_positive_side(p))) + ) && (h_wrapping || + (h_planes_are_reversed + ? (t_plane.has_on_negative_side(p) && b_plane.has_on_negative_side(p)) + : (t_plane.has_on_positive_side(p) && b_plane.has_on_positive_side(p)) ))) + { + do_select = true; + break; + } + } + //if face is not selected : check if intersecting a plane (to avoid no point between point -> no selection) + if(!do_select) + { + if(!w_wrapping && + (CGAL::do_intersect(l_plane, get(triangulator, fd)) || + CGAL::do_intersect(r_plane, get(triangulator, fd)))) + { + for(auto vd : CGAL::vertices_around_face(halfedge(fd, tm), tm)){ + Point_3 p = get(vpm, vd); + if( h_planes_are_reversed + ? (t_plane.has_on_negative_side(p) && b_plane.has_on_negative_side(p)) + : (t_plane.has_on_positive_side(p) && b_plane.has_on_positive_side(p)) ){ + do_select = true; + break; + } + } + } + if(!h_wrapping && + (CGAL::do_intersect(b_plane, get(triangulator, fd)) || + CGAL::do_intersect(t_plane, get(triangulator, fd)))) + { + for(auto vd : CGAL::vertices_around_face(halfedge(fd, tm), tm)){ + Point_3 p = get(vpm, vd); + if( w_planes_are_reversed + ? (r_plane.has_on_negative_side(p) && l_plane.has_on_negative_side(p)) + : (r_plane.has_on_positive_side(p) && l_plane.has_on_positive_side(p)) ){ + do_select = true; + break; + } + } + } + } + if(do_select) + all_selected_faces.push_back(fd); + } + //add a "same-CC" constraint:the one that contains start_face + + //create a mesh from the selected faces + CGAL::Face_filtered_graph ffg(tm, all_selected_faces); + TriangleMesh subsm; + face_descriptor subsm_center_face; + CGAL::copy_face_graph(ffg, subsm, + CGAL::Emptyset_iterator(), CGAL::Emptyset_iterator(), + boost::make_function_output_iterator( + [&subsm_center_face, start_face, tm]( + const std::pair& p) + { + if (p.first==start_face){ subsm_center_face=p.second; } + })); + //get fcc map + std::vector fccmap(num_faces(tm)); // TODO: use dynamic map + CGAL::Polygon_mesh_processing::connected_components(ffg,CGAL::make_property_map(fccmap)); + + //create ffg from CC containing the start_face and give its faces as output + CGAL::Face_filtered_graph > selected_cc(ffg,fccmap[start_face], CGAL::make_property_map(fccmap)); + + //cut the band + bool take_r = (width > height); + //filter out the rest + for( auto fd : faces(selected_cc)) + { + bool do_select = true; + //to avoid non sphere topo + Plane_3 test_plane(text_center, r_plane.orthogonal_vector()); + if(!take_r) + test_plane = Plane_3(text_center, t_plane.orthogonal_vector()); + + if(do_select) + *out++ = fd; + } +#endif + return out; +} + +} +} +#endif // CGAL_POLYGON_MESH_PROCESSING_WALK_TO_SELECT_H diff --git a/Polygon_mesh_processing/include/CGAL/Polyhedron_simplex_type.h b/Polygon_mesh_processing/include/CGAL/Polyhedron_simplex_type.h new file mode 100644 index 000000000000..a530fa27ca41 --- /dev/null +++ b/Polygon_mesh_processing/include/CGAL/Polyhedron_simplex_type.h @@ -0,0 +1,33 @@ +// Copyright (c) 2014 GeometryFactory (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// You can redistribute it and/or modify it under the terms of the GNU +// General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. +// +// Licensees holding a valid commercial license may use this file in +// accordance with the commercial license agreement provided with the software. +// +// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: GPL-3.0+ +// +// Author(s) : Sébastien Loriot + +#ifndef CGAL_POLYHEDRON_SIMPLEX_TYPE_H +#define CGAL_POLYHEDRON_SIMPLEX_TYPE_H + +namespace CGAL{ + +enum Polyhedron_simplex_type { + POLYHEDRON_VERTEX, + POLYHEDRON_EDGE, + POLYHEDRON_FACET, + POLYHEDRON_NONE}; +} + +#endif // CGAL_POLYHEDRON_SIMPLEX_TYPE_H From 1df3c820cad87e64e77bdca2841cd029ae4b318c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 29 Sep 2023 12:06:59 +0200 Subject: [PATCH 027/120] walk along the intersection with a plane for now --- .../straightest_geodesic_sm_example.cpp | 60 ++++++++++++ .../Polygon_mesh_processing/walk_to_select.h | 91 ++++++++++++++++++- 2 files changed, 146 insertions(+), 5 deletions(-) create mode 100644 Polygon_mesh_processing/examples/Polygon_mesh_processing/straightest_geodesic_sm_example.cpp diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/straightest_geodesic_sm_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/straightest_geodesic_sm_example.cpp new file mode 100644 index 000000000000..3d8bab392d50 --- /dev/null +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/straightest_geodesic_sm_example.cpp @@ -0,0 +1,60 @@ +#include +#include +#include + +#include + + +#include + +namespace PMP = CGAL::Polygon_mesh_processing; + +typedef CGAL::Exact_predicates_inexact_constructions_kernel K; +typedef CGAL::Surface_mesh Mesh; +typedef PMP::Face_location Face_location; +typedef PMP::Edge_location Edge_location; + + +int main(int argc, char** argv) +{ + std::string filename = (argc > 1) ? std::string(argv[1]) + : CGAL::data_file_path("meshes/elephant.off"); + + Mesh mesh; + if(!CGAL::IO::read_polygon_mesh(filename, mesh) || !CGAL::is_triangle_mesh(mesh)) + { + std::cerr << "Invalid input." << std::endl; + return 1; + } + + std::size_t nb_faces = faces(mesh).size(); + + // take two random faces and pick the centroid + CGAL::Random rnd = CGAL::get_default_random(); + // CGAL::Random rnd(1695720148); + + std::cout << "seed " << rnd.get_seed() << std::endl; + Mesh::Face_index f = *std::next(faces(mesh).begin(), rnd.get_int(0, nb_faces)); + + Face_location src(f, CGAL::make_array(0.3,0.3,0.4)); + + K::Point_3 src_pt = PMP::construct_point(src, mesh); + std::cout << "src = " << src_pt << "\n"; + double target_distance = 0.5; + + + std::vector path; + PMP::walk_and_intersection_point_collection(mesh, src_pt, src.first, + K::Plane_3(src_pt, K::Vector_3(0,1,0)), + K::Plane_3(src_pt, K::Vector_3(1,0,0)), + target_distance, + path); + + + std::ofstream out("straightest_geodesic_path.polylines.txt"); + out << path.size() << " "; + std::copy(path.begin(), path.end(), std::ostream_iterator(out, " ")); + out << "\n"; + + return 0; +} diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/walk_to_select.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/walk_to_select.h index 6c52c94522e3..0ab3238b8c69 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/walk_to_select.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/walk_to_select.h @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include #include #include @@ -35,8 +35,9 @@ #include #include #include +#include -#include +#include #include @@ -444,12 +445,12 @@ get_sorted_intersected_halfedges(typename boost::graph_traits::fac if (k!=1) return boost::none; // TODO: avoid the constructions - boost::optional > + std::optional > inter_res = intersection( typename K::Segment_3(get(vpm, source(int_edges[0], tm)), get(vpm, target(int_edges[0], tm))), axis_1 ); - CGAL_assertion( inter_res != boost::none ); - const typename K::Point_3* pt = boost::get(&(*inter_res)); + CGAL_assertion( inter_res != std::nullopt ); + const typename K::Point_3* pt = std::get_if(&(*inter_res)); CGAL_assertion( pt!=NULL ); if (axis_2.oriented_side( *pt )!= ON_NEGATIVE_SIDE) @@ -570,6 +571,86 @@ two_side_walk_and_intersection_point_collection(const TriangleMesh& tm, return center_pos; } +template +std::size_t +walk_and_intersection_point_collection(const TriangleMesh& tm, + const typename K::Point_3& center, + typename boost::graph_traits::face_descriptor start_face, + const typename K::Plane_3& axis_1, + const typename K::Plane_3& axis_2, + double target_distance, + std::vector& inter_points, + const NamedParameters& np = parameters::default_values()) +{ + typedef boost::graph_traits BGT; + typedef typename BGT::halfedge_descriptor halfedge_descriptor; + using parameters::choose_parameter; + using parameters::get_parameter; + + // Vertex point maps + typedef typename GetVertexPointMap::const_type Vpm; + Vpm vpm = choose_parameter(get_parameter(np, internal_np::vertex_point), + get_const_property_map(boost::vertex_point, tm)); + + typedef walker_internal::Edge_intersection_with_plane Predicate; + typedef walker_internal::Walk_in_polygon_mesh_visitor Visitor; + + boost::optional< std::array > opt_int_edges = + get_sorted_intersected_halfedges(start_face, tm, vpm, axis_1, axis_2); + + if (opt_int_edges == boost::none) + { + std::cout << "no intersected edge, nothing is done\n"; + return 0; + } + + // first compute the out point from the center triangle (TODO: make more robust) + std::optional > + inter_res = intersection( typename K::Segment_3(get(vpm, source((*opt_int_edges)[0], tm)), + get(vpm, target((*opt_int_edges)[0], tm))), + axis_1 ); + if ( inter_res == std::nullopt) + { + CGAL_warning(!"Intersection 1 not found"); + return 0; + } + const typename K::Point_3* pt = std::get_if(&(*inter_res)); + if ( pt == nullptr ) + { + CGAL_warning(!"Intersection 1 is not a point"); + return 0; + } + + // walk only if needed + if ( CGAL::compare_squared_distance(center, *pt, target_distance*target_distance) == SMALLER ) + { + Predicate predicate(center, axis_1.orthogonal_vector(), + axis_2.orthogonal_vector(), tm, vpm); + Visitor visitor = Visitor(tm, vpm, target_distance, center); + visitor.register_initial_intersection({(*opt_int_edges)[0], *pt}); + walk_in_polygon_mesh(tm, + (*opt_int_edges)[0], CGAL::POLYHEDRON_EDGE, + BGT::null_halfedge(), CGAL::POLYHEDRON_NONE, + predicate, visitor); + visitor.get_intersection_points(std::back_inserter(inter_points)); + inter_points.back() = visitor.end_point(); // replace the last point by the real endpoint of the walk + // reverse sequence + std::reverse(inter_points.begin(), inter_points.end()); + } + else + inter_points.push_back(*pt); + + // add the center + save its position + std::size_t center_pos = inter_points.size(); + if (inter_points.back()!=center) + inter_points.push_back(center); + else + --center_pos; + + return center_pos; +} + template struct Weight_map{ const TriangleMesh& tm; From 5c41af03bf1e262ca109b6918a034d96a14352b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 29 Sep 2023 15:11:21 +0200 Subject: [PATCH 028/120] add missing deps Eigen is only needed for one function but for now they are all in the same header --- .../examples/Polygon_mesh_processing/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt b/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt index 9fc1422d0515..313392014806 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt @@ -76,8 +76,11 @@ if(TARGET CGAL::Eigen3_support) create_single_source_cgal_program("locally_shortest_path_sm_example.cpp") target_link_libraries(locally_shortest_path_sm_example PUBLIC CGAL::Eigen3_support) create_single_source_cgal_program("trace_bezier_segment_sm_example.cpp") + target_link_libraries(trace_bezier_segment_sm_example PUBLIC CGAL::Eigen3_support) create_single_source_cgal_program("geodesic_circles_sm_example.cpp") + target_link_libraries(geodesic_circles_sm_example PUBLIC CGAL::Eigen3_support) create_single_source_cgal_program("straightest_geodesic_sm_example.cpp") + target_link_libraries(straightest_geodesic_sm_example PUBLIC CGAL::Eigen3_support) else() message(STATUS "NOTICE: Examples that use Eigen will not be compiled.") endif() From ee4f2291e63ee01ce9ad39a3b4e78d22740ae0fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 29 Sep 2023 16:09:00 +0200 Subject: [PATCH 029/120] fix compilation (does not run yet). --- .../straightest_geodesic_sm_example.cpp | 15 +- .../Bsurf/locally_shortest_path.h | 157 ++++++++++-------- 2 files changed, 102 insertions(+), 70 deletions(-) diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/straightest_geodesic_sm_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/straightest_geodesic_sm_example.cpp index 3d8bab392d50..e0ac8f5e26d6 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/straightest_geodesic_sm_example.cpp +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/straightest_geodesic_sm_example.cpp @@ -4,8 +4,11 @@ #include - +#if 0 #include +#else + +#endif namespace PMP = CGAL::Polygon_mesh_processing; @@ -43,17 +46,23 @@ int main(int argc, char** argv) double target_distance = 0.5; + K::Vector_2 dir(1,1); +#if 0 std::vector path; PMP::walk_and_intersection_point_collection(mesh, src_pt, src.first, K::Plane_3(src_pt, K::Vector_3(0,1,0)), K::Plane_3(src_pt, K::Vector_3(1,0,0)), target_distance, path); - +#else + std::vector path = PMP::straightest_geodesic(src, dir, target_distance, mesh); +#endif std::ofstream out("straightest_geodesic_path.polylines.txt"); out << path.size() << " "; - std::copy(path.begin(), path.end(), std::ostream_iterator(out, " ")); + + for (auto fl : path) + out << " " << PMP::construct_point(fl, mesh); out << "\n"; return 0; diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index 03171c598936..b7d7b132e796 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -733,7 +733,7 @@ struct Locally_shortest_path_imp Point_3 v2 = get(vpm,target(next(h,mesh),mesh)); Vector_3 u = v1 - v0, v = v2 - v0, w = point - v0; - double d00 = u.length_squared(), d01 = u*v, d11 = v.length_squared(), d20 = w*u, + double d00 = u.squared_length(), d01 = u*v, d11 = v.squared_length(), d20 = w*u, d21 = w*v, d = d00 * d11 - d01 * d01; if (d == 0) @@ -822,7 +822,7 @@ struct Locally_shortest_path_imp std::vector e_to = polar_basis(vpm, mesh, from ,to); Vector_3 n_to = face_normal(vpm, mesh, to); double rot = teta + phi_ji + M_PI - phi_ij; - e_to =e_to*sqrt(v.length_squared()); + e_to =e_to*sqrt(v.squared_length()); v = rotate_vector(e_to, n_to, rot); } @@ -845,8 +845,8 @@ struct Locally_shortest_path_imp } std::array flat_from = init_flat_triangle(h,vpm,mesh); - std::array flat_to = unfold_face(h,vpm,mesh,flat_from); - Vector_2 bary = Vector_2{0.333, 0.333}; + std::array flat_to = unfold_face(h,vpm,mesh,flat_from); + Vector_2 c0 = 0.33*(flat_from[0]+flat_from[1]+flat_from[2]); Vector_2 c1 = 0.33*(flat_to[0]+flat_to[1]+flat_to[2]); Vector_2 e0 = flat_from[0] - c0; @@ -868,7 +868,7 @@ struct Locally_shortest_path_imp teta = 2 * M_PI - teta; double rot = teta + phi_ji + M_PI - phi_ij; - e1 =e1/sqrt(e1.length_squared())*sqrt(v.length_squared()); + e1 =e1/sqrt(e1.squared_length())*sqrt(v.squared_length()); Vector_3 v_3d = rotate_vector(Vector_3{e1.x(),e1.y(),0}, Vector_3{0,0,1}, rot); @@ -887,7 +887,7 @@ struct Locally_shortest_path_imp Vector_3 w=v; face_descriptor f_start=src.first; for(std::size_t i=0;i + point_is_vert(const Face_location& p,const FT& tol=1e-5) + { + auto bary=p.second; + if (bary[0] > tol && bary[1] <= tol && bary[2] <= tol) + return {true, 0}; + if (bary[1] > tol && bary[0] <= tol && bary[2] <= tol) + return {true, 1}; + if (bary[2] > tol && bary[0] <= tol && bary[1] <= tol) + return {true, 2}; + + return {false, -1}; + } + static //https://rootllama.wordpress.com/2014/06/20/ray-line-segment-intersection-test-in-2d/ std::pair @@ -977,23 +992,23 @@ struct Locally_shortest_path_imp { Vector_2 v1 = origin-left; Vector_2 v2 = right - left; - Vector_2 v3 = Vector_2{-direction.y, direction.x}; - double t0 = (v2.x()*v1.y()-v2.y()*v1.x())/ v2*v3; - double t1 = -left*v3/ v2*v3; + Vector_2 v3(-direction.y(), direction.x()); + double t0 = (v2.x()*v1.y()-v2.y()*v1.x()) / (v2*v3); + double t1 = -left*v3/ ( v2*v3 ); return std::make_pair(t0, t1); }; static - std::tuple + std::tuple segment_in_tri(const Vector_2& p, - const std::array& tri, - const Vector_2& dir,const halfedge_descriptor& h_curr,const int offset) + const std::array& tri, + const Vector_2& dir,const int offset) { //rotated the triangle in order to test intersection at meaningful edges before std::array rotated_tri=tri; - for(size_t k=0;k 0 && t1 >= -1e-4 && t1 <= 1 + 1e-4) { - return {((i+1)%3 + offset)%3, clamp(t1, 0.f, 1.f)}; //return the offset w.r.t h_ref + return {((i+1)%3 + offset)%3, std::clamp(t1, 0., 1.)}; //return the offset w.r.t h_ref } } CGAL_assertion(false); return {-1,-1}; } + static FT get_total_angle(const vertex_descriptor& vid, const TriangleMesh& mesh, @@ -1038,8 +1054,7 @@ struct Locally_shortest_path_imp const VertexPointMap &vpm, const vertex_descriptor& vid, const face_descriptor& tid, - const int kv, - const Vector_2 &dir) + const FT& init_angle) { FT total_angle=get_total_angle(vid,mesh,vpm); FT theta = 0.5 * total_angle; @@ -1050,11 +1065,9 @@ struct Locally_shortest_path_imp Point_3 vert = get(vpm,vid); Point_3 vert_adj=get(vpm,source(h,mesh)); - Vector_3 v = vert - vert_adj; + FT acc = init_angle; - FT acc = angle(v, dir); FT prev_angle = acc; - face_descriptor curr_tid = tid; while (acc < theta) { h=prev(opposite(h,mesh),mesh); @@ -1063,13 +1076,13 @@ struct Locally_shortest_path_imp acc += angle(vert_adj - vert,next_vert_adj - vert); vert_adj=next_vert_adj; } - auto offset = theta - prev; - Point_3 prev_vert_adj=get(vpm,target(next(h,mesh,mesh))); + auto offset = theta - prev_angle; + Point_3 prev_vert_adj=get(vpm,target(next(h,mesh),mesh)); FT l = sqrt(squared_distance(prev_vert_adj,vert)); FT phi = angle(vert - prev_vert_adj, vert_adj - prev_vert_adj); FT x = l * std::sin(offset) / std::sin(M_PI - phi - offset); - FT alpha = x / sqrt(squared_distance(vert_adj - prev_vert_adj)); + FT alpha = x / sqrt(squared_distance(vert_adj, prev_vert_adj)); halfedge_descriptor prev_h=prev(h,mesh); std::array flat_tid = init_flat_triangle(prev_h,vpm,mesh); @@ -1082,43 +1095,41 @@ struct Locally_shortest_path_imp static std::vector> - straightest_goedesic(const Face_location& p, + straightest_geodesic(const Face_location& p, const TriangleMesh& mesh, const VertexPointMap &vpm, const Vector_2& dir,const FT& len) { auto get_halfedge_offset=[&mesh](const halfedge_descriptor& h_ref,const halfedge_descriptor& h_curr) { - if(source(h_ref,mesh)==source(h_curr,mesh)) - return 0; - - if(source(next(h_ref,mesh),mesh)==source(h_curr,mesh)) - return 1; - - if(source(prev(h_ref,mesh),mesh)==source(h_curr,mesh)) - return 2; + if(source(h_ref,mesh)==source(h_curr,mesh)) return 0; + if(source(next(h_ref,mesh),mesh)==source(h_curr,mesh)) return 1; + if(source(prev(h_ref,mesh),mesh)==source(h_curr,mesh)) return 2; - std::cout<<"Error! Halfedges are in different faces"<& bary_edge) { - std::array bary_edge_in_face=make_array(0,0,0); + std::array bary_edge_in_face=make_array(0.,0.,0.); if (h_face!=h_edge) { if (h_face==next(h_edge, mesh)) @@ -1191,7 +1203,7 @@ struct Locally_shortest_path_imp { int curr_offset=get_halfedge_offset(h_ref,h_curr); - auto [k, t1] = segment_in_tri(flat_p, curr_flat_tid, curr_dir,h_curr,curr_offset); + auto [k, t1] = segment_in_tri(flat_p, curr_flat_tid, curr_dir,curr_offset); CGAL_assertion(k!=-1); std::array new_bary=make_array(0.,0.,0.); @@ -1205,13 +1217,22 @@ struct Locally_shortest_path_imp if (is_vert) { - uint vid = get_vertex(kv,curr_tid); + vertex_descriptor vid = target(get_halfedge(kv, h_ref), mesh); accumulated += - sqrt(squared_distance(construct_point(curr_p,mesh) - get(vpm,vid))); + sqrt(squared_distance(construct_point(curr_p,mesh), get(vpm,vid))); + + // Point_3 vert = get(vpm,vid); + // Point_3 vert_adj=get(vpm,source(h,mesh)); + // Vector_3 v = vert - vert_adj; + + //TODO add a 2D version of approximate_angle in CGAL + // FT init_angle = approximate_angle(curr_flat_tid[(kv+2)%3]-curr_flat_tid[kv], curr_dir); + auto tmp =curr_flat_tid[(kv+2)%3]-curr_flat_tid[kv]; + FT init_angle = approximate_angle(Vector_3(tmp.x(), tmp.y(), 0), Vector_3(curr_dir.x(), curr_dir.y(), 0)); std::tie(curr_dir, curr_tid, h_curr) = - polthier_condition_at_vert(mesh,vpm,vid,curr_tid,kv,curr_dir); + polthier_condition_at_vert(mesh,vpm,vid,curr_tid, init_angle); h_ref=halfedge(curr_tid,mesh); curr_flat_tid=init_flat_triangle(h_ref,vpm,mesh); @@ -1222,15 +1243,15 @@ struct Locally_shortest_path_imp } else { - h_curr=opposite(get_halfedge(curr_tid,k),mesh); + h_curr=opposite(get_halfedge(k, h_ref),mesh); face_descriptor adj = face(h_curr,mesh); std::array curr_alpha=make_array(t1,1-t1); //reversed because will switch face new_bary=edge_barycentric_coordinate(h_curr,halfedge(adj,mesh),curr_alpha); prev_p = curr_p; curr_p.first=adj; curr_p.second= new_bary; - accumulated += sqrt(squared_distance(construct_point(curr_p,mesh) - construct_point(prev_p,mesh))); - curr_dir=parallel_transport_through_flattening(curr_dir,vpm,mesh,curr_tid,adj); + accumulated += sqrt(squared_distance(construct_point(curr_p,mesh), construct_point(prev_p,mesh))); + parallel_transport_through_flattening(curr_dir,vpm,mesh,curr_tid,adj); curr_tid = adj; h_ref=halfedge(curr_tid,mesh); @@ -1243,10 +1264,10 @@ struct Locally_shortest_path_imp } double excess = accumulated - len; - Vector_3 prev_pos = construct_point(result.rbegin()[1],mesh); - Vector_3 last_pos = construct_point(result.back(),mesh); - double alpha = excess / sqrt((last_pos - prev_pos).length_squared()); - Point_3 pos = alpha * prev_pos + (1 - alpha) * last_pos; + Point_3 prev_pos = construct_point(*std::next(result.rbegin()),mesh); + Point_3 last_pos = construct_point(result.back(),mesh); + double alpha = excess / sqrt((last_pos - prev_pos).squared_length()); + Point_3 pos = barycenter(prev_pos, alpha, last_pos, 1 - alpha); auto [inside, bary] = point_in_triangle(vpm,mesh,prev_p.first,pos); @@ -1596,21 +1617,6 @@ struct Geodesic_circle_impl std::vector> graph = {}; }; - static - std::tuple - point_is_vert(const Face_location& p,const FT& tol=1e-5) - { - auto bary=p.second; - if (bary[0] > tol && bary[1] <= tol && bary[2] <= tol) - return {true, 0}; - if (bary[1] > tol && bary[0] <= tol && bary[2] <= tol) - return {true, 1}; - if (bary[2] > tol && bary[0] <= tol && bary[1] <= tol) - return {true, 2}; - - return {false, -1}; - } - static void connect_nodes(geodesic_solver &solver, const vertex_descriptor& a, @@ -2418,6 +2424,23 @@ void approximate_geodesic_distance_field(const Face_location& } } +template +std::vector> +straightest_geodesic(const Face_location &src, + const typename K::Vector_2& dir, + const typename K::FT len, + const TriangleMesh &tmesh) +{ + //TODO replace with named parameter + using VPM = typename boost::property_map::const_type; + using Impl = internal::Locally_shortest_path_imp; + VPM vpm = get(CGAL::vertex_point, tmesh); + + + return Impl::straightest_geodesic(src, tmesh, vpm, dir, len); +} + + } // namespace Polygon_mesh_processing } // namespace CGAL From 44fabf47e3132b03e128f3e7224a29431099c795 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 29 Sep 2023 16:44:05 +0200 Subject: [PATCH 030/120] add missing class --- .../Polygon_mesh_processing/geodesic_circles_sm_example.cpp | 3 --- .../CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/geodesic_circles_sm_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/geodesic_circles_sm_example.cpp index 413561efc7b3..fac91379a0f2 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/geodesic_circles_sm_example.cpp +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/geodesic_circles_sm_example.cpp @@ -4,7 +4,6 @@ #include - namespace PMP = CGAL::Polygon_mesh_processing; typedef CGAL::Exact_predicates_inexact_constructions_kernel K; @@ -43,12 +42,10 @@ int main(int argc, char** argv) std::cout << "src = " << PMP::construct_point(src, mesh) << "\n"; auto distance_map = mesh.add_property_map("v:dstmap").first; - std::vector edge_locations; CGAL::Polygon_mesh_processing::approximate_geodesic_distance_field(src, distance_map, mesh); std::ofstream out("circles.polylines.txt"); - for(auto v: vertices(mesh)) std::cout << get(distance_map, v) << "\n"; for (double r : radii) { for (Mesh::Face_index f : faces(mesh)) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index b7d7b132e796..f29c133ec09f 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -2066,7 +2066,7 @@ struct Geodesic_circle_impl std::vector> nodes; nodes.reserve(6); - auto [is_vert,offset]=point_is_vert(p); + auto [is_vert,offset]=Locally_shortest_path_imp::point_is_vert(p); if (is_vert) { vertex_descriptor vid = get_vid(offset,p.first); nodes.push_back({vid, 0}); From e74b2ac044c756430526b476cb53b1586f8f716d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 5 Jan 2024 15:01:20 +0100 Subject: [PATCH 031/120] Revert "add missing entry" This reverts commit 768f6b8ef2104bbf4c588a611647e5fd0ad91515. --- Polyhedron/demo/Polyhedron/CMakeLists.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/Polyhedron/demo/Polyhedron/CMakeLists.txt b/Polyhedron/demo/Polyhedron/CMakeLists.txt index 7b1725d718ab..325a670e8096 100644 --- a/Polyhedron/demo/Polyhedron/CMakeLists.txt +++ b/Polyhedron/demo/Polyhedron/CMakeLists.txt @@ -264,8 +264,6 @@ if(CGAL_Qt5_FOUND AND Qt5_FOUND) add_item(scene_edit_box_item Plugins/PCA/Scene_edit_box_item.cpp) - add_item(scene_edit_path_item Plugins/Bsurf/Scene_edit_path_item.cpp) - add_item(scene_image_item Scene_image_item.cpp) add_item(scene_surface_mesh_item Scene_surface_mesh_item.cpp) From bdffb60b038870ae06a13ecfc9f940f5abda6bf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 5 Jan 2024 15:01:23 +0100 Subject: [PATCH 032/120] Revert "copy edit plugin/item as a base for editable shortest path" This reverts commit 11991e62474863eed5111d02c1ae4eea15a3d42d. --- .../Polyhedron/Plugins/Bsurf/CMakeLists.txt | 6 - .../Bsurf/Locally_shortest_path_plugin.cpp | 155 -- .../Plugins/Bsurf/Scene_edit_path_item.cpp | 1309 ----------------- .../Plugins/Bsurf/Scene_edit_path_item.h | 66 - .../Bsurf/Scene_edit_path_item_config.h | 10 - 5 files changed, 1546 deletions(-) delete mode 100644 Polyhedron/demo/Polyhedron/Plugins/Bsurf/CMakeLists.txt delete mode 100644 Polyhedron/demo/Polyhedron/Plugins/Bsurf/Locally_shortest_path_plugin.cpp delete mode 100644 Polyhedron/demo/Polyhedron/Plugins/Bsurf/Scene_edit_path_item.cpp delete mode 100644 Polyhedron/demo/Polyhedron/Plugins/Bsurf/Scene_edit_path_item.h delete mode 100644 Polyhedron/demo/Polyhedron/Plugins/Bsurf/Scene_edit_path_item_config.h diff --git a/Polyhedron/demo/Polyhedron/Plugins/Bsurf/CMakeLists.txt b/Polyhedron/demo/Polyhedron/Plugins/Bsurf/CMakeLists.txt deleted file mode 100644 index 925904c3d238..000000000000 --- a/Polyhedron/demo/Polyhedron/Plugins/Bsurf/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -include(polyhedron_demo_macros) - -polyhedron_demo_plugin(locally_shortest_path_plugin Locally_shortest_path_plugin) -target_link_libraries(locally_shortest_path_plugin PUBLIC scene_edit_path_item - scene_surface_mesh_item) - diff --git a/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Locally_shortest_path_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Locally_shortest_path_plugin.cpp deleted file mode 100644 index 424762f7ddfd..000000000000 --- a/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Locally_shortest_path_plugin.cpp +++ /dev/null @@ -1,155 +0,0 @@ -#include - -#include -#include -#include "Scene_edit_path_item.h" -#include "Scene_surface_mesh_item.h" -#include -#include -#include -#include -#include -#include - - -#include -#include -using namespace CGAL::Three; -class Locally_shortest_path_plugin : - public QObject, - public Polyhedron_demo_plugin_interface -{ - Q_OBJECT - Q_INTERFACES(CGAL::Three::Polyhedron_demo_plugin_interface) - Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.PluginInterface/1.0") - -public: - void init(QMainWindow* mainWindow, CGAL::Three::Scene_interface* scene_interface, Messages_interface*); - QList actions() const { - return QList() << actionBbox - << actionExport; - } - - bool applicable(QAction* a) const { - if(a==actionBbox &&scene->numberOfEntries() > 0) - return true; - else if(a==actionExport ) - { - for(int i = 0, end = scene->numberOfEntries(); - i < end; ++i) - { - if(qobject_cast(scene->item(i))) - { - return true; - } - } - } - return false;} -public Q_SLOTS: - - void bbox(); - void enableAction(); - void exportToPoly(); - void connectNewViewer(QObject* o) - { - for(int i=0; inumberOfEntries(); ++i) - { - Scene_edit_path_item* item = qobject_cast( - scene->item(i)); - if(item) - o->installEventFilter(item); - } - } - -private: - CGAL::Three::Scene_interface* scene; - QMainWindow* mw; - QAction* actionBbox; - QAction* actionExport; - - -}; // end Locally_shortest_path_plugin - -void Locally_shortest_path_plugin::init(QMainWindow* mainWindow, CGAL::Three::Scene_interface* scene_interface, Messages_interface*) -{ - scene = scene_interface; - mw = mainWindow; - actionBbox = new QAction(tr("Create Locally Shortest Path"), mainWindow); - connect(actionBbox, SIGNAL(triggered()), - this, SLOT(bbox())); - actionExport = new QAction(tr("PIPO"), mainWindow); - connect(actionExport, SIGNAL(triggered()), - this, SLOT(exportToPoly())); - connect(mw, SIGNAL(newViewerCreated(QObject*)), - this, SLOT(connectNewViewer(QObject*))); -} - -void Locally_shortest_path_plugin::bbox() -{ - for(int i = 0, end = scene->numberOfEntries(); - i < end; ++i) - { - if(qobject_cast(scene->item(i))) - return; - } - QApplication::setOverrideCursor(Qt::WaitCursor); - Scene_edit_path_item* item = new Scene_edit_path_item(scene); - connect(item, SIGNAL(destroyed()), - this, SLOT(enableAction())); - item->setName("Edit box"); - item->setRenderingMode(FlatPlusEdges); - Q_FOREACH(CGAL::QGLViewer* viewer, CGAL::QGLViewer::QGLViewerPool()) - viewer->installEventFilter(item); - - scene->addItem(item); - actionBbox->setEnabled(false); - - QApplication::restoreOverrideCursor(); -} - -void Locally_shortest_path_plugin::enableAction() { - actionBbox->setEnabled(true); -} - -void Locally_shortest_path_plugin::exportToPoly() -{ - int id =0; - const CGAL::qglviewer::Vec v_offset = Three::mainViewer()->offset(); - EPICK::Vector_3 offset(v_offset.x, v_offset.y, v_offset.z); - Scene_edit_path_item* item = nullptr; - for(int i = 0, end = scene->numberOfEntries(); - i < end; ++i) - { - item = qobject_cast(scene->item(i)); - if(item) - { - id = i; - break; - } - } - - EPICK::Point_3 points[8]; - for(int i=0; i<8; ++i) - { - points[i] = EPICK::Point_3(item->point(i,0),item->point(i,1), item->point(i,2))-offset; - } - - Scene_surface_mesh_item* poly_item = new Scene_surface_mesh_item(); - CGAL::make_hexahedron(points[0], - points[3], - points[2], - points[1], - points[5], - points[4], - points[7], - points[6], - *poly_item->polyhedron()); - CGAL::Polygon_mesh_processing::triangulate_faces(*poly_item->polyhedron()); - poly_item->setName("Edit box"); - poly_item->setRenderingMode(FlatPlusEdges); - poly_item->invalidateOpenGLBuffers(); - scene->replaceItem(id, poly_item, true); - item->deleteLater(); - actionBbox->setEnabled(true); -} -#include "Locally_shortest_path_plugin.moc" diff --git a/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Scene_edit_path_item.cpp b/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Scene_edit_path_item.cpp deleted file mode 100644 index 2eaa561a90f0..000000000000 --- a/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Scene_edit_path_item.cpp +++ /dev/null @@ -1,1309 +0,0 @@ -#include "Scene_edit_path_item.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace CGAL::Three; -typedef Viewer_interface Vi; -typedef Triangle_container Tc; -typedef Edge_container Ec; -typedef Scene_edit_path_item_priv Priv; - -struct Scene_edit_path_item::vertex{ - int id; - double *x; - double *y; - double *z; - - CGAL::Point_3 position()const - { - return CGAL::Point_3(*x,*y,*z); - } - double operator[](int i) - { - switch(i) - { - case 0: - return *x; - case 1: - return *y; - case 2: - return *z; - default: - return 0; - } - } -}; -struct Scene_edit_path_item::edge{ - vertex* source; - vertex* target; -}; -struct Scene_edit_path_item::face{ - vertex* vertices[4]; -}; - -struct Scene_edit_path_item_priv{ - typedef CGAL::Simple_cartesian Kernel; - enum Face_containers{ - Faces = 0, - S_Faces, - Spheres, - S_Spheres, - P_Spheres, - P_Faces, - Nbf - }; - - enum Line_containers{ - Edges = 0, - S_Edges, - P_Edges, - Nbe - }; - - enum HL_Primitive{ - VERTEX=0, - EDGE, - FACE, - NO_TYPE - }; - - Scene_edit_path_item_priv(const Scene_interface *scene_interface, Scene_edit_path_item* ebi) - { - const CGAL::qglviewer::Vec offset = static_cast(CGAL::QGLViewer::QGLViewerPool().first())->offset(); - ready_to_hl = true; - scene = scene_interface; - item = ebi; - selection_on = false; - Scene_item::Bbox bb = scene->bbox(); - double x=(bb.xmin()+bb.xmax())/2; - double y=(bb.ymin()+bb.ymax())/2; - double z=(bb.zmin()+bb.zmax())/2; - center_ = CGAL::qglviewer::Vec(x,y,z); - relative_center_ = CGAL::qglviewer::Vec(0,0,0); - remodel_frame = new Scene_item::ManipulatedFrame(); - remodel_frame->setTranslationSensitivity(1.0); - frame = new Scene_item::ManipulatedFrame(); - frame->setPosition(center_+offset); - frame->setSpinningSensitivity(100.0); //forbid spinning - constraint.setRotationConstraintType(CGAL::qglviewer::AxisPlaneConstraint::AXIS); - constraint.setTranslationConstraintType(CGAL::qglviewer::AxisPlaneConstraint::FREE); - constraint.setRotationConstraintDirection(CGAL::qglviewer::Vec(.0,.0,.1)); - frame->setConstraint(&constraint); - //create the sphere model - pool[0] = bb.xmin(); pool[3] = bb.xmax(); - pool[1] = bb.ymin(); pool[4] = bb.ymax(); - pool[2] = bb.zmin(); pool[5] = bb.zmax(); - - vertex_spheres.resize(0); - normal_spheres.resize(0); - create_flat_sphere(1.0f, vertex_spheres, normal_spheres,10); - // 5-----6 - // . | . | - // 4------7 | - // | | | | - // | 1-|---2 - // | . |. - // 0------3 - - //vertices - for( int i = 0; i< 8; ++i) - { - - vertices[i].x = ((i/2)%2==0)? &pool[0]:&pool[3]; - vertices[i].y = (i/4==0)? &pool[1]:&pool[4]; - vertices[i].z = (((i+1)/2)%2==1)? &pool[2]:&pool[5]; - vertices[i].id = i; - } - - // .--5--. - // 4 | 6 | - // .--7-1-. 2 - // | | | | - // 0 .-39--. - // | 8 |10 - // .--11--. - - //edges - for( int i=0; i<12; ++i) - { - if(i<4) - { - edges[i].source = &vertices[i]; - edges[i].target = &vertices[i+4]; - } - else if(i<8) - { - edges[i].source = &vertices[i]; - edges[i].target = &vertices[(i+1)%4 +4]; - } - else - { - edges[i].source = &vertices[i%4]; - edges[i].target = &vertices[(i+1) %4]; - } - vertex_edges.resize(0); - } - - // - // 5------6 - // | | - // .-----. | 2 | - // . |5 . | | | - // .------.2 | 5------1------2------6------5 - // | 1 | | | | | | | | - // | 4.-|-3-. | 1 | 0 | 3 | 5 | - // | . 0 |. | | | | | - // .------. 4------0------3------7------4 - // | | - // | 4 | - // | | - // 4------7 - - - //faces - for( int i=0; i<4; ++i) - { - faces[0].vertices[i] = &vertices[i]; - } - - for( int i=1; i<4; ++i) - { - faces[i].vertices[0] = &vertices[i]; - faces[i].vertices[1] = &vertices[(i-1)]; - faces[i].vertices[2] = &vertices[i+3]; - faces[i].vertices[3] = &vertices[i+4]; - } - - faces[4].vertices[0] = &vertices[0]; - faces[4].vertices[1] = &vertices[3]; - faces[4].vertices[2] = &vertices[7]; - faces[4].vertices[3] = &vertices[4]; - - for( int i=0; i<4; ++i) - { - faces[5].vertices[i] = &vertices[i+4]; - } - - vertex_faces.resize(0); - normal_faces.resize(0); - - for(int i=0; i<8; ++i) - for(int j=0; j<3; ++j) - last_pool[i][j] = vertices[i][j]; - reset_selection(); - last_picked_id = -1; - last_picked_type = -1; - QPixmap pix(":/cgal/cursors/resources/rotate_around_cursor.png"); - rotate_cursor = QCursor(pix); - } - ~Scene_edit_path_item_priv(){ - delete frame; - delete remodel_frame; - } - mutable std::vector vertex_edges; - mutable std::vector color_edges; - mutable std::vector vertex_spheres; - mutable std::vector normal_spheres; - mutable std::vector center_spheres; - mutable std::vector color_spheres; - mutable std::vector vertex_faces; - mutable std::vector normal_faces; - mutable std::vector color_faces; - mutable std::vector hl_vertex; - mutable std::vector hl_normal; - - double pool[6]; - //id|coord - double last_pool[8][3]; - bool ready_to_hl; - - - CGAL::qglviewer::ManipulatedFrame* frame; - CGAL::qglviewer::ManipulatedFrame* remodel_frame; - CGAL::qglviewer::Vec rf_last_pos; - CGAL::qglviewer::LocalConstraint constraint; - CGAL::qglviewer::Vec center_; - CGAL::qglviewer::Vec relative_center_; - - mutable Scene_edit_path_item::vertex vertices[8]; - mutable Scene_edit_path_item::edge edges[12]; - mutable Scene_edit_path_item::face faces[6]; - - std::vector selected_vertices; - void reset_selection(); - bool selection_on; - void picking(int& type, int& id, Viewer_interface *viewer); - - void initializeBuffers(Viewer_interface *viewer)const; - - void computeElements() const; - void draw_picking(Viewer_interface*); - void remodel_box(const QVector3D &dir); - double applyX(int id, double x, double dirx); - double applyY(int id, double y, double diry); - double applyZ(int id, double z, double dirz); - const Scene_interface* scene; - Scene_edit_path_item* item; - QPoint picked_pixel; - HL_Primitive hl_type; - int last_picked_id; - int last_picked_type; - QCursor rotate_cursor; - -}; - -Scene_edit_path_item::Scene_edit_path_item() -{ - d = nullptr; -} -Scene_edit_path_item::Scene_edit_path_item(const Scene_interface *scene_interface) -{ - d = new Scene_edit_path_item_priv(scene_interface, this); - - are_buffers_filled = false; - - Q_FOREACH(CGAL::QGLViewer* v, CGAL::QGLViewer::QGLViewerPool()) - { - v->setMouseTracking(true); - } - connect(Three::mainWindow(), SIGNAL(newViewerCreated(QObject*)), - this, SLOT(connectNewViewer(QObject*))); - - setTriangleContainer(Priv::P_Faces , new Tc(Vi::PROGRAM_NO_SELECTION, false)); - setTriangleContainer(Priv::Faces , new Tc(Vi::PROGRAM_WITH_LIGHT, false)); - setTriangleContainer(Priv::S_Faces , new Tc(Vi::PROGRAM_WITH_LIGHT, false)); - setTriangleContainer(Priv::Spheres , new Tc(Vi::PROGRAM_SPHERES, false)); - setTriangleContainer(Priv::S_Spheres, new Tc(Vi::PROGRAM_SPHERES, false)); - setTriangleContainer(Priv::P_Spheres, new Tc(Vi::PROGRAM_DARK_SPHERES, false)); - - - for(int i=Priv::Nbe-1; i>=0; --i) - { - setEdgeContainer(i, - new Ec(Three::mainViewer()->isOpenGL_4_3() - ? Vi::PROGRAM_SOLID_WIREFRAME - : Vi::PROGRAM_NO_SELECTION, - false)); - } -} -QString Scene_edit_path_item::toolTip() const { - - return QString(); -} - -void Scene_edit_path_item::drawSpheres(Viewer_interface *viewer, const QMatrix4x4 f_matrix ) const -{ - GLdouble d_mat[16]; - QMatrix4x4 mv_mat; - viewer->camera()->getModelViewMatrix(d_mat); - for (int i=0; i<16; ++i) - mv_mat.data()[i] = GLfloat(d_mat[i]); - mv_mat = mv_mat*f_matrix; - double radius =std::sqrt( - (point(6,0) - point(0,0)) * (point(6,0) - point(0,0)) + - (point(6,1) - point(0,1)) * (point(6,1) - point(0,1)) + - (point(6,2) - point(0,2)) * (point(6,2) - point(0,2))) *0.02 ; - - Tc* tc = getTriangleContainer(Priv::Spheres); - tc->setFrameMatrix(f_matrix); - tc->setMvMatrix(mv_mat); - tc->setClipping(false); - tc->getVao(viewer)->bind(); - tc->getVao(viewer)->program->setAttributeValue("radius",radius); - tc->getVao(viewer)->release(); - tc->setColor(QColor(Qt::red)); - tc->draw(viewer, true); -} - -void Scene_edit_path_item::draw(Viewer_interface *viewer) const -{ - if(!isInit(viewer)) - initGL(viewer); - if ( getBuffersFilled() && - ! getBuffersInit(viewer)) - { - initializeBuffers(viewer); - setBuffersInit(viewer, true); - } - if(!getBuffersFilled()) - { - computeElements(); - initializeBuffers(viewer); - } - QMatrix4x4 f_matrix; - for (int i=0; i<16; ++i){ - f_matrix.data()[i] = (float)d->frame->matrix()[i]; - } - - drawSpheres(viewer, f_matrix); - -} - -void Scene_edit_path_item::drawEdges(Viewer_interface* viewer) const -{ - if(!isInit(viewer)) - initGL(viewer); - if ( getBuffersFilled() && - ! getBuffersInit(viewer)) - { - initializeBuffers(viewer); - setBuffersInit(viewer, true); - } - if(!getBuffersFilled()) - { - computeElements(); - initializeBuffers(viewer); - } - QMatrix4x4 f_matrix; - for (int i=0; i<16; ++i){ - f_matrix.data()[i] = (float)d->frame->matrix()[i]; - } - Ec* ec = getEdgeContainer(Priv::Edges); - if(viewer->isOpenGL_4_3()) - { - QVector2D vp(viewer->width(), viewer->height()); - ec->setViewport(vp); - ec->setWidth(6.0f); - } - ec->setClipping(false); - ec->setFrameMatrix(f_matrix); - ec->setColor(QColor(Qt::black)); - ec->draw(viewer, true); - - if(renderingMode() == Wireframe) - { - drawSpheres(viewer, f_matrix); - } - drawHl(viewer); - drawTransparent(viewer); -} - -void Scene_edit_path_item::compute_bbox() const -{ - const CGAL::qglviewer::Vec offset = static_cast(CGAL::QGLViewer::QGLViewerPool().first())->offset(); - - - QVector3D vmin(d->pool[0], d->pool[1], d->pool[2]); - QVector3D vmax(d->pool[3], d->pool[4], d->pool[5]); - - for(int i=0; i< 3; ++i) - { - vmin[i] += d->frame->translation()[i]-d->center_[i]-offset[i]; - vmax[i] += d->frame->translation()[i]-d->center_[i]-offset[i]; - } - - setBbox(Scene_item::Bbox(vmin.x(),vmin.y(),vmin.z(),vmax.x(),vmax.y(),vmax.z())); -} - - - - -void push_xyz(std::vector &v, - const Scene_edit_path_item::Kernel::Point_3& p, - CGAL::qglviewer::Vec center_ = CGAL::qglviewer::Vec(0,0,0)) -{ - v.push_back(p.x()-center_.x); - v.push_back(p.y()-center_.y); - v.push_back(p.z()-center_.z); -} - -void push_normal(std::vector &v, int id) -{ - switch(id) - { - case 0: - v.push_back(0); - v.push_back(-1); - v.push_back(0); - break; - case 1: - v.push_back(-1); - v.push_back(0); - v.push_back(0); - break; - case 2: - v.push_back(0); - v.push_back(0); - v.push_back(-1); - break; - case 3: - v.push_back(1); - v.push_back(0); - v.push_back(0); - break; - case 4: - v.push_back(0); - v.push_back(0); - v.push_back(1); - break; - case 5: - v.push_back(0); - v.push_back(1); - v.push_back(0); - break; - default: - break; - } -} - -void Scene_edit_path_item_priv::computeElements() const -{ - vertex_edges.clear(); - vertex_faces.clear(); - normal_faces.clear(); - center_spheres.clear(); - color_edges.clear(); - color_faces.clear(); - color_spheres.clear(); - - //edges - for( int i=0; i<12; ++i) - { - if(i<4) - { - vertex_edges.push_back(vertices[i].position().x()-center_.x); - vertex_edges.push_back(vertices[i].position().y()-center_.y); - vertex_edges.push_back(vertices[i].position().z()-center_.z); - - center_spheres.push_back(vertices[i].position().x()-center_.x); - center_spheres.push_back(vertices[i].position().y()-center_.y); - center_spheres.push_back(vertices[i].position().z()-center_.z); - - vertex_edges.push_back(vertices[i+4].position().x()-center_.x); - vertex_edges.push_back(vertices[i+4].position().y()-center_.y); - vertex_edges.push_back(vertices[i+4].position().z()-center_.z); - - } - else if(i<8) - { - vertex_edges.push_back(vertices[i].position().x()-center_.x); - vertex_edges.push_back(vertices[i].position().y()-center_.y); - vertex_edges.push_back(vertices[i].position().z()-center_.z); - - center_spheres.push_back(vertices[i].position().x()-center_.x); - center_spheres.push_back(vertices[i].position().y()-center_.y); - center_spheres.push_back(vertices[i].position().z()-center_.z); - - vertex_edges.push_back(vertices[(i+1)%4 +4].position().x()-center_.x); - vertex_edges.push_back(vertices[(i+1)%4 +4].position().y()-center_.y); - vertex_edges.push_back(vertices[(i+1)%4 +4].position().z()-center_.z); - } - else - { - vertex_edges.push_back(vertices[i%4].position().x()-center_.x); - vertex_edges.push_back(vertices[i%4].position().y()-center_.y); - vertex_edges.push_back(vertices[i%4].position().z()-center_.z); - - vertex_edges.push_back(vertices[(i+1) %4].position().x()-center_.x); - vertex_edges.push_back(vertices[(i+1) %4].position().y()-center_.y); - vertex_edges.push_back(vertices[(i+1) %4].position().z()-center_.z); - } - color_edges.push_back(0); - color_edges.push_back((20.0*i+10)/255); - color_edges.push_back(0); - - color_edges.push_back(0); - color_edges.push_back((20.0*i+10)/255); - color_edges.push_back(0); - - } - //faces - for( int i=0; i<6; ++i) - { - push_xyz(vertex_faces, faces[i].vertices[0]->position(), center_); - push_xyz(vertex_faces, faces[i].vertices[3]->position(), center_); - push_xyz(vertex_faces, faces[i].vertices[2]->position(), center_); - - push_xyz(vertex_faces, faces[i].vertices[0]->position(), center_); - push_xyz(vertex_faces, faces[i].vertices[2]->position(), center_); - push_xyz(vertex_faces, faces[i].vertices[1]->position(), center_); - - for( int j=0; j<6; ++j) - { - push_normal(normal_faces, i); - - color_faces.push_back(0); - color_faces.push_back(0); - color_faces.push_back((20.0*i+10)/255); - } - } - - //spheres - for( int i=0; i<8; ++i) - { - color_spheres.push_back((20.0*i+10)/255); - color_spheres.push_back(0); - color_spheres.push_back(0); - } -} - -Scene_edit_path_item::~Scene_edit_path_item() -{ - CGAL::QGLViewer* viewer = *CGAL::QGLViewer::QGLViewerPool().begin(); - viewer->setMouseTracking(false); - - delete d; -} - -// Indicate if rendering mode is supported -bool Scene_edit_path_item::supportsRenderingMode(RenderingMode m) const { - return (m==Wireframe || m==FlatPlusEdges); -} - -Scene_item::ManipulatedFrame* -Scene_edit_path_item::manipulatedFrame() -{ - return d->frame; -} - -double Scene_edit_path_item::point(short i, short j) const -{ - CGAL::qglviewer::Vec pos(d->vertices[i].position().x()-d->center_.x, - d->vertices[i].position().y()-d->center_.y, - d->vertices[i].position().z()-d->center_.z); - return (d->frame->inverseCoordinatesOf(pos))[j]; -} - -void Scene_edit_path_item::highlight(Viewer_interface *viewer) -{ - d->ready_to_hl = true; - viewer->makeCurrent(); - int type = -1, id = -1; - //pick - if(!d->selection_on) - { - d->picking(type, id, viewer); - d->last_picked_id = id; - d->last_picked_type = type; - } - //highlight - d->hl_normal.clear(); - d->hl_vertex.clear(); - if(type !=-1) - { - switch(d->last_picked_type) - { - case 0: - { - //compute - d->hl_vertex.push_back(d->vertices[d->last_picked_id].position().x()-d->center_.x); - d->hl_vertex.push_back(d->vertices[d->last_picked_id].position().y()-d->center_.y); - d->hl_vertex.push_back(d->vertices[d->last_picked_id].position().z()-d->center_.z); - //fill buffers - Tc* tc = getTriangleContainer(Priv::S_Spheres); - tc->reset_vbos(ALL); - tc->allocate( - Tc::Flat_vertices, - d->vertex_spheres.data(), - static_cast(d->vertex_spheres.size()*sizeof(float))); - - tc->allocate( - Tc::Flat_normals, - d->normal_spheres.data(), - static_cast(d->normal_spheres.size()*sizeof(float))); - - tc->allocate( - Tc::Facet_centers, - d->hl_vertex.data(), - static_cast(d->hl_vertex.size()*sizeof(float))); - - tc->setFlatDataSize(d->vertex_spheres.size()); - tc->setCenterSize(d->hl_vertex.size()); - //draw - d->hl_type = Scene_edit_path_item_priv::VERTEX; - Q_FOREACH(CGAL::QGLViewer* v, CGAL::QGLViewer::QGLViewerPool()) - { - CGAL::Three::Viewer_interface* viewer = - static_cast(v); - tc->initializeBuffers(viewer); - } - break; - } - case 1: - { - //compute - d->hl_vertex.push_back(d->edges[d->last_picked_id].source->position().x()-d->center_.x); - d->hl_vertex.push_back(d->edges[d->last_picked_id].source->position().y()-d->center_.y); - d->hl_vertex.push_back(d->edges[d->last_picked_id].source->position().z()-d->center_.z); - - d->hl_vertex.push_back(d->edges[d->last_picked_id].target->position().x()-d->center_.x); - d->hl_vertex.push_back(d->edges[d->last_picked_id].target->position().y()-d->center_.y); - d->hl_vertex.push_back(d->edges[d->last_picked_id].target->position().z()-d->center_.z); - - //fill buffers - Ec* ec = getEdgeContainer(Priv::S_Edges); - ec->reset_vbos(ALL); - ec->allocate( - Ec::Vertices, - d->hl_vertex.data(), - static_cast(d->hl_vertex.size()*sizeof(float))); - ec->setFlatDataSize(d->hl_vertex.size()); - //draw - d->hl_type = Scene_edit_path_item_priv::EDGE; - Q_FOREACH(CGAL::QGLViewer* v, CGAL::QGLViewer::QGLViewerPool()) - { - CGAL::Three::Viewer_interface* viewer = - static_cast(v); - ec->initializeBuffers(viewer); - } - break; - } - case 2: - { - //compute - push_xyz(d->hl_vertex, d->faces[d->last_picked_id].vertices[0]->position(), d->center_); - push_xyz(d->hl_vertex, d->faces[d->last_picked_id].vertices[3]->position(), d->center_); - push_xyz(d->hl_vertex, d->faces[d->last_picked_id].vertices[2]->position(), d->center_); - - push_xyz(d->hl_vertex, d->faces[d->last_picked_id].vertices[0]->position(), d->center_); - push_xyz(d->hl_vertex, d->faces[d->last_picked_id].vertices[2]->position(), d->center_); - push_xyz(d->hl_vertex, d->faces[d->last_picked_id].vertices[1]->position(), d->center_); - - for( int j=0; j<6; ++j) - { - push_normal(d->hl_normal, d->last_picked_id); - } - //fill buffers - Tc* tc = getTriangleContainer(Priv::S_Faces); - tc->reset_vbos(ALL); - tc->allocate( - Tc::Flat_vertices, - d->hl_vertex.data(), - static_cast(d->hl_vertex.size()*sizeof(float))); - - tc->allocate( - Tc::Flat_normals, - d->hl_normal.data(), - static_cast(d->hl_normal.size()*sizeof(float))); - tc->setFlatDataSize(d->hl_vertex.size()); - //draw - d->hl_type = Scene_edit_path_item_priv::FACE; - Q_FOREACH(CGAL::QGLViewer* v, CGAL::QGLViewer::QGLViewerPool()) - { - CGAL::Three::Viewer_interface* viewer = - static_cast(v); - tc->initializeBuffers(viewer); - } - break; - } - default: - d->hl_type = Scene_edit_path_item_priv::NO_TYPE; - break; - } - } - else - clearHL(); - redraw(); - - d->ready_to_hl = false; -} - -void Scene_edit_path_item::clearHL() -{ - Viewer_interface* viewer = dynamic_cast(*CGAL::QGLViewer::QGLViewerPool().begin()); - viewer->makeCurrent(); - d->hl_normal.clear(); - d->hl_vertex.clear(); - - Tc* tc = getTriangleContainer(Priv::S_Spheres); - tc->reset_vbos(ALL); - tc->allocate(Tc::Flat_vertices, d->vertex_spheres.data(), - static_cast(d->vertex_spheres.size()*sizeof(float))); - tc->allocate(Tc::Flat_normals, - d->normal_spheres.data(), - static_cast(d->normal_spheres.size()*sizeof(float))); - - tc->allocate(Tc::Facet_centers, nullptr, 0); - - tc->initializeBuffers(viewer); - tc->setFlatDataSize(0); - tc->setCenterSize(0); - //draw - Ec* ec = getEdgeContainer(Priv::S_Edges); - ec->reset_vbos(ALL); - ec->allocate(Ec::Vertices, nullptr, 0); - ec->initializeBuffers(viewer); - ec->setFlatDataSize(0); - - tc = getTriangleContainer(Priv::S_Faces); - tc->reset_vbos(ALL); - tc->allocate(Tc::Flat_vertices, nullptr, 0); - tc->allocate(Tc::Flat_normals, nullptr, 0); - tc->initializeBuffers(viewer); - tc->setFlatDataSize(0); - d->hl_type = Scene_edit_path_item_priv::NO_TYPE; - - itemChanged(); - -} -void Scene_edit_path_item_priv::reset_selection() -{ - selection_on = false; - CGAL::QGLViewer* viewer = *CGAL::QGLViewer::QGLViewerPool().begin(); - viewer->setManipulatedFrame(frame); - viewer->setMouseBinding(Qt::ShiftModifier, Qt::LeftButton, CGAL::qglviewer::SELECT); - constraint.setTranslationConstraintType(CGAL::qglviewer::AxisPlaneConstraint::FREE); - selected_vertices.clear(); -} - -//intercept events for picking -bool Scene_edit_path_item::eventFilter(QObject *obj, QEvent *event) -{ - if(!visible()) - return false; - Viewer_interface* viewer = qobject_cast(obj); - if(!viewer) - return false; - if(event->type() == QEvent::MouseButtonPress) - { - QMouseEvent* e = static_cast(event); - if(e->modifiers() == Qt::NoModifier) - { - //pick - int type, picked; - d->picked_pixel = e->pos(); - d->picking(type, picked, viewer); - viewer->makeCurrent(); - if(type !=-1) - { - bool found = false; - QApplication::setOverrideCursor(Qt::DragMoveCursor); - CGAL::qglviewer::Vec pos = viewer->camera()->pointUnderPixel(d->picked_pixel, found); - if(found) - { - d->rf_last_pos = pos; - d->remodel_frame->setPosition(pos); - } - for(int i=0; i<8; ++i) - for(int j=0; j<3; ++j) - d->last_pool[i][j] = d->vertices[i][j]; - d->selection_on = true; - if(type == 0) - { - d->selected_vertices.push_back(&d->vertices[picked]); - d->constraint.setTranslationConstraintType(CGAL::qglviewer::AxisPlaneConstraint::FREE); - d->remodel_frame->setConstraint(&d->constraint); - } - else if(type == 1) - { - d->selected_vertices.push_back(d->edges[picked].source); - d->selected_vertices.push_back(d->edges[picked].target); - Kernel::Point_3 s(d->edges[picked].source->position()), t(d->edges[picked].target->position()); - - CGAL::qglviewer::Vec normal(t.x()-s.x(), t.y()-s.y(), t.z()-s.z()); - d->constraint.setTranslationConstraintType(CGAL::qglviewer::AxisPlaneConstraint::PLANE); - d->constraint.setTranslationConstraintDirection(normal); - d->remodel_frame->setConstraint(&d->constraint); - } - else if(type == 2) - { - for(int i=0; i<4; ++i) - d->selected_vertices.push_back(d->faces[picked].vertices[i]); - Kernel::Point_3 a1(d->faces[picked].vertices[1]->position()), a0(d->faces[picked].vertices[0]->position()) - ,a3(d->faces[picked].vertices[3]->position()); - Kernel::Vector_3 a = a1 - a0, - b = a3 - a0; - Kernel::Vector_3 n = CGAL::cross_product(a,b); - - d->remodel_frame->setConstraint(&d->constraint); - d->constraint.setTranslationConstraintType(CGAL::qglviewer::AxisPlaneConstraint::AXIS); - d->constraint.setTranslationConstraintDirection(CGAL::qglviewer::Vec(n.x(), n.y(), n.z())); - - } - - viewer->setManipulatedFrame(d->remodel_frame); - viewer->setMouseBinding( - Qt::NoModifier, - Qt::LeftButton, - CGAL::qglviewer::FRAME, - CGAL::qglviewer::TRANSLATE); - } - else - { - d->reset_selection(); - } - } - return false; - } - else if(event->type() == QEvent::MouseMove) - { - QMouseEvent* e = static_cast(event); - if(e->modifiers() == Qt::NoModifier) - { - if(d->selection_on) - { - d->remodel_frame->setOrientation(d->frame->orientation()); - CGAL::qglviewer::Vec td(d->remodel_frame->transformOf(d->remodel_frame->position() - - d->rf_last_pos)); - QVector3D dir(td.x, td.y, td.z); - d->remodel_box(dir); - } - d->ready_to_hl= true; - d->picked_pixel = e->pos(); - QTimer::singleShot(0, this, - [this, viewer](){ - highlight(viewer); - }); - } - else if(e->modifiers() == Qt::ControlModifier && - e->buttons() == Qt::LeftButton) - { - QApplication::setOverrideCursor(d->rotate_cursor); - } - else if(d->selection_on) - { - d->reset_selection(); - } - d->picked_pixel = e->pos(); - return false; - } - else if(event->type() == QEvent::MouseButtonRelease) - { - d->reset_selection(); - QApplication::setOverrideCursor(QCursor()); - viewer->setMouseBinding( - Qt::NoModifier, - Qt::LeftButton, - CGAL::qglviewer::CAMERA, - CGAL::qglviewer::ROTATE); - } - else if(event->type() == QEvent::KeyRelease) - { - QKeyEvent* e = static_cast(event); - if(e->key() == Qt::Key_Control) - { - QApplication::setOverrideCursor(QCursor()); - } - } - return false; -} - -void Scene_edit_path_item_priv::draw_picking(Viewer_interface* viewer) -{ - - QMatrix4x4 f_matrix; - for (int i=0; i<16; ++i){ - f_matrix.data()[i] = (float)frame->matrix()[i]; - } - GLdouble d_mat[16]; - QMatrix4x4 mv_mat; - viewer->camera()->getModelViewMatrix(d_mat); - for (int i=0; i<16; ++i) - mv_mat.data()[i] = GLfloat(d_mat[i]); - mv_mat = mv_mat*f_matrix; - - - if(item->renderingMode() == FlatPlusEdges) - { - Tc* tc = item->getTriangleContainer(P_Faces); - tc->setFrameMatrix(f_matrix); - tc->setClipping(false); - tc->draw(viewer, false); - } - double radius =std::sqrt( - (item->point(6,0) - item->point(0,0)) * (item->point(6,0) - item->point(0,0)) + - (item->point(6,1) - item->point(0,1)) * (item->point(6,1) - item->point(0,1)) + - (item->point(6,2) - item->point(0,2)) * (item->point(6,2) - item->point(0,2))) *0.02 ; - Tc* tc = item->getTriangleContainer(P_Spheres); - tc->setFrameMatrix(f_matrix); - tc->setClipping(false); - tc->getVao(viewer)->bind(); - tc->getVao(viewer)->program->setAttributeValue("radius", (float)radius); - tc->getVao(viewer)->release(); - tc->draw(viewer, false); - - Ec* ec = item->getEdgeContainer(P_Edges); - if(viewer->isOpenGL_4_3()) - { - - QVector2D vp(viewer->width(), viewer->height()); - ec->setViewport(vp); - ec->setWidth(6.0f); - } - ec->setFrameMatrix(f_matrix); - ec->setClipping(false); - ec->draw(viewer, false); -} - -void Scene_edit_path_item_priv::remodel_box(const QVector3D &dir) -{ - CGAL::qglviewer::AxisPlaneConstraint::Type prev_cons = constraint.translationConstraintType(); - constraint.setTranslationConstraintType(CGAL::qglviewer::AxisPlaneConstraint::FREE); - Q_FOREACH(Scene_edit_path_item::vertex* selected_vertex, selected_vertices ) - { - int id = selected_vertex->id; - CGAL_assume(id<8); - *selected_vertex->x = applyX(id, last_pool[id][0], dir.x()); - *selected_vertex->y = applyY(id, last_pool[id][1], dir.y()); - *selected_vertex->z = applyZ(id, last_pool[id][2], dir.z()); - for( int i=0; i<3; ++i) - relative_center_[i] =(pool[i]+pool[i+3])/2 - center_[i]; - for( int i=0; i<3; ++i) - center_[i] =(pool[i]+pool[i+3])/2; - frame->translate(frame->inverseTransformOf(relative_center_)); - } - item->invalidateOpenGLBuffers(); - constraint.setTranslationConstraintType(prev_cons); -} - -double Scene_edit_path_item_priv::applyX(int id, double x, double dirx) -{ - switch(id) - { - case 0: - case 1: - case 4: - case 5: - if(x+dirx < pool[3]) - return x+dirx; - else - return pool[3]; - case 2: - case 3: - case 6: - case 7: - if(x+dirx > pool[0]) - return x+dirx; - else - return pool[0]; - default: - return 0; - } - return 0; -} - -double Scene_edit_path_item_priv::applyY(int id, double y, double diry) -{ - switch(id) - { - case 0: - case 1: - case 2: - case 3: - if(y+diry < pool[4]) - return y+diry; - else - return pool[4]; - case 4: - case 5: - case 6: - case 7: - if(y+diry > pool[1]) - return y+diry; - else - return pool[1]; - default: - return 0; - } - return 0; -} - -double Scene_edit_path_item_priv::applyZ(int id, double z, double dirz) -{ - switch(id) - { - case 1: - case 2: - case 5: - case 6: - if(z+dirz < pool[5]) - return z+dirz; - else - return pool[5]; - case 0: - case 3: - case 4: - case 7: - if(z+dirz > pool[2]) - return z+dirz; - else - return pool[2]; - default: - return 0; - } - return 0; -} - -//type : 0 = vertex, 1 = edge, 2 = face -void Scene_edit_path_item_priv::picking(int& type, int& id, Viewer_interface *viewer) -{ - viewer->makeCurrent(); - type = -1; - id = -1; - int deviceWidth = viewer->camera()->screenWidth(); - int deviceHeight = viewer->camera()->screenHeight(); - QOpenGLFramebufferObject* fbo = new QOpenGLFramebufferObject(deviceWidth, deviceHeight,QOpenGLFramebufferObject::Depth); - fbo->bind(); - viewer->glEnable(GL_DEPTH_TEST); - viewer->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - QColor bgColor(viewer->backgroundColor()); - //draws the image in the fbo - viewer->setBackgroundColor(::Qt::white); - draw_picking(viewer); - - const auto buffer = read_pixel_as_ubyte_rgba(picked_pixel, viewer, viewer->camera()); - //decode ID and pick (don't forget the case nothing is picked - if(!(buffer[0]==buffer[1] && buffer[1]==buffer[2])) - { - int r(std::ceil((buffer[0]-10)/20)), g(std::ceil((buffer[1]-10)/20)), b(std::ceil((buffer[2]-10)/20)); - id = (std::max)(r,g); - id = (std::max)(id,b); - if(buffer[0] > 0) - { - if(id <8) - type = 0 ; - } - else if(buffer[1] > 0) - { - if(id <12) - { - type = 1; - } - } - else if(buffer[2] > 0) - { - if(id <6) - { - type = 2; - } - } - } - viewer->setBackgroundColor(bgColor); - fbo->release(); - delete fbo; -} - -void Scene_edit_path_item::drawHl(Viewer_interface* viewer)const -{ - GLfloat offset_factor; - GLfloat offset_units; - QMatrix4x4 f_matrix; - for (int i=0; i<16; ++i){ - f_matrix.data()[i] = (float)d->frame->matrix()[i]; - } - GLdouble d_mat[16]; - QMatrix4x4 mv_mat; - viewer->camera()->getModelViewMatrix(d_mat); - for (int i=0; i<16; ++i) - mv_mat.data()[i] = GLfloat(d_mat[i]); - mv_mat = mv_mat*f_matrix; - - if(d->hl_type == Scene_edit_path_item_priv::VERTEX) - { - Tc* tc = getTriangleContainer(Priv::S_Spheres); - - tc->setFrameMatrix(f_matrix); - tc->setMvMatrix(mv_mat); - tc->setColor(QColor(Qt::yellow)); - - double radius =std::sqrt( - (point(6,0) - point(0,0)) * (point(6,0) - point(0,0)) + - (point(6,1) - point(0,1)) * (point(6,1) - point(0,1)) + - (point(6,2) - point(0,2)) * (point(6,2) - point(0,2))) *0.02 ; - tc->setClipping(false); - tc->getVao(viewer)->bind(); - tc->getVao(viewer)->program->setUniformValue("radius", (float)radius); - tc->getVao(viewer)->release(); - tc->draw(viewer, true); - } - else if(d->hl_type == Scene_edit_path_item_priv::EDGE) - { - Ec* ec = getEdgeContainer(Priv::S_Edges); - if(viewer->isOpenGL_4_3()) - { - QVector2D vp(viewer->width(), viewer->height()); - ec->setViewport(vp); - ec->setWidth(6.0f); - } - ec->setClipping(false); - ec->setFrameMatrix(f_matrix); - ec->setColor(QColor(Qt::yellow)); - ec->draw(viewer, true); - - } - else if(d->hl_type == Scene_edit_path_item_priv::FACE) - { - viewer->glGetFloatv(GL_POLYGON_OFFSET_FACTOR, &offset_factor); - viewer->glGetFloatv(GL_POLYGON_OFFSET_UNITS, &offset_units); - viewer->glEnable(GL_BLEND); - viewer->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - Tc* tc = getTriangleContainer(Priv::S_Faces); - tc->setMvMatrix(mv_mat); - tc->setFrameMatrix(f_matrix); - tc->setClipping(false); - - tc->setColor(QColor(Qt::yellow)); - tc->setAlpha(0.5); - tc->draw(viewer, true); - viewer->glPolygonOffset(offset_factor, offset_units); - viewer->glDisable(GL_BLEND); - } -} -void Scene_edit_path_item::drawTransparent(CGAL::Three::Viewer_interface*viewer)const -{ - if(renderingMode() != FlatPlusEdges) - return; - if(!isInit(viewer)) - initGL(viewer); - if ( getBuffersFilled() && - ! getBuffersInit(viewer)) - { - initializeBuffers(viewer); - setBuffersInit(viewer, true); - } - if(!getBuffersFilled()) - { - computeElements(); - initializeBuffers(viewer); - } - QMatrix4x4 f_matrix; - for (int i=0; i<16; ++i){ - f_matrix.data()[i] = (float)d->frame->matrix()[i]; - } - - GLdouble d_mat[16]; - QMatrix4x4 mv_mat; - viewer->camera()->getModelViewMatrix(d_mat); - for (int i=0; i<16; ++i) - mv_mat.data()[i] = GLfloat(d_mat[i]); - mv_mat = mv_mat*f_matrix; - - viewer->glEnable(GL_BLEND); - viewer->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - Tc* tc = getTriangleContainer(Priv::Faces); - tc->setMvMatrix(mv_mat); - tc->setFrameMatrix(f_matrix); - tc->setClipping(false); - tc->setColor(QColor(128,128,128,128)); - tc->setAlpha(0.5); - tc->draw(viewer, true); - viewer->glDisable(GL_BLEND); -} - -void Scene_edit_path_item::invalidateOpenGLBuffers() -{ - compute_bbox(); - setBuffersFilled(false); - getTriangleContainer(Priv::Faces)->reset_vbos(ALL); - getTriangleContainer(Priv::P_Faces)->reset_vbos(ALL); - getTriangleContainer(Priv::Spheres)->reset_vbos(ALL); - getTriangleContainer(Priv::P_Spheres)->reset_vbos(ALL); - getEdgeContainer(Priv::Edges)->reset_vbos(ALL); - getEdgeContainer(Priv::P_Edges)->reset_vbos(ALL); -} - -void Scene_edit_path_item::computeElements() const -{ - d->computeElements(); - getEdgeContainer(Priv::Edges)->allocate( - Ec::Vertices, - d->vertex_edges.data(), - static_cast(d->vertex_edges.size()*sizeof(float))); - Ec* ec = getEdgeContainer(Priv::P_Edges); - ec->allocate( - Ec::Vertices, - d->vertex_edges.data(), - static_cast(d->vertex_edges.size()*sizeof(float))); - - ec->allocate( - Ec::Colors, - d->color_edges.data(), - static_cast(d->color_edges.size()*sizeof(float))); - - - Tc* tc = getTriangleContainer(Priv::Spheres); - tc->allocate( - Tc::Flat_vertices, - d->vertex_spheres.data(), - static_cast(d->vertex_spheres.size()*sizeof(float))); - - tc->allocate( - Tc::Flat_normals, - d->normal_spheres.data(), - static_cast(d->normal_spheres.size()*sizeof(float))); - tc->allocate( - Tc::Facet_centers, - d->center_spheres.data(), - static_cast(d->center_spheres.size()*sizeof(float))); - - tc = getTriangleContainer(Priv::P_Spheres); - tc->allocate( - Tc::Flat_vertices, - d->vertex_spheres.data(), - static_cast(d->vertex_spheres.size()*sizeof(float))); - - tc->allocate( - Tc::Facet_centers, - d->center_spheres.data(), - static_cast(d->center_spheres.size()*sizeof(float))); - - tc->allocate( - Tc::FColors, - d->color_spheres.data(), - static_cast(d->color_spheres.size()*sizeof(float))); - - tc = getTriangleContainer(Priv::Faces); - tc->allocate( - Tc::Flat_vertices, - d->vertex_faces.data(), - static_cast(d->vertex_faces.size()*sizeof(float))); - - tc->allocate( - Tc::Flat_normals, - d->normal_faces.data(), - static_cast(d->normal_faces.size()*sizeof(float))); - tc = getTriangleContainer(Priv::P_Faces); - tc->allocate( - Tc::Flat_vertices, - d->vertex_faces.data(), - static_cast(d->vertex_faces.size()*sizeof(float))); - tc->allocate( - Tc::FColors, - d->color_faces.data(), - static_cast(d->color_faces.size()*sizeof(float))); - setBuffersFilled(true); -} - -void Scene_edit_path_item::initializeBuffers(Viewer_interface *v) const -{ - - getTriangleContainer(Priv::Faces)->initializeBuffers(v); - getTriangleContainer(Priv::P_Faces)->initializeBuffers(v); - - getTriangleContainer(Priv::Spheres)->initializeBuffers(v); - getTriangleContainer(Priv::Spheres)->initializeBuffers(v); - getTriangleContainer(Priv::P_Spheres)->initializeBuffers(v); - getTriangleContainer(Priv::P_Spheres)->initializeBuffers(v); - - getEdgeContainer(Priv::Edges)->initializeBuffers(v); - getEdgeContainer(Priv::P_Edges)->initializeBuffers(v); - - - getTriangleContainer(Priv::Faces)->setFlatDataSize(d->vertex_faces.size()); - getTriangleContainer(Priv::P_Faces)->setFlatDataSize(d->vertex_faces.size()); - - getTriangleContainer(Priv::Spheres)->setFlatDataSize(d->vertex_spheres.size()); - getTriangleContainer(Priv::Spheres)->setCenterSize(d->center_spheres.size()); - getTriangleContainer(Priv::P_Spheres)->setFlatDataSize(d->vertex_spheres.size()); - getTriangleContainer(Priv::P_Spheres)->setCenterSize(d->center_spheres.size()); - - getEdgeContainer(Priv::Edges)->setFlatDataSize(d->vertex_edges.size()); - getEdgeContainer(Priv::P_Edges)->setFlatDataSize(d->vertex_edges.size()); -} - -void Scene_edit_path_item::connectNewViewer(QObject *o) -{ - Vi* viewer = qobject_cast(o); - if(!viewer) - return; - viewer->setMouseTracking(true); -} diff --git a/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Scene_edit_path_item.h b/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Scene_edit_path_item.h deleted file mode 100644 index f410c4c15e21..000000000000 --- a/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Scene_edit_path_item.h +++ /dev/null @@ -1,66 +0,0 @@ -#ifndef SCENE_EDIT_PATH_ITEM_H -#define SCENE_EDIT_PATH_ITEM_H - -#include -#include -#include -#include "create_sphere.h" -#include "Scene_edit_path_item_config.h" -struct Scene_edit_path_item_priv; -class SCENE_EDIT_PATH_ITEM_EXPORT Scene_edit_path_item: - public CGAL::Three::Scene_item_rendering_helper, - public CGAL::Three::Scene_transparent_interface -{ - Q_OBJECT - Q_INTERFACES(CGAL::Three::Scene_transparent_interface) - Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.TransparentInterface/1.0") - public: - typedef CGAL::Simple_cartesian Kernel; - struct vertex; - struct edge; - struct face; - Scene_edit_path_item(); - Scene_edit_path_item(const CGAL::Three::Scene_interface* scene_interface); - ~Scene_edit_path_item(); - bool isFinite() const { return true; } - bool isEmpty() const { return false; } - void compute_bbox() const; - - bool manipulatable() const { return true; } - ManipulatedFrame* manipulatedFrame(); - Scene_edit_path_item* clone() const { - return nullptr; - } - - QString toolTip() const; - - bool eventFilter(QObject *, QEvent *); - // Indicate if rendering mode is supported - bool supportsRenderingMode(RenderingMode m) const; - void draw(CGAL::Three::Viewer_interface *) const; - void drawTransparent(CGAL::Three::Viewer_interface*)const; - void drawHl(CGAL::Three::Viewer_interface *) const; - void drawEdges(CGAL::Three::Viewer_interface* viewer) const; - void drawSpheres(CGAL::Three::Viewer_interface* viewer, const QMatrix4x4 f_matrix) const; - void invalidateOpenGLBuffers(); - - // 5-----6 - // . | . | - // 4------7 | - // | | | | - // | 1-|---2 - // | . |. - // 0------3 - - double point(short i, short j) const; - void initializeBuffers(CGAL::Three::Viewer_interface *) const; - void computeElements() const; -public Q_SLOTS: - void highlight(CGAL::Three::Viewer_interface* viewer); - void clearHL(); - void connectNewViewer(QObject* o); -protected: - friend struct Scene_edit_path_item_priv; - Scene_edit_path_item_priv* d; -}; -#endif // SCENE_EDIT_PATH_ITEM_H diff --git a/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Scene_edit_path_item_config.h b/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Scene_edit_path_item_config.h deleted file mode 100644 index 43a77157719d..000000000000 --- a/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Scene_edit_path_item_config.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef SCENE_EDIT_PATH_ITEM_CONFIG_H -#define SCENE_EDIT_PATH_ITEM_CONFIG_H - -#ifdef scene_edit_path_item_EXPORTS -# define SCENE_EDIT_PATH_ITEM_EXPORT Q_DECL_EXPORT -#else -# define SCENE_EDIT_PATH_ITEM_EXPORT Q_DECL_IMPORT -#endif - -#endif // SCENE_EDIT_PATH_ITEM_CONFIG_H From 3cf13af65a845c9ad79a39b046c7fcf37bc47e16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 10 Jan 2024 11:45:50 +0100 Subject: [PATCH 033/120] update include to dijkstra --- .../CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index f29c133ec09f..03e9afdbdaf0 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -20,7 +20,7 @@ #include #include -#include +#include //TODO: split to avoid redundant linking #include #include From 37ff2be8b5c0709695ab21c383fac40146e779fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Mon, 8 Jan 2024 14:48:39 +0100 Subject: [PATCH 034/120] first version of locally shortest path plugin that starts to work copy and update of edit box item/plugin --- Polyhedron/demo/Polyhedron/CMakeLists.txt | 3 + .../Polyhedron/Plugins/Bsurf/CMakeLists.txt | 8 + .../Bsurf/Locally_shortest_path_item.cpp | 850 ++++++++++++++++++ .../Bsurf/Locally_shortest_path_item.h | 69 ++ .../Bsurf/Locally_shortest_path_item_config.h | 10 + .../Bsurf/Locally_shortest_path_plugin.cpp | 110 +++ 6 files changed, 1050 insertions(+) create mode 100644 Polyhedron/demo/Polyhedron/Plugins/Bsurf/CMakeLists.txt create mode 100644 Polyhedron/demo/Polyhedron/Plugins/Bsurf/Locally_shortest_path_item.cpp create mode 100644 Polyhedron/demo/Polyhedron/Plugins/Bsurf/Locally_shortest_path_item.h create mode 100644 Polyhedron/demo/Polyhedron/Plugins/Bsurf/Locally_shortest_path_item_config.h create mode 100644 Polyhedron/demo/Polyhedron/Plugins/Bsurf/Locally_shortest_path_plugin.cpp diff --git a/Polyhedron/demo/Polyhedron/CMakeLists.txt b/Polyhedron/demo/Polyhedron/CMakeLists.txt index a8235cdb832e..ae10fd1429d8 100644 --- a/Polyhedron/demo/Polyhedron/CMakeLists.txt +++ b/Polyhedron/demo/Polyhedron/CMakeLists.txt @@ -262,6 +262,9 @@ if(CGAL_Qt6_FOUND AND Qt6_FOUND) add_item(scene_edit_box_item Plugins/PCA/Scene_edit_box_item.cpp) + add_item(locally_shortest_path_item Plugins/Bsurf/Locally_shortest_path_item.cpp) + target_link_libraries(locally_shortest_path_item PUBLIC scene_surface_mesh_item scene_polylines_item) + add_item(scene_image_item Scene_image_item.cpp) add_item(scene_surface_mesh_item Scene_surface_mesh_item.cpp) diff --git a/Polyhedron/demo/Polyhedron/Plugins/Bsurf/CMakeLists.txt b/Polyhedron/demo/Polyhedron/Plugins/Bsurf/CMakeLists.txt new file mode 100644 index 000000000000..a58dd0a49d64 --- /dev/null +++ b/Polyhedron/demo/Polyhedron/Plugins/Bsurf/CMakeLists.txt @@ -0,0 +1,8 @@ +include(polyhedron_demo_macros) + +polyhedron_demo_plugin(locally_shortest_path_plugin Locally_shortest_path_plugin) +target_link_libraries(locally_shortest_path_plugin PUBLIC locally_shortest_path_item + scene_surface_mesh_item + scene_polylines_item) + + diff --git a/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Locally_shortest_path_item.cpp b/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Locally_shortest_path_item.cpp new file mode 100644 index 000000000000..9a5cd1b89182 --- /dev/null +++ b/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Locally_shortest_path_item.cpp @@ -0,0 +1,850 @@ +#include "Locally_shortest_path_item.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +using namespace CGAL::Three; +typedef Viewer_interface Vi; +typedef Triangle_container Tc; +typedef Edge_container Ec; +typedef Locally_shortest_path_item_priv Priv; + + +typedef Scene_surface_mesh_item::Face_graph Mesh; +typedef CGAL::AABB_face_graph_triangle_primitive Primitive; +typedef CGAL::AABB_traits Traits; +typedef CGAL::AABB_tree Tree; + +// TODO: update +struct Locally_shortest_path_item::vertex{ + int id; + double x; + double y; + double z; + + CGAL::Point_3 position()const + { + return CGAL::Point_3(x,y,z); + } + double operator[](int i) + { + switch(i) + { + case 0: + return x; + case 1: + return y; + case 2: + return z; + default: + return 0; + } + } + + template + void set(const P& p, int id_) + { + x=p.x(); + y=p.y(); + z=p.z(); + id=id_; + } +}; + +struct Locally_shortest_path_item_priv{ + typedef CGAL::Simple_cartesian Kernel; + enum Face_containers{ + Faces = 0, + S_Faces, + Spheres, + S_Spheres, + P_Spheres, + P_Faces, + Nbf + }; + + enum Line_containers{ + Edges = 0, + S_Edges, + P_Edges, + Nbe + }; + + enum HL_Primitive{ + VERTEX=0, + EDGE, + FACE, + NO_TYPE + }; + + Locally_shortest_path_item_priv(const CGAL::Three::Scene_interface* scene_interface, + const Scene_surface_mesh_item* sm_item, + Scene_polylines_item* polyline_item, + Locally_shortest_path_item* ebi) + { + const CGAL::qglviewer::Vec offset = static_cast(CGAL::QGLViewer::QGLViewerPool().first())->offset(); + ready_to_hl = true; + scene = scene_interface; + mesh_item = sm_item; + spath_item=polyline_item; + spath_item->polylines.resize(1); + const Mesh& mesh = *mesh_item->face_graph(); + aabb_tree = Tree(faces(mesh).first, + faces(mesh).second, + mesh); + item = ebi; + selection_on = false; + Scene_item::Bbox bb = scene->bbox(); + double x=(bb.xmin()+bb.xmax())/2; + double y=(bb.ymin()+bb.ymax())/2; + double z=(bb.zmin()+bb.zmax())/2; + center_ = CGAL::qglviewer::Vec(x,y,z); + relative_center_ = CGAL::qglviewer::Vec(0,0,0); + remodel_frame = new Scene_item::ManipulatedFrame(); + remodel_frame->setTranslationSensitivity(1.0); + frame = new Scene_item::ManipulatedFrame(); + frame->setPosition(center_+offset); + frame->setSpinningSensitivity(100.0); //forbid spinning + constraint.setRotationConstraintType(CGAL::qglviewer::AxisPlaneConstraint::AXIS); + constraint.setTranslationConstraintType(CGAL::qglviewer::AxisPlaneConstraint::FREE); + constraint.setRotationConstraintDirection(CGAL::qglviewer::Vec(.0,.0,.1)); + frame->setConstraint(&constraint); + //create the sphere model + vertex_spheres.resize(0); + normal_spheres.resize(0); + create_flat_sphere(1.0f, vertex_spheres, normal_spheres,10); + + + // TODO: change the default + vertices.resize(2); + vertices[0].set( mesh.point(*mesh.vertices().begin()), 0 ); + vertices[1].set( mesh.point(*std::next(mesh.vertices().begin())), 1 ); + //~ vertices[2].set( mesh.point(*std::next(mesh.vertices().begin(),2)), 2 ); + + vertex_faces.resize(0); + normal_faces.resize(0); + + reset_selection(); + last_picked_id = -1; + last_picked_type = -1; + QPixmap pix(":/cgal/cursors/resources/rotate_around_cursor.png"); + rotate_cursor = QCursor(pix); + } + + ~Locally_shortest_path_item_priv(){ + delete frame; + delete remodel_frame; + } + + mutable std::vector vertex_edges; + mutable std::vector color_edges; + mutable std::vector vertex_spheres; + mutable std::vector normal_spheres; + mutable std::vector center_spheres; + mutable std::vector color_spheres; + mutable std::vector vertex_faces; + mutable std::vector normal_faces; + mutable std::vector color_faces; + mutable std::vector hl_vertex; + mutable std::vector hl_normal; + + bool ready_to_hl; + + CGAL::qglviewer::ManipulatedFrame* frame; + CGAL::qglviewer::ManipulatedFrame* remodel_frame; + CGAL::qglviewer::Vec rf_last_pos; + CGAL::qglviewer::LocalConstraint constraint; + CGAL::qglviewer::Vec center_; + CGAL::qglviewer::Vec relative_center_; + + mutable std::vector vertices; + std::vector selected_vertices; + + void reset_selection(); + bool selection_on; + void picking(int& type, int& id, Viewer_interface *viewer); + + void initializeBuffers(Viewer_interface *viewer)const; + + void computeElements() const; + void draw_picking(Viewer_interface*); + void update_points(const QVector3D &dir); + + const Scene_interface* scene; + const Scene_surface_mesh_item* mesh_item; + Scene_polylines_item* spath_item; + Locally_shortest_path_item* item; + Tree aabb_tree; + QPoint picked_pixel; + HL_Primitive hl_type; + int last_picked_id; + int last_picked_type; + QCursor rotate_cursor; + +}; + +Locally_shortest_path_item::Locally_shortest_path_item(const CGAL::Three::Scene_interface* scene_interface, + const Scene_surface_mesh_item *sm_item, + Scene_polylines_item* polyline_item) +{ + d = new Locally_shortest_path_item_priv(scene_interface, sm_item, polyline_item, this); + + are_buffers_filled = false; + + for(CGAL::QGLViewer* v : CGAL::QGLViewer::QGLViewerPool()) + { + v->setMouseTracking(true); + } + connect(Three::mainWindow(), SIGNAL(newViewerCreated(QObject*)), + this, SLOT(connectNewViewer(QObject*))); + + setTriangleContainer(Priv::P_Faces , new Tc(Vi::PROGRAM_NO_SELECTION, false)); + setTriangleContainer(Priv::Faces , new Tc(Vi::PROGRAM_WITH_LIGHT, false)); + setTriangleContainer(Priv::S_Faces , new Tc(Vi::PROGRAM_WITH_LIGHT, false)); + setTriangleContainer(Priv::Spheres , new Tc(Vi::PROGRAM_SPHERES, false)); + setTriangleContainer(Priv::S_Spheres, new Tc(Vi::PROGRAM_SPHERES, false)); + setTriangleContainer(Priv::P_Spheres, new Tc(Vi::PROGRAM_DARK_SPHERES, false)); + + + for(int i=Priv::Nbe-1; i>=0; --i) + { + setEdgeContainer(i, + new Ec(Three::mainViewer()->isOpenGL_4_3() + ? Vi::PROGRAM_SOLID_WIREFRAME + : Vi::PROGRAM_NO_SELECTION, + false)); + } + contextMenu(); +} +QString Locally_shortest_path_item::toolTip() const { + + return QString(); +} + + +QMenu* Locally_shortest_path_item::contextMenu() +{ + // disable "Alpha slider" in menu + QMenu* resMenu = Scene_item::contextMenu(); + bool prop = property("menu_changed").toBool(); + if(!prop) + { + setProperty("menu_changed", true); + } + return resMenu; +} + +void Locally_shortest_path_item::drawSpheres(Viewer_interface *viewer, const QMatrix4x4 f_matrix ) const +{ + GLdouble d_mat[16]; + QMatrix4x4 mv_mat; + viewer->camera()->getModelViewMatrix(d_mat); + for (int i=0; i<16; ++i) + mv_mat.data()[i] = GLfloat(d_mat[i]); + mv_mat = mv_mat*f_matrix; + // TODO: must depend on the mesh (and zoom?) + double radius = 0.02 ; + + Tc* tc = getTriangleContainer(Priv::Spheres); + tc->setFrameMatrix(f_matrix); + tc->setMvMatrix(mv_mat); + tc->setClipping(false); + tc->getVao(viewer)->bind(); + tc->getVao(viewer)->program->setAttributeValue("radius",radius); + tc->getVao(viewer)->release(); + tc->setColor(QColor(Qt::red)); + tc->draw(viewer, true); +} + +void Locally_shortest_path_item::drawPath() const +{ + namespace PMP = CGAL::Polygon_mesh_processing; + typedef PMP::Edge_location Edge_location; + typedef PMP::Face_location Face_location; + + const Mesh& mesh = *d->mesh_item->face_graph(); + + std::vector edge_locations; + CGAL::Epick::Point_3 src_pt(d->vertices[0].x,d->vertices[0].y,d->vertices[0].z), + tgt_pt(d->vertices[1].x,d->vertices[1].y,d->vertices[1].z); + +//TODO store that in the vector vertices + Face_location src = PMP::locate_with_AABB_tree(src_pt, d->aabb_tree, mesh); + Face_location tgt = PMP::locate_with_AABB_tree(tgt_pt, d->aabb_tree, mesh); + + PMP::locally_shortest_path(src, tgt, mesh, edge_locations); + d->spath_item->polylines.back().clear(); + d->spath_item->polylines.back().push_back(src_pt); + for (auto el : edge_locations) + d->spath_item->polylines.back().push_back(PMP::construct_point(el, mesh)); + d->spath_item->polylines.back().push_back(tgt_pt); + d->spath_item->invalidateOpenGLBuffers(); +} + +void Locally_shortest_path_item::draw(Viewer_interface *viewer) const +{ + if(!isInit(viewer)) + initGL(viewer); + if ( getBuffersFilled() && + ! getBuffersInit(viewer)) + { + initializeBuffers(viewer); + setBuffersInit(viewer, true); + } + if(!getBuffersFilled()) + { + computeElements(); + initializeBuffers(viewer); + } + QMatrix4x4 f_matrix; + for (int i=0; i<16; ++i){ + f_matrix.data()[i] = (float)d->frame->matrix()[i]; + } + + drawSpheres(viewer, f_matrix); + drawHl(viewer); + + drawPath(); +} + +void Locally_shortest_path_item::compute_bbox() const +{ + double xmin=d->vertices[0].x, xmax=xmin; + double ymin=d->vertices[0].y, ymax=ymin; + double zmin=d->vertices[0].z, zmax=zmin; + for(std::size_t i=0; ivertices.size(); ++i) + { + xmin=(std::min)(xmin, d->vertices[i].x); + ymin=(std::min)(ymin, d->vertices[i].y); + zmin=(std::min)(zmin, d->vertices[i].z); + xmax=(std::max)(xmax, d->vertices[i].x); + ymax=(std::max)(ymax, d->vertices[i].y); + zmax=(std::max)(zmax, d->vertices[i].z); + } + + setBbox(Scene_item::Bbox(xmin, ymin, zmin,xmax, ymax, zmax)); +} + +void Locally_shortest_path_item_priv::computeElements() const +{ + vertex_edges.clear(); + vertex_faces.clear(); + normal_faces.clear(); + center_spheres.clear(); + color_edges.clear(); + color_faces.clear(); + color_spheres.clear(); + + for (std::size_t i=0; isetMouseTracking(false); + + delete d; +} + +// Indicate if rendering mode is supported +bool Locally_shortest_path_item::supportsRenderingMode(RenderingMode m) const { + return m==FlatPlusEdges; +} + +Scene_item::ManipulatedFrame* +Locally_shortest_path_item::manipulatedFrame() +{ + return d->frame; +} + +void Locally_shortest_path_item::highlight(Viewer_interface *viewer) +{ + d->ready_to_hl = true; + viewer->makeCurrent(); + int type = -1, id = -1; + //pick + if(!d->selection_on) + { + d->picking(type, id, viewer); + d->last_picked_id = id; + d->last_picked_type = type; + } + //highlight + d->hl_normal.clear(); + d->hl_vertex.clear(); + if(type !=-1) + { + switch(d->last_picked_type) + { + case 0: + { + //compute + d->hl_vertex.push_back(d->vertices[d->last_picked_id].x); + d->hl_vertex.push_back(d->vertices[d->last_picked_id].y); + d->hl_vertex.push_back(d->vertices[d->last_picked_id].z); + //fill buffers + Tc* tc = getTriangleContainer(Priv::S_Spheres); + tc->reset_vbos(ALL); + tc->allocate( + Tc::Flat_vertices, + d->vertex_spheres.data(), + static_cast(d->vertex_spheres.size()*sizeof(float))); + + tc->allocate( + Tc::Flat_normals, + d->normal_spheres.data(), + static_cast(d->normal_spheres.size()*sizeof(float))); + + tc->allocate( + Tc::Facet_centers, + d->hl_vertex.data(), + static_cast(d->hl_vertex.size()*sizeof(float))); + + tc->setFlatDataSize(d->vertex_spheres.size()); + tc->setCenterSize(d->hl_vertex.size()); + //draw + d->hl_type = Locally_shortest_path_item_priv::VERTEX; + for(CGAL::QGLViewer* v : CGAL::QGLViewer::QGLViewerPool()) + { + CGAL::Three::Viewer_interface* viewer = + static_cast(v); + tc->initializeBuffers(viewer); + } + break; + } + default: + d->hl_type = Locally_shortest_path_item_priv::NO_TYPE; + break; + } + } + else + clearHL(); + redraw(); + + d->ready_to_hl = false; +} + +void Locally_shortest_path_item::clearHL() +{ + Viewer_interface* viewer = dynamic_cast(*CGAL::QGLViewer::QGLViewerPool().begin()); + viewer->makeCurrent(); + d->hl_normal.clear(); + d->hl_vertex.clear(); + + Tc* tc = getTriangleContainer(Priv::S_Spheres); + tc->reset_vbos(ALL); + tc->allocate(Tc::Flat_vertices, d->vertex_spheres.data(), + static_cast(d->vertex_spheres.size()*sizeof(float))); + tc->allocate(Tc::Flat_normals, + d->normal_spheres.data(), + static_cast(d->normal_spheres.size()*sizeof(float))); + + tc->allocate(Tc::Facet_centers, nullptr, 0); + + tc->initializeBuffers(viewer); + tc->setFlatDataSize(0); + tc->setCenterSize(0); + //draw + Ec* ec = getEdgeContainer(Priv::S_Edges); + ec->reset_vbos(ALL); + ec->allocate(Ec::Vertices, nullptr, 0); + ec->initializeBuffers(viewer); + ec->setFlatDataSize(0); + + tc = getTriangleContainer(Priv::S_Faces); + tc->reset_vbos(ALL); + tc->allocate(Tc::Flat_vertices, nullptr, 0); + tc->allocate(Tc::Flat_normals, nullptr, 0); + tc->initializeBuffers(viewer); + tc->setFlatDataSize(0); + d->hl_type = Locally_shortest_path_item_priv::NO_TYPE; + + itemChanged(); + +} +void Locally_shortest_path_item_priv::reset_selection() +{ + selection_on = false; + CGAL::QGLViewer* viewer = *CGAL::QGLViewer::QGLViewerPool().begin(); + viewer->setManipulatedFrame(frame); + viewer->setMouseBinding(Qt::ShiftModifier, Qt::LeftButton, CGAL::qglviewer::SELECT); + constraint.setTranslationConstraintType(CGAL::qglviewer::AxisPlaneConstraint::FREE); + selected_vertices.clear(); +} + +//intercept events for picking +bool Locally_shortest_path_item::eventFilter(QObject *obj, QEvent *event) +{ + if(!visible()) + return false; + Viewer_interface* viewer = qobject_cast(obj); + if(!viewer) + return false; + if(event->type() == QEvent::MouseButtonPress) + { + QMouseEvent* e = static_cast(event); + if(e->modifiers() == Qt::NoModifier) + { + //pick + int type, picked; + d->picked_pixel = e->pos(); + d->picking(type, picked, viewer); + viewer->makeCurrent(); + if(type == 0) + { + bool found = false; + QApplication::setOverrideCursor(Qt::DragMoveCursor); + CGAL::qglviewer::Vec pos = viewer->camera()->pointUnderPixel(d->picked_pixel, found); + if(found) + { + d->rf_last_pos = pos; + d->remodel_frame->setPosition(pos); + } + + d->selection_on = true; + d->selected_vertices.push_back(&d->vertices[picked]); + d->constraint.setTranslationConstraintType(CGAL::qglviewer::AxisPlaneConstraint::FREE); + d->remodel_frame->setConstraint(&d->constraint); + + viewer->setManipulatedFrame(d->remodel_frame); + viewer->setMouseBinding( + Qt::NoModifier, + Qt::LeftButton, + CGAL::qglviewer::FRAME, + CGAL::qglviewer::TRANSLATE); + } + else + { + d->reset_selection(); + } + } + return false; + } + else if(event->type() == QEvent::MouseMove) + { + QMouseEvent* e = static_cast(event); + if(e->modifiers() == Qt::NoModifier) + { + if(d->selection_on) + { + d->remodel_frame->setOrientation(d->frame->orientation()); + CGAL::qglviewer::Vec td(d->remodel_frame->position() - d->rf_last_pos); + QVector3D dir(td.x, td.y, td.z); + d->update_points(dir); + } + d->ready_to_hl= true; + d->picked_pixel = e->pos(); + QTimer::singleShot(0, this, + [this, viewer](){ + highlight(viewer); + }); + } + else if(e->modifiers() == Qt::ControlModifier && + e->buttons() == Qt::LeftButton) + { + QApplication::setOverrideCursor(d->rotate_cursor); + } + else if(d->selection_on) + { + d->reset_selection(); + } + d->picked_pixel = e->pos(); + return false; + } + else if(event->type() == QEvent::MouseButtonRelease) + { + d->reset_selection(); + QApplication::setOverrideCursor(QCursor()); + viewer->setMouseBinding( + Qt::NoModifier, + Qt::LeftButton, + CGAL::qglviewer::CAMERA, + CGAL::qglviewer::ROTATE); + } + else if(event->type() == QEvent::KeyRelease) + { + QKeyEvent* e = static_cast(event); + if(e->key() == Qt::Key_Control) + { + QApplication::setOverrideCursor(QCursor()); + } + } + return false; +} + +void Locally_shortest_path_item_priv::draw_picking(Viewer_interface* viewer) +{ + + QMatrix4x4 f_matrix; + for (int i=0; i<16; ++i){ + f_matrix.data()[i] = (float)frame->matrix()[i]; + } + GLdouble d_mat[16]; + QMatrix4x4 mv_mat; + viewer->camera()->getModelViewMatrix(d_mat); + for (int i=0; i<16; ++i) + mv_mat.data()[i] = GLfloat(d_mat[i]); + mv_mat = mv_mat*f_matrix; + + // TODO: radius + double radius = 0.02 ; + Tc* tc = item->getTriangleContainer(P_Spheres); + tc->setFrameMatrix(f_matrix); + tc->setClipping(false); + tc->getVao(viewer)->bind(); + tc->getVao(viewer)->program->setAttributeValue("radius", (float)radius); + tc->getVao(viewer)->release(); + tc->draw(viewer, false); +} + +void Locally_shortest_path_item_priv::update_points(const QVector3D &dir) +{ + CGAL::qglviewer::AxisPlaneConstraint::Type prev_cons = constraint.translationConstraintType(); + constraint.setTranslationConstraintType(CGAL::qglviewer::AxisPlaneConstraint::FREE); + for(Locally_shortest_path_item::vertex* selected_vertex: selected_vertices ) + { + int id = selected_vertex->id; + CGAL_assume(id<8 && id >=0); + double x = selected_vertex->x + dir.x(); + double y = selected_vertex->y + dir.y(); + double z = selected_vertex->z + dir.z(); + + // project point onto the mesh + auto p = aabb_tree.closest_point(CGAL::Epick::Point_3(x,y,z)); + selected_vertex->x = p.x(); + selected_vertex->y = p.y(); + selected_vertex->z = p.z(); + } + item->invalidateOpenGLBuffers(); + constraint.setTranslationConstraintType(prev_cons); +} + +//type : 0 = vertex, 1 = edge, 2 = face +void Locally_shortest_path_item_priv::picking(int& type, int& id, Viewer_interface *viewer) +{ + viewer->makeCurrent(); + type = -1; + id = -1; + int deviceWidth = viewer->camera()->screenWidth(); + int deviceHeight = viewer->camera()->screenHeight(); + QOpenGLFramebufferObject* fbo = new QOpenGLFramebufferObject(deviceWidth, deviceHeight,QOpenGLFramebufferObject::Depth); + fbo->bind(); + viewer->glEnable(GL_DEPTH_TEST); + viewer->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + QColor bgColor(viewer->backgroundColor()); + //draws the image in the fbo + viewer->setBackgroundColor(::Qt::white); + draw_picking(viewer); + + const auto buffer = read_pixel_as_ubyte_rgba(picked_pixel, viewer, viewer->camera()); + //decode ID and pick (don't forget the case nothing is picked + if(!(buffer[0]==buffer[1] && buffer[1]==buffer[2])) + { + int r(std::ceil((buffer[0]-10)/20)), g(std::ceil((buffer[1]-10)/20)), b(std::ceil((buffer[2]-10)/20)); +// std::cout << " r=" << r << " g=" << g << " b=" << b << "\n"; + id = (std::max)(r,g); + id = (std::max)(id,b); + if(buffer[0] > 0) + { + if(id <8) + type = 0 ; + } + else if(buffer[1] > 0) + { + if(id <12) + { + type = 1; + } + } + else if(buffer[2] > 0) + { + if(id <6) + { + type = 2; + } + } + } + viewer->setBackgroundColor(bgColor); + fbo->release(); + delete fbo; +} + +void Locally_shortest_path_item::drawHl(Viewer_interface* viewer)const +{ + QMatrix4x4 f_matrix; + for (int i=0; i<16; ++i){ + f_matrix.data()[i] = (float)d->frame->matrix()[i]; + } + GLdouble d_mat[16]; + QMatrix4x4 mv_mat; + viewer->camera()->getModelViewMatrix(d_mat); + for (int i=0; i<16; ++i) + mv_mat.data()[i] = GLfloat(d_mat[i]); + mv_mat = mv_mat*f_matrix; + + if(d->hl_type == Locally_shortest_path_item_priv::VERTEX) + { + Tc* tc = getTriangleContainer(Priv::S_Spheres); + + tc->setFrameMatrix(f_matrix); + tc->setMvMatrix(mv_mat); + tc->setColor(QColor(Qt::yellow)); + + // TODO radius + double radius = 0.02 ; + tc->setClipping(false); + tc->getVao(viewer)->bind(); + tc->getVao(viewer)->program->setUniformValue("radius", (float)radius); + tc->getVao(viewer)->release(); + tc->draw(viewer, true); + } +} + +void Locally_shortest_path_item::invalidateOpenGLBuffers() +{ + compute_bbox(); + setBuffersFilled(false); + //~ getTriangleContainer(Priv::Faces)->reset_vbos(ALL); + //~ getTriangleContainer(Priv::P_Faces)->reset_vbos(ALL); + getTriangleContainer(Priv::Spheres)->reset_vbos(ALL); + getTriangleContainer(Priv::P_Spheres)->reset_vbos(ALL); + //~ getEdgeContainer(Priv::Edges)->reset_vbos(ALL); + //~ getEdgeContainer(Priv::P_Edges)->reset_vbos(ALL); +} + +void Locally_shortest_path_item::computeElements() const +{ + d->computeElements(); + //~ getEdgeContainer(Priv::Edges)->allocate( + //~ Ec::Vertices, + //~ d->vertex_edges.data(), + //~ static_cast(d->vertex_edges.size()*sizeof(float))); + //~ Ec* ec = getEdgeContainer(Priv::P_Edges); + //~ ec->allocate( + //~ Ec::Vertices, + //~ d->vertex_edges.data(), + //~ static_cast(d->vertex_edges.size()*sizeof(float))); + + //~ ec->allocate( + //~ Ec::Colors, + //~ d->color_edges.data(), + //~ static_cast(d->color_edges.size()*sizeof(float))); + + + Tc* tc = getTriangleContainer(Priv::Spheres); + tc->allocate( + Tc::Flat_vertices, + d->vertex_spheres.data(), + static_cast(d->vertex_spheres.size()*sizeof(float))); + + tc->allocate( + Tc::Flat_normals, + d->normal_spheres.data(), + static_cast(d->normal_spheres.size()*sizeof(float))); + tc->allocate( + Tc::Facet_centers, + d->center_spheres.data(), + static_cast(d->center_spheres.size()*sizeof(float))); + + tc = getTriangleContainer(Priv::P_Spheres); + tc->allocate( + Tc::Flat_vertices, + d->vertex_spheres.data(), + static_cast(d->vertex_spheres.size()*sizeof(float))); + + tc->allocate( + Tc::Facet_centers, + d->center_spheres.data(), + static_cast(d->center_spheres.size()*sizeof(float))); + + tc->allocate( + Tc::FColors, + d->color_spheres.data(), + static_cast(d->color_spheres.size()*sizeof(float))); + + //~ tc = getTriangleContainer(Priv::Faces); + //~ tc->allocate( + //~ Tc::Flat_vertices, + //~ d->vertex_faces.data(), + //~ static_cast(d->vertex_faces.size()*sizeof(float))); + + //~ tc->allocate( + //~ Tc::Flat_normals, + //~ d->normal_faces.data(), + //~ static_cast(d->normal_faces.size()*sizeof(float))); + //~ tc = getTriangleContainer(Priv::P_Faces); + //~ tc->allocate( + //~ Tc::Flat_vertices, + //~ d->vertex_faces.data(), + //~ static_cast(d->vertex_faces.size()*sizeof(float))); + //~ tc->allocate( + //~ Tc::FColors, + //~ d->color_faces.data(), + //~ static_cast(d->color_faces.size()*sizeof(float))); + setBuffersFilled(true); +} + +void Locally_shortest_path_item::initializeBuffers(Viewer_interface *v) const +{ + + //~ getTriangleContainer(Priv::Faces)->initializeBuffers(v); + //~ getTriangleContainer(Priv::P_Faces)->initializeBuffers(v); + + getTriangleContainer(Priv::Spheres)->initializeBuffers(v); + getTriangleContainer(Priv::Spheres)->initializeBuffers(v); + getTriangleContainer(Priv::P_Spheres)->initializeBuffers(v); + getTriangleContainer(Priv::P_Spheres)->initializeBuffers(v); + + //~ getEdgeContainer(Priv::Edges)->initializeBuffers(v); + //~ getEdgeContainer(Priv::P_Edges)->initializeBuffers(v); + + + //~ getTriangleContainer(Priv::Faces)->setFlatDataSize(d->vertex_faces.size()); + //~ getTriangleContainer(Priv::P_Faces)->setFlatDataSize(d->vertex_faces.size()); + + getTriangleContainer(Priv::Spheres)->setFlatDataSize(d->vertex_spheres.size()); + getTriangleContainer(Priv::Spheres)->setCenterSize(d->center_spheres.size()); + getTriangleContainer(Priv::P_Spheres)->setFlatDataSize(d->vertex_spheres.size()); + getTriangleContainer(Priv::P_Spheres)->setCenterSize(d->center_spheres.size()); + + //~ getEdgeContainer(Priv::Edges)->setFlatDataSize(d->vertex_edges.size()); + //~ getEdgeContainer(Priv::P_Edges)->setFlatDataSize(d->vertex_edges.size()); +} + +void Locally_shortest_path_item::connectNewViewer(QObject *o) +{ + Vi* viewer = qobject_cast(o); + if(!viewer) + return; + viewer->setMouseTracking(true); +} diff --git a/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Locally_shortest_path_item.h b/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Locally_shortest_path_item.h new file mode 100644 index 000000000000..b86d7baf7880 --- /dev/null +++ b/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Locally_shortest_path_item.h @@ -0,0 +1,69 @@ +#ifndef LOCALLY_SHORTEST_PATH_ITEM_H +#define LOCALLY_SHORTEST_PATH_ITEM_H + +#include +#include +#include "Scene_surface_mesh_item.h" +#include "Scene_polylines_item.h" +#include "create_sphere.h" +#include "Locally_shortest_path_item_config.h" + +struct Locally_shortest_path_item_priv; +class LOCALLY_SHORTEST_PATH_ITEM_EXPORT Locally_shortest_path_item: + public CGAL::Three::Scene_item_rendering_helper +{ + Q_OBJECT + public: + typedef CGAL::Simple_cartesian Kernel; + struct vertex; + struct edge; + struct face; + + Locally_shortest_path_item(const CGAL::Three::Scene_interface* scene_interface, + const Scene_surface_mesh_item* sm_item, + Scene_polylines_item* polyline_item); + ~Locally_shortest_path_item(); + bool isFinite() const { return true; } + bool isEmpty() const { return false; } + void compute_bbox() const; + + bool manipulatable() const { return true; } + ManipulatedFrame* manipulatedFrame(); + Locally_shortest_path_item* clone() const { + return nullptr; + } + + QString toolTip() const; + QMenu* contextMenu(); + + bool eventFilter(QObject *, QEvent *); + // Indicate if rendering mode is supported + bool supportsRenderingMode(RenderingMode m) const; + void draw(CGAL::Three::Viewer_interface *) const; + void drawHl(CGAL::Three::Viewer_interface *) const; + //~ void drawEdges(CGAL::Three::Viewer_interface* viewer) const; + void drawSpheres(CGAL::Three::Viewer_interface* viewer, const QMatrix4x4 f_matrix) const; + void drawPath() const; + void invalidateOpenGLBuffers(); + + // 5-----6 + // . | . | + // 4------7 | + // | | | | + // | 1-|---2 + // | . |. + // 0------3 + + double point(short i, short j) const; + void initializeBuffers(CGAL::Three::Viewer_interface *) const; + void computeElements() const; +public Q_SLOTS: + void highlight(CGAL::Three::Viewer_interface* viewer); + void clearHL(); + void connectNewViewer(QObject* o); + +protected: + friend struct Locally_shortest_path_item_priv; + Locally_shortest_path_item_priv* d; +}; +#endif // SCENE_EDIT_BOX_ITEM_H diff --git a/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Locally_shortest_path_item_config.h b/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Locally_shortest_path_item_config.h new file mode 100644 index 000000000000..06796b33eba1 --- /dev/null +++ b/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Locally_shortest_path_item_config.h @@ -0,0 +1,10 @@ +#ifndef LOCALLY_SHORTEST_PATH_ITEM_CONFIG_H +#define LOCALLY_SHORTEST_PATH_ITEM_CONFIG_H + +#ifdef locally_shortest_path_item_EXPORTS +# define LOCALLY_SHORTEST_PATH_ITEM_EXPORT Q_DECL_EXPORT +#else +# define LOCALLY_SHORTEST_PATH_ITEM_EXPORT Q_DECL_IMPORT +#endif + +#endif // LOCALLY_SHORTEST_PATH_ITEM_CONFIG_H diff --git a/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Locally_shortest_path_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Locally_shortest_path_plugin.cpp new file mode 100644 index 000000000000..fb871cafc907 --- /dev/null +++ b/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Locally_shortest_path_plugin.cpp @@ -0,0 +1,110 @@ +#include + +#include +#include +#include "Locally_shortest_path_item.h" +#include "Scene_surface_mesh_item.h" +#include "Scene_polylines_item.h" +#include +#include +#include +#include +#include +#include + + +#include +#include +using namespace CGAL::Three; +class Locally_shortest_path_plugin : + public QObject, + public Polyhedron_demo_plugin_interface +{ + Q_OBJECT + Q_INTERFACES(CGAL::Three::Polyhedron_demo_plugin_interface) + Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.PluginInterface/1.0") + +public: + void init(QMainWindow* mainWindow, CGAL::Three::Scene_interface* scene_interface, Messages_interface*); + QList actions() const { + return QList() << actionTracePath; + } + + bool applicable(QAction*) const { + if(scene->numberOfEntries() > 0) + { + int item_id = scene->mainSelectionIndex(); + return qobject_cast(scene->item(item_id)); + } + return false; + } +public Q_SLOTS: + + void trace_path(); + void enableAction(); + void connectNewViewer(QObject* o) + { + for(int i=0; inumberOfEntries(); ++i) + { + Locally_shortest_path_item* item = qobject_cast( + scene->item(i)); + if(item) + o->installEventFilter(item); + } + } + +private: + CGAL::Three::Scene_interface* scene; + QMainWindow* mw; + QAction* actionTracePath; +}; // end Locally_shortest_path_plugin + +void Locally_shortest_path_plugin::init(QMainWindow* mainWindow, CGAL::Three::Scene_interface* scene_interface, Messages_interface*) +{ + scene = scene_interface; + mw = mainWindow; + actionTracePath = new QAction(tr("Create Locally Shortest Path"), mainWindow); + connect(actionTracePath, SIGNAL(triggered()), + this, SLOT(trace_path())); + connect(mw, SIGNAL(newViewerCreated(QObject*)), + this, SLOT(connectNewViewer(QObject*))); +} + +void Locally_shortest_path_plugin::trace_path() +{ + for(int i = 0, end = scene->numberOfEntries(); + i < end; ++i) + { + if(qobject_cast(scene->item(i))) + return; + } + QApplication::setOverrideCursor(Qt::WaitCursor); + + Scene_surface_mesh_item* sm_item = qobject_cast(scene->item(scene->mainSelectionIndex())); + + Scene_polylines_item* polyline_item = new Scene_polylines_item(); + + polyline_item->setName(tr("Locally Shortest Path")); + polyline_item->setColor(Qt::red); + scene->addItem(polyline_item); + polyline_item->invalidateOpenGLBuffers(); + + Locally_shortest_path_item* item = new Locally_shortest_path_item(scene, sm_item, polyline_item); + connect(item, SIGNAL(destroyed()), + this, SLOT(enableAction())); + item->setName("Edit box"); + item->setRenderingMode(FlatPlusEdges); + for(CGAL::QGLViewer* viewer : CGAL::QGLViewer::QGLViewerPool()) + viewer->installEventFilter(item); + + scene->addItem(item); + actionTracePath->setEnabled(false); + + QApplication::restoreOverrideCursor(); +} + +void Locally_shortest_path_plugin::enableAction() { + actionTracePath->setEnabled(true); +} + +#include "Locally_shortest_path_plugin.moc" From 2ac824f0971d2f74e8456312a303a25ec10440f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 10 Jan 2024 11:55:43 +0100 Subject: [PATCH 035/120] fix headers --- .../walk_in_polygon_mesh.h | 11 +---------- .../Polygon_mesh_processing/walk_to_select.h | 17 ++++------------- .../include/CGAL/Polyhedron_simplex_type.h | 11 +---------- 3 files changed, 6 insertions(+), 33 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/walk_in_polygon_mesh.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/walk_in_polygon_mesh.h index 179577dc1d01..e56bd7d15725 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/walk_in_polygon_mesh.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/walk_in_polygon_mesh.h @@ -2,19 +2,10 @@ // All rights reserved. // // This file is part of CGAL (www.cgal.org). -// You can redistribute it and/or modify it under the terms of the GNU -// General Public License as published by the Free Software Foundation, -// either version 3 of the License, or (at your option) any later version. -// -// Licensees holding a valid commercial license may use this file in -// accordance with the commercial license agreement provided with the software. -// -// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. // // $URL$ // $Id$ -// SPDX-License-Identifier: GPL-3.0+ +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial // // Author(s) : Sébastien Loriot diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/walk_to_select.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/walk_to_select.h index 0ab3238b8c69..1ea703d948e3 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/walk_to_select.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/walk_to_select.h @@ -1,22 +1,13 @@ -// Copyright (c) 2014-2019 GeometryFactory (France). All rights reserved. +// Copyright (c) 2014 GeometryFactory (France). +// All rights reserved. // // This file is part of CGAL (www.cgal.org). -// You can redistribute it and/or modify it under the terms of the GNU -// General Public License as published by the Free Software Foundation, -// either version 3 of the License, or (at your option) any later version. -// -// Licensees holding a valid commercial license may use this file in -// accordance with the commercial license agreement provided with the software. -// -// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. // // $URL$ // $Id$ -// SPDX-License-Identifier: GPL-3.0+ -// -// Author(s) : Maxime Gimeno +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial // +// Author(s) : Maxime Gimero and Sébastien Loriot #ifndef CGAL_POLYGON_MESH_PROCESSING_WALK_TO_SELECT_H #define CGAL_POLYGON_MESH_PROCESSING_WALK_TO_SELECT_H diff --git a/Polygon_mesh_processing/include/CGAL/Polyhedron_simplex_type.h b/Polygon_mesh_processing/include/CGAL/Polyhedron_simplex_type.h index a530fa27ca41..3e2171ed6290 100644 --- a/Polygon_mesh_processing/include/CGAL/Polyhedron_simplex_type.h +++ b/Polygon_mesh_processing/include/CGAL/Polyhedron_simplex_type.h @@ -2,19 +2,10 @@ // All rights reserved. // // This file is part of CGAL (www.cgal.org). -// You can redistribute it and/or modify it under the terms of the GNU -// General Public License as published by the Free Software Foundation, -// either version 3 of the License, or (at your option) any later version. -// -// Licensees holding a valid commercial license may use this file in -// accordance with the commercial license agreement provided with the software. -// -// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. // // $URL$ // $Id$ -// SPDX-License-Identifier: GPL-3.0+ +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial // // Author(s) : Sébastien Loriot From 9f0d60a59d4ffba57876e8a0fcb10dce07197567 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 10 Jan 2024 15:32:01 +0100 Subject: [PATCH 036/120] add tracing bezier assertions are currently being raised in the code of the header --- .../Bsurf/Locally_shortest_path_item.cpp | 153 +++++++++--------- .../Bsurf/Locally_shortest_path_item.h | 3 +- .../Bsurf/Locally_shortest_path_plugin.cpp | 47 +++++- 3 files changed, 120 insertions(+), 83 deletions(-) diff --git a/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Locally_shortest_path_item.cpp b/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Locally_shortest_path_item.cpp index 9a5cd1b89182..2825a00e541d 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Locally_shortest_path_item.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Locally_shortest_path_item.cpp @@ -96,6 +96,7 @@ struct Locally_shortest_path_item_priv{ Locally_shortest_path_item_priv(const CGAL::Three::Scene_interface* scene_interface, const Scene_surface_mesh_item* sm_item, Scene_polylines_item* polyline_item, + std::size_t nb_pts, Locally_shortest_path_item* ebi) { const CGAL::qglviewer::Vec offset = static_cast(CGAL::QGLViewer::QGLViewerPool().first())->offset(); @@ -132,10 +133,23 @@ struct Locally_shortest_path_item_priv{ // TODO: change the default - vertices.resize(2); - vertices[0].set( mesh.point(*mesh.vertices().begin()), 0 ); - vertices[1].set( mesh.point(*std::next(mesh.vertices().begin())), 1 ); - //~ vertices[2].set( mesh.point(*std::next(mesh.vertices().begin(),2)), 2 ); + if (nb_pts==2) + { + //shortest_path + vertices.resize(2); + vertices[0].set( mesh.point(*mesh.vertices().begin()), 0 ); + vertices[1].set( mesh.point(*std::next(mesh.vertices().begin())), 1 ); + } + else if (nb_pts==4) + { + // bezier + vertices.resize(4); + vertices[0].set( mesh.point(*mesh.vertices().begin()), 0 ); + vertices[1].set( mesh.point(*std::next(mesh.vertices().begin())), 1 ); + vertices[2].set( mesh.point(*std::next(mesh.vertices().begin(),2)), 2 ); + vertices[3].set( mesh.point(*std::next(mesh.vertices().begin(),3)), 3 ); + } + vertex_faces.resize(0); normal_faces.resize(0); @@ -196,14 +210,15 @@ struct Locally_shortest_path_item_priv{ int last_picked_id; int last_picked_type; QCursor rotate_cursor; - + bool path_invalidated=true; }; Locally_shortest_path_item::Locally_shortest_path_item(const CGAL::Three::Scene_interface* scene_interface, const Scene_surface_mesh_item *sm_item, - Scene_polylines_item* polyline_item) + Scene_polylines_item* polyline_item, + std::size_t nb_pts) { - d = new Locally_shortest_path_item_priv(scene_interface, sm_item, polyline_item, this); + d = new Locally_shortest_path_item_priv(scene_interface, sm_item, polyline_item, nb_pts, this); are_buffers_filled = false; @@ -259,7 +274,7 @@ void Locally_shortest_path_item::drawSpheres(Viewer_interface *viewer, const QMa mv_mat.data()[i] = GLfloat(d_mat[i]); mv_mat = mv_mat*f_matrix; // TODO: must depend on the mesh (and zoom?) - double radius = 0.02 ; + double radius = 0.01 ; Tc* tc = getTriangleContainer(Priv::Spheres); tc->setFrameMatrix(f_matrix); @@ -274,27 +289,59 @@ void Locally_shortest_path_item::drawSpheres(Viewer_interface *viewer, const QMa void Locally_shortest_path_item::drawPath() const { + if (!d->path_invalidated) return; namespace PMP = CGAL::Polygon_mesh_processing; typedef PMP::Edge_location Edge_location; typedef PMP::Face_location Face_location; const Mesh& mesh = *d->mesh_item->face_graph(); - std::vector edge_locations; - CGAL::Epick::Point_3 src_pt(d->vertices[0].x,d->vertices[0].y,d->vertices[0].z), - tgt_pt(d->vertices[1].x,d->vertices[1].y,d->vertices[1].z); - -//TODO store that in the vector vertices - Face_location src = PMP::locate_with_AABB_tree(src_pt, d->aabb_tree, mesh); - Face_location tgt = PMP::locate_with_AABB_tree(tgt_pt, d->aabb_tree, mesh); - - PMP::locally_shortest_path(src, tgt, mesh, edge_locations); - d->spath_item->polylines.back().clear(); - d->spath_item->polylines.back().push_back(src_pt); - for (auto el : edge_locations) - d->spath_item->polylines.back().push_back(PMP::construct_point(el, mesh)); - d->spath_item->polylines.back().push_back(tgt_pt); - d->spath_item->invalidateOpenGLBuffers(); + if (d->vertices.size()==2) + { + std::vector edge_locations; + CGAL::Epick::Point_3 src_pt(d->vertices[0].x,d->vertices[0].y,d->vertices[0].z), + tgt_pt(d->vertices[1].x,d->vertices[1].y,d->vertices[1].z); + + //TODO store that in the vector vertices + Face_location src = PMP::locate_with_AABB_tree(src_pt, d->aabb_tree, mesh); + Face_location tgt = PMP::locate_with_AABB_tree(tgt_pt, d->aabb_tree, mesh); + + PMP::locally_shortest_path(src, tgt, mesh, edge_locations); + d->spath_item->polylines.back().clear(); + d->spath_item->polylines.back().push_back(src_pt); + for (auto el : edge_locations) + d->spath_item->polylines.back().push_back(PMP::construct_point(el, mesh)); + d->spath_item->polylines.back().push_back(tgt_pt); + d->spath_item->setRenderingMode(Wireframe); + d->spath_item->invalidateOpenGLBuffers(); + } + else if (d->vertices.size()==4) + { + std::vector edge_locations; + CGAL::Epick::Point_3 c1_pt(d->vertices[0].x,d->vertices[0].y,d->vertices[0].z), + c2_pt(d->vertices[1].x,d->vertices[1].y,d->vertices[1].z), + c3_pt(d->vertices[2].x,d->vertices[2].y,d->vertices[2].z), + c4_pt(d->vertices[3].x,d->vertices[3].y,d->vertices[3].z); + + //TODO store that in the vector vertices + Face_location c1 = PMP::locate_with_AABB_tree(c1_pt, d->aabb_tree, mesh); + Face_location c2 = PMP::locate_with_AABB_tree(c2_pt, d->aabb_tree, mesh); + Face_location c3 = PMP::locate_with_AABB_tree(c3_pt, d->aabb_tree, mesh); + Face_location c4 = PMP::locate_with_AABB_tree(c4_pt, d->aabb_tree, mesh); + + PMP::Bezier_segment control_points=CGAL::make_array(c1, c2, c3, c4); + + std::vector face_locations = + PMP::recursive_de_Casteljau(mesh, control_points, 8); + + // TODO: we should connect points with geodesics and not segments + d->spath_item->polylines.back().clear(); + for (auto fl : face_locations) + d->spath_item->polylines.back().push_back(PMP::construct_point(fl, mesh)); + d->spath_item->setRenderingMode(Wireframe); + d->spath_item->invalidateOpenGLBuffers(); + } + d->path_invalidated=false; } void Locally_shortest_path_item::draw(Viewer_interface *viewer) const @@ -561,6 +608,8 @@ bool Locally_shortest_path_item::eventFilter(QObject *obj, QEvent *event) CGAL::qglviewer::Vec td(d->remodel_frame->position() - d->rf_last_pos); QVector3D dir(td.x, td.y, td.z); d->update_points(dir); + d->rf_last_pos=d->remodel_frame->position(); + d->path_invalidated=true; } d->ready_to_hl= true; d->picked_pixel = e->pos(); @@ -617,7 +666,7 @@ void Locally_shortest_path_item_priv::draw_picking(Viewer_interface* viewer) mv_mat = mv_mat*f_matrix; // TODO: radius - double radius = 0.02 ; + double radius = 0.01 ; Tc* tc = item->getTriangleContainer(P_Spheres); tc->setFrameMatrix(f_matrix); tc->setClipping(false); @@ -671,7 +720,6 @@ void Locally_shortest_path_item_priv::picking(int& type, int& id, Viewer_interfa if(!(buffer[0]==buffer[1] && buffer[1]==buffer[2])) { int r(std::ceil((buffer[0]-10)/20)), g(std::ceil((buffer[1]-10)/20)), b(std::ceil((buffer[2]-10)/20)); -// std::cout << " r=" << r << " g=" << g << " b=" << b << "\n"; id = (std::max)(r,g); id = (std::max)(id,b); if(buffer[0] > 0) @@ -721,7 +769,7 @@ void Locally_shortest_path_item::drawHl(Viewer_interface* viewer)const tc->setColor(QColor(Qt::yellow)); // TODO radius - double radius = 0.02 ; + double radius = 0.01 ; tc->setClipping(false); tc->getVao(viewer)->bind(); tc->getVao(viewer)->program->setUniformValue("radius", (float)radius); @@ -734,32 +782,13 @@ void Locally_shortest_path_item::invalidateOpenGLBuffers() { compute_bbox(); setBuffersFilled(false); - //~ getTriangleContainer(Priv::Faces)->reset_vbos(ALL); - //~ getTriangleContainer(Priv::P_Faces)->reset_vbos(ALL); getTriangleContainer(Priv::Spheres)->reset_vbos(ALL); getTriangleContainer(Priv::P_Spheres)->reset_vbos(ALL); - //~ getEdgeContainer(Priv::Edges)->reset_vbos(ALL); - //~ getEdgeContainer(Priv::P_Edges)->reset_vbos(ALL); } void Locally_shortest_path_item::computeElements() const { d->computeElements(); - //~ getEdgeContainer(Priv::Edges)->allocate( - //~ Ec::Vertices, - //~ d->vertex_edges.data(), - //~ static_cast(d->vertex_edges.size()*sizeof(float))); - //~ Ec* ec = getEdgeContainer(Priv::P_Edges); - //~ ec->allocate( - //~ Ec::Vertices, - //~ d->vertex_edges.data(), - //~ static_cast(d->vertex_edges.size()*sizeof(float))); - - //~ ec->allocate( - //~ Ec::Colors, - //~ d->color_edges.data(), - //~ static_cast(d->color_edges.size()*sizeof(float))); - Tc* tc = getTriangleContainer(Priv::Spheres); tc->allocate( @@ -791,54 +820,20 @@ void Locally_shortest_path_item::computeElements() const Tc::FColors, d->color_spheres.data(), static_cast(d->color_spheres.size()*sizeof(float))); - - //~ tc = getTriangleContainer(Priv::Faces); - //~ tc->allocate( - //~ Tc::Flat_vertices, - //~ d->vertex_faces.data(), - //~ static_cast(d->vertex_faces.size()*sizeof(float))); - - //~ tc->allocate( - //~ Tc::Flat_normals, - //~ d->normal_faces.data(), - //~ static_cast(d->normal_faces.size()*sizeof(float))); - //~ tc = getTriangleContainer(Priv::P_Faces); - //~ tc->allocate( - //~ Tc::Flat_vertices, - //~ d->vertex_faces.data(), - //~ static_cast(d->vertex_faces.size()*sizeof(float))); - //~ tc->allocate( - //~ Tc::FColors, - //~ d->color_faces.data(), - //~ static_cast(d->color_faces.size()*sizeof(float))); setBuffersFilled(true); } void Locally_shortest_path_item::initializeBuffers(Viewer_interface *v) const { - - //~ getTriangleContainer(Priv::Faces)->initializeBuffers(v); - //~ getTriangleContainer(Priv::P_Faces)->initializeBuffers(v); - getTriangleContainer(Priv::Spheres)->initializeBuffers(v); getTriangleContainer(Priv::Spheres)->initializeBuffers(v); getTriangleContainer(Priv::P_Spheres)->initializeBuffers(v); getTriangleContainer(Priv::P_Spheres)->initializeBuffers(v); - //~ getEdgeContainer(Priv::Edges)->initializeBuffers(v); - //~ getEdgeContainer(Priv::P_Edges)->initializeBuffers(v); - - - //~ getTriangleContainer(Priv::Faces)->setFlatDataSize(d->vertex_faces.size()); - //~ getTriangleContainer(Priv::P_Faces)->setFlatDataSize(d->vertex_faces.size()); - getTriangleContainer(Priv::Spheres)->setFlatDataSize(d->vertex_spheres.size()); getTriangleContainer(Priv::Spheres)->setCenterSize(d->center_spheres.size()); getTriangleContainer(Priv::P_Spheres)->setFlatDataSize(d->vertex_spheres.size()); getTriangleContainer(Priv::P_Spheres)->setCenterSize(d->center_spheres.size()); - - //~ getEdgeContainer(Priv::Edges)->setFlatDataSize(d->vertex_edges.size()); - //~ getEdgeContainer(Priv::P_Edges)->setFlatDataSize(d->vertex_edges.size()); } void Locally_shortest_path_item::connectNewViewer(QObject *o) diff --git a/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Locally_shortest_path_item.h b/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Locally_shortest_path_item.h index b86d7baf7880..29a131b37d42 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Locally_shortest_path_item.h +++ b/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Locally_shortest_path_item.h @@ -21,7 +21,8 @@ class LOCALLY_SHORTEST_PATH_ITEM_EXPORT Locally_shortest_path_item: Locally_shortest_path_item(const CGAL::Three::Scene_interface* scene_interface, const Scene_surface_mesh_item* sm_item, - Scene_polylines_item* polyline_item); + Scene_polylines_item* polyline_item, + std::size_t nb_pts); ~Locally_shortest_path_item(); bool isFinite() const { return true; } bool isEmpty() const { return false; } diff --git a/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Locally_shortest_path_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Locally_shortest_path_plugin.cpp index fb871cafc907..e35b63500231 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Locally_shortest_path_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Locally_shortest_path_plugin.cpp @@ -27,7 +27,7 @@ class Locally_shortest_path_plugin : public: void init(QMainWindow* mainWindow, CGAL::Three::Scene_interface* scene_interface, Messages_interface*); QList actions() const { - return QList() << actionTracePath; + return QList() << actionTracePath << actionTraceBezier; } bool applicable(QAction*) const { @@ -41,6 +41,7 @@ class Locally_shortest_path_plugin : public Q_SLOTS: void trace_path(); + void trace_bezier(); void enableAction(); void connectNewViewer(QObject* o) { @@ -57,6 +58,7 @@ public Q_SLOTS: CGAL::Three::Scene_interface* scene; QMainWindow* mw; QAction* actionTracePath; + QAction* actionTraceBezier; }; // end Locally_shortest_path_plugin void Locally_shortest_path_plugin::init(QMainWindow* mainWindow, CGAL::Three::Scene_interface* scene_interface, Messages_interface*) @@ -64,8 +66,11 @@ void Locally_shortest_path_plugin::init(QMainWindow* mainWindow, CGAL::Three::Sc scene = scene_interface; mw = mainWindow; actionTracePath = new QAction(tr("Create Locally Shortest Path"), mainWindow); + actionTraceBezier= new QAction(tr("Trace Bezier Curve"), mainWindow); connect(actionTracePath, SIGNAL(triggered()), this, SLOT(trace_path())); + connect(actionTraceBezier, SIGNAL(triggered()), + this, SLOT(trace_bezier())); connect(mw, SIGNAL(newViewerCreated(QObject*)), this, SLOT(connectNewViewer(QObject*))); } @@ -89,22 +94,58 @@ void Locally_shortest_path_plugin::trace_path() scene->addItem(polyline_item); polyline_item->invalidateOpenGLBuffers(); - Locally_shortest_path_item* item = new Locally_shortest_path_item(scene, sm_item, polyline_item); + Locally_shortest_path_item* item = new Locally_shortest_path_item(scene, sm_item, polyline_item,2); connect(item, SIGNAL(destroyed()), this, SLOT(enableAction())); - item->setName("Edit box"); + item->setName("Source and Target Points"); item->setRenderingMode(FlatPlusEdges); for(CGAL::QGLViewer* viewer : CGAL::QGLViewer::QGLViewerPool()) viewer->installEventFilter(item); scene->addItem(item); actionTracePath->setEnabled(false); + actionTraceBezier->setEnabled(false); + + QApplication::restoreOverrideCursor(); +} + +void Locally_shortest_path_plugin::trace_bezier() +{ + for(int i = 0, end = scene->numberOfEntries(); + i < end; ++i) + { + if(qobject_cast(scene->item(i))) + return; + } + QApplication::setOverrideCursor(Qt::WaitCursor); + + Scene_surface_mesh_item* sm_item = qobject_cast(scene->item(scene->mainSelectionIndex())); + + Scene_polylines_item* polyline_item = new Scene_polylines_item(); + + polyline_item->setName(tr("Locally Shortest Path")); + polyline_item->setColor(Qt::red); + scene->addItem(polyline_item); + polyline_item->invalidateOpenGLBuffers(); + + Locally_shortest_path_item* item = new Locally_shortest_path_item(scene, sm_item, polyline_item,4); + connect(item, SIGNAL(destroyed()), + this, SLOT(enableAction())); + item->setName("Control Points"); + item->setRenderingMode(FlatPlusEdges); + for(CGAL::QGLViewer* viewer : CGAL::QGLViewer::QGLViewerPool()) + viewer->installEventFilter(item); + + scene->addItem(item); + actionTracePath->setEnabled(false); + actionTraceBezier->setEnabled(false); QApplication::restoreOverrideCursor(); } void Locally_shortest_path_plugin::enableAction() { actionTracePath->setEnabled(true); + actionTraceBezier->setEnabled(true); } #include "Locally_shortest_path_plugin.moc" From 42538ca4bb94e55eb20f6644be02060a8751fda3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Mon, 29 Jan 2024 18:39:04 +0100 Subject: [PATCH 037/120] clean up initial path if src/tgt are on vertex/edge --- .../Bsurf/locally_shortest_path.h | 168 +++++++++++++++++- 1 file changed, 161 insertions(+), 7 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index 03e9afdbdaf0..3a6cab060e62 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -29,8 +29,8 @@ namespace CGAL { namespace Polygon_mesh_processing { template -void locally_shortest_path(const Face_location &src, - const Face_location &tgt, +void locally_shortest_path(Face_location src, + Face_location tgt, const TriangleMesh &tmesh, EdgeLocationRange &edge_locations); @@ -2217,8 +2217,8 @@ struct Geodesic_circle_impl } // namespace internal template -void locally_shortest_path(const Face_location &src, - const Face_location &tgt, +void locally_shortest_path(Face_location src, + Face_location tgt, const TriangleMesh &tmesh, EdgeLocationRange &edge_locations) { @@ -2227,6 +2227,8 @@ void locally_shortest_path(const Face_location &src, typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + typedef typename boost::graph_traits::vertex_descriptor + vertex_descriptor; //TODO replace with named parameter using VPM = typename boost::property_map::const_type; @@ -2336,12 +2338,164 @@ void locally_shortest_path(const Face_location &src, Impl2::strip_on_dual_graph(solver, tmesh, get(fim, src.first), get(fim,tgt.first)); #endif + + CGAL_assertion(face(opposite(initial_path.front(), tmesh), tmesh)==src.first); + CGAL_assertion(face(initial_path.back(), tmesh)==tgt.first); + + // retrieve the vertex id of a face location describing a vertex + auto is_vertex = [](const Face_location& fl) + { + if (fl.second[0]==0 && fl.second[1]==0) + return 2; + if (fl.second[1]==0 && fl.second[2]==0) + return 0; + if (fl.second[0]==0 && fl.second[2]==0) + return 1; + return -1; + }; + + // retrieve the source vertex id of a face location describing a point on a halfedge + auto is_edge = [](const Face_location& fl) + { + if (fl.second[0]==0 && fl.second[1]!=0 && fl.second[2]!=0) + return 1; + if (fl.second[1]==0 && fl.second[2]!=0 && fl.second[0]!=0) + return 2; + if (fl.second[2]==0 && fl.second[0]!=0 && fl.second[1]!=0) + return 0; + return -1; + }; + + // strip initial_path from extra halfedges after src and also update src + int vi = is_vertex(src); + if (vi!=-1) + { + halfedge_descriptor hsrc=prev(halfedge(src.first, tmesh), tmesh); + for (int i=0; i fl_src = {FT(0),FT(0),FT(0)}; + fl_src[vid]=FT(1); + src=std::make_pair(face(hsrc, tmesh), fl_src); + } + } + else + { + int ei = is_edge(src); + if (ei!=-1) + { + halfedge_descriptor hsrc=prev(halfedge(src.first, tmesh), tmesh); + for (int i=0; i fl_src = {FT(0),FT(0),FT(0)}; + fl_src[hid]=src.second[(ei+1)%3]; + fl_src[(hid+1)%3]=src.second[ei]; + src=std::make_pair(face(new_hsrc, tmesh), fl_src); + } + } + } + + // strip initial_path from extra halfedges before src and also update tgt + vi = is_vertex(tgt); + if (vi!=-1) + { + halfedge_descriptor htgt=prev(halfedge(tgt.first, tmesh), tmesh); + for (int i=0; i fl_tgt = {FT(0),FT(0),FT(0)}; + fl_tgt[vid]=FT(1); + src=std::make_pair(face(new_htgt, tmesh), fl_tgt); + } + } + else + { + int ei = is_edge(tgt); + if (ei!=-1) + { + halfedge_descriptor htgt=prev(halfedge(tgt.first, tmesh), tmesh); + for (int i=0; i fl_tgt = {FT(0),FT(0),FT(0)}; + fl_tgt[hid]=tgt.second[(ei+1)%3]; + fl_tgt[(hid+1)%3]=tgt.second[ei]; + tgt=std::make_pair(face(new_htgt, tmesh), fl_tgt); + } + } + } + + CGAL_assertion(face(opposite(initial_path.front(), tmesh), tmesh)==src.first); + CGAL_assertion(face(initial_path.back(), tmesh)==tgt.first); + std::vector< std::array> portals=Impl::unfold_strip(initial_path,src,tgt,vpm,tmesh); std::size_t max_index=0; - //TODO: if src and/or target are vertices are edges, the selected face should matter to avoid cycling - // around them. ==> after dijkstra, clean the strip if the halfedge is incident to the source/target - std::vector lerps=Impl::funnel(portals,max_index); // TODO: if you comment this if you don't want to shorten the path (option?). // but this part is really fast so maybe does not make sense. From 5cfaa04ef7cce874e591e5754d96ae3f074ed5b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Mon, 29 Jan 2024 18:55:34 +0100 Subject: [PATCH 038/120] move to a function --- .../Bsurf/locally_shortest_path.h | 314 +++++++++--------- 1 file changed, 163 insertions(+), 151 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index 3a6cab060e62..61c9334d39b0 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -1398,7 +1398,165 @@ struct Locally_shortest_path_imp // } #endif -}; + static + void + strip_initial_path(const TriangleMesh& tmesh, + Face_location& src, + Face_location& tgt, + std::vector& initial_path) + { + CGAL_assertion(face(opposite(initial_path.front(), tmesh), tmesh)==src.first); + CGAL_assertion(face(initial_path.back(), tmesh)==tgt.first); + + // retrieve the vertex id of a face location describing a vertex + auto is_vertex = [](const Face_location& fl) + { + if (fl.second[0]==0 && fl.second[1]==0) + return 2; + if (fl.second[1]==0 && fl.second[2]==0) + return 0; + if (fl.second[0]==0 && fl.second[2]==0) + return 1; + return -1; + }; + + // retrieve the source vertex id of a face location describing a point on a halfedge + auto is_edge = [](const Face_location& fl) + { + if (fl.second[0]==0 && fl.second[1]!=0 && fl.second[2]!=0) + return 1; + if (fl.second[1]==0 && fl.second[2]!=0 && fl.second[0]!=0) + return 2; + if (fl.second[2]==0 && fl.second[0]!=0 && fl.second[1]!=0) + return 0; + return -1; + }; + + // strip initial_path from extra halfedges after src and also update src + int vi = is_vertex(src); + if (vi!=-1) + { + halfedge_descriptor hsrc=prev(halfedge(src.first, tmesh), tmesh); + for (int i=0; i fl_src = {FT(0),FT(0),FT(0)}; + fl_src[vid]=FT(1); + src=std::make_pair(face(hsrc, tmesh), fl_src); + } + } + else + { + int ei = is_edge(src); + if (ei!=-1) + { + halfedge_descriptor hsrc=prev(halfedge(src.first, tmesh), tmesh); + for (int i=0; i fl_src = {FT(0),FT(0),FT(0)}; + fl_src[hid]=src.second[(ei+1)%3]; + fl_src[(hid+1)%3]=src.second[ei]; + src=std::make_pair(face(new_hsrc, tmesh), fl_src); + } + } + } + + // strip initial_path from extra halfedges before src and also update tgt + vi = is_vertex(tgt); + if (vi!=-1) + { + halfedge_descriptor htgt=prev(halfedge(tgt.first, tmesh), tmesh); + for (int i=0; i fl_tgt = {FT(0),FT(0),FT(0)}; + fl_tgt[vid]=FT(1); + src=std::make_pair(face(new_htgt, tmesh), fl_tgt); + } + } + else + { + int ei = is_edge(tgt); + if (ei!=-1) + { + halfedge_descriptor htgt=prev(halfedge(tgt.first, tmesh), tmesh); + for (int i=0; i fl_tgt = {FT(0),FT(0),FT(0)}; + fl_tgt[hid]=tgt.second[(ei+1)%3]; + fl_tgt[(hid+1)%3]=tgt.second[ei]; + tgt=std::make_pair(face(new_htgt, tmesh), fl_tgt); + } + } + } + } +}; // end of Locally_shortest_path_imp template struct Bezier_tracing_impl @@ -1582,7 +1740,6 @@ struct Bezier_tracing_impl return {{polygon[0], Q0, R0, S}, {S, R1, Q2, polygon[3]}}; } - }; @@ -2227,8 +2384,6 @@ void locally_shortest_path(Face_location src, typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; - typedef typename boost::graph_traits::vertex_descriptor - vertex_descriptor; //TODO replace with named parameter using VPM = typename boost::property_map::const_type; @@ -2342,153 +2497,10 @@ void locally_shortest_path(Face_location src, CGAL_assertion(face(opposite(initial_path.front(), tmesh), tmesh)==src.first); CGAL_assertion(face(initial_path.back(), tmesh)==tgt.first); - // retrieve the vertex id of a face location describing a vertex - auto is_vertex = [](const Face_location& fl) - { - if (fl.second[0]==0 && fl.second[1]==0) - return 2; - if (fl.second[1]==0 && fl.second[2]==0) - return 0; - if (fl.second[0]==0 && fl.second[2]==0) - return 1; - return -1; - }; - - // retrieve the source vertex id of a face location describing a point on a halfedge - auto is_edge = [](const Face_location& fl) - { - if (fl.second[0]==0 && fl.second[1]!=0 && fl.second[2]!=0) - return 1; - if (fl.second[1]==0 && fl.second[2]!=0 && fl.second[0]!=0) - return 2; - if (fl.second[2]==0 && fl.second[0]!=0 && fl.second[1]!=0) - return 0; - return -1; - }; - - // strip initial_path from extra halfedges after src and also update src - int vi = is_vertex(src); - if (vi!=-1) - { - halfedge_descriptor hsrc=prev(halfedge(src.first, tmesh), tmesh); - for (int i=0; i fl_src = {FT(0),FT(0),FT(0)}; - fl_src[vid]=FT(1); - src=std::make_pair(face(hsrc, tmesh), fl_src); - } - } - else - { - int ei = is_edge(src); - if (ei!=-1) - { - halfedge_descriptor hsrc=prev(halfedge(src.first, tmesh), tmesh); - for (int i=0; i fl_src = {FT(0),FT(0),FT(0)}; - fl_src[hid]=src.second[(ei+1)%3]; - fl_src[(hid+1)%3]=src.second[ei]; - src=std::make_pair(face(new_hsrc, tmesh), fl_src); - } - } - } - - // strip initial_path from extra halfedges before src and also update tgt - vi = is_vertex(tgt); - if (vi!=-1) - { - halfedge_descriptor htgt=prev(halfedge(tgt.first, tmesh), tmesh); - for (int i=0; i fl_tgt = {FT(0),FT(0),FT(0)}; - fl_tgt[vid]=FT(1); - src=std::make_pair(face(new_htgt, tmesh), fl_tgt); - } - } - else - { - int ei = is_edge(tgt); - if (ei!=-1) - { - halfedge_descriptor htgt=prev(halfedge(tgt.first, tmesh), tmesh); - for (int i=0; i fl_tgt = {FT(0),FT(0),FT(0)}; - fl_tgt[hid]=tgt.second[(ei+1)%3]; - fl_tgt[(hid+1)%3]=tgt.second[ei]; - tgt=std::make_pair(face(new_htgt, tmesh), fl_tgt); - } - } - } + // remove extra halfedges from inital_path and update src/tgt to get a minimal sequence + // in case src and/or tgt are on a vertex or an edge + Impl::strip_initial_path(tmesh, src, tgt, initial_path); + if (initial_path.empty()) return; CGAL_assertion(face(opposite(initial_path.front(), tmesh), tmesh)==src.first); CGAL_assertion(face(initial_path.back(), tmesh)==tgt.first); From 8d54b8e0e0ad261561a13f3975421b64c5c902e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Tue, 30 Jan 2024 20:01:27 +0100 Subject: [PATCH 039/120] add test for striping initial path + bug fixes Still some duplicate paths to be fixed --- .../Bsurf/locally_shortest_path.h | 139 ++++++++-- .../Polygon_mesh_processing/CMakeLists.txt | 2 + .../Polygon_mesh_processing/test_Bsurf.cpp | 258 ++++++++++++++++++ 3 files changed, 383 insertions(+), 16 deletions(-) create mode 100644 Polygon_mesh_processing/test/Polygon_mesh_processing/test_Bsurf.cpp diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index 61c9334d39b0..05cc5c7dc420 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -111,7 +111,7 @@ struct Locally_shortest_path_imp const TriangleMesh& mesh) { static int i = -1; - std::cout << "dump current path in path_"+std::to_string(i)+".polylines.txt\n"; + std::cout << "dump current path in path_"+std::to_string(i+1)+".polylines.txt\n"; std::ofstream out("path_"+std::to_string(++i)+".polylines.txt"); out << path.size()+2 << " " << construct_point(src, mesh); for(std::size_t i=0; i> result(s+1); + std::vector> result(s+1); // Vector_2 should be Point_2 as they are funnel endpoints in 2D (after flattening) result[0]=init_source_triangle(initial_path[0], vpm, mesh, src); #ifdef CGAL_DEBUG_BSURF std::cout << "unfolding faces\n"; @@ -433,7 +433,7 @@ struct Locally_shortest_path_imp std::vector points = std::vector{{apex_index, apex}}; points.reserve(portals.size()); // @Speed: is this slower than an inlined function? - auto area = [](const Vector_2 a, const Vector_2 b, const Vector_2 c) { + auto area = [](const Vector_2 a, const Vector_2 b, const Vector_2 c) { // TODO replace with orientation predicate return determinant(b - a, c - a); }; @@ -549,8 +549,8 @@ struct Locally_shortest_path_imp void straighten_path(std::vector< std::array>& portals, std::vector& lerps, std::vector& path, - const Face_location& src, - const Face_location& tgt, + Face_location& src, + Face_location& tgt, const VertexPointMap &vpm, const TriangleMesh &mesh, std::size_t index) { #ifdef CGAL_DEBUG_BSURF @@ -560,7 +560,7 @@ struct Locally_shortest_path_imp vertex_descriptor vertex=boost::graph_traits::null_vertex(); // TODO: use a while loop breaking when no apex vertices not already visited are available - for (std::size_t i = 0; i < portals.size() * 2 && index != std::size_t(-1); i++) + for (std::size_t i = 0; i < portals.size() * 2 && index != std::size_t(-1); ++i) { #ifdef CGAL_DEBUG_BSURF std::cout << "Improving path " << path.size() << " hedges\n"; @@ -604,6 +604,22 @@ struct Locally_shortest_path_imp std::size_t curr_index = index+1; std::vector new_hedges; + + // indicate if a point location is on an edge (interior or endpoints) + auto is_on_hedge = [&mesh](const Face_location& loc, halfedge_descriptor h_loop) + { + int k=0; + halfedge_descriptor hloc=prev(halfedge(loc.first, mesh), mesh); + while(hloc!=h_loop) + { + hloc=next(hloc,mesh); + ++k; + if (k==3) return -1; + } + return loc.second[(k+1)%3]==0 ? k : -1; + }; + + if (is_target) { face_descriptor target_face; @@ -622,12 +638,56 @@ struct Locally_shortest_path_imp target_face = face(opposite(path[curr_index], mesh), mesh); halfedge_descriptor h_loop=opposite(prev(opposite(h_curr, mesh), mesh), mesh); + + // don't pick first h_loop if src is visible on the other side of it + const int ksrc=is_on_hedge(src, opposite(h_loop, mesh)); + if (ksrc!=-1) + { + src.first=face(h_loop,mesh); + int new_k=0; + halfedge_descriptor hsrc=prev(halfedge(src.first, mesh), mesh); + while(hsrc!=h_loop) + { + hsrc=next(hsrc,mesh); + ++new_k; + } + std::array new_second = CGAL::make_array(FT(0.),FT(0.),FT(0.)); + new_second[new_k]=src.second[(ksrc+2)%3]; + new_second[(new_k+2)%3]=src.second[ksrc]; + src.second=new_second; + h_loop=opposite(prev(h_loop,mesh), mesh); + } + do { new_hedges.push_back(h_loop); h_loop=opposite(prev(h_loop,mesh), mesh); } while(target_face!=face(h_loop, mesh)); - new_hedges.push_back(h_loop); + + // don't pick last h_loop is tgt is visible on the other side of it + const int ktgt = is_on_hedge(tgt,h_loop); + if (ktgt!=-1) + { + halfedge_descriptor oh_loop=opposite(h_loop,mesh); + tgt.first=face(oh_loop,mesh); + int new_k=0; + halfedge_descriptor htgt=prev(halfedge(tgt.first, mesh), mesh); + while(htgt!=oh_loop) + { + htgt=next(htgt,mesh); + ++new_k; + } + std::array new_second = CGAL::make_array(FT(0.),FT(0.),FT(0.)); + new_second[new_k]=tgt.second[(ktgt+2)%3]; + new_second[(new_k+2)%3]=tgt.second[ktgt]; + tgt.second=new_second; + } + else + new_hedges.push_back(h_loop); + + + + } else { @@ -647,12 +707,52 @@ struct Locally_shortest_path_imp target_face=face(opposite(path[curr_index], mesh), mesh); halfedge_descriptor h_loop=opposite(next(opposite(h_curr, mesh), mesh), mesh); // skip the face before h_curr (as we won't remove it from path) + + // don't pick first h_loop if src is visible on the other side of it + const int ksrc=is_on_hedge(src, opposite(h_loop, mesh)); + if (ksrc!=-1) + { + src.first=face(h_loop,mesh); + int new_k=0; + halfedge_descriptor hsrc=prev(halfedge(src.first, mesh), mesh); + while(hsrc!=h_loop) + { + hsrc=next(hsrc,mesh); + ++new_k; + } + std::array new_second = CGAL::make_array(FT(0.),FT(0.),FT(0.)); + new_second[new_k]=src.second[(ksrc+2)%3]; + new_second[(new_k+2)%3]=src.second[ksrc]; + src.second=new_second; + h_loop=opposite(next(h_loop,mesh), mesh); + } + do { new_hedges.push_back(h_loop); h_loop=opposite(next(h_loop,mesh), mesh); } while(target_face!=face(h_loop, mesh)); - new_hedges.push_back(h_loop); + + // don't pick last h_loop is tgt is visible on the other side of it + const int ktgt = is_on_hedge(tgt,h_loop); + if (ktgt!=-1) + { + halfedge_descriptor oh_loop=opposite(h_loop,mesh); + tgt.first=face(oh_loop,mesh); + int new_k=0; + halfedge_descriptor htgt=prev(halfedge(tgt.first, mesh), mesh); + while(htgt!=oh_loop) + { + htgt=next(htgt,mesh); + ++new_k; + } + std::array new_second = CGAL::make_array(FT(0.),FT(0.),FT(0.)); + new_second[new_k]=tgt.second[(ktgt+2)%3]; + new_second[(new_k+2)%3]=tgt.second[ktgt]; + tgt.second=new_second; + } + else + new_hedges.push_back(h_loop); } // replace the halfedges incident to the apex vertex with the opposite part of the ring @@ -663,6 +763,8 @@ struct Locally_shortest_path_imp portals=unfold_strip(path,src,tgt,vpm,mesh); lerps=funnel(portals,index); + CGAL_assertion(lerps.size()==path.size()); + #ifdef CGAL_DEBUG_BSURF dump_path(path, lerps, src, tgt, mesh); #endif @@ -1424,11 +1526,11 @@ struct Locally_shortest_path_imp auto is_edge = [](const Face_location& fl) { if (fl.second[0]==0 && fl.second[1]!=0 && fl.second[2]!=0) - return 1; - if (fl.second[1]==0 && fl.second[2]!=0 && fl.second[0]!=0) return 2; - if (fl.second[2]==0 && fl.second[0]!=0 && fl.second[1]!=0) + if (fl.second[1]==0 && fl.second[2]!=0 && fl.second[0]!=0) return 0; + if (fl.second[2]==0 && fl.second[0]!=0 && fl.second[1]!=0) + return 1; return -1; }; @@ -1471,7 +1573,7 @@ struct Locally_shortest_path_imp if (ei!=-1) { halfedge_descriptor hsrc=prev(halfedge(src.first, tmesh), tmesh); - for (int i=0; i fl_tgt = {FT(0),FT(0),FT(0)}; fl_tgt[vid]=FT(1); - src=std::make_pair(face(new_htgt, tmesh), fl_tgt); + tgt=std::make_pair(face(new_htgt, tmesh), fl_tgt); } } else @@ -1533,7 +1636,7 @@ struct Locally_shortest_path_imp if (ei!=-1) { halfedge_descriptor htgt=prev(halfedge(tgt.first, tmesh), tmesh); - for (int i=0; i src, CGAL_assertion(face(opposite(initial_path.front(), tmesh), tmesh)==src.first); CGAL_assertion(face(initial_path.back(), tmesh)==tgt.first); +// TODO : if (edge(vsrc, vtgt, mesh) || tgt && src on the same edge ) return; + + // here portals contains 2D coordinates of endpoints of edges in initial_path std::vector< std::array> portals=Impl::unfold_strip(initial_path,src,tgt,vpm,tmesh); std::size_t max_index=0; + // lerps are barycentric coordinates of the shortest path restricted to the unfolded strip std::vector lerps=Impl::funnel(portals,max_index); // TODO: if you comment this if you don't want to shorten the path (option?). // but this part is really fast so maybe does not make sense. diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt b/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt index 44e32386664c..a170dcdd48f1 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt @@ -87,6 +87,8 @@ if(TARGET CGAL::Eigen3_support) target_link_libraries(test_decimation_of_planar_patches PUBLIC CGAL::Eigen3_support) create_single_source_cgal_program("remeshing_quality_test.cpp" ) target_link_libraries(remeshing_quality_test PUBLIC CGAL::Eigen3_support) + create_single_source_cgal_program("test_Bsurf.cpp") + target_link_libraries(test_Bsurf PUBLIC CGAL::Eigen3_support) else() message(STATUS "NOTICE: Tests that use the Eigen library will not be compiled.") endif() diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_Bsurf.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_Bsurf.cpp new file mode 100644 index 000000000000..6ca79b7b1ab0 --- /dev/null +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_Bsurf.cpp @@ -0,0 +1,258 @@ +#include +#include +#include + +#include + + +namespace PMP = CGAL::Polygon_mesh_processing; + +typedef CGAL::Exact_predicates_inexact_constructions_kernel K; +typedef CGAL::Surface_mesh Mesh; +typedef PMP::Face_location Face_location; +typedef PMP::Edge_location Edge_location; + + +int main(int argc, char** argv) +{ + std::string filename = (argc > 1) ? std::string(argv[1]) + : CGAL::data_file_path("meshes/elephant.off"); + + Mesh mesh; + if(!CGAL::IO::read_polygon_mesh(filename, mesh) || !CGAL::is_triangle_mesh(mesh)) + { + std::cerr << "Invalid input." << std::endl; + return 1; + } + + std::size_t nb_hedges = halfedges(mesh).size(); + + // take two random faces and pick the centroid + // CGAL::Random rnd = CGAL::get_default_random(); + CGAL::Random rnd(1706604646); + // CGAL::Random rnd(1695724381); + // CGAL::Random rnd(1695813638); + + std::cout << "seed " << rnd.get_seed() << std::endl; + Mesh::Halfedge_index h = *std::next(halfedges(mesh).begin(), rnd.get_int(0, nb_hedges)); + + std::ofstream out("locally_shortest_path.polylines.txt"); + +// test src/tgt being 2 opposite vertices of an edge +#if 1 + for (int i=0; i<3; ++i) + { + Mesh::Vertex_index v1 = target(next(h, mesh), mesh); + Mesh::Vertex_index v2 = target(next(opposite(h, mesh), mesh), mesh); + + for(Mesh::Halfedge_index h1 : CGAL::halfedges_around_target(v1, mesh)) + { + Mesh::Face_index f1 = face(h1, mesh); + Mesh::Halfedge_index hf1 = prev(halfedge(f1, mesh), mesh); + int i1=0; + while (target(hf1, mesh)!= v1) + { + hf1=next(hf1,mesh); + ++i1; + } + Face_location src(f1, CGAL::make_array(0.,0.,0.)); + src.second[i1]=1; + for(Mesh::Halfedge_index h2 : CGAL::halfedges_around_target(v2, mesh)) + { + Mesh::Face_index f2 = face(h2, mesh); + Mesh::Halfedge_index hf2 = prev(halfedge(f2, mesh), mesh); + int i2=0; + while (target(hf2, mesh)!= v2) + { + hf2=next(hf2,mesh); + ++i2; + } + Face_location tgt(f2, CGAL::make_array(0.,0.,0.)); + tgt.second[i2]=1; + + std::cout << "Running " << f1 << " " << v1 << " | " << f2 << " " << v2 << "\n"; + + std::vector edge_locations; + auto src_bk=src, tgt_bk=tgt; + PMP::locally_shortest_path(src, tgt, mesh, edge_locations); + assert(get(PMP::get_descriptor_from_location(src,mesh))==v1); + assert(get(PMP::get_descriptor_from_location(tgt,mesh))==v2); + + out << edge_locations.size()+2; + out << " " << PMP::construct_point(src, mesh); + for (auto el : edge_locations) + out << " " << PMP::construct_point(el, mesh); + out << " " << PMP::construct_point(tgt, mesh) << "\n"; + out << std::flush; + assert(edge_locations.size()==1); + + src=src_bk; + tgt=tgt_bk; + edge_locations.clear(); + PMP::locally_shortest_path(tgt, src, mesh, edge_locations); + assert(get(PMP::get_descriptor_from_location(src,mesh))==v1); + assert(get(PMP::get_descriptor_from_location(tgt,mesh))==v2); + + out << edge_locations.size()+2; + out << " " << PMP::construct_point(src, mesh); + for (auto el : edge_locations) + out << " " << PMP::construct_point(el, mesh); + out << " " << PMP::construct_point(tgt, mesh) << "\n"; + out << std::flush; + assert(edge_locations.size()==1); + } + } + h = next(h, mesh); + } +#endif + +#if 1 +//TODO: debug why different paths! + // test src is a vertex and tgt is on an edge + for (int i=0; i<3; ++i) + { + Mesh::Vertex_index v1 = target(next(h, mesh), mesh); + Mesh::Halfedge_index h2 = opposite(next(opposite(h, mesh), mesh), mesh); + + for(Mesh::Halfedge_index h1 : CGAL::halfedges_around_target(v1, mesh)) + { + Mesh::Face_index f1 = face(h1, mesh); + Mesh::Halfedge_index hf1 = prev(halfedge(f1, mesh), mesh); + int i1=0; + while (target(hf1, mesh)!= v1) + { + hf1=next(hf1,mesh); + ++i1; + } + Face_location src(f1, CGAL::make_array(0.,0.,0.)); + src.second[i1]=1; + + // define tgt + Mesh::Face_index f2 = face(h2, mesh); + Mesh::Halfedge_index hf2 = prev(halfedge(f2, mesh), mesh); + int k=0; + while(hf2!=h2) + { + hf2=next(hf2, mesh); + ++k; + } + + Face_location tgt(f2, CGAL::make_array(0.5,0.5,0.5)); + tgt.second[(k+1)%3]=0; + + assert(get(PMP::get_descriptor_from_location(src,mesh))==v1); + assert(edge(get(PMP::get_descriptor_from_location(tgt,mesh)), mesh)==edge(h2,mesh)); + + std::cout << "Running " << f1 << " " << v1 << " | " << f2 << " " << edge(h2,mesh) << "\n"; + + std::vector edge_locations; + auto src_bk=src, tgt_bk=tgt; + PMP::locally_shortest_path(src, tgt, mesh, edge_locations); + + out << edge_locations.size()+2; + out << " " << PMP::construct_point(src, mesh); + for (auto el : edge_locations) + out << " " << PMP::construct_point(el, mesh); + out << " " << PMP::construct_point(tgt, mesh) << "\n"; + out << std::flush; + assert(edge_locations.size()==1); + + assert(get(PMP::get_descriptor_from_location(src,mesh))==v1); + assert(edge(get(PMP::get_descriptor_from_location(tgt,mesh)), mesh)==edge(h2,mesh)); + + src=src_bk; + tgt=tgt_bk; + edge_locations.clear(); + PMP::locally_shortest_path(tgt, src, mesh, edge_locations); + + out << edge_locations.size()+2; + out << " " << PMP::construct_point(src, mesh); + for (auto el : edge_locations) + out << " " << PMP::construct_point(el, mesh); + out << " " << PMP::construct_point(tgt, mesh) << "\n"; + out << std::flush; + assert(edge_locations.size()==1); + + assert(get(PMP::get_descriptor_from_location(src,mesh))==v1); + assert(edge(get(PMP::get_descriptor_from_location(tgt,mesh)), mesh)==edge(h2,mesh)); + + + } + h = next(h, mesh); + } +#endif + + // TODO: debug several paths + // test src is on an edge and tgt is on an edge + for (int i=0; i<3; ++i) + { + Mesh::Halfedge_index h1 = opposite(next(h, mesh), mesh); + Mesh::Halfedge_index h2 = opposite(next(opposite(h, mesh), mesh), mesh); + + // define src + Mesh::Face_index f1 = face(h1, mesh); + Mesh::Halfedge_index hf1 = prev(halfedge(f1, mesh), mesh); + int k1=0; + while(hf1!=h1) + { + hf1=next(hf1, mesh); + ++k1; + } + + Face_location src(f1, CGAL::make_array(0.5,0.5,0.5)); + src.second[(k1+1)%3]=0; + + // define tgt + Mesh::Face_index f2 = face(h2, mesh); + Mesh::Halfedge_index hf2 = prev(halfedge(f2, mesh), mesh); + int k2=0; + while(hf2!=h2) + { + hf2=next(hf2, mesh); + ++k2; + } + + Face_location tgt(f2, CGAL::make_array(0.5,0.5,0.5)); + tgt.second[(k2+1)%3]=0; + + assert(edge(get(PMP::get_descriptor_from_location(src,mesh)), mesh)==edge(h1,mesh)); + assert(edge(get(PMP::get_descriptor_from_location(tgt,mesh)), mesh)==edge(h2,mesh)); + + std::cout << "Running " << f1 << " " << edge(h1,mesh) << " | " << f2 << " " << edge(h2,mesh) << "\n"; + + std::vector edge_locations; + auto src_bk=src, tgt_bk=tgt; + PMP::locally_shortest_path(src, tgt, mesh, edge_locations); + + out << edge_locations.size()+2; + out << " " << PMP::construct_point(src, mesh); + for (auto el : edge_locations) + out << " " << PMP::construct_point(el, mesh); + out << " " << PMP::construct_point(tgt, mesh) << "\n"; + out << std::flush; + assert(edge_locations.size()==1); + + assert(edge(get(PMP::get_descriptor_from_location(src,mesh)), mesh)==edge(h1,mesh)); + assert(edge(get(PMP::get_descriptor_from_location(tgt,mesh)), mesh)==edge(h2,mesh)); + + src=src_bk; + tgt=tgt_bk; + edge_locations.clear(); + PMP::locally_shortest_path(tgt, src, mesh, edge_locations); + + out << edge_locations.size()+2; + out << " " << PMP::construct_point(src, mesh); + for (auto el : edge_locations) + out << " " << PMP::construct_point(el, mesh); + out << " " << PMP::construct_point(tgt, mesh) << "\n"; + out << std::flush; + assert(edge_locations.size()==1); + + assert(edge(get(PMP::get_descriptor_from_location(src,mesh)), mesh)==edge(h1,mesh)); + assert(edge(get(PMP::get_descriptor_from_location(tgt,mesh)), mesh)==edge(h2,mesh)); + + h = next(h, mesh); + } + + return 0; +} From 1a0cf3517036eeb432e3f6c2928af7cd2158873e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Tue, 30 Jan 2024 20:10:01 +0100 Subject: [PATCH 040/120] add some debug --- .../Bsurf/locally_shortest_path.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index 05cc5c7dc420..4579a4a33600 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -2602,11 +2602,24 @@ void locally_shortest_path(Face_location src, // remove extra halfedges from inital_path and update src/tgt to get a minimal sequence // in case src and/or tgt are on a vertex or an edge +#ifdef CGAL_DEBUG_BSURF + std::size_t initial_path_size_before = initial_path.size(); +#endif Impl::strip_initial_path(tmesh, src, tgt, initial_path); if (initial_path.empty()) return; CGAL_assertion(face(opposite(initial_path.front(), tmesh), tmesh)==src.first); CGAL_assertion(face(initial_path.back(), tmesh)==tgt.first); +#ifdef CGAL_DEBUG_BSURF + if (initial_path_size_before != initial_path.size()) + { + std::cout << "initial_path cured: " << initial_path_size_before << " ---> " << initial_path.size() << "\n"; + for (halfedge_descriptor h : initial_path) + { + std::cout << " - " << edge(h,tmesh) << "\n"; + } + } +#endif // TODO : if (edge(vsrc, vtgt, mesh) || tgt && src on the same edge ) return; From d61a50095327aae733ae70ddc6b1670156dc2b51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Tue, 30 Jan 2024 20:56:25 +0100 Subject: [PATCH 041/120] handle already visible src and tgt --- .../Bsurf/locally_shortest_path.h | 73 ++++++++++++++++--- 1 file changed, 64 insertions(+), 9 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index 4579a4a33600..bbe3aee701c6 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -2482,11 +2482,69 @@ void locally_shortest_path(Face_location src, const TriangleMesh &tmesh, EdgeLocationRange &edge_locations) { + typedef boost::graph_traits BGT; + typedef typename BGT::halfedge_descriptor halfedge_descriptor; + typedef typename BGT::vertex_descriptor vertex_descriptor; + typedef typename BGT::face_descriptor face_descriptor; + + // start by checking if it is not a trivial path if (src.first == tgt.first) return; + auto variant_src = get_descriptor_from_location(src,tmesh); + auto variant_tgt = get_descriptor_from_location(tgt,tmesh); + + std::vector src_visible_face; + if (const face_descriptor* f_ptr = std::get_if(&variant_src)) + { + src_visible_face.push_back(*f_ptr); + } + else + { + if (const halfedge_descriptor* h_ptr = std::get_if(&variant_src)) + { + if (!is_border(*h_ptr, tmesh)) + src_visible_face.push_back(face(*h_ptr, tmesh)); + if (!is_border(opposite(*h_ptr, tmesh), tmesh)) + src_visible_face.push_back(face(opposite(*h_ptr, tmesh), tmesh)); + } + else + { + vertex_descriptor v = std::get(variant_src); + for(halfedge_descriptor h : halfedges_around_target(v, tmesh)) + { + if (!is_border(h, tmesh)) src_visible_face.push_back(face(h, tmesh)); + } + } + } + std::sort(src_visible_face.begin(), src_visible_face.end()); + if (const face_descriptor* f_ptr = std::get_if(&variant_tgt)) + { + if (std::find(src_visible_face.begin(), src_visible_face.end(), *f_ptr)!=src_visible_face.end()) + return; + } + else + { + auto is_visible = [&src_visible_face, &tmesh](halfedge_descriptor h) + { + return !is_border(h, tmesh) && + std::find(src_visible_face.begin(), + src_visible_face.end(), face(h, tmesh))!=src_visible_face.end(); + }; + if (const halfedge_descriptor* h_ptr = std::get_if(&variant_tgt)) + { + if (is_visible(*h_ptr) || is_visible(opposite(*h_ptr, tmesh))) return; + } + else + { + vertex_descriptor v = std::get(variant_tgt); + for(halfedge_descriptor h : halfedges_around_target(v, tmesh)) + { + if (is_visible(h)) return; + } + } + } + - typedef typename boost::graph_traits::halfedge_descriptor - halfedge_descriptor; //TODO replace with named parameter using VPM = typename boost::property_map::const_type; @@ -2495,13 +2553,12 @@ void locally_shortest_path(Face_location src, VPM vpm = get(CGAL::vertex_point, tmesh); + +// TODO : if (edge(vsrc, vtgt, mesh) || tgt && src on the same edge ) return; + + //TODO: handle cases of src and tgt not in the same connected component (assert?) #ifdef CGAL_BSURF_USE_DIJKSTRA_SP - typedef typename boost::graph_traits::face_descriptor - face_descriptor; - typedef typename boost::graph_traits::edge_descriptor - edge_descriptor; - typename boost::property_map< TriangleMesh, CGAL::dynamic_face_property_t>::const_type predecessor_map = @@ -2621,8 +2678,6 @@ void locally_shortest_path(Face_location src, } #endif -// TODO : if (edge(vsrc, vtgt, mesh) || tgt && src on the same edge ) return; - // here portals contains 2D coordinates of endpoints of edges in initial_path std::vector< std::array> portals=Impl::unfold_strip(initial_path,src,tgt,vpm,tmesh); std::size_t max_index=0; From bd8c69f9a48b28695acdb1a67eb4f71e5bb185e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 31 Jan 2024 10:19:05 +0100 Subject: [PATCH 042/120] fix edge update --- .../Bsurf/locally_shortest_path.h | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index bbe3aee701c6..eee49f471a0f 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -1580,7 +1580,7 @@ struct Locally_shortest_path_imp initial_path.erase(initial_path.begin(), std::next(initial_path.begin())); if (initial_path.empty()) return; - halfedge_descriptor new_hsrc=halfedge(face(opposite(initial_path[0], tmesh), tmesh), tmesh); + halfedge_descriptor new_hsrc=prev(halfedge(face(opposite(initial_path[0], tmesh), tmesh), tmesh), tmesh); int hid=0; while (opposite(new_hsrc, tmesh)!=hsrc) { @@ -1589,8 +1589,8 @@ struct Locally_shortest_path_imp CGAL_assertion(hid!=3); } std::array fl_src = {FT(0),FT(0),FT(0)}; - fl_src[hid]=src.second[(ei+1)%3]; - fl_src[(hid+1)%3]=src.second[ei]; + fl_src[hid]=src.second[(ei+2)%3]; + fl_src[(hid+2)%3]=src.second[ei]; src=std::make_pair(face(new_hsrc, tmesh), fl_src); } } @@ -1643,7 +1643,7 @@ struct Locally_shortest_path_imp initial_path.pop_back(); if (initial_path.empty()) return; - halfedge_descriptor new_htgt=halfedge(face(initial_path.back(), tmesh), tmesh); + halfedge_descriptor new_htgt=prev(halfedge(face(initial_path.back(), tmesh), tmesh), tmesh); int hid=0; while (opposite(new_htgt, tmesh)!=htgt) { @@ -1652,8 +1652,8 @@ struct Locally_shortest_path_imp CGAL_assertion(hid!=3); } std::array fl_tgt = {FT(0),FT(0),FT(0)}; - fl_tgt[hid]=tgt.second[(ei+1)%3]; - fl_tgt[(hid+1)%3]=tgt.second[ei]; + fl_tgt[hid]=tgt.second[(ei+2)%3]; + fl_tgt[(hid+2)%3]=tgt.second[ei]; tgt=std::make_pair(face(new_htgt, tmesh), fl_tgt); } } @@ -2675,6 +2675,7 @@ void locally_shortest_path(Face_location src, { std::cout << " - " << edge(h,tmesh) << "\n"; } + std::cout << " Updated src/tgt " << construct_point(src, tmesh) << " | " << construct_point(tgt, tmesh) << "\n"; } #endif From 6994bb9025246f99d571721a5aabb9f5ee9c1bd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 31 Jan 2024 11:44:49 +0100 Subject: [PATCH 043/120] update for length 1 path --- .../Bsurf/locally_shortest_path.h | 70 +++++++++++-------- 1 file changed, 41 insertions(+), 29 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index eee49f471a0f..ac159189ea82 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -557,7 +557,8 @@ struct Locally_shortest_path_imp dump_path(path, lerps, src, tgt, mesh); #endif - vertex_descriptor vertex=boost::graph_traits::null_vertex(); + typedef boost::graph_traits BGT; + vertex_descriptor vertex=BGT::null_vertex(); // TODO: use a while loop breaking when no apex vertices not already visited are available for (std::size_t i = 0; i < portals.size() * 2 && index != std::size_t(-1); ++i) @@ -568,9 +569,11 @@ struct Locally_shortest_path_imp std::cout << "tgt = " << construct_point(tgt, mesh) << "\n"; #endif - vertex_descriptor new_vertex=boost::graph_traits::null_vertex(); + vertex_descriptor new_vertex=BGT::null_vertex(); halfedge_descriptor h_curr = path[index]; - halfedge_descriptor h_next = path[index + 1]; + halfedge_descriptor h_next = path.size()>index+1 + ? path[index + 1] + : BGT::null_halfedge(); // only for debug bool is_target = false; if (lerps[index] == 0) { new_vertex = target(h_curr,mesh); @@ -583,6 +586,7 @@ struct Locally_shortest_path_imp #ifdef CGAL_DEBUG_BSURF std::cout << " Current strip with Apex: " << get(vpm, new_vertex) << "\n"; + std::cout << " is_target? " << is_target << "\n"; for (auto h : path) { std::cout << " 4 " << get(vpm, source(h, mesh)) @@ -596,11 +600,8 @@ struct Locally_shortest_path_imp // if I hit the source vertex v of h_curr, then h_next has v as source, thus we turn ccw around v in path // Similarly, if I hit the target vertex v of h_curr, then h_next has v as target, thus we turn cw around v in path - if ( !(!is_target || opposite(next(h_curr, mesh), mesh)==h_next) ) - std::cout << edge(h_curr, mesh) << " | " << edge(opposite(next(h_curr, mesh), mesh), mesh) << " vs " << edge(h_next, mesh) << "\n"; - - CGAL_assertion(!is_target || opposite(next(h_curr, mesh), mesh)==h_next); - CGAL_assertion(is_target || opposite(prev(h_curr, mesh), mesh)==h_next); + CGAL_assertion(BGT::null_halfedge()==h_next || !is_target || opposite(next(h_curr, mesh), mesh)==h_next); + CGAL_assertion(BGT::null_halfedge()==h_next || is_target || opposite(prev(h_curr, mesh), mesh)==h_next); std::size_t curr_index = index+1; std::vector new_hedges; @@ -624,18 +625,25 @@ struct Locally_shortest_path_imp { face_descriptor target_face; - while (target(path[curr_index], mesh) == new_vertex) + if (curr_index == path.size()) + { + target_face=tgt.first; + } + else { - if(curr_index==path.size()-1) + while (target(path[curr_index], mesh) == new_vertex) { - target_face=tgt.first; - curr_index=path.size(); - break; + if(curr_index==path.size()-1) + { + target_face=tgt.first; + curr_index=path.size(); + break; + } + ++curr_index; } - ++curr_index; + if (curr_index != path.size()) + target_face = face(opposite(path[curr_index], mesh), mesh); } - if (curr_index != path.size()) - target_face = face(opposite(path[curr_index], mesh), mesh); halfedge_descriptor h_loop=opposite(prev(opposite(h_curr, mesh), mesh), mesh); @@ -684,28 +692,30 @@ struct Locally_shortest_path_imp } else new_hedges.push_back(h_loop); - - - - } else { face_descriptor target_face; - while (source(path[curr_index], mesh) == new_vertex) + if (curr_index == path.size()) + { + target_face=tgt.first; + } + else { - if(curr_index==path.size()-1) + while (source(path[curr_index], mesh) == new_vertex) { - target_face=tgt.first; - curr_index=path.size(); - break; + if(curr_index==path.size()-1) + { + target_face=tgt.first; + curr_index=path.size(); + break; + } + ++curr_index; } - ++curr_index; + if (curr_index != path.size()) + target_face=face(opposite(path[curr_index], mesh), mesh); } - if (curr_index != path.size()) - target_face=face(opposite(path[curr_index], mesh), mesh); - halfedge_descriptor h_loop=opposite(next(opposite(h_curr, mesh), mesh), mesh); // skip the face before h_curr (as we won't remove it from path) // don't pick first h_loop if src is visible on the other side of it @@ -2675,6 +2685,8 @@ void locally_shortest_path(Face_location src, { std::cout << " - " << edge(h,tmesh) << "\n"; } + std::cout << " src=" << src.first << " ("<< src.second[0] << "," << src.second[1] << "," << src.second[2] << ")\n"; + std::cout << " tgt=" << tgt.first << " ("<< tgt.second[0] << "," << tgt.second[1] << "," << tgt.second[2] << ")\n"; std::cout << " Updated src/tgt " << construct_point(src, tmesh) << " | " << construct_point(tgt, tmesh) << "\n"; } #endif From b3fefe5092ca337def014519a2c329a83b1ebe76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 31 Jan 2024 11:54:29 +0100 Subject: [PATCH 044/120] update test --- .../Polygon_mesh_processing/test_Bsurf.cpp | 68 ++++++++++++------- 1 file changed, 45 insertions(+), 23 deletions(-) diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_Bsurf.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_Bsurf.cpp index 6ca79b7b1ab0..0ba0dd3cf947 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_Bsurf.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_Bsurf.cpp @@ -28,23 +28,26 @@ int main(int argc, char** argv) std::size_t nb_hedges = halfedges(mesh).size(); // take two random faces and pick the centroid - // CGAL::Random rnd = CGAL::get_default_random(); - CGAL::Random rnd(1706604646); + CGAL::Random rnd = CGAL::get_default_random(); + //CGAL::Random rnd(1706604646); // CGAL::Random rnd(1695724381); // CGAL::Random rnd(1695813638); std::cout << "seed " << rnd.get_seed() << std::endl; Mesh::Halfedge_index h = *std::next(halfedges(mesh).begin(), rnd.get_int(0, nb_hedges)); - +// for (Mesh::Halfedge_index h : halfedges(mesh)) +// { std::ofstream out("locally_shortest_path.polylines.txt"); // test src/tgt being 2 opposite vertices of an edge #if 1 - for (int i=0; i<3; ++i) + for (int loop=0; loop<3; ++loop) { Mesh::Vertex_index v1 = target(next(h, mesh), mesh); Mesh::Vertex_index v2 = target(next(opposite(h, mesh), mesh), mesh); + bool first_run = true; + std::size_t expected_size=0; for(Mesh::Halfedge_index h1 : CGAL::halfedges_around_target(v1, mesh)) { Mesh::Face_index f1 = face(h1, mesh); @@ -84,7 +87,13 @@ int main(int argc, char** argv) out << " " << PMP::construct_point(el, mesh); out << " " << PMP::construct_point(tgt, mesh) << "\n"; out << std::flush; - assert(edge_locations.size()==1); + + if (first_run) + { + first_run=false; + expected_size=edge_locations.size(); + } + CGAL_assertion(edge_locations.size() == expected_size); src=src_bk; tgt=tgt_bk; @@ -94,12 +103,12 @@ int main(int argc, char** argv) assert(get(PMP::get_descriptor_from_location(tgt,mesh))==v2); out << edge_locations.size()+2; - out << " " << PMP::construct_point(src, mesh); + out << " " << PMP::construct_point(tgt, mesh); for (auto el : edge_locations) out << " " << PMP::construct_point(el, mesh); - out << " " << PMP::construct_point(tgt, mesh) << "\n"; + out << " " << PMP::construct_point(src, mesh) << "\n"; out << std::flush; - assert(edge_locations.size()==1); + CGAL_assertion(edge_locations.size() == expected_size); } } h = next(h, mesh); @@ -107,12 +116,13 @@ int main(int argc, char** argv) #endif #if 1 -//TODO: debug why different paths! // test src is a vertex and tgt is on an edge for (int i=0; i<3; ++i) { Mesh::Vertex_index v1 = target(next(h, mesh), mesh); Mesh::Halfedge_index h2 = opposite(next(opposite(h, mesh), mesh), mesh); + bool first_run=true; + std::size_t expected_size=0; for(Mesh::Halfedge_index h1 : CGAL::halfedges_around_target(v1, mesh)) { @@ -139,12 +149,14 @@ int main(int argc, char** argv) Face_location tgt(f2, CGAL::make_array(0.5,0.5,0.5)); tgt.second[(k+1)%3]=0; + tgt.second[(k+2)%3]=0.25; + tgt.second[(k)%3]=0.75; assert(get(PMP::get_descriptor_from_location(src,mesh))==v1); assert(edge(get(PMP::get_descriptor_from_location(tgt,mesh)), mesh)==edge(h2,mesh)); std::cout << "Running " << f1 << " " << v1 << " | " << f2 << " " << edge(h2,mesh) << "\n"; - + // std::cout << " " << PMP::construct_point(src, mesh) << " | " << PMP::construct_point(tgt, mesh) << "\n"; std::vector edge_locations; auto src_bk=src, tgt_bk=tgt; PMP::locally_shortest_path(src, tgt, mesh, edge_locations); @@ -155,7 +167,13 @@ int main(int argc, char** argv) out << " " << PMP::construct_point(el, mesh); out << " " << PMP::construct_point(tgt, mesh) << "\n"; out << std::flush; - assert(edge_locations.size()==1); + + if (first_run) + { + first_run=false; + expected_size=edge_locations.size(); + } + CGAL_assertion(edge_locations.size()==expected_size); assert(get(PMP::get_descriptor_from_location(src,mesh))==v1); assert(edge(get(PMP::get_descriptor_from_location(tgt,mesh)), mesh)==edge(h2,mesh)); @@ -166,23 +184,20 @@ int main(int argc, char** argv) PMP::locally_shortest_path(tgt, src, mesh, edge_locations); out << edge_locations.size()+2; - out << " " << PMP::construct_point(src, mesh); + out << " " << PMP::construct_point(tgt, mesh); for (auto el : edge_locations) out << " " << PMP::construct_point(el, mesh); - out << " " << PMP::construct_point(tgt, mesh) << "\n"; + out << " " << PMP::construct_point(src, mesh) << "\n"; out << std::flush; - assert(edge_locations.size()==1); + CGAL_assertion(edge_locations.size()==expected_size); assert(get(PMP::get_descriptor_from_location(src,mesh))==v1); assert(edge(get(PMP::get_descriptor_from_location(tgt,mesh)), mesh)==edge(h2,mesh)); - - } h = next(h, mesh); } #endif - - // TODO: debug several paths +#if 1 // test src is on an edge and tgt is on an edge for (int i=0; i<3; ++i) { @@ -201,7 +216,8 @@ int main(int argc, char** argv) Face_location src(f1, CGAL::make_array(0.5,0.5,0.5)); src.second[(k1+1)%3]=0; - + src.second[(k1+2)%3]=0.25; + src.second[(k1)%3]=0.75; // define tgt Mesh::Face_index f2 = face(h2, mesh); Mesh::Halfedge_index hf2 = prev(halfedge(f2, mesh), mesh); @@ -214,6 +230,8 @@ int main(int argc, char** argv) Face_location tgt(f2, CGAL::make_array(0.5,0.5,0.5)); tgt.second[(k2+1)%3]=0; + tgt.second[(k2+2)%3]=0.25; + tgt.second[(k2)%3]=0.75; assert(edge(get(PMP::get_descriptor_from_location(src,mesh)), mesh)==edge(h1,mesh)); assert(edge(get(PMP::get_descriptor_from_location(tgt,mesh)), mesh)==edge(h2,mesh)); @@ -230,7 +248,8 @@ int main(int argc, char** argv) out << " " << PMP::construct_point(el, mesh); out << " " << PMP::construct_point(tgt, mesh) << "\n"; out << std::flush; - assert(edge_locations.size()==1); + + std::size_t expected_size = edge_locations.size(); assert(edge(get(PMP::get_descriptor_from_location(src,mesh)), mesh)==edge(h1,mesh)); assert(edge(get(PMP::get_descriptor_from_location(tgt,mesh)), mesh)==edge(h2,mesh)); @@ -241,18 +260,21 @@ int main(int argc, char** argv) PMP::locally_shortest_path(tgt, src, mesh, edge_locations); out << edge_locations.size()+2; - out << " " << PMP::construct_point(src, mesh); + out << " " << PMP::construct_point(tgt, mesh); for (auto el : edge_locations) out << " " << PMP::construct_point(el, mesh); - out << " " << PMP::construct_point(tgt, mesh) << "\n"; + out << " " << PMP::construct_point(src, mesh) << "\n"; out << std::flush; - assert(edge_locations.size()==1); + + CGAL_assertion(edge_locations.size()==expected_size); assert(edge(get(PMP::get_descriptor_from_location(src,mesh)), mesh)==edge(h1,mesh)); assert(edge(get(PMP::get_descriptor_from_location(tgt,mesh)), mesh)==edge(h2,mesh)); h = next(h, mesh); } + #endif +// } return 0; } From 3b5501538e6447ea7e8135060a830406ce025145 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 31 Jan 2024 14:02:06 +0100 Subject: [PATCH 045/120] fix loop --- .../Bsurf/locally_shortest_path.h | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index ac159189ea82..4866acfded8f 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -587,14 +587,16 @@ struct Locally_shortest_path_imp #ifdef CGAL_DEBUG_BSURF std::cout << " Current strip with Apex: " << get(vpm, new_vertex) << "\n"; std::cout << " is_target? " << is_target << "\n"; + std::cout << " -- path --\n"; for (auto h : path) { std::cout << " 4 " << get(vpm, source(h, mesh)) << " " << get(vpm, target(h, mesh)) << " " << get(vpm, target(next(h, mesh), mesh)) << " " << get(vpm, source(h, mesh)) << std::endl; - + std::cout << edge(h, mesh) << std::endl; } + std::cout << " ----------\n"; #endif // if I hit the source vertex v of h_curr, then h_next has v as source, thus we turn ccw around v in path @@ -666,11 +668,11 @@ struct Locally_shortest_path_imp h_loop=opposite(prev(h_loop,mesh), mesh); } - do { + while(target_face!=face(h_loop, mesh)) + { new_hedges.push_back(h_loop); h_loop=opposite(prev(h_loop,mesh), mesh); } - while(target_face!=face(h_loop, mesh)); // don't pick last h_loop is tgt is visible on the other side of it const int ktgt = is_on_hedge(tgt,h_loop); @@ -737,11 +739,12 @@ struct Locally_shortest_path_imp h_loop=opposite(next(h_loop,mesh), mesh); } - do { + while(target_face!=face(h_loop, mesh)) + { new_hedges.push_back(h_loop); h_loop=opposite(next(h_loop,mesh), mesh); } - while(target_face!=face(h_loop, mesh)); + // don't pick last h_loop is tgt is visible on the other side of it const int ktgt = is_on_hedge(tgt,h_loop); @@ -771,6 +774,19 @@ struct Locally_shortest_path_imp new_path.insert(new_path.end(), path.begin()+curr_index, path.end()); path.swap(new_path); +#ifdef CGAL_DEBUG_BSURF + std::cout << " -- new path --\n"; + for (auto h : path) + { + std::cout << " 4 " << get(vpm, source(h, mesh)) + << " " << get(vpm, target(h, mesh)) + << " " << get(vpm, target(next(h, mesh), mesh)) + << " " << get(vpm, source(h, mesh)) << std::endl; + std::cout << edge(h, mesh) << std::endl; + } + std::cout << " ----------\n"; +#endif + portals=unfold_strip(path,src,tgt,vpm,mesh); lerps=funnel(portals,index); CGAL_assertion(lerps.size()==path.size()); From 88fe6cf04f3dae60c5419af275ad7b022fcc64e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 31 Jan 2024 18:45:51 +0100 Subject: [PATCH 046/120] add test on grid vertices are duplicated in the output --> post-processing needed --- .../Polygon_mesh_processing/CMakeLists.txt | 2 + .../test_Bsurf_grid.cpp | 95 +++++++++++++++++++ 2 files changed, 97 insertions(+) create mode 100644 Polygon_mesh_processing/test/Polygon_mesh_processing/test_Bsurf_grid.cpp diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt b/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt index a170dcdd48f1..1a6208e0dd3f 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt @@ -89,6 +89,8 @@ if(TARGET CGAL::Eigen3_support) target_link_libraries(remeshing_quality_test PUBLIC CGAL::Eigen3_support) create_single_source_cgal_program("test_Bsurf.cpp") target_link_libraries(test_Bsurf PUBLIC CGAL::Eigen3_support) + create_single_source_cgal_program("test_Bsurf_grid.cpp") + target_link_libraries(test_Bsurf_grid PUBLIC CGAL::Eigen3_support) else() message(STATUS "NOTICE: Tests that use the Eigen library will not be compiled.") endif() diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_Bsurf_grid.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_Bsurf_grid.cpp new file mode 100644 index 000000000000..6b698d57f190 --- /dev/null +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_Bsurf_grid.cpp @@ -0,0 +1,95 @@ +#include +#include +#include +#include +#include + +#include + + +namespace PMP = CGAL::Polygon_mesh_processing; + +typedef CGAL::Exact_predicates_inexact_constructions_kernel K; +typedef CGAL::Surface_mesh Mesh; +typedef PMP::Face_location Face_location; +typedef PMP::Edge_location Edge_location; + + +void test_vertex_pair(unsigned int vid1, unsigned int vid2, const Mesh& mesh) +{ + Mesh::Vertex_index v1(vid1), v2(vid2); + std::cout << "running " << v1 << " " << v2 << "\n"; + static int rid=-1; + std::string fname="grid_shortest_path"+std::to_string(++rid)+".polylines.txt"; + std::cout << "writing in " << fname << "\n"; + + std::ofstream out(fname); + + Mesh::Face_index f1=face(halfedge(v1, mesh), mesh), f2 = face(halfedge(v2, mesh), mesh); + + Face_location src(f1, CGAL::make_array(0.,0.,0.)); + Face_location tgt(f2, CGAL::make_array(0.,0.,0.)); + + Mesh::Halfedge_index hf1 = prev(halfedge(f1, mesh), mesh); + int i1=0; + while (target(hf1, mesh)!= v1) + { + hf1=next(hf1,mesh); + ++i1; + } + src.second[i1]=1; + Mesh::Halfedge_index hf2 = prev(halfedge(f2, mesh), mesh); + int i2=0; + while (target(hf2, mesh)!= v2) + { + hf2=next(hf2,mesh); + ++i2; + } + tgt.second[i2]=1; + + std::vector edge_locations; + auto src_bk=src, tgt_bk=tgt; + PMP::locally_shortest_path(src, tgt, mesh, edge_locations); + assert(get(PMP::get_descriptor_from_location(src,mesh))==v1); + assert(get(PMP::get_descriptor_from_location(tgt,mesh))==v2); + + out << edge_locations.size()+2; + out << " " << PMP::construct_point(src, mesh); + for (auto el : edge_locations) + out << " " << PMP::construct_point(el, mesh); + out << " " << PMP::construct_point(tgt, mesh) << "\n"; + out << std::flush; + std::size_t expected_size = edge_locations.size(); + + /// other direction + + src=src_bk; + tgt=tgt_bk; + edge_locations.clear(); + PMP::locally_shortest_path(tgt, src, mesh, edge_locations); + assert(get(PMP::get_descriptor_from_location(src,mesh))==v1); + assert(get(PMP::get_descriptor_from_location(tgt,mesh))==v2); + + out << edge_locations.size()+2; + out << " " << PMP::construct_point(tgt, mesh); + for (auto el : edge_locations) + out << " " << PMP::construct_point(el, mesh); + out << " " << PMP::construct_point(src, mesh) << "\n"; + out << std::flush; + CGAL_assertion(edge_locations.size() == expected_size); +} + +int main() +{ + Mesh tmp; + CGAL::make_grid(10, 10, tmp, [](int i, int j){return K::Point_3(i,j,0);}, true); + Mesh mesh; + CGAL::Polygon_mesh_processing::extrude_mesh(tmp, mesh, K::Vector_3(0,0,-1)); + std::ofstream("grid.off") << mesh; + + test_vertex_pair(84,36,mesh); + test_vertex_pair(40,36,mesh); + test_vertex_pair(78,28,mesh); + + return 0; +} From 1515f8c66cd77a718749103ca3377e7768cbb955 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Thu, 1 Feb 2024 10:18:02 +0100 Subject: [PATCH 047/120] move Edge_location and add convinence function --- .../Bsurf/locally_shortest_path.h | 7 +-- .../CGAL/Polygon_mesh_processing/locate.h | 60 +++++++++++++++++++ 2 files changed, 62 insertions(+), 5 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index 4866acfded8f..696a34cca502 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -34,11 +34,6 @@ void locally_shortest_path(Face_location src, const TriangleMesh &tmesh, EdgeLocationRange &edge_locations); -template -using Edge_location = - std::pair::edge_descriptor, - std::array>; - template using Bezier_segment = std::array, 4>; @@ -2584,7 +2579,9 @@ void locally_shortest_path(Face_location src, //TODO: handle cases of src and tgt not in the same connected component (assert?) + #ifdef CGAL_BSURF_USE_DIJKSTRA_SP + typedef typename BGT::edge_descriptor edge_descriptor; typename boost::property_map< TriangleMesh, CGAL::dynamic_face_property_t>::const_type predecessor_map = diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/locate.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/locate.h index 592e5008702f..9ef19e80aa23 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/locate.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/locate.h @@ -59,6 +59,12 @@ template using descriptor_variant = std::variant::vertex_descriptor, typename boost::graph_traits::halfedge_descriptor, typename boost::graph_traits::face_descriptor>; +/// \ingroup PMP_locate_grp +/// +/// A variant used in the function `get_descriptor_from_location()` overload for `Edge_location`. +template +using vertex_or_halfedge_variant = std::variant::vertex_descriptor, + typename boost::graph_traits::halfedge_descriptor>; /// \ingroup PMP_locate_grp /// @@ -81,6 +87,18 @@ template using Face_location = std::pair::face_descriptor, Barycentric_coordinates >; +/// \ingroup PMP_locate_grp +/// +/// If `pm` is the input surface mesh and given the pair (`e`, `bc`) +/// such that `bc` is a pair of barycentric coordinates `(w0, w1)`, the correspondence +/// between the coordinates in `bc` and the vertices of the edge `e` is the following: +/// - `w0` corresponds to `source(e, pm)` +/// - `w1` corresponds to `target(e, pm)` +template +using Edge_location = + std::pair::edge_descriptor, + std::array>; + namespace internal { // The Ray must have the same ambient dimension as the property map's value type (aka, the point type) @@ -557,6 +575,48 @@ get_descriptor_from_location(const Face_location& loc, return fd; } + +/// \ingroup PMP_locate_grp +/// +/// \brief Given a location, returns a descriptor to the simplex of smallest dimension +/// on which the point corresponding to the location lies. +/// +/// \details In other words: +/// - if the point lies on a vertex, this function returns a `boost::graph_traits::%vertex_descriptor` `v`; +/// - if the point lies on a halfedge, this function returns a `boost::graph_traits::%halfedge_descriptor` `hd` +/// (note that in that case, `loc.first == face(hd, tm)` holds). +/// +/// \tparam FT must be a model of `FieldNumberType` +/// \tparam TriangleMesh must be a model of `FaceGraph` +/// +/// \param loc a location with `loc.first` a face of `tm` +/// \param tm a triangulated surface mesh +/// +/// \pre `loc.first` is an edge descriptor corresponding to an edge of `tm`. +/// \pre `loc` describes the barycentric coordinates of a point that lives within the edge (boundary included), +/// meaning the barycentric coordinates are all positive. +/// +template +vertex_or_halfedge_variant +get_descriptor_from_location(const Edge_location& loc, + const TriangleMesh& tm) +{ + typedef typename boost::graph_traits::edge_descriptor edge_descriptor; + + typedef std::array Barycentric_coordinates; + + const edge_descriptor ed = loc.first; + const Barycentric_coordinates& bar = loc.second; + + if (bar[0]==FT(0)) + return source(ed, tm); + + if (bar[1]==FT(0)) + return target(ed, tm); + + return halfedge(ed, tm); +} + /// \ingroup PMP_locate_grp /// /// \brief Given a location in a face, returns the geometric position described From 5bff11f8723d1061a7d9f825b41cdf03a8df889c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Thu, 1 Feb 2024 10:18:46 +0100 Subject: [PATCH 048/120] add simplification function showing that we need exact predicates --- .../test_Bsurf_grid.cpp | 71 ++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_Bsurf_grid.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_Bsurf_grid.cpp index 6b698d57f190..6c96dd5ec71c 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_Bsurf_grid.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_Bsurf_grid.cpp @@ -15,15 +15,82 @@ typedef PMP::Face_location Face_location; typedef PMP::Edge_location Edge_location; +//TODO: move to header and add doc +template +void simplify_sequence(std::vector>& edge_locations, const TriangleMesh& tm) +{ + std::cout << " simplify_sequence:\n"; + std::cout << " input.size() = " << edge_locations.size() << "\n"; + typedef boost::graph_traits BGT; + typedef typename BGT::vertex_descriptor vertex_descriptor; + + auto print_seq = [&]() + { + std::cout << " "; + typedef typename BGT::halfedge_descriptor halfedge_descriptor; + for (auto el : edge_locations) + { + auto var = PMP::get_descriptor_from_location(el, tm); + if (const vertex_descriptor* vd_ptr = std::get_if(&var)) + std::cout << " " << *vd_ptr; + else + { + if (const halfedge_descriptor* ed_ptr = std::get_if(&var)) + std::cout << " " << *ed_ptr; + else + std::cout << " ERROR"; + } + } + std::cout << "\n"; + }; + print_seq(); + + + std::vector> filtered_locations; + std::size_t end=edge_locations.size(); + filtered_locations.reserve(edge_locations.size()); + for(std::size_t curr=0; curr(&var)) + { + do{ + vertex_descriptor vd = *vd_ptr; + ++curr; + if (curr!=end) + { + var = PMP::get_descriptor_from_location(edge_locations[curr], tm); + vd_ptr = std::get_if(&var); + if (vd_ptr==nullptr || *vd_ptr!=vd) + break; + } + else + break; + } + while(true); + } + else + ++curr; + } + + if (filtered_locations.size()!=edge_locations.size()) + filtered_locations.swap(edge_locations); + + std::cout << " output.size() = " << edge_locations.size() << "\n"; + print_seq(); +} + void test_vertex_pair(unsigned int vid1, unsigned int vid2, const Mesh& mesh) { Mesh::Vertex_index v1(vid1), v2(vid2); std::cout << "running " << v1 << " " << v2 << "\n"; static int rid=-1; std::string fname="grid_shortest_path"+std::to_string(++rid)+".polylines.txt"; - std::cout << "writing in " << fname << "\n"; + std::cout << " writing in " << fname << "\n"; std::ofstream out(fname); + out << std::setprecision(17); Mesh::Face_index f1=face(halfedge(v1, mesh), mesh), f2 = face(halfedge(v2, mesh), mesh); @@ -53,6 +120,8 @@ void test_vertex_pair(unsigned int vid1, unsigned int vid2, const Mesh& mesh) assert(get(PMP::get_descriptor_from_location(src,mesh))==v1); assert(get(PMP::get_descriptor_from_location(tgt,mesh))==v2); + simplify_sequence(edge_locations, mesh); + out << edge_locations.size()+2; out << " " << PMP::construct_point(src, mesh); for (auto el : edge_locations) From d28866c4e9d0b3be498019c825a3cc438162b084 Mon Sep 17 00:00:00 2001 From: ClaudioMancinelli Date: Thu, 1 Feb 2024 12:04:10 +0100 Subject: [PATCH 049/120] WIP Straightest path --- .../straightest_geodesic_sm_example.cpp | 4 +- .../Bsurf/locally_shortest_path.h | 169 ++++++++++++++++-- 2 files changed, 152 insertions(+), 21 deletions(-) diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/straightest_geodesic_sm_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/straightest_geodesic_sm_example.cpp index e0ac8f5e26d6..44be28822e9f 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/straightest_geodesic_sm_example.cpp +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/straightest_geodesic_sm_example.cpp @@ -33,8 +33,8 @@ int main(int argc, char** argv) std::size_t nb_faces = faces(mesh).size(); // take two random faces and pick the centroid - CGAL::Random rnd = CGAL::get_default_random(); - // CGAL::Random rnd(1695720148); + //CGAL::Random rnd = CGAL::get_default_random(); + CGAL::Random rnd(1706709591); std::cout << "seed " << rnd.get_seed() << std::endl; Mesh::Face_index f = *std::next(faces(mesh).begin(), rnd.get_int(0, nb_faces)); diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index 696a34cca502..f92934d58929 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -255,30 +256,49 @@ struct Locally_shortest_path_imp point_coords); } + //case k=0 assume that flat_tid has been flattened putting x-axis aligned with h_edge static std::array - unfold_face(halfedge_descriptor h, + unfold_face(const halfedge_descriptor& h_edge, const VertexPointMap &vpm, const TriangleMesh &mesh, - const std::array& flat_tid) + const std::array& flat_tid,const int k=0) { - halfedge_descriptor h_opp = opposite(h, mesh); - + halfedge_descriptor h_opp = opposite(h_edge, mesh); vertex_descriptor v = target(next(h_opp,mesh),mesh); - vertex_descriptor a = target(h_opp,mesh);//consistent with init_flat_triangle - vertex_descriptor b = source(h_opp, mesh); + vertex_descriptor a = source(h_edge, mesh); + vertex_descriptor b = target(h_edge,mesh); FT r0 = squared_distance(get(vpm,v), get(vpm,a)); FT r1 = squared_distance(get(vpm,v), get(vpm,b)); - Vector_2 v2 = intersect_circles(flat_tid[1], r1, flat_tid[0], r0); + Vector_2 v2 = intersect_circles(flat_tid[(k+1)%3], r1, flat_tid[k], r0); + halfedge_descriptor h_ref_opp = halfedge(face(h_opp,mesh),mesh); std::array res; - res[0]=flat_tid[0]; - res[1]=flat_tid[1]; - res[2]=v2; + if(h_ref_opp==h_opp) + { + std::cout<<"COUCOU 0"<& prev_coords, + const array& curr_coords, + const VertexPointMap& vpm, + const TriangleMesh& mesh) + { + + int k=0; + + if(h_edge==prev(h_ref,mesh)) + k=2; + else if(h_edge==next(h_ref,mesh)) + k=1; + else + assert(h_edge==h_ref); + + std::cout<<" k is "< flat_curr = init_flat_triangle(h_ref,vpm,mesh); + std::array flat_prev = unfold_face(h_edge,vpm,mesh,flat_curr,k); + + std::cout<<" Flat_prev"<>& edge_locations, const VertexPointMap &vpm, const TriangleMesh& mesh, @@ -1092,6 +1185,8 @@ struct Locally_shortest_path_imp return {false, -1}; } +//TODO get rid of tol or at least get it dependent on the input? +// static std::tuple point_is_vert(const Face_location& p,const FT& tol=1e-5) @@ -1117,7 +1212,7 @@ struct Locally_shortest_path_imp Vector_2 v2 = right - left; Vector_2 v3(-direction.y(), direction.x()); double t0 = (v2.x()*v1.y()-v2.y()*v1.x()) / (v2*v3); - double t1 = -left*v3/ ( v2*v3 ); + double t1 = v1*v3/ ( v2*v3 ); return std::make_pair(t0, t1); }; @@ -1130,13 +1225,19 @@ struct Locally_shortest_path_imp //rotated the triangle in order to test intersection at meaningful edges before std::array rotated_tri=tri; // TODO rotated_tri.begin() + offset ? + std::cout << " offset " << offset << "\n"; + std::cout << " tri " << tri[0] << " " << tri[1] << " " << tri[2] << "\n"; + std::cout << " p " << p << "\n"; + std::cout << " p+dir " << p+dir << "\n"; for(int k=0;k 0 && t1 >= -1e-4 && t1 <= 1 + 1e-4) { @@ -1179,6 +1280,7 @@ struct Locally_shortest_path_imp const face_descriptor& tid, const FT& init_angle) { + //TODO use interval for robustness FT total_angle=get_total_angle(vid,mesh,vpm); FT theta = 0.5 * total_angle; halfedge_descriptor h=halfedge(tid,mesh); @@ -1223,11 +1325,12 @@ struct Locally_shortest_path_imp const VertexPointMap &vpm, const Vector_2& dir,const FT& len) { + // XXXXXXXXXXXXXXXX auto get_halfedge_offset=[&mesh](const halfedge_descriptor& h_ref,const halfedge_descriptor& h_curr) { - if(source(h_ref,mesh)==source(h_curr,mesh)) return 0; - if(source(next(h_ref,mesh),mesh)==source(h_curr,mesh)) return 1; - if(source(prev(h_ref,mesh),mesh)==source(h_curr,mesh)) return 2; + if( h_ref == h_curr) return 0; + if( next(h_ref, mesh) == h_curr ) return 1; + if( prev(h_ref, mesh) == h_curr ) return 2; std::cout<<"Error! Halfedges are in different faces"<" << accumulated << " vs " << len << "\n"; + int curr_offset=get_halfedge_offset(h_ref,h_curr); auto [k, t1] = segment_in_tri(flat_p, curr_flat_tid, curr_dir,curr_offset); @@ -1338,6 +1457,8 @@ struct Locally_shortest_path_imp point_on_edge.second=new_bary; std::tie(is_vert, kv) = point_is_vert(point_on_edge); + std::cout << " is_vert? " << is_vert << "\n"; + if (is_vert) { vertex_descriptor vid = target(get_halfedge(kv, h_ref), mesh); @@ -1359,6 +1480,7 @@ struct Locally_shortest_path_imp h_ref=halfedge(curr_tid,mesh); curr_flat_tid=init_flat_triangle(h_ref,vpm,mesh); + prev_p = curr_p; curr_p=point_on_edge; int k=get_vid_offset(h_ref,target(h_curr,mesh)); @@ -1367,23 +1489,32 @@ struct Locally_shortest_path_imp else { h_curr=opposite(get_halfedge(k, h_ref),mesh); + std::cout << " h_curr " << edge(h_curr, mesh)<<"\n"; face_descriptor adj = face(h_curr,mesh); + std::cout << " adj " << adj<<"\n"; + Vector_2 intersection_point=new_bary[0]*curr_flat_tid[0]+new_bary[1]*curr_flat_tid[1]+new_bary[2]*curr_flat_tid[2]; + std::cout<<" New intersection point is "<< intersection_point< curr_alpha=make_array(t1,1-t1); //reversed because will switch face new_bary=edge_barycentric_coordinate(h_curr,halfedge(adj,mesh),curr_alpha); prev_p = curr_p; curr_p.first=adj; curr_p.second= new_bary; + std::cout<<"barycentric coordinates"< Date: Thu, 1 Feb 2024 13:29:44 +0100 Subject: [PATCH 050/120] clean up patch --- .../straightest_geodesic_sm_example.cpp | 4 +- .../Bsurf/locally_shortest_path.h | 154 +++++++----------- 2 files changed, 57 insertions(+), 101 deletions(-) diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/straightest_geodesic_sm_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/straightest_geodesic_sm_example.cpp index 44be28822e9f..16da75321312 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/straightest_geodesic_sm_example.cpp +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/straightest_geodesic_sm_example.cpp @@ -33,8 +33,8 @@ int main(int argc, char** argv) std::size_t nb_faces = faces(mesh).size(); // take two random faces and pick the centroid - //CGAL::Random rnd = CGAL::get_default_random(); - CGAL::Random rnd(1706709591); + CGAL::Random rnd = CGAL::get_default_random(); + //CGAL::Random rnd(1706709591); std::cout << "seed " << rnd.get_seed() << std::endl; Mesh::Face_index f = *std::next(faces(mesh).begin(), rnd.get_int(0, nb_faces)); diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index f92934d58929..4b8a400ddc65 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -17,7 +17,6 @@ #include #include -#include #include #include @@ -26,6 +25,10 @@ #include #include +#ifdef CGAL_DEBUG_BSURF +#include +#endif + namespace CGAL { namespace Polygon_mesh_processing { @@ -280,19 +283,16 @@ struct Locally_shortest_path_imp if(h_ref_opp==h_opp) { - std::cout<<"COUCOU 0"<& prev_coords, const array& curr_coords, @@ -1001,10 +998,15 @@ struct Locally_shortest_path_imp else assert(h_edge==h_ref); - std::cout<<" k is "< flat_curr = init_flat_triangle(h_ref,vpm,mesh); std::array flat_prev = unfold_face(h_edge,vpm,mesh,flat_curr,k); + Vector_2 prev_flat_p=prev_coords[0]*flat_prev[0]+prev_coords[1]*flat_prev[1]+prev_coords[2]*flat_prev[2]; + Vector_2 curr_flat_p=curr_coords[0]*flat_curr[0]+curr_coords[1]*flat_curr[1]+curr_coords[2]*flat_curr[2]; + + +#ifdef CGAL_DEBUG_BSURF + std::cout<<" k is "< flat_from = init_flat_triangle(h,vpm,mesh); - std::array flat_to = unfold_face(h,vpm,mesh,flat_from); - - Vector_2 c0 = 0.33*(flat_from[0]+flat_from[1]+flat_from[2]); - Vector_2 c1 = 0.33*(flat_to[0]+flat_to[1]+flat_to[2]); - Vector_2 e0 = flat_from[0] - c0; - Vector_2 e1 = flat_to[0] - c1; - - Vector_2 w = c1 - c0; - FT phi_ij = angle(e0, w); - if (e0.x()*w.y()-e0.y()*w.x() < 0) - phi_ij = 2 * M_PI - phi_ij; - w *= -1; - FT phi_ji = angle(e1, w); - if (e1.x()*w.y()-e1.y()*w.x() < 0) - phi_ji = 2 * M_PI - phi_ji; - - - double teta = angle(e0, v); - if (e0.x()*v.y()-e0.y()*v.x() < 0) - teta = 2 * M_PI - teta; - - double rot = teta + phi_ji + M_PI - phi_ij; - e1 =e1/sqrt(e1.squared_length())*sqrt(v.squared_length()); - - Vector_3 v_3d = rotate_vector(Vector_3{e1.x(),e1.y(),0}, Vector_3{0,0,1}, rot); - - v=Vector_2{v_3d.x(),v_3d.y()}; } - #endif + Vector_3 parallel_transport_along_path(const std::vector>& edge_locations, const VertexPointMap &vpm, const TriangleMesh& mesh, @@ -1225,19 +1178,21 @@ static Vector_2 switch_reference_frame_vector(const Eigen::Matrix3d &T, const Ve //rotated the triangle in order to test intersection at meaningful edges before std::array rotated_tri=tri; // TODO rotated_tri.begin() + offset ? + for(int k=0;k 0 && t1 >= -1e-4 && t1 <= 1 + 1e-4) { @@ -1280,7 +1235,7 @@ static Vector_2 switch_reference_frame_vector(const Eigen::Matrix3d &T, const Ve const face_descriptor& tid, const FT& init_angle) { - //TODO use interval for robustness + //TODO use interval for robustness + snap if ambiguous FT total_angle=get_total_angle(vid,mesh,vpm); FT theta = 0.5 * total_angle; halfedge_descriptor h=halfedge(tid,mesh); @@ -1325,7 +1280,6 @@ static Vector_2 switch_reference_frame_vector(const Eigen::Matrix3d &T, const Ve const VertexPointMap &vpm, const Vector_2& dir,const FT& len) { - // XXXXXXXXXXXXXXXX auto get_halfedge_offset=[&mesh](const halfedge_descriptor& h_ref,const halfedge_descriptor& h_curr) { if( h_ref == h_curr) return 0; @@ -1338,15 +1292,13 @@ static Vector_2 switch_reference_frame_vector(const Eigen::Matrix3d &T, const Ve return -1; }; - - +#ifdef CGAL_DEBUG_BSURF auto fn = compute_face_normal(p.first, mesh); auto dir3 = rotate_vector(mesh.point(target(halfedge(p.first, mesh), mesh)) - mesh.point(source(halfedge(p.first, mesh), mesh)), fn, CGAL_PI/4.); - std::cout << "direction " << construct_point(p, mesh) << " " << construct_point(p, mesh)+dir3 << "\n"; - +#endif auto get_vid_offset=[&mesh](const halfedge_descriptor& h_ref,const vertex_descriptor& vid) { @@ -1426,9 +1378,9 @@ static Vector_2 switch_reference_frame_vector(const Eigen::Matrix3d &T, const Ve result.push_back(p); - +#ifdef CGAL_DEBUG_BSURF std::cout << "p= " << construct_point(p,mesh) << ")\n"; - +#endif auto [is_vert, kv] = point_is_vert(p); auto [is_edge, ke] = point_is_edge(p); @@ -1437,12 +1389,14 @@ static Vector_2 switch_reference_frame_vector(const Eigen::Matrix3d &T, const Ve else if (is_edge) h_curr=get_halfedge(ke,h_ref); - +#ifdef CGAL_DEBUG_BSURF std::cout << "Accululated loop starts\n"; +#endif while (accumulated < len) { +#ifdef CGAL_DEBUG_BSURF std::cout << "--->" << accumulated << " vs " << len << "\n"; - +#endif int curr_offset=get_halfedge_offset(h_ref,h_curr); auto [k, t1] = segment_in_tri(flat_p, curr_flat_tid, curr_dir,curr_offset); @@ -1456,9 +1410,9 @@ static Vector_2 switch_reference_frame_vector(const Eigen::Matrix3d &T, const Ve point_on_edge.first=curr_tid; point_on_edge.second=new_bary; std::tie(is_vert, kv) = point_is_vert(point_on_edge); - +#ifdef CGAL_DEBUG_BSURF std::cout << " is_vert? " << is_vert << "\n"; - +#endif if (is_vert) { vertex_descriptor vid = target(get_halfedge(kv, h_ref), mesh); @@ -1480,7 +1434,6 @@ static Vector_2 switch_reference_frame_vector(const Eigen::Matrix3d &T, const Ve h_ref=halfedge(curr_tid,mesh); curr_flat_tid=init_flat_triangle(h_ref,vpm,mesh); - prev_p = curr_p; curr_p=point_on_edge; int k=get_vid_offset(h_ref,target(h_curr,mesh)); @@ -1489,32 +1442,35 @@ static Vector_2 switch_reference_frame_vector(const Eigen::Matrix3d &T, const Ve else { h_curr=opposite(get_halfedge(k, h_ref),mesh); - std::cout << " h_curr " << edge(h_curr, mesh)<<"\n"; + face_descriptor adj = face(h_curr,mesh); - std::cout << " adj " << adj<<"\n"; - Vector_2 intersection_point=new_bary[0]*curr_flat_tid[0]+new_bary[1]*curr_flat_tid[1]+new_bary[2]*curr_flat_tid[2]; - std::cout<<" New intersection point is "<< intersection_point< curr_alpha=make_array(t1,1-t1); //reversed because will switch face new_bary=edge_barycentric_coordinate(h_curr,halfedge(adj,mesh),curr_alpha); prev_p = curr_p; curr_p.first=adj; curr_p.second= new_bary; - std::cout<<"barycentric coordinates"< Date: Thu, 1 Feb 2024 14:41:15 +0100 Subject: [PATCH 051/120] dd trace geodesic polygon --- .../Polygon_mesh_processing/CMakeLists.txt | 2 + .../trace_polygon_example.cpp | 66 +++++++++++++++++++ .../Bsurf/locally_shortest_path.h | 36 +++++++++- 3 files changed, 102 insertions(+), 2 deletions(-) create mode 100644 Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_example.cpp diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt b/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt index 7227d05d34a4..2de21c13f9fc 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt @@ -87,6 +87,8 @@ if(TARGET CGAL::Eigen3_support) target_link_libraries(geodesic_circles_sm_example PUBLIC CGAL::Eigen3_support) create_single_source_cgal_program("straightest_geodesic_sm_example.cpp") target_link_libraries(straightest_geodesic_sm_example PUBLIC CGAL::Eigen3_support) + create_single_source_cgal_program("trace_polygon_example.cpp") + target_link_libraries(trace_polygon_example PUBLIC CGAL::Eigen3_support) create_single_source_cgal_program("geodesic_isolevel_refinement.cpp") target_link_libraries(geodesic_isolevel_refinement PUBLIC CGAL::Eigen3_support) create_single_source_cgal_program("interpolated_corrected_curvatures_SM.cpp") diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_example.cpp new file mode 100644 index 000000000000..f70d78ac9240 --- /dev/null +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_example.cpp @@ -0,0 +1,66 @@ +#include +#include +#include + +#include + +#if 0 +#include +#else + +#endif + +namespace PMP = CGAL::Polygon_mesh_processing; + +typedef CGAL::Exact_predicates_inexact_constructions_kernel K; +typedef CGAL::Surface_mesh Mesh; +typedef PMP::Face_location Face_location; +typedef PMP::Edge_location Edge_location; + + +int main(int argc, char** argv) +{ + std::string filename = (argc > 1) ? std::string(argv[1]) + : CGAL::data_file_path("meshes/elephant.off"); + + Mesh mesh; + if(!CGAL::IO::read_polygon_mesh(filename, mesh) || !CGAL::is_triangle_mesh(mesh)) + { + std::cerr << "Invalid input." << std::endl; + return 1; + } + + std::size_t nb_faces = faces(mesh).size(); + int n_sides=4; + double len=0.05; + // take two random faces and pick the centroid + CGAL::Random rnd = CGAL::get_default_random(); + //CGAL::Random rnd(1706709591); + + + std::cout << "seed " << rnd.get_seed() << std::endl; + Mesh::Face_index f = *std::next(faces(mesh).begin(), rnd.get_int(0, nb_faces)); + + Face_location center(f, CGAL::make_array(0.3,0.3,0.4)); + std::vector directions(n_sides); + std::vector lens(n_sides,len); + double step=2*CGAL_PI/n_sides; + for(int i=0;i polygon = PMP::trace_geodesic_polygon(center,directions,lens,mesh); + + + std::ofstream out("geodesic_polygon.txt"); + out << polygon.size() << " "; + + for (auto p : polygon) + out << " " << p; + out << "\n"; + + return 0; +} diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index 4b8a400ddc65..9cc6b05414c5 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -983,8 +983,8 @@ struct Locally_shortest_path_imp static Vector_2 compute_new_dir(const halfedge_descriptor h_ref, const halfedge_descriptor h_edge, - const array& prev_coords, - const array& curr_coords, + const std::array& prev_coords, + const std::array& curr_coords, const VertexPointMap& vpm, const TriangleMesh& mesh) { @@ -2878,6 +2878,7 @@ void approximate_geodesic_distance_field(const Face_location& } } + template std::vector> straightest_geodesic(const Face_location &src, @@ -2894,6 +2895,37 @@ straightest_geodesic(const Face_location &src, return Impl::straightest_geodesic(src, tmesh, vpm, dir, len); } +template +std::vector +trace_geodesic_polygon(const Face_location ¢er, + const std::vector& directions, + const std::vector& lengths, + const TriangleMesh &tmesh) +{ + size_t n=directions.size(); + std::vector result; + std::vector> vertices(n); + using VPM = typename boost::property_map::const_type; + using Impl = internal::Locally_shortest_path_imp; + for(std::size_t i=0;i(center,directions[i],lengths[i],tmesh).back(); + + std::vector> edge_locations; + + for(std::size_t i=0;i(vertices[i],vertices[(i+1)%n],tmesh, edge_locations); + result.push_back(construct_point(vertices[i],tmesh)); + for(auto& el : edge_locations) + { + result.push_back(construct_point(el, tmesh)); + } + } + + return result; + +} } // namespace Polygon_mesh_processing } // namespace CGAL From e50c2f9e5f811fdc2d3e436bfd6774988f558628 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Thu, 1 Feb 2024 15:17:46 +0100 Subject: [PATCH 052/120] accept external solver to avoid rebuilding it tracing in the demo is now very fast! --- .../Bsurf/locally_shortest_path.h | 115 ++++++++++++++---- .../Bsurf/Locally_shortest_path_item.cpp | 11 +- 2 files changed, 98 insertions(+), 28 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index 9cc6b05414c5..fc884beef23a 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -32,16 +32,25 @@ namespace CGAL { namespace Polygon_mesh_processing { +#ifndef CGAL_BSURF_USE_DIJKSTRA_SP +template +struct Dual_geodesic_solver; +#endif + template void locally_shortest_path(Face_location src, Face_location tgt, const TriangleMesh &tmesh, - EdgeLocationRange &edge_locations); + EdgeLocationRange &edge_locations +#ifndef CGAL_BSURF_USE_DIJKSTRA_SP + , const Dual_geodesic_solver& solver = Dual_geodesic_solver() +#endif +); + template using Bezier_segment = std::array, 4>; - template #ifdef DOXYGEN_RUNNING @@ -586,9 +595,11 @@ struct Locally_shortest_path_imp vertex_descriptor new_vertex=BGT::null_vertex(); halfedge_descriptor h_curr = path[index]; + CGAL_assertion_code( halfedge_descriptor h_next = path.size()>index+1 ? path[index + 1] - : BGT::null_halfedge(); // only for debug + : BGT::null_halfedge(); + ) bool is_target = false; if (lerps[index] == 0) { new_vertex = target(h_curr,mesh); @@ -1925,10 +1936,14 @@ struct Bezier_tracing_impl Face_location geodesic_lerp(const TriangleMesh &mesh, const Face_location& src, - const Face_location& tgt,const FT& t) + const Face_location& tgt,const FT& t +#ifndef CGAL_BSURF_USE_DIJKSTRA_SP + , const Dual_geodesic_solver& solver +#endif + ) { std::vector> edge_locations; - locally_shortest_path(src,tgt,mesh, edge_locations); + locally_shortest_path(src,tgt,mesh, edge_locations, solver); std::vector parameters=path_parameters(edge_locations,mesh,src,tgt); Face_location point = eval_point_on_geodesic(edge_locations,mesh,src,tgt,parameters,t); @@ -1940,15 +1955,27 @@ struct Bezier_tracing_impl std::pair,Bezier_segment> subdivide_bezier_polygon(const TriangleMesh& mesh, const Bezier_segment& polygon, - const FT& t) + const FT& t +#ifndef CGAL_BSURF_USE_DIJKSTRA_SP + , const Dual_geodesic_solver& solver +#endif + ) { +#ifndef CGAL_BSURF_USE_DIJKSTRA_SP + Face_location Q0 = geodesic_lerp(mesh, polygon[0], polygon[1], t, solver); + Face_location Q1 = geodesic_lerp(mesh, polygon[1], polygon[2], t, solver); + Face_location Q2 = geodesic_lerp(mesh, polygon[2], polygon[3], t, solver); + Face_location R0 = geodesic_lerp(mesh, Q0, Q1, t, solver); + Face_location R1 = geodesic_lerp(mesh, Q1, Q2, t, solver); + Face_location S = geodesic_lerp(mesh, R0, R1, t, solver); +#else Face_location Q0 = geodesic_lerp(mesh, polygon[0], polygon[1], t); Face_location Q1 = geodesic_lerp(mesh, polygon[1], polygon[2], t); Face_location Q2 = geodesic_lerp(mesh, polygon[2], polygon[3], t); Face_location R0 = geodesic_lerp(mesh, Q0, Q1, t); Face_location R1 = geodesic_lerp(mesh, Q1, Q2, t); Face_location S = geodesic_lerp(mesh, R0, R1, t); - +#endif return {{polygon[0], Q0, R0, S}, {S, R1, Q2, polygon[3]}}; } }; @@ -1977,13 +2004,6 @@ struct Geodesic_circle_impl }; std::vector> graph; }; - struct dual_geodesic_solver { - struct edge { - int node = -1; - double len = DBL_MAX; - }; - std::vector> graph = {}; - }; static void connect_nodes(geodesic_solver &solver, @@ -2106,7 +2126,7 @@ struct Geodesic_circle_impl // } // } static - dual_geodesic_solver + Dual_geodesic_solver make_dual_geodesic_solver(const VertexPointMap &vpm, const FaceIndexMap& tidmap, const TriangleMesh &mesh) @@ -2123,7 +2143,7 @@ struct Geodesic_circle_impl return sqrt((c1 - c0).squared_length()); }; - dual_geodesic_solver solver; + Dual_geodesic_solver solver; solver.graph.resize(faces(mesh).size()); for (auto f : faces(mesh)) { halfedge_descriptor h=halfedge(f,mesh); @@ -2234,10 +2254,10 @@ struct Geodesic_circle_impl } } - template + template static void visit_dual_geodesic_graph(std::vector &field, - const dual_geodesic_solver &solver, + const Dual_geodesic_solver &solver, const std::vector &sources, Update &&update, Stop &&stop, @@ -2495,9 +2515,10 @@ struct Geodesic_circle_impl return solve_with_targets(solver, source_nodes, target_nodes); } + template static std::vector - strip_on_dual_graph(const dual_geodesic_solver &solver, + strip_on_dual_graph(const Dual_geodesic_solver &solver, const TriangleMesh &mesh, const int src, const int tgt) @@ -2583,12 +2604,42 @@ struct Geodesic_circle_impl } // namespace internal +#ifndef CGAL_BSURF_USE_DIJKSTRA_SP +template +struct Dual_geodesic_solver +{ + struct Edge { + int node = -1; + FT len = DBL_MAX; + }; + std::vector> graph = {}; +}; + +template +void init_geodesic_dual_solver(Dual_geodesic_solver& solver, const TriangleMesh& tmesh) +{ + //TODO replace with named parameter + using VPM = typename boost::property_map::const_type; + using K = typename Kernel_traits::value_type>::type; + VPM vpm = get(CGAL::vertex_point, tmesh); + typedef typename GetInitializedFaceIndexMap::const_type FIM; + typedef typename GetInitializedVertexIndexMap::const_type VIM; + const FIM fim = get_initialized_face_index_map(tmesh, parameters::default_values()); + + using Impl2 = typename internal::Geodesic_circle_impl; + solver=Impl2::make_dual_geodesic_solver(vpm, fim, tmesh); +} +#endif template void locally_shortest_path(Face_location src, Face_location tgt, const TriangleMesh &tmesh, - EdgeLocationRange &edge_locations) + EdgeLocationRange &edge_locations +#ifndef CGAL_BSURF_USE_DIJKSTRA_SP + , const Dual_geodesic_solver& solver +#endif +) { typedef boost::graph_traits BGT; typedef typename BGT::halfedge_descriptor halfedge_descriptor; @@ -2758,12 +2809,12 @@ void locally_shortest_path(Face_location src, using Impl2 = typename internal::Geodesic_circle_impl; - typename Impl2::dual_geodesic_solver solver = Impl2::make_dual_geodesic_solver(vpm, fim, tmesh); - std::vector initial_path = - Impl2::strip_on_dual_graph(solver, tmesh, get(fim, src.first), get(fim,tgt.first)); + std::vector initial_path + = (solver.graph.empty()) + ? Impl2::strip_on_dual_graph(Impl2::make_dual_geodesic_solver(vpm, fim, tmesh), tmesh, get(fim, src.first), get(fim,tgt.first)) + : Impl2::strip_on_dual_graph(solver, tmesh, get(fim, src.first), get(fim,tgt.first)); #endif - CGAL_assertion(face(opposite(initial_path.front(), tmesh), tmesh)==src.first); CGAL_assertion(face(initial_path.back(), tmesh)==tgt.first); @@ -2813,13 +2864,25 @@ template std::vector> recursive_de_Casteljau(const TriangleMesh& mesh, const Bezier_segment& control_points, - const int num_subdiv) + const int num_subdiv +#ifndef CGAL_BSURF_USE_DIJKSTRA_SP + , const Dual_geodesic_solver& solver = Dual_geodesic_solver() +#endif + ) { //TODO replace with named parameter using VPM = typename boost::property_map::const_type; using K = typename Kernel_traits::value_type>::type; using Impl = internal::Bezier_tracing_impl; + // init solver if empty + const Dual_geodesic_solver* solver_ptr=&solver; + Dual_geodesic_solver local_solver; + if (solver.graph.empty()) + { + solver_ptr = &local_solver; + init_geodesic_dual_solver(local_solver, mesh); + } std::vector> segments(1,control_points); std::vector> result; @@ -2829,7 +2892,7 @@ recursive_de_Casteljau(const TriangleMesh& mesh, result.reserve(segments.size() * 2); for (std::size_t i = 0; i < segments.size(); ++i) { - auto [split0, split1] = Impl::subdivide_bezier_polygon(mesh, segments[i], 0.5); + auto [split0, split1] = Impl::subdivide_bezier_polygon(mesh, segments[i], 0.5, *solver_ptr); result.push_back(split0); result.push_back(split1); } diff --git a/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Locally_shortest_path_item.cpp b/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Locally_shortest_path_item.cpp index 2825a00e541d..6e6e595fe46c 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Locally_shortest_path_item.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Locally_shortest_path_item.cpp @@ -159,6 +159,10 @@ struct Locally_shortest_path_item_priv{ last_picked_type = -1; QPixmap pix(":/cgal/cursors/resources/rotate_around_cursor.png"); rotate_cursor = QCursor(pix); + +#ifndef CGAL_BSURF_USE_DIJKSTRA_SP + CGAL::Polygon_mesh_processing::init_geodesic_dual_solver(geodesic_solver, mesh); +#endif } ~Locally_shortest_path_item_priv(){ @@ -211,6 +215,9 @@ struct Locally_shortest_path_item_priv{ int last_picked_type; QCursor rotate_cursor; bool path_invalidated=true; +#ifndef CGAL_BSURF_USE_DIJKSTRA_SP + CGAL::Polygon_mesh_processing::Dual_geodesic_solver geodesic_solver; +#endif }; Locally_shortest_path_item::Locally_shortest_path_item(const CGAL::Three::Scene_interface* scene_interface, @@ -306,7 +313,7 @@ void Locally_shortest_path_item::drawPath() const Face_location src = PMP::locate_with_AABB_tree(src_pt, d->aabb_tree, mesh); Face_location tgt = PMP::locate_with_AABB_tree(tgt_pt, d->aabb_tree, mesh); - PMP::locally_shortest_path(src, tgt, mesh, edge_locations); + PMP::locally_shortest_path(src, tgt, mesh, edge_locations, d->geodesic_solver); d->spath_item->polylines.back().clear(); d->spath_item->polylines.back().push_back(src_pt); for (auto el : edge_locations) @@ -332,7 +339,7 @@ void Locally_shortest_path_item::drawPath() const PMP::Bezier_segment control_points=CGAL::make_array(c1, c2, c3, c4); std::vector face_locations = - PMP::recursive_de_Casteljau(mesh, control_points, 8); + PMP::recursive_de_Casteljau(mesh, control_points, 8, d->geodesic_solver); // TODO: we should connect points with geodesics and not segments d->spath_item->polylines.back().clear(); From 831844a2e0e9fcdaa819110f1b8e4d33fd1b1425 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Thu, 1 Feb 2024 15:40:52 +0100 Subject: [PATCH 053/120] print face locations in the infos --- .../Bsurf/locally_shortest_path.h | 2 - .../Bsurf/Locally_shortest_path_item.cpp | 44 +++++++++++++------ 2 files changed, 30 insertions(+), 16 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index fc884beef23a..989f867fc7cb 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -2968,8 +2968,6 @@ trace_geodesic_polygon(const Face_location ¢er size_t n=directions.size(); std::vector result; std::vector> vertices(n); - using VPM = typename boost::property_map::const_type; - using Impl = internal::Locally_shortest_path_imp; for(std::size_t i=0;i(center,directions[i],lengths[i],tmesh).back(); diff --git a/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Locally_shortest_path_item.cpp b/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Locally_shortest_path_item.cpp index 6e6e595fe46c..ba9cd6a2ec2f 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Locally_shortest_path_item.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Bsurf/Locally_shortest_path_item.cpp @@ -19,7 +19,11 @@ #include +#include + using namespace CGAL::Three; +namespace PMP = CGAL::Polygon_mesh_processing; + typedef Viewer_interface Vi; typedef Triangle_container Tc; typedef Edge_container Ec; @@ -139,6 +143,7 @@ struct Locally_shortest_path_item_priv{ vertices.resize(2); vertices[0].set( mesh.point(*mesh.vertices().begin()), 0 ); vertices[1].set( mesh.point(*std::next(mesh.vertices().begin())), 1 ); + locations.resize(2); } else if (nb_pts==4) { @@ -148,6 +153,7 @@ struct Locally_shortest_path_item_priv{ vertices[1].set( mesh.point(*std::next(mesh.vertices().begin())), 1 ); vertices[2].set( mesh.point(*std::next(mesh.vertices().begin(),2)), 2 ); vertices[3].set( mesh.point(*std::next(mesh.vertices().begin(),3)), 3 ); + locations.resize(4); } @@ -192,6 +198,7 @@ struct Locally_shortest_path_item_priv{ CGAL::qglviewer::Vec relative_center_; mutable std::vector vertices; + mutable std::vector> locations; std::vector selected_vertices; void reset_selection(); @@ -216,7 +223,7 @@ struct Locally_shortest_path_item_priv{ QCursor rotate_cursor; bool path_invalidated=true; #ifndef CGAL_BSURF_USE_DIJKSTRA_SP - CGAL::Polygon_mesh_processing::Dual_geodesic_solver geodesic_solver; + PMP::Dual_geodesic_solver geodesic_solver; #endif }; @@ -254,9 +261,16 @@ Locally_shortest_path_item::Locally_shortest_path_item(const CGAL::Three::Scene_ } contextMenu(); } -QString Locally_shortest_path_item::toolTip() const { - - return QString(); +QString Locally_shortest_path_item::toolTip() const +{ + std::stringstream ss; + ss << "Face locations:
"; + ss << std::setprecision(17); + for (auto fl : d->locations) + { + ss << " - " << fl.first << " (" << fl.second[0] << "," << fl.second[1] << "," << fl.second[2] << ")
"; + } + return QString::fromStdString(ss.str()); } @@ -297,7 +311,6 @@ void Locally_shortest_path_item::drawSpheres(Viewer_interface *viewer, const QMa void Locally_shortest_path_item::drawPath() const { if (!d->path_invalidated) return; - namespace PMP = CGAL::Polygon_mesh_processing; typedef PMP::Edge_location Edge_location; typedef PMP::Face_location Face_location; @@ -310,10 +323,10 @@ void Locally_shortest_path_item::drawPath() const tgt_pt(d->vertices[1].x,d->vertices[1].y,d->vertices[1].z); //TODO store that in the vector vertices - Face_location src = PMP::locate_with_AABB_tree(src_pt, d->aabb_tree, mesh); - Face_location tgt = PMP::locate_with_AABB_tree(tgt_pt, d->aabb_tree, mesh); + d->locations[0] = PMP::locate_with_AABB_tree(src_pt, d->aabb_tree, mesh); + d->locations[1] = PMP::locate_with_AABB_tree(tgt_pt, d->aabb_tree, mesh); - PMP::locally_shortest_path(src, tgt, mesh, edge_locations, d->geodesic_solver); + PMP::locally_shortest_path(d->locations[0], d->locations[1], mesh, edge_locations, d->geodesic_solver); d->spath_item->polylines.back().clear(); d->spath_item->polylines.back().push_back(src_pt); for (auto el : edge_locations) @@ -331,12 +344,15 @@ void Locally_shortest_path_item::drawPath() const c4_pt(d->vertices[3].x,d->vertices[3].y,d->vertices[3].z); //TODO store that in the vector vertices - Face_location c1 = PMP::locate_with_AABB_tree(c1_pt, d->aabb_tree, mesh); - Face_location c2 = PMP::locate_with_AABB_tree(c2_pt, d->aabb_tree, mesh); - Face_location c3 = PMP::locate_with_AABB_tree(c3_pt, d->aabb_tree, mesh); - Face_location c4 = PMP::locate_with_AABB_tree(c4_pt, d->aabb_tree, mesh); - - PMP::Bezier_segment control_points=CGAL::make_array(c1, c2, c3, c4); + d->locations[0] = PMP::locate_with_AABB_tree(c1_pt, d->aabb_tree, mesh); + d->locations[1] = PMP::locate_with_AABB_tree(c2_pt, d->aabb_tree, mesh); + d->locations[2] = PMP::locate_with_AABB_tree(c3_pt, d->aabb_tree, mesh); + d->locations[3] = PMP::locate_with_AABB_tree(c4_pt, d->aabb_tree, mesh); + + PMP::Bezier_segment control_points=CGAL::make_array(d->locations[0], + d->locations[1], + d->locations[2], + d->locations[3]); std::vector face_locations = PMP::recursive_de_Casteljau(mesh, control_points, 8, d->geodesic_solver); From 9f7711d94036528a433a2454cac8df2a40704417 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Thu, 1 Feb 2024 16:08:28 +0100 Subject: [PATCH 054/120] add missing point and trace several polygons --- .../trace_polygon_example.cpp | 31 ++++++++++--------- .../Bsurf/locally_shortest_path.h | 1 + 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_example.cpp index f70d78ac9240..f0b9427ca8c4 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_example.cpp +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_example.cpp @@ -31,36 +31,39 @@ int main(int argc, char** argv) } std::size_t nb_faces = faces(mesh).size(); - int n_sides=4; - double len=0.05; + int n_sides=6; + std::vector lenghts = { 0.3, 0.2, 0.1, 0.05 }; + // take two random faces and pick the centroid CGAL::Random rnd = CGAL::get_default_random(); //CGAL::Random rnd(1706709591); - + // 1706798575 <------- crash + // 1706799948 <------- crash std::cout << "seed " << rnd.get_seed() << std::endl; Mesh::Face_index f = *std::next(faces(mesh).begin(), rnd.get_int(0, nb_faces)); Face_location center(f, CGAL::make_array(0.3,0.3,0.4)); std::vector directions(n_sides); - std::vector lens(n_sides,len); double step=2*CGAL_PI/n_sides; for(int i=0;i polygon = PMP::trace_geodesic_polygon(center,directions,lens,mesh); - - - std::ofstream out("geodesic_polygon.txt"); - out << polygon.size() << " "; - - for (auto p : polygon) - out << " " << p; - out << "\n"; + std::ofstream out("geodesic_polygon.polylines.txt"); + out << std::setprecision(17); + for (double len : lenghts) + { + std::vector lens(n_sides,len); + std::vector polygon = PMP::trace_geodesic_polygon(center,directions,lens,mesh); + out << polygon.size(); + for (auto p : polygon) + out << " " << p; + out << "\n"; + } return 0; } diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index 989f867fc7cb..6cd3bc0b20b5 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -2983,6 +2983,7 @@ trace_geodesic_polygon(const Face_location ¢er result.push_back(construct_point(el, tmesh)); } } + result.push_back(construct_point(vertices[0],tmesh)); return result; From 73e68ffbf45d013131c55a9a9778550034ff9ed7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 2 Feb 2024 10:25:48 +0100 Subject: [PATCH 055/120] simplify the code by always stripping the path --- .../Bsurf/locally_shortest_path.h | 124 ++---------------- 1 file changed, 11 insertions(+), 113 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index 6cd3bc0b20b5..c8db76425614 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -595,11 +595,7 @@ struct Locally_shortest_path_imp vertex_descriptor new_vertex=BGT::null_vertex(); halfedge_descriptor h_curr = path[index]; - CGAL_assertion_code( - halfedge_descriptor h_next = path.size()>index+1 - ? path[index + 1] - : BGT::null_halfedge(); - ) + bool is_target = false; if (lerps[index] == 0) { new_vertex = target(h_curr,mesh); @@ -627,28 +623,9 @@ struct Locally_shortest_path_imp // if I hit the source vertex v of h_curr, then h_next has v as source, thus we turn ccw around v in path // Similarly, if I hit the target vertex v of h_curr, then h_next has v as target, thus we turn cw around v in path - - CGAL_assertion(BGT::null_halfedge()==h_next || !is_target || opposite(next(h_curr, mesh), mesh)==h_next); - CGAL_assertion(BGT::null_halfedge()==h_next || is_target || opposite(prev(h_curr, mesh), mesh)==h_next); - std::size_t curr_index = index+1; std::vector new_hedges; - // indicate if a point location is on an edge (interior or endpoints) - auto is_on_hedge = [&mesh](const Face_location& loc, halfedge_descriptor h_loop) - { - int k=0; - halfedge_descriptor hloc=prev(halfedge(loc.first, mesh), mesh); - while(hloc!=h_loop) - { - hloc=next(hloc,mesh); - ++k; - if (k==3) return -1; - } - return loc.second[(k+1)%3]==0 ? k : -1; - }; - - if (is_target) { face_descriptor target_face; @@ -675,51 +652,12 @@ struct Locally_shortest_path_imp halfedge_descriptor h_loop=opposite(prev(opposite(h_curr, mesh), mesh), mesh); - // don't pick first h_loop if src is visible on the other side of it - const int ksrc=is_on_hedge(src, opposite(h_loop, mesh)); - if (ksrc!=-1) - { - src.first=face(h_loop,mesh); - int new_k=0; - halfedge_descriptor hsrc=prev(halfedge(src.first, mesh), mesh); - while(hsrc!=h_loop) - { - hsrc=next(hsrc,mesh); - ++new_k; - } - std::array new_second = CGAL::make_array(FT(0.),FT(0.),FT(0.)); - new_second[new_k]=src.second[(ksrc+2)%3]; - new_second[(new_k+2)%3]=src.second[ksrc]; - src.second=new_second; - h_loop=opposite(prev(h_loop,mesh), mesh); - } - while(target_face!=face(h_loop, mesh)) { new_hedges.push_back(h_loop); h_loop=opposite(prev(h_loop,mesh), mesh); } - - // don't pick last h_loop is tgt is visible on the other side of it - const int ktgt = is_on_hedge(tgt,h_loop); - if (ktgt!=-1) - { - halfedge_descriptor oh_loop=opposite(h_loop,mesh); - tgt.first=face(oh_loop,mesh); - int new_k=0; - halfedge_descriptor htgt=prev(halfedge(tgt.first, mesh), mesh); - while(htgt!=oh_loop) - { - htgt=next(htgt,mesh); - ++new_k; - } - std::array new_second = CGAL::make_array(FT(0.),FT(0.),FT(0.)); - new_second[new_k]=tgt.second[(ktgt+2)%3]; - new_second[(new_k+2)%3]=tgt.second[ktgt]; - tgt.second=new_second; - } - else - new_hedges.push_back(h_loop); + new_hedges.push_back(h_loop); } else { @@ -746,52 +684,12 @@ struct Locally_shortest_path_imp } halfedge_descriptor h_loop=opposite(next(opposite(h_curr, mesh), mesh), mesh); // skip the face before h_curr (as we won't remove it from path) - // don't pick first h_loop if src is visible on the other side of it - const int ksrc=is_on_hedge(src, opposite(h_loop, mesh)); - if (ksrc!=-1) - { - src.first=face(h_loop,mesh); - int new_k=0; - halfedge_descriptor hsrc=prev(halfedge(src.first, mesh), mesh); - while(hsrc!=h_loop) - { - hsrc=next(hsrc,mesh); - ++new_k; - } - std::array new_second = CGAL::make_array(FT(0.),FT(0.),FT(0.)); - new_second[new_k]=src.second[(ksrc+2)%3]; - new_second[(new_k+2)%3]=src.second[ksrc]; - src.second=new_second; - h_loop=opposite(next(h_loop,mesh), mesh); - } - while(target_face!=face(h_loop, mesh)) { new_hedges.push_back(h_loop); h_loop=opposite(next(h_loop,mesh), mesh); } - - - // don't pick last h_loop is tgt is visible on the other side of it - const int ktgt = is_on_hedge(tgt,h_loop); - if (ktgt!=-1) - { - halfedge_descriptor oh_loop=opposite(h_loop,mesh); - tgt.first=face(oh_loop,mesh); - int new_k=0; - halfedge_descriptor htgt=prev(halfedge(tgt.first, mesh), mesh); - while(htgt!=oh_loop) - { - htgt=next(htgt,mesh); - ++new_k; - } - std::array new_second = CGAL::make_array(FT(0.),FT(0.),FT(0.)); - new_second[new_k]=tgt.second[(ktgt+2)%3]; - new_second[(new_k+2)%3]=tgt.second[ktgt]; - tgt.second=new_second; - } - else - new_hedges.push_back(h_loop); + new_hedges.push_back(h_loop); } // replace the halfedges incident to the apex vertex with the opposite part of the ring @@ -800,6 +698,8 @@ struct Locally_shortest_path_imp new_path.insert(new_path.end(), path.begin()+curr_index, path.end()); path.swap(new_path); + strip_path(mesh, src, tgt, path); + #ifdef CGAL_DEBUG_BSURF std::cout << " -- new path --\n"; for (auto h : path) @@ -808,6 +708,7 @@ struct Locally_shortest_path_imp << " " << get(vpm, target(h, mesh)) << " " << get(vpm, target(next(h, mesh), mesh)) << " " << get(vpm, source(h, mesh)) << std::endl; + std::cout << face(h, mesh) << std::endl; std::cout << edge(h, mesh) << std::endl; } std::cout << " ----------\n"; @@ -1621,14 +1522,11 @@ struct Locally_shortest_path_imp static void - strip_initial_path(const TriangleMesh& tmesh, - Face_location& src, - Face_location& tgt, - std::vector& initial_path) + strip_path(const TriangleMesh& tmesh, + Face_location& src, + Face_location& tgt, + std::vector& initial_path) { - CGAL_assertion(face(opposite(initial_path.front(), tmesh), tmesh)==src.first); - CGAL_assertion(face(initial_path.back(), tmesh)==tgt.first); - // retrieve the vertex id of a face location describing a vertex auto is_vertex = [](const Face_location& fl) { @@ -2823,7 +2721,7 @@ void locally_shortest_path(Face_location src, #ifdef CGAL_DEBUG_BSURF std::size_t initial_path_size_before = initial_path.size(); #endif - Impl::strip_initial_path(tmesh, src, tgt, initial_path); + Impl::strip_path(tmesh, src, tgt, initial_path); if (initial_path.empty()) return; CGAL_assertion(face(opposite(initial_path.front(), tmesh), tmesh)==src.first); From 26c6393e29b4a3264f114f1970cb9dc0c5879df2 Mon Sep 17 00:00:00 2001 From: ClaudioMancinelli Date: Fri, 2 Feb 2024 12:10:16 +0100 Subject: [PATCH 056/120] WIP straightest at vertex --- .../Bsurf/locally_shortest_path.h | 239 ++++++++++++++++-- 1 file changed, 219 insertions(+), 20 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index c8db76425614..70105598ec52 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -12,7 +12,6 @@ #ifndef CGAL_POLYGON_MESH_PROCESSING_BSURF_LOCALLY_SHORTEST_PATH_H #define CGAL_POLYGON_MESH_PROCESSING_BSURF_LOCALLY_SHORTEST_PATH_H - // #include #include @@ -164,6 +163,8 @@ struct Locally_shortest_path_imp get(vpm, triangle_vertices[2])); auto ry = squared_distance(get(vpm, triangle_vertices[1]), get(vpm, triangle_vertices[2])); + + tr2d[2] = intersect_circles(tr2d[0], rx, tr2d[1], ry); return tr2d; @@ -1108,6 +1109,11 @@ struct Locally_shortest_path_imp //TODO: replace intersection with CGAL code if (t0 > 0 && t1 >= -1e-4 && t1 <= 1 + 1e-4) { + int h=((i+1)%3 + offset)%3; + Vector_2 intersection_point=(1-t1)*tri[h]+t1*tri[(h+1)%3]; + #ifdef CGAL_DEBUG_BSURF + std::cout<<"intersection point "< flat_tid = init_flat_triangle(prev_h,vpm,mesh); - - Vector_2 q = (1 - alpha) * flat_tid[0] + alpha * flat_tid[1]; - - Vector_2 new_dir = q - flat_tid[2]; + std::array flat_tid = init_flat_triangle(halfedge(face(h,mesh),mesh),vpm,mesh); + int kv=get_vid_offset(halfedge(face(h,mesh),mesh),vid); + +Vector_2 flat_p=flat_tid[kv]; + Vector_2 q = (1 - alpha) * flat_tid[(kv+1)%3] + alpha * flat_tid[(kv+2)%3]; + + Vector_2 new_dir = q - flat_tid[kv]; + #ifdef CGAL_DEBUG_BSURF + std::cout<<"final h"<< edge(h,mesh)< new_bary=make_array(0.,0.,0.); Face_location point_on_edge; @@ -1327,8 +1381,12 @@ struct Locally_shortest_path_imp #endif if (is_vert) { - vertex_descriptor vid = target(get_halfedge(kv, h_ref), mesh); - + vertex_descriptor vid = target(get_halfedge(kv, prev(h_ref,mesh)), mesh); + #ifdef CGAL_DEBUG_BSURF + std::cout<< "hit vertex "<< vid < tmp_bary=make_array(0.,0.,0.); + tmp_bary[kv]=1; + curr_flat_tid=init_flat_triangle(h_ref,vpm,mesh); prev_p = curr_p; - curr_p=point_on_edge; + + curr_p=Face_location(curr_tid,tmp_bary); int k=get_vid_offset(h_ref,target(h_curr,mesh)); flat_p=curr_flat_tid[k]; } @@ -1364,6 +1429,7 @@ struct Locally_shortest_path_imp accumulated += sqrt(squared_distance(construct_point(curr_p,mesh), construct_point(prev_p,mesh))); curr_tid = adj; h_ref=halfedge(curr_tid,mesh); + //TODO curr_dir should be normalized everytime curr_dir = compute_new_dir(h_ref,h_curr,prev_p.second,curr_p.second,vpm,mesh); curr_flat_tid=init_flat_triangle(h_ref,vpm,mesh); flat_p= curr_p.second[0]*curr_flat_tid[0]+curr_p.second[1]*curr_flat_tid[1]+curr_p.second[2]*curr_flat_tid[2]; @@ -1386,10 +1452,22 @@ struct Locally_shortest_path_imp } double excess = accumulated - len; + + Point_3 prev_pos = construct_point(*std::next(result.rbegin()),mesh); Point_3 last_pos = construct_point(result.back(),mesh); double alpha = excess / sqrt((last_pos - prev_pos).squared_length()); - Point_3 pos = barycenter(prev_pos, alpha, last_pos, 1 - alpha); + Point_3 pos = barycenter(prev_pos, 1-alpha, last_pos, alpha); + #ifdef CGAL_DEBUG_BSURF + std::cout<<"excess "<< excess< ¢er size_t n=directions.size(); std::vector result; std::vector> vertices(n); + for(std::size_t i=0;i(center,directions[i],lengths[i],tmesh).back(); + std::cout<< i <<" -th vertex is "<< construct_point(vertices[i],tmesh)<<" "; + } + + + + + std::vector> edge_locations; + + for(std::size_t i=0;i(vertices[i],vertices[(i+1)%n],tmesh, edge_locations); + result.push_back(construct_point(vertices[i],tmesh)); + for(auto& el : edge_locations) + { + result.push_back(construct_point(el, tmesh)); + } + } + + return result; + + +} +template +typename K::FT path_length(const std::vector>& path, + const Face_location& src, + const Face_location& tgt, + const TriangleMesh &tmesh) + { + if(path.size()==0) + return sqrt(squared_distance(construct_point(src,tmesh),construct_point(tgt,tmesh))); + + using VPM = typename boost::property_map::const_type; + VPM vpm = get(CGAL::vertex_point, tmesh); + + typename K::FT len=sqrt(squared_distance(construct_point(src,tmesh),construct_point(path[0],tmesh))); + typename K::FT dist=[&](const Edge_location& p0,const Edge_location& p1) + { + return sqrt(squared_distance(construct_point(p0,tmesh),construct_point(p1,tmesh))); + }; + + len=std::accumulate(path.begin(),path.end(),dist); + + len+=sqrt(squared_distance(construct_point(path.back(),tmesh),construct_point(tgt,tmesh))); + + return len; +} +// template +// std::vector +// tangent_path_direction(const std::vector>& path, +// const Face_location& src, +// const Face_location& tgt, +// const TriangleMesh &tmesh,const bool initial=true) +// { +// auto find = [](const std::array &vec, int x) { +// for (int i = 0; i < vec.size(); i++) +// if (vec[i] == x) +// return i; +// return -1; +// }; +// typename K::Vector_2 direction; +// using VPM = typename boost::property_map::const_type; +// VPM vpm = get(CGAL::vertex_point, tmesh); + + +// if(initial) +// { + +// halfedge_descriptor h_ref=halfedge(src.first,mesh); +// std::array flat_tid=init_flat_triangle(h_ref,vpm,tmesh); +// if(path.size()==0) +// { +// assert(src.first==tgt.first);//TODO:src and tgt may have different faces because we do not update them when cleaning the strip +// typename K::Vector_2 flat_src=src.second[0]*flat_tid[0]+src.second[1]*flat_tid[1]+src.second[2]*flat_tid[2]; +// typename K::Vector_2 flat_tgt=tgt.second[0]*flat_tid[0]+tgt.second[1]*flat_tid[1]+tgt.second[2]*flat_tid[2]; +// direction=normalize(flat_tgt-flat_src); +// }else{ +// halfedge_descriptor h_edge=halfedge(path[0].first,tmesh); +// int k=0; + +// if(h_edge==prev(h_ref,mesh)) +// k=2; +// else if(h_edge==next(h_ref,mesh)) +// k=1; +// else +// assert(h_edge==h_ref); +// } + + + + + + + + + + +// } + + + + + + +// } + +template +std::vector +trace_agap_polygon(const Face_location ¢er, + const std::vector& directions, + const std::vector& lengths, + const TriangleMesh &tmesh) +{ + size_t n=directions.size(); + std::vector result; + std::vector> vertices(n); + using VPM = typename boost::property_map::const_type; + using Impl = internal::Locally_shortest_path_imp; for(std::size_t i=0;i(center,directions[i],lengths[i],tmesh).back(); From 3da5af57baf4bf20165377b9bd132f1e50a7db51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 2 Feb 2024 13:59:31 +0100 Subject: [PATCH 057/120] clean up patch --- .../Bsurf/locally_shortest_path.h | 71 ++++++++----------- 1 file changed, 28 insertions(+), 43 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index 70105598ec52..4718844fc9e4 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -12,6 +12,7 @@ #ifndef CGAL_POLYGON_MESH_PROCESSING_BSURF_LOCALLY_SHORTEST_PATH_H #define CGAL_POLYGON_MESH_PROCESSING_BSURF_LOCALLY_SHORTEST_PATH_H + // #include #include @@ -163,8 +164,6 @@ struct Locally_shortest_path_imp get(vpm, triangle_vertices[2])); auto ry = squared_distance(get(vpm, triangle_vertices[1]), get(vpm, triangle_vertices[2])); - - tr2d[2] = intersect_circles(tr2d[0], rx, tr2d[1], ry); return tr2d; @@ -1109,11 +1108,11 @@ struct Locally_shortest_path_imp //TODO: replace intersection with CGAL code if (t0 > 0 && t1 >= -1e-4 && t1 <= 1 + 1e-4) { +#ifdef CGAL_DEBUG_BSURF int h=((i+1)%3 + offset)%3; Vector_2 intersection_point=(1-t1)*tri[h]+t1*tri[(h+1)%3]; - #ifdef CGAL_DEBUG_BSURF - std::cout<<"intersection point "< flat_tid = init_flat_triangle(halfedge(face(h,mesh),mesh),vpm,mesh); int kv=get_vid_offset(halfedge(face(h,mesh),mesh),vid); -Vector_2 flat_p=flat_tid[kv]; - Vector_2 q = (1 - alpha) * flat_tid[(kv+1)%3] + alpha * flat_tid[(kv+2)%3]; + Vector_2 q = (1 - alpha) * flat_tid[(kv+1)%3] + alpha * flat_tid[(kv+2)%3]; Vector_2 new_dir = q - flat_tid[kv]; - #ifdef CGAL_DEBUG_BSURF +#ifdef CGAL_DEBUG_BSURF + Vector_2 flat_p=flat_tid[kv]; std::cout<<"final h"<< edge(h,mesh)< new_bary=make_array(0.,0.,0.); Face_location point_on_edge; @@ -1382,11 +1379,11 @@ Vector_2 flat_p=flat_tid[kv]; if (is_vert) { vertex_descriptor vid = target(get_halfedge(kv, prev(h_ref,mesh)), mesh); - #ifdef CGAL_DEBUG_BSURF +#ifdef CGAL_DEBUG_BSURF std::cout<< "hit vertex "<< vid < ¢er std::vector result; std::vector> vertices(n); for(std::size_t i=0;i(center,directions[i],lengths[i],tmesh).back(); - std::cout<< i <<" -th vertex is "<< construct_point(vertices[i],tmesh)<<" "; - } - - - + vertices[i]= straightest_geodesic(center,directions[i],lengths[i],tmesh).back(); std::vector> edge_locations; @@ -3063,8 +3050,6 @@ trace_agap_polygon(const Face_location ¢er, size_t n=directions.size(); std::vector result; std::vector> vertices(n); - using VPM = typename boost::property_map::const_type; - using Impl = internal::Locally_shortest_path_imp; for(std::size_t i=0;i(center,directions[i],lengths[i],tmesh).back(); From d1a5995ea9fad6f4563230840ef1b5dc39d1ed89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 2 Feb 2024 14:08:29 +0100 Subject: [PATCH 058/120] reusable solver --- .../trace_polygon_example.cpp | 5 +++- .../Bsurf/locally_shortest_path.h | 26 ++++++++++++++++--- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_example.cpp index f0b9427ca8c4..aa2343c0994c 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_example.cpp +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_example.cpp @@ -55,10 +55,13 @@ int main(int argc, char** argv) std::ofstream out("geodesic_polygon.polylines.txt"); out << std::setprecision(17); + + PMP::Dual_geodesic_solver solver; + PMP::init_geodesic_dual_solver(solver, mesh); for (double len : lenghts) { std::vector lens(n_sides,len); - std::vector polygon = PMP::trace_geodesic_polygon(center,directions,lens,mesh); + std::vector polygon = PMP::trace_geodesic_polygon(center,directions,lens,mesh, solver); out << polygon.size(); for (auto p : polygon) out << " " << p; diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index 4718844fc9e4..e27015fe48e7 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -2932,7 +2932,8 @@ std::vector trace_geodesic_polygon(const Face_location ¢er, const std::vector& directions, const std::vector& lengths, - const TriangleMesh &tmesh) + const TriangleMesh &tmesh, + const Dual_geodesic_solver& solver = {}) { size_t n=directions.size(); std::vector result; @@ -2942,10 +2943,18 @@ trace_geodesic_polygon(const Face_location ¢er std::vector> edge_locations; + const Dual_geodesic_solver* solver_ptr=&solver; + Dual_geodesic_solver local_solver; + if (solver.graph.empty()) + { + solver_ptr = &local_solver; + init_geodesic_dual_solver(local_solver, tmesh); + } + for(std::size_t i=0;i(vertices[i],vertices[(i+1)%n],tmesh, edge_locations); + locally_shortest_path(vertices[i],vertices[(i+1)%n],tmesh, edge_locations, *solver_ptr); result.push_back(construct_point(vertices[i],tmesh)); for(auto& el : edge_locations) { @@ -3045,7 +3054,8 @@ std::vector trace_agap_polygon(const Face_location ¢er, const std::vector& directions, const std::vector& lengths, - const TriangleMesh &tmesh) + const TriangleMesh &tmesh, + const Dual_geodesic_solver& solver = {}) { size_t n=directions.size(); std::vector result; @@ -3055,10 +3065,18 @@ trace_agap_polygon(const Face_location ¢er, std::vector> edge_locations; + const Dual_geodesic_solver* solver_ptr=&solver; + Dual_geodesic_solver local_solver; + if (solver.graph.empty()) + { + solver_ptr = &local_solver; + init_geodesic_dual_solver(local_solver, tmesh); + } + for(std::size_t i=0;i(vertices[i],vertices[(i+1)%n],tmesh, edge_locations); + locally_shortest_path(vertices[i],vertices[(i+1)%n],tmesh, edge_locations, *solver_ptr); result.push_back(construct_point(vertices[i],tmesh)); for(auto& el : edge_locations) { From 34ad600d7ce487c84d700dcffb94595fae794c96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 2 Feb 2024 20:17:49 +0100 Subject: [PATCH 059/120] WIP generic polygon tracing (to be debugged...) --- .../Polygon_mesh_processing/CMakeLists.txt | 2 + .../trace_polygon_example.cpp | 101 +++++++++++++----- .../trace_regular_polygons_example.cpp | 72 +++++++++++++ .../Bsurf/locally_shortest_path.h | 36 ++++++- 4 files changed, 184 insertions(+), 27 deletions(-) create mode 100644 Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_regular_polygons_example.cpp diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt b/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt index 2de21c13f9fc..76b30110d690 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt @@ -89,6 +89,8 @@ if(TARGET CGAL::Eigen3_support) target_link_libraries(straightest_geodesic_sm_example PUBLIC CGAL::Eigen3_support) create_single_source_cgal_program("trace_polygon_example.cpp") target_link_libraries(trace_polygon_example PUBLIC CGAL::Eigen3_support) + create_single_source_cgal_program("trace_regular_polygons_example.cpp") + target_link_libraries(trace_regular_polygons_example PUBLIC CGAL::Eigen3_support) create_single_source_cgal_program("geodesic_isolevel_refinement.cpp") target_link_libraries(geodesic_isolevel_refinement PUBLIC CGAL::Eigen3_support) create_single_source_cgal_program("interpolated_corrected_curvatures_SM.cpp") diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_example.cpp index aa2343c0994c..9244f05db129 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_example.cpp +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_example.cpp @@ -23,6 +23,9 @@ int main(int argc, char** argv) std::string filename = (argc > 1) ? std::string(argv[1]) : CGAL::data_file_path("meshes/elephant.off"); + std::string filename_poly = (argc > 2) ? std::string(argv[2]) + : CGAL::data_file_path("XXXXX"); + Mesh mesh; if(!CGAL::IO::read_polygon_mesh(filename, mesh) || !CGAL::is_triangle_mesh(mesh)) { @@ -30,42 +33,92 @@ int main(int argc, char** argv) return 1; } - std::size_t nb_faces = faces(mesh).size(); - int n_sides=6; - std::vector lenghts = { 0.3, 0.2, 0.1, 0.05 }; + std::vector> polygons; + std::ifstream in(filename_poly); + if (!in) + { + std::cerr << "Error cannot open " << filename_poly << "\n"; + return 1; + } - // take two random faces and pick the centroid - CGAL::Random rnd = CGAL::get_default_random(); - //CGAL::Random rnd(1706709591); - // 1706798575 <------- crash - // 1706799948 <------- crash + int nb_pt; + K::Point_3 pt; + CGAL::Bbox_2 bb2; + while (in >> nb_pt) + { + polygons.emplace_back(); + polygons.back().reserve(nb_pt-1); + for (int i=0; i> pt; + polygons.back().emplace_back(pt.x(), pt.y()); + bb2+=polygons.back().back().bbox(); + } + in >> pt; + if (!in) + { + std::cerr << "Error reading input polygons\n"; + return 1; + } + // check if last point is duplicated + if (polygons.back().back().x()!=pt.x() || polygons.back().back().y()!=pt.y()) + { + polygons.back().emplace_back(pt.x(), pt.y()); + bb2+=polygons.back().back().bbox(); + } + if (!in) break; + } - std::cout << "seed " << rnd.get_seed() << std::endl; - Mesh::Face_index f = *std::next(faces(mesh).begin(), rnd.get_int(0, nb_faces)); + std::cout << polygons.size() << " polygons read\n"; + // tracing center + std::size_t nb_faces = faces(mesh).size(); + Mesh::Face_index f = *std::next(faces(mesh).begin(), (2154)%nb_faces); Face_location center(f, CGAL::make_array(0.3,0.3,0.4)); - std::vector directions(n_sides); - double step=2*CGAL_PI/n_sides; - for(int i=0;i solver; PMP::init_geodesic_dual_solver(solver, mesh); - for (double len : lenghts) + + for (const std::vector& polygon : polygons) { - std::vector lens(n_sides,len); - std::vector polygon = PMP::trace_geodesic_polygon(center,directions,lens,mesh, solver); - out << polygon.size(); - for (auto p : polygon) + std::vector> polar_coords = + PMP::convert_polygon_to_polar_coordinates(polygon, center_2); + + std::vector directions; + std::vector lens; + lens.reserve(polar_coords.size()); + directions.reserve(polar_coords.size()); + + for (const std::pair& coord : polar_coords) + { + std::cout << scaling << " * " << coord.first << " = " << scaling * coord.first << "\n"; + lens.push_back(scaling * coord.first); + directions.emplace_back(std::cos(coord.second), std::sin(coord.second)); + std::cout << std::cos(coord.second) << " " << std::sin(coord.second) << "\n"; + } + + // last point is duplicated + std::vector out_polygon = PMP::trace_geodesic_polygon(center,directions,lens,mesh, solver); + out << out_polygon.size(); + for (auto p : out_polygon) out << " " << p; - out << "\n"; + out << std::endl; } return 0; diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_regular_polygons_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_regular_polygons_example.cpp new file mode 100644 index 000000000000..aa2343c0994c --- /dev/null +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_regular_polygons_example.cpp @@ -0,0 +1,72 @@ +#include +#include +#include + +#include + +#if 0 +#include +#else + +#endif + +namespace PMP = CGAL::Polygon_mesh_processing; + +typedef CGAL::Exact_predicates_inexact_constructions_kernel K; +typedef CGAL::Surface_mesh Mesh; +typedef PMP::Face_location Face_location; +typedef PMP::Edge_location Edge_location; + + +int main(int argc, char** argv) +{ + std::string filename = (argc > 1) ? std::string(argv[1]) + : CGAL::data_file_path("meshes/elephant.off"); + + Mesh mesh; + if(!CGAL::IO::read_polygon_mesh(filename, mesh) || !CGAL::is_triangle_mesh(mesh)) + { + std::cerr << "Invalid input." << std::endl; + return 1; + } + + std::size_t nb_faces = faces(mesh).size(); + int n_sides=6; + std::vector lenghts = { 0.3, 0.2, 0.1, 0.05 }; + + // take two random faces and pick the centroid + CGAL::Random rnd = CGAL::get_default_random(); + //CGAL::Random rnd(1706709591); + // 1706798575 <------- crash + // 1706799948 <------- crash + + std::cout << "seed " << rnd.get_seed() << std::endl; + Mesh::Face_index f = *std::next(faces(mesh).begin(), rnd.get_int(0, nb_faces)); + + Face_location center(f, CGAL::make_array(0.3,0.3,0.4)); + std::vector directions(n_sides); + double step=2*CGAL_PI/n_sides; + for(int i=0;i solver; + PMP::init_geodesic_dual_solver(solver, mesh); + for (double len : lenghts) + { + std::vector lens(n_sides,len); + std::vector polygon = PMP::trace_geodesic_polygon(center,directions,lens,mesh, solver); + out << polygon.size(); + for (auto p : polygon) + out << " " << p; + out << "\n"; + } + + return 0; +} diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index e27015fe48e7..6281e01c8abf 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -2927,6 +2927,32 @@ straightest_geodesic(const Face_location &src, return Impl::straightest_geodesic(src, tmesh, vpm, dir, len); } +// we don't expect the last point to be duplicated here +template +std::vector> +convert_polygon_to_polar_coordinates(const std::vector & polygon, + std::optional center = std::nullopt) +{ + std::vector> result; + // compute naively the center + if (center==std::nullopt) + { + Bbox_2 bb = bbox_2(polygon.begin(), polygon.end()); + center = typename K::Point_2((bb.xmax()+bb.xmin())/2., (bb.ymax()+bb.ymin())/2); + } + + for (const typename K::Point_2& pt : polygon) + { + typename K::FT d = approximate_sqrt(squared_distance(*center, pt)); + typename K::FT polar = std::atan2((pt.y()-center->y())/* /d */, (pt.x()-center->x())/* /d */); + result.emplace_back(d, polar); + // std::cout << center->x()+d*std::cos(polar) << " " << center->x()+d*std::sin(polar) << " 0\n"; + } + + return result; +} + + template std::vector trace_geodesic_polygon(const Face_location ¢er, @@ -2938,9 +2964,13 @@ trace_geodesic_polygon(const Face_location ¢er size_t n=directions.size(); std::vector result; std::vector> vertices(n); + std::cout << "trace_geodesic_polygon\n"; for(std::size_t i=0;i(center,directions[i],lengths[i],tmesh).back(); - + std::cout << construct_point(vertices[i], tmesh) << "\n"; + } + exit(1); std::vector> edge_locations; const Dual_geodesic_solver* solver_ptr=&solver; @@ -2963,9 +2993,8 @@ trace_geodesic_polygon(const Face_location ¢er } return result; - - } + template typename K::FT path_length(const std::vector>& path, const Face_location& src, @@ -2990,6 +3019,7 @@ typename K::FT path_length(const std::vector // std::vector // tangent_path_direction(const std::vector>& path, From 3468a870ab3b8483b6530ed062d773a046f3c167 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Mon, 5 Feb 2024 09:08:59 +0100 Subject: [PATCH 060/120] WIP testsuite --- .../Polygon_mesh_processing/test_Bsurf.cpp | 126 +++++++++++++++--- 1 file changed, 111 insertions(+), 15 deletions(-) diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_Bsurf.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_Bsurf.cpp index 0ba0dd3cf947..21aad711f0d6 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_Bsurf.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_Bsurf.cpp @@ -24,6 +24,87 @@ int main(int argc, char** argv) std::cerr << "Invalid input." << std::endl; return 1; } + PMP::Dual_geodesic_solver solver; + CGAL::Polygon_mesh_processing::init_geodesic_dual_solver(solver, mesh); + + +//////////////////////////// +//////////////////////////// +//////////////////////////// +#if 0 +{ + std::ofstream out("locally_shortest_path.polylines.txt"); + + Mesh::Face_index f1(3260), f2(4548); + Mesh::Vertex_index v1(2534), v2(2217); + + std::cout << "Manual Run " << f1 << " " << v1 << " | " << f2 << " " << v2 << "\n"; + + Face_location src(f1, CGAL::make_array(0.,0.,0.)); + Face_location tgt(f2, CGAL::make_array(0.,0.,0.)); + + + Mesh::Halfedge_index hf1 = prev(halfedge(f1, mesh), mesh); + int i1=0; + while (target(hf1, mesh)!= v1) + { + hf1=next(hf1,mesh); + ++i1; + } + src.second[i1]=1; + Mesh::Halfedge_index hf2 = prev(halfedge(f2, mesh), mesh); + int i2=0; + while (target(hf2, mesh)!= v2) + { + hf2=next(hf2,mesh); + ++i2; + } + tgt.second[i2]=1; + + std::vector edge_locations; + auto src_bk=src, tgt_bk=tgt; + PMP::locally_shortest_path(src, tgt, mesh, edge_locations, solver); + assert(get(PMP::get_descriptor_from_location(src,mesh))==v1); + assert(get(PMP::get_descriptor_from_location(tgt,mesh))==v2); + + out << edge_locations.size()+2; + out << " " << PMP::construct_point(src, mesh); + for (auto el : edge_locations) + out << " " << PMP::construct_point(el, mesh); + out << " " << PMP::construct_point(tgt, mesh) << "\n"; + out << std::flush; + std::size_t expected_size = edge_locations.size(); + + /// other direction + + src=src_bk; + tgt=tgt_bk; + edge_locations.clear(); + PMP::locally_shortest_path(tgt, src, mesh, edge_locations, solver); + assert(get(PMP::get_descriptor_from_location(src,mesh))==v1); + assert(get(PMP::get_descriptor_from_location(tgt,mesh))==v2); + + out << edge_locations.size()+2; + out << " " << PMP::construct_point(tgt, mesh); + for (auto el : edge_locations) + out << " " << PMP::construct_point(el, mesh); + out << " " << PMP::construct_point(src, mesh) << "\n"; + out << std::flush; + CGAL_assertion(edge_locations.size() == expected_size); + + return 1; +} +#endif +//////////////////////////// +//////////////////////////// +//////////////////////////// + + + + + + + std::size_t nb_hedges = halfedges(mesh).size(); @@ -34,9 +115,18 @@ int main(int argc, char** argv) // CGAL::Random rnd(1695813638); std::cout << "seed " << rnd.get_seed() << std::endl; - Mesh::Halfedge_index h = *std::next(halfedges(mesh).begin(), rnd.get_int(0, nb_hedges)); -// for (Mesh::Halfedge_index h : halfedges(mesh)) -// { + // Mesh::Halfedge_index h = *std::next(halfedges(mesh).begin(), rnd.get_int(0, nb_hedges)); + // Mesh::Halfedge_index h = *std::next(halfedges(mesh).begin(), 2*6178); // <---- interesting two different locally shortest paths + // Mesh::Halfedge_index h = *std::next(halfedges(mesh).begin(), 2*2301); // <---- DEBUG ME +// for (Mesh::Halfedge_index h : halfedges(mesh)) +for (auto it = std::next(halfedges(mesh).begin(), 7262); it!=halfedges(mesh).end(); ++it) +//~ for (auto it = std::next(halfedges(mesh).begin(), 2*6282); it!=halfedges(mesh).end(); ++it) <---- crash before patch +//~ for (auto it = halfedges(mesh).begin(); it!=halfedges(mesh).end(); ++it) +{ +Mesh::Halfedge_index h = *it; + + std::cout << "h = " << h << "\n"; + std::ofstream out("locally_shortest_path.polylines.txt"); // test src/tgt being 2 opposite vertices of an edge @@ -77,7 +167,7 @@ int main(int argc, char** argv) std::vector edge_locations; auto src_bk=src, tgt_bk=tgt; - PMP::locally_shortest_path(src, tgt, mesh, edge_locations); + PMP::locally_shortest_path(src, tgt, mesh, edge_locations, solver); assert(get(PMP::get_descriptor_from_location(src,mesh))==v1); assert(get(PMP::get_descriptor_from_location(tgt,mesh))==v2); @@ -93,12 +183,16 @@ int main(int argc, char** argv) first_run=false; expected_size=edge_locations.size(); } - CGAL_assertion(edge_locations.size() == expected_size); + if(edge_locations.size() != expected_size) + { + std::cout << edge_locations.size() << " vs " << expected_size << "\n"; + } + CGAL_warning(edge_locations.size() == expected_size); src=src_bk; tgt=tgt_bk; edge_locations.clear(); - PMP::locally_shortest_path(tgt, src, mesh, edge_locations); + PMP::locally_shortest_path(tgt, src, mesh, edge_locations, solver); assert(get(PMP::get_descriptor_from_location(src,mesh))==v1); assert(get(PMP::get_descriptor_from_location(tgt,mesh))==v2); @@ -108,10 +202,12 @@ int main(int argc, char** argv) out << " " << PMP::construct_point(el, mesh); out << " " << PMP::construct_point(src, mesh) << "\n"; out << std::flush; - CGAL_assertion(edge_locations.size() == expected_size); + CGAL_warning(edge_locations.size() == expected_size); } } h = next(h, mesh); + + //~ return 2; } #endif @@ -159,7 +255,7 @@ int main(int argc, char** argv) // std::cout << " " << PMP::construct_point(src, mesh) << " | " << PMP::construct_point(tgt, mesh) << "\n"; std::vector edge_locations; auto src_bk=src, tgt_bk=tgt; - PMP::locally_shortest_path(src, tgt, mesh, edge_locations); + PMP::locally_shortest_path(src, tgt, mesh, edge_locations, solver); out << edge_locations.size()+2; out << " " << PMP::construct_point(src, mesh); @@ -173,7 +269,7 @@ int main(int argc, char** argv) first_run=false; expected_size=edge_locations.size(); } - CGAL_assertion(edge_locations.size()==expected_size); + CGAL_warning(edge_locations.size()==expected_size); assert(get(PMP::get_descriptor_from_location(src,mesh))==v1); assert(edge(get(PMP::get_descriptor_from_location(tgt,mesh)), mesh)==edge(h2,mesh)); @@ -181,7 +277,7 @@ int main(int argc, char** argv) src=src_bk; tgt=tgt_bk; edge_locations.clear(); - PMP::locally_shortest_path(tgt, src, mesh, edge_locations); + PMP::locally_shortest_path(tgt, src, mesh, edge_locations, solver); out << edge_locations.size()+2; out << " " << PMP::construct_point(tgt, mesh); @@ -189,7 +285,7 @@ int main(int argc, char** argv) out << " " << PMP::construct_point(el, mesh); out << " " << PMP::construct_point(src, mesh) << "\n"; out << std::flush; - CGAL_assertion(edge_locations.size()==expected_size); + CGAL_warning(edge_locations.size()==expected_size); assert(get(PMP::get_descriptor_from_location(src,mesh))==v1); assert(edge(get(PMP::get_descriptor_from_location(tgt,mesh)), mesh)==edge(h2,mesh)); @@ -240,7 +336,7 @@ int main(int argc, char** argv) std::vector edge_locations; auto src_bk=src, tgt_bk=tgt; - PMP::locally_shortest_path(src, tgt, mesh, edge_locations); + PMP::locally_shortest_path(src, tgt, mesh, edge_locations, solver); out << edge_locations.size()+2; out << " " << PMP::construct_point(src, mesh); @@ -257,7 +353,7 @@ int main(int argc, char** argv) src=src_bk; tgt=tgt_bk; edge_locations.clear(); - PMP::locally_shortest_path(tgt, src, mesh, edge_locations); + PMP::locally_shortest_path(tgt, src, mesh, edge_locations, solver); out << edge_locations.size()+2; out << " " << PMP::construct_point(tgt, mesh); @@ -266,7 +362,7 @@ int main(int argc, char** argv) out << " " << PMP::construct_point(src, mesh) << "\n"; out << std::flush; - CGAL_assertion(edge_locations.size()==expected_size); + CGAL_warning(edge_locations.size()==expected_size); assert(edge(get(PMP::get_descriptor_from_location(src,mesh)), mesh)==edge(h1,mesh)); assert(edge(get(PMP::get_descriptor_from_location(tgt,mesh)), mesh)==edge(h2,mesh)); @@ -274,7 +370,7 @@ int main(int argc, char** argv) h = next(h, mesh); } #endif -// } +} return 0; } From adb823ac460b8757244ed450f737338a0eb4e0ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Mon, 5 Feb 2024 14:49:02 +0100 Subject: [PATCH 061/120] fix invalid barycentric coordinate and add missing point --- .../Polygon_mesh_processing/Bsurf/locally_shortest_path.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index 6281e01c8abf..af0ebbfb065d 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -1451,7 +1451,7 @@ struct Locally_shortest_path_imp Point_3 prev_pos = construct_point(*std::next(result.rbegin()),mesh); Point_3 last_pos = construct_point(result.back(),mesh); double alpha = excess / sqrt((last_pos - prev_pos).squared_length()); - Point_3 pos = barycenter(prev_pos, 1-alpha, last_pos, alpha); + Point_3 pos = barycenter(prev_pos, alpha, last_pos, 1-alpha); #ifdef CGAL_DEBUG_BSURF std::cout << "excess " << excess << std::endl; std::cout << "prev_pos " << prev_pos << std::endl; @@ -2964,13 +2964,16 @@ trace_geodesic_polygon(const Face_location ¢er size_t n=directions.size(); std::vector result; std::vector> vertices(n); +#ifdef CGAL_DEBUG_BSURF std::cout << "trace_geodesic_polygon\n"; +#endif for(std::size_t i=0;i(center,directions[i],lengths[i],tmesh).back(); +#ifdef CGAL_DEBUG_BSURF std::cout << construct_point(vertices[i], tmesh) << "\n"; +#endif } - exit(1); std::vector> edge_locations; const Dual_geodesic_solver* solver_ptr=&solver; @@ -2991,6 +2994,7 @@ trace_geodesic_polygon(const Face_location ¢er result.push_back(construct_point(el, tmesh)); } } + result.push_back(construct_point(vertices[0],tmesh)); return result; } From cba29df96613d8f5d5b7e27af5ed322a1bd31268 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Mon, 5 Feb 2024 14:49:20 +0100 Subject: [PATCH 062/120] clean up examples --- .../Polygon_mesh_processing/trace_polygon_example.cpp | 4 +--- .../trace_regular_polygons_example.cpp | 6 ++---- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_example.cpp index 9244f05db129..ef6a53423de6 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_example.cpp +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_example.cpp @@ -84,7 +84,7 @@ int main(int argc, char** argv) // convert polygons to polar coordinates typename K::Point_2 center_2((bb2.xmax()+bb2.xmin())/2., (bb2.ymax()+bb2.ymin())/2.); double diag = std::sqrt( CGAL::square(bb2.xmin()-bb2.xmax()) + CGAL::square(bb2.xmin()-bb2.xmax()) ); - const double expected_diag = 0.05; // user parameter for scaling + const double expected_diag = 0.45; // user parameter for scaling const double scaling = expected_diag/diag; std::ofstream out("geodesic_polygon.polylines.txt"); @@ -107,10 +107,8 @@ int main(int argc, char** argv) for (const std::pair& coord : polar_coords) { - std::cout << scaling << " * " << coord.first << " = " << scaling * coord.first << "\n"; lens.push_back(scaling * coord.first); directions.emplace_back(std::cos(coord.second), std::sin(coord.second)); - std::cout << std::cos(coord.second) << " " << std::sin(coord.second) << "\n"; } // last point is duplicated diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_regular_polygons_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_regular_polygons_example.cpp index aa2343c0994c..27353bba05d8 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_regular_polygons_example.cpp +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_regular_polygons_example.cpp @@ -32,13 +32,11 @@ int main(int argc, char** argv) std::size_t nb_faces = faces(mesh).size(); int n_sides=6; - std::vector lenghts = { 0.3, 0.2, 0.1, 0.05 }; + std::vector lenghts = { 0.03, 0.02, 0.01, 0.005 }; // take two random faces and pick the centroid CGAL::Random rnd = CGAL::get_default_random(); - //CGAL::Random rnd(1706709591); - // 1706798575 <------- crash - // 1706799948 <------- crash + //CGAL::Random rnd(1707129825); std::cout << "seed " << rnd.get_seed() << std::endl; Mesh::Face_index f = *std::next(faces(mesh).begin(), rnd.get_int(0, nb_faces)); From 3871e7dbce77ee190bd8f4b24d8fe8b17195436d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Mon, 5 Feb 2024 14:55:10 +0100 Subject: [PATCH 063/120] clean up test --- .../Polygon_mesh_processing/test_Bsurf.cpp | 90 +------------------ 1 file changed, 1 insertion(+), 89 deletions(-) diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_Bsurf.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_Bsurf.cpp index 21aad711f0d6..970ca76c6df5 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_Bsurf.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_Bsurf.cpp @@ -28,84 +28,6 @@ int main(int argc, char** argv) CGAL::Polygon_mesh_processing::init_geodesic_dual_solver(solver, mesh); -//////////////////////////// -//////////////////////////// -//////////////////////////// -#if 0 -{ - std::ofstream out("locally_shortest_path.polylines.txt"); - - Mesh::Face_index f1(3260), f2(4548); - Mesh::Vertex_index v1(2534), v2(2217); - - std::cout << "Manual Run " << f1 << " " << v1 << " | " << f2 << " " << v2 << "\n"; - - Face_location src(f1, CGAL::make_array(0.,0.,0.)); - Face_location tgt(f2, CGAL::make_array(0.,0.,0.)); - - - Mesh::Halfedge_index hf1 = prev(halfedge(f1, mesh), mesh); - int i1=0; - while (target(hf1, mesh)!= v1) - { - hf1=next(hf1,mesh); - ++i1; - } - src.second[i1]=1; - Mesh::Halfedge_index hf2 = prev(halfedge(f2, mesh), mesh); - int i2=0; - while (target(hf2, mesh)!= v2) - { - hf2=next(hf2,mesh); - ++i2; - } - tgt.second[i2]=1; - - std::vector edge_locations; - auto src_bk=src, tgt_bk=tgt; - PMP::locally_shortest_path(src, tgt, mesh, edge_locations, solver); - assert(get(PMP::get_descriptor_from_location(src,mesh))==v1); - assert(get(PMP::get_descriptor_from_location(tgt,mesh))==v2); - - out << edge_locations.size()+2; - out << " " << PMP::construct_point(src, mesh); - for (auto el : edge_locations) - out << " " << PMP::construct_point(el, mesh); - out << " " << PMP::construct_point(tgt, mesh) << "\n"; - out << std::flush; - std::size_t expected_size = edge_locations.size(); - - /// other direction - - src=src_bk; - tgt=tgt_bk; - edge_locations.clear(); - PMP::locally_shortest_path(tgt, src, mesh, edge_locations, solver); - assert(get(PMP::get_descriptor_from_location(src,mesh))==v1); - assert(get(PMP::get_descriptor_from_location(tgt,mesh))==v2); - - out << edge_locations.size()+2; - out << " " << PMP::construct_point(tgt, mesh); - for (auto el : edge_locations) - out << " " << PMP::construct_point(el, mesh); - out << " " << PMP::construct_point(src, mesh) << "\n"; - out << std::flush; - CGAL_assertion(edge_locations.size() == expected_size); - - return 1; -} -#endif -//////////////////////////// -//////////////////////////// -//////////////////////////// - - - - - - - - std::size_t nb_hedges = halfedges(mesh).size(); // take two random faces and pick the centroid @@ -115,15 +37,8 @@ int main(int argc, char** argv) // CGAL::Random rnd(1695813638); std::cout << "seed " << rnd.get_seed() << std::endl; - // Mesh::Halfedge_index h = *std::next(halfedges(mesh).begin(), rnd.get_int(0, nb_hedges)); + Mesh::Halfedge_index h = *std::next(halfedges(mesh).begin(), rnd.get_int(0, nb_hedges)); // Mesh::Halfedge_index h = *std::next(halfedges(mesh).begin(), 2*6178); // <---- interesting two different locally shortest paths - // Mesh::Halfedge_index h = *std::next(halfedges(mesh).begin(), 2*2301); // <---- DEBUG ME -// for (Mesh::Halfedge_index h : halfedges(mesh)) -for (auto it = std::next(halfedges(mesh).begin(), 7262); it!=halfedges(mesh).end(); ++it) -//~ for (auto it = std::next(halfedges(mesh).begin(), 2*6282); it!=halfedges(mesh).end(); ++it) <---- crash before patch -//~ for (auto it = halfedges(mesh).begin(); it!=halfedges(mesh).end(); ++it) -{ -Mesh::Halfedge_index h = *it; std::cout << "h = " << h << "\n"; @@ -206,8 +121,6 @@ Mesh::Halfedge_index h = *it; } } h = next(h, mesh); - - //~ return 2; } #endif @@ -370,7 +283,6 @@ Mesh::Halfedge_index h = *it; h = next(h, mesh); } #endif -} return 0; } From 501b6a6d3cbda98e7ec4da567898b08d6ad38489 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Tue, 6 Feb 2024 10:21:20 +0100 Subject: [PATCH 064/120] init angle should be in degrees --- .../Bsurf/locally_shortest_path.h | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index af0ebbfb065d..7993582ef5e3 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -1185,7 +1185,7 @@ struct Locally_shortest_path_imp FT prev_angle = acc; #ifdef CGAL_DEBUG_BSURF - std::cout<<"initial h"<< edge(h,mesh)<& } } - +// TODO: center is put is output, shall we keep it like that? template std::vector> straightest_geodesic(const Face_location &src, From ed5cad194df843c747453ae7c4d0fc00b3324eed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 7 Feb 2024 15:57:06 +0100 Subject: [PATCH 065/120] add draft for drawing several polygons with different centers needs parallel transport and initial rotation matrix --- .../trace_polygon_example.cpp | 16 +++++ .../Bsurf/locally_shortest_path.h | 69 +++++++++++++++++-- 2 files changed, 80 insertions(+), 5 deletions(-) diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_example.cpp index ef6a53423de6..6ef0542bef67 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_example.cpp +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_example.cpp @@ -119,5 +119,21 @@ int main(int argc, char** argv) out << std::endl; } + // second method + out.close(); + out.open("geodesic_polygons.polylines.txt"); + out << std::setprecision(17); + + std::vector> polygons_3 + = PMP::trace_geodesic_polygons(center, polygons, scaling, mesh, solver); + + for (const auto& polygon : polygons_3) + { + out << polygon.size(); + for (auto p : polygon) + out << " " << p; + out << std::endl; + } + return 0; } diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index 7993582ef5e3..a26e236c7574 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -2951,14 +2951,15 @@ convert_polygon_to_polar_coordinates(const std::vector & po return result; } - +//TODO: do we want to handle polylines? +// href of center.first is the y axis in 2D template std::vector trace_geodesic_polygon(const Face_location ¢er, - const std::vector& directions, - const std::vector& lengths, - const TriangleMesh &tmesh, - const Dual_geodesic_solver& solver = {}) + const std::vector& directions, + const std::vector& lengths, + const TriangleMesh &tmesh, + const Dual_geodesic_solver& solver = {}) { size_t n=directions.size(); std::vector result; @@ -2998,6 +2999,64 @@ trace_geodesic_polygon(const Face_location ¢er return result; } +//TODO add groups of polygons for better rendering +template +std::vector> +trace_geodesic_polygons(const Face_location ¢er, + const std::vector>& polygons, + const typename K::FT scaling, + const TriangleMesh &tmesh, + const Dual_geodesic_solver& solver = {}) +{ + std::vector polygon_bboxes; + polygon_bboxes.reserve(polygons.size()); + Bbox_2 gbox; + for (const std::vector& polygon : polygons) + { + polygon_bboxes.push_back( bbox_2(polygon.begin(), polygon.end()) ); + gbox+=polygon_bboxes.back(); + } + + const Dual_geodesic_solver* solver_ptr=&solver; + Dual_geodesic_solver local_solver; + if (solver.graph.empty()) + { + solver_ptr = &local_solver; + init_geodesic_dual_solver(local_solver, tmesh); + } + + std::vector> result(polygons.size()); + + for(std::size_t i=0;i polygon_center= + straightest_geodesic(center, dir, scaling * std::sqrt(dir.squared_length()),tmesh).back(); + + + std::vector> polar_coords = + convert_polygon_to_polar_coordinates(polygons[i], + typename K::Point_2((polygon_bboxes[i].xmin()+polygon_bboxes[i].xmax())/2., + (polygon_bboxes[i].ymin()+polygon_bboxes[i].ymax())/2.)); + + std::vector directions; + std::vector lens; + lens.reserve(polar_coords.size()); + directions.reserve(polar_coords.size()); + + for (const std::pair& coord : polar_coords) + { + lens.push_back(scaling * coord.first); + directions.emplace_back(std::cos(coord.second), std::sin(coord.second)); + } + result[i] = trace_geodesic_polygon(polygon_center, directions, lens, tmesh, *solver_ptr); + } + + return result; +} + + template typename K::FT path_length(const std::vector>& path, const Face_location& src, From f9cef82dd1ae40132f8bd9add96e76369ba720bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 9 Feb 2024 10:33:38 +0100 Subject: [PATCH 066/120] add parallel transport --- .../Bsurf/locally_shortest_path.h | 69 ++++++++++++++++--- 1 file changed, 59 insertions(+), 10 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index a26e236c7574..3206a56c876c 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -882,7 +882,7 @@ struct Locally_shortest_path_imp } static - Eigen::Matrix3d tranformation_matrix(const Vector_2 &x1, const Vector_2 &y1, + Eigen::Matrix3d transformation_matrix(const Vector_2 &x1, const Vector_2 &y1, const Vector_2 &O1) { Eigen::Matrix3d T = Eigen::Matrix3d::Zero(); @@ -894,13 +894,12 @@ struct Locally_shortest_path_imp //h_ref is the reference halfedge of the face we are in, h_edge is the halfedge along which we want to unfold static Vector_2 compute_new_dir(const halfedge_descriptor h_ref, - const halfedge_descriptor h_edge, - const std::array& prev_coords, - const std::array& curr_coords, - const VertexPointMap& vpm, - const TriangleMesh& mesh) + const halfedge_descriptor h_edge, + const std::array& prev_coords, + const std::array& curr_coords, + const VertexPointMap& vpm, + const TriangleMesh& mesh) { - int k=0; if(h_edge==prev(h_ref,mesh)) @@ -938,6 +937,35 @@ struct Locally_shortest_path_imp } + //h_edge is the halfedge along which we want to unfold + // TODO: use interval to control the error? + static + Vector_2 parallel_transport_f2f(const halfedge_descriptor h_edge, // face(opposite(h_edge)) = previous face face(h_edge)==current_face + const Vector_2& prev_dir, + const VertexPointMap& vpm, + const TriangleMesh& mesh) + { + halfedge_descriptor h_ref = halfedge(face(h_edge,mesh), mesh); + int k=0; + + if(h_edge==prev(h_ref,mesh)) + k=2; + else if(h_edge==next(h_ref,mesh)) + k=1; + else + assert(h_edge==h_ref); + + std::array flat_curr = init_flat_triangle(h_ref,vpm,mesh); + std::array flat_prev = unfold_face(h_edge,vpm,mesh,flat_curr,k); + + Vector_2 prev_origin = flat_prev[0]; + Vector_2 prev_y = flat_prev[1]-flat_prev[0]; + Vector_2 prev_x = Vector_2(prev_y.y(), - prev_y.x()); + + Eigen::Matrix3d T= transformation_matrix(prev_x, prev_y, prev_origin); + return switch_reference_frame_vector(T, prev_dir); + } + static Vector_2 switch_reference_frame_vector(const Eigen::Matrix3d &T, const Vector_2 &p) { Eigen::Vector3d V; V << p.x(), p.y(), 0; @@ -3008,6 +3036,12 @@ trace_geodesic_polygons(const Face_location ¢e const TriangleMesh &tmesh, const Dual_geodesic_solver& solver = {}) { + using VPM = typename boost::property_map::const_type; + using Impl = internal::Locally_shortest_path_imp; + VPM vpm = get(CGAL::vertex_point, tmesh); + + + using halfedge_descriptor = typename boost::graph_traits::halfedge_descriptor; std::vector polygon_bboxes; polygon_bboxes.reserve(polygons.size()); Bbox_2 gbox; @@ -3026,20 +3060,35 @@ trace_geodesic_polygons(const Face_location ¢e } std::vector> result(polygons.size()); + typename K::Vector_2 initial_dir(1,0);// TODO: input parameter or 2D PCA of the centers? for(std::size_t i=0;i polygon_center= - straightest_geodesic(center, dir, scaling * std::sqrt(dir.squared_length()),tmesh).back(); + std::vector< Face_location > spath = + straightest_geodesic(center, dir, scaling * std::sqrt(dir.squared_length()),tmesh); + Face_location polygon_center = spath.back(); + std::vector> shortest_path; + locally_shortest_path(center, polygon_center, tmesh, shortest_path, *solver_ptr); + + // update direction + typename K::Vector_2 v = initial_dir; + for(std::size_t i=0;i> polar_coords = convert_polygon_to_polar_coordinates(polygons[i], typename K::Point_2((polygon_bboxes[i].xmin()+polygon_bboxes[i].xmax())/2., (polygon_bboxes[i].ymin()+polygon_bboxes[i].ymax())/2.)); + + double theta = atan2(v.y(),v.x()); + std::vector directions; std::vector lens; lens.reserve(polar_coords.size()); @@ -3048,7 +3097,7 @@ trace_geodesic_polygons(const Face_location ¢e for (const std::pair& coord : polar_coords) { lens.push_back(scaling * coord.first); - directions.emplace_back(std::cos(coord.second), std::sin(coord.second)); + directions.emplace_back(std::cos(coord.second+theta), std::sin(coord.second+theta)); } result[i] = trace_geodesic_polygon(polygon_center, directions, lens, tmesh, *solver_ptr); } From ea10d5a22ead1af96f7c6fb660e794b1fc29650f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 9 Feb 2024 15:58:56 +0100 Subject: [PATCH 067/120] another method for text, using parallel transport for now --- .../Bsurf/locally_shortest_path.h | 164 ++++++++++++++++-- 1 file changed, 151 insertions(+), 13 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index 3206a56c876c..cc1caa50ad53 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -3105,30 +3105,168 @@ trace_geodesic_polygons(const Face_location ¢e return result; } +//TODO add groups of polygons for better rendering +template +std::vector> +trace_geodesic_label(const Face_location ¢er, + const std::vector>& polygons, + const typename K::FT scaling, + const TriangleMesh &tmesh, + const Dual_geodesic_solver& solver = {}) +{ + using VPM = typename boost::property_map::const_type; + using Impl = internal::Locally_shortest_path_imp; + VPM vpm = get(CGAL::vertex_point, tmesh); + using Point_2 = typename K::Point_2; + + using halfedge_descriptor = typename boost::graph_traits::halfedge_descriptor; + std::vector polygon_bboxes; + polygon_bboxes.reserve(polygons.size()); + Bbox_2 gbox; + for (const std::vector& polygon : polygons) + { + polygon_bboxes.push_back( bbox_2(polygon.begin(), polygon.end()) ); + gbox+=polygon_bboxes.back(); + } + + const Dual_geodesic_solver* solver_ptr=&solver; + Dual_geodesic_solver local_solver; + if (solver.graph.empty()) + { + solver_ptr = &local_solver; + init_geodesic_dual_solver(local_solver, tmesh); + } + + std::vector> result(polygons.size()); + typename K::Vector_2 initial_dir(1,0);// TODO: input parameter or 2D PCA of the centers? + + // 1D partition of the letters + Point_2 c2( (gbox.xmin()+gbox.xmax())/2., + (gbox.ymin()+gbox.ymax())/2. ); + Point_2 left_most(gbox.xmin(), c2.y()); + Point_2 right_most(gbox.xmax(), c2.y()); + double len = (gbox.xmax()-gbox.xmin())/2.; + + std::vector< Face_location > left_path = + straightest_geodesic(center, left_most-c2, scaling * len, tmesh); + std::vector< Face_location > right_path = + straightest_geodesic(center, right_most-c2, scaling * len, tmesh); + + CGAL_assertion(left_path.size() >=2); + CGAL_assertion(right_path.size() >=2); + + for(std::size_t i=0;i polygon_center; + double xc = (polygon_bboxes[i].xmin()+polygon_bboxes[i].xmax())/2.; + + + auto get_polygon_center = [&tmesh](const std::vector>& path, + double targetd) + { + // use left + double acc=0.; + std::size_t k=0; + while(true) + { + double len = std::sqrt(squared_distance(construct_point(path[k], tmesh), + construct_point(path[k+1], tmesh))); + acc+=len; + if (acc == targetd) + { + return path[k+1]; + } + + if (acc > targetd) + { + double excess = acc-targetd; + + Face_location loc_k=path[k], loc_k1=path[k+1]; + CGAL_assertion_code(bool OK=) + locate_in_common_face(loc_k, loc_k1, tmesh); + CGAL_assertion(OK); + + Face_location polygon_center; + polygon_center.first=loc_k.first; + double alpha = excess/len; + + for(int ii=0; ii<3;++ii) + polygon_center.second[ii] = loc_k.second[ii]*alpha+loc_k1.second[ii]*(1.-alpha); + + return polygon_center; + } + + if (++k==path.size()-1) + { + return path.back(); + } + } + }; + + if (xc> shortest_path; + locally_shortest_path(center, polygon_center, tmesh, shortest_path, *solver_ptr); + + // update direction + typename K::Vector_2 v = initial_dir; + for(std::size_t i=0;i> polar_coords = + convert_polygon_to_polar_coordinates(polygons[i], + typename K::Point_2((polygon_bboxes[i].xmin()+polygon_bboxes[i].xmax())/2., + (gbox.ymin()+gbox.ymax())/2.)); + + + double theta = atan2(v.y(),v.x()); + + std::vector directions; + std::vector lens; + lens.reserve(polar_coords.size()); + directions.reserve(polar_coords.size()); + + for (const std::pair& coord : polar_coords) + { + lens.push_back(scaling * coord.first); + directions.emplace_back(std::cos(coord.second+theta), std::sin(coord.second+theta)); + } + result[i] = trace_geodesic_polygon(polygon_center, directions, lens, tmesh, *solver_ptr); + } + + return result; +} + template typename K::FT path_length(const std::vector>& path, const Face_location& src, const Face_location& tgt, const TriangleMesh &tmesh) - { - if(path.size()==0) - return sqrt(squared_distance(construct_point(src,tmesh),construct_point(tgt,tmesh))); +{ + if(path.size()==0) + return sqrt(squared_distance(construct_point(src,tmesh),construct_point(tgt,tmesh))); - using VPM = typename boost::property_map::const_type; - VPM vpm = get(CGAL::vertex_point, tmesh); + using VPM = typename boost::property_map::const_type; + VPM vpm = get(CGAL::vertex_point, tmesh); - typename K::FT len=sqrt(squared_distance(construct_point(src,tmesh),construct_point(path[0],tmesh))); - typename K::FT dist=[&](const Edge_location& p0,const Edge_location& p1) - { - return sqrt(squared_distance(construct_point(p0,tmesh),construct_point(p1,tmesh))); - }; + typename K::FT len=sqrt(squared_distance(construct_point(src,tmesh),construct_point(path[0],tmesh))); + typename K::FT dist=[&](const Edge_location& p0,const Edge_location& p1) + { + return sqrt(squared_distance(construct_point(p0,tmesh),construct_point(p1,tmesh))); + }; - len=std::accumulate(path.begin(),path.end(),dist); + len=std::accumulate(path.begin(),path.end(),dist); - len+=sqrt(squared_distance(construct_point(path.back(),tmesh),construct_point(tgt,tmesh))); + len+=sqrt(squared_distance(construct_point(path.back(),tmesh),construct_point(tgt,tmesh))); - return len; + return len; } // template From 645aa83f092870d0b63a54894b314993f22ba9be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 9 Feb 2024 15:59:46 +0100 Subject: [PATCH 068/120] call the new method --- .../trace_polygon_example.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_example.cpp index 6ef0542bef67..f61e2d4c4f4c 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_example.cpp +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_example.cpp @@ -135,5 +135,21 @@ int main(int argc, char** argv) out << std::endl; } + // third method + out.close(); + out.open("geodesic_label.polylines.txt"); + out << std::setprecision(17); + + polygons_3.clear(); + polygons_3 = PMP::trace_geodesic_label(center, polygons, scaling, mesh, solver); + + for (const auto& polygon : polygons_3) + { + out << polygon.size(); + for (auto p : polygon) + out << " " << p; + out << std::endl; + } + return 0; } From c87b601e6514412f2ec08bffc840ce79f13714fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 9 Feb 2024 17:51:33 +0100 Subject: [PATCH 069/120] update angle wrt initial straightest --- .../Bsurf/locally_shortest_path.h | 72 +++++++++++++------ 1 file changed, 50 insertions(+), 22 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index cc1caa50ad53..0f12debfa949 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -3118,8 +3118,9 @@ trace_geodesic_label(const Face_location ¢er, using Impl = internal::Locally_shortest_path_imp; VPM vpm = get(CGAL::vertex_point, tmesh); using Point_2 = typename K::Point_2; + using Vector_2 = typename K::Vector_2; + - using halfedge_descriptor = typename boost::graph_traits::halfedge_descriptor; std::vector polygon_bboxes; polygon_bboxes.reserve(polygons.size()); Bbox_2 gbox; @@ -3138,7 +3139,7 @@ trace_geodesic_label(const Face_location ¢er, } std::vector> result(polygons.size()); - typename K::Vector_2 initial_dir(1,0);// TODO: input parameter or 2D PCA of the centers? + Vector_2 initial_dir(1,0);// TODO: input parameter or 2D PCA of the centers? // 1D partition of the letters Point_2 c2( (gbox.xmin()+gbox.xmax())/2., @@ -3160,9 +3161,8 @@ trace_geodesic_label(const Face_location ¢er, Face_location polygon_center; double xc = (polygon_bboxes[i].xmin()+polygon_bboxes[i].xmax())/2.; - - auto get_polygon_center = [&tmesh](const std::vector>& path, - double targetd) + auto get_polygon_center = [&tmesh, &vpm](const std::vector>& path, + double targetd) { // use left double acc=0.; @@ -3174,7 +3174,22 @@ trace_geodesic_label(const Face_location ¢er, acc+=len; if (acc == targetd) { - return path[k+1]; + double theta=0; + if (k!=0) + { + Face_location loc_k=path[k], loc_k1=path[k+1]; + CGAL_assertion_code(bool OK=) + locate_in_common_face(loc_k, loc_k1, tmesh); + CGAL_assertion(OK); + std::array flat_triangle = + Impl::init_flat_triangle(halfedge(loc_k.first,tmesh),vpm,tmesh); + Vector_2 src = loc_k.second[0]*flat_triangle[0]+loc_k.second[1]*flat_triangle[1]+loc_k.second[2]*flat_triangle[2]; + Vector_2 tgt = loc_k1.second[0]*flat_triangle[0]+loc_k1.second[1]*flat_triangle[1]+loc_k1.second[2]*flat_triangle[2]; + + Vector_2 dir2 = tgt-src; + theta = atan2(dir2.y(), dir2.x()); + } + return std::make_pair(path[k+1], theta); } if (acc > targetd) @@ -3193,41 +3208,54 @@ trace_geodesic_label(const Face_location ¢er, for(int ii=0; ii<3;++ii) polygon_center.second[ii] = loc_k.second[ii]*alpha+loc_k1.second[ii]*(1.-alpha); - return polygon_center; + std::array flat_triangle = + Impl::init_flat_triangle(halfedge(polygon_center.first,tmesh),vpm,tmesh); + Vector_2 src = loc_k.second[0]*flat_triangle[0]+loc_k.second[1]*flat_triangle[1]+loc_k.second[2]*flat_triangle[2]; + Vector_2 tgt = loc_k1.second[0]*flat_triangle[0]+loc_k1.second[1]*flat_triangle[1]+loc_k1.second[2]*flat_triangle[2]; + + Vector_2 dir2 = tgt-src; + double theta = atan2(dir2.y(), dir2.x()); + + return std::make_pair(polygon_center, theta); } if (++k==path.size()-1) { - return path.back(); + Face_location loc_k=path[k-1], loc_k1=path[k]; + CGAL_assertion_code(bool OK=) + locate_in_common_face(loc_k, loc_k1, tmesh); + CGAL_assertion(OK); + std::array flat_triangle = + Impl::init_flat_triangle(halfedge(loc_k.first,tmesh),vpm,tmesh); + Vector_2 src = loc_k.second[0]*flat_triangle[0]+loc_k.second[1]*flat_triangle[1]+loc_k.second[2]*flat_triangle[2]; + Vector_2 tgt = loc_k1.second[0]*flat_triangle[0]+loc_k1.second[1]*flat_triangle[1]+loc_k1.second[2]*flat_triangle[2]; + + Vector_2 dir2 = tgt-src; + double theta = atan2(dir2.y(), dir2.x()); + + return std::make_pair(path.back(), theta); } } }; - + double theta; if (xc> shortest_path; locally_shortest_path(center, polygon_center, tmesh, shortest_path, *solver_ptr); - // update direction - typename K::Vector_2 v = initial_dir; - for(std::size_t i=0;i> polar_coords = convert_polygon_to_polar_coordinates(polygons[i], typename K::Point_2((polygon_bboxes[i].xmin()+polygon_bboxes[i].xmax())/2., (gbox.ymin()+gbox.ymax())/2.)); - - double theta = atan2(v.y(),v.x()); - - std::vector directions; + std::vector directions; std::vector lens; lens.reserve(polar_coords.size()); directions.reserve(polar_coords.size()); From 0376540a1c4daa33f769ec3b94bb015c7b67dc4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Tue, 20 Feb 2024 15:19:50 +0100 Subject: [PATCH 070/120] add an example tracing a svg --- Data/data/polylines_2/nano.svg | 27 ++++ .../cmake/modules/CGAL_NanoSVG_support.cmake | 6 + .../Polygon_mesh_processing/CMakeLists.txt | 11 ++ .../trace_svg_example.cpp | 123 ++++++++++++++++++ .../Bsurf/locally_shortest_path.h | 70 +++++++++- 5 files changed, 235 insertions(+), 2 deletions(-) create mode 100644 Data/data/polylines_2/nano.svg create mode 100644 Installation/cmake/modules/CGAL_NanoSVG_support.cmake create mode 100644 Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_svg_example.cpp diff --git a/Data/data/polylines_2/nano.svg b/Data/data/polylines_2/nano.svg new file mode 100644 index 000000000000..15635a16f745 --- /dev/null +++ b/Data/data/polylines_2/nano.svg @@ -0,0 +1,27 @@ + + + + + + diff --git a/Installation/cmake/modules/CGAL_NanoSVG_support.cmake b/Installation/cmake/modules/CGAL_NanoSVG_support.cmake new file mode 100644 index 000000000000..b1db19f31522 --- /dev/null +++ b/Installation/cmake/modules/CGAL_NanoSVG_support.cmake @@ -0,0 +1,6 @@ +if(NanoSVG_FOUND AND NOT TARGET CGAL::NanoSVG_support) + add_library(CGAL::NanoSVG_support INTERFACE IMPORTED) + set_target_properties(CGAL::NanoSVG_support PROPERTIES + INTERFACE_COMPILE_DEFINITIONS "CGAL_NANOSVG_ENABLED") + target_link_libraries(CGAL::NanoSVG_support INTERFACE NanoSVG::nanosvg) +endif() diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt b/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt index 76b30110d690..ba4750ed988e 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt @@ -56,6 +56,9 @@ create_single_source_cgal_program("isotropic_remeshing_with_custom_sizing_exampl create_single_source_cgal_program("triangle_mesh_autorefinement.cpp") create_single_source_cgal_program("soup_autorefinement.cpp") +find_package(NanoSVG) +include(CGAL_NanoSVG_support) + find_package(Eigen3 3.2.0 QUIET) #(requires 3.2.0 or greater) include(CGAL_Eigen3_support) if(TARGET CGAL::Eigen3_support) @@ -81,6 +84,7 @@ if(TARGET CGAL::Eigen3_support) target_link_libraries(remesh_almost_planar_patches PUBLIC CGAL::Eigen3_support) create_single_source_cgal_program("locally_shortest_path_sm_example.cpp") target_link_libraries(locally_shortest_path_sm_example PUBLIC CGAL::Eigen3_support) + #b/surf create_single_source_cgal_program("trace_bezier_segment_sm_example.cpp") target_link_libraries(trace_bezier_segment_sm_example PUBLIC CGAL::Eigen3_support) create_single_source_cgal_program("geodesic_circles_sm_example.cpp") @@ -93,6 +97,13 @@ if(TARGET CGAL::Eigen3_support) target_link_libraries(trace_regular_polygons_example PUBLIC CGAL::Eigen3_support) create_single_source_cgal_program("geodesic_isolevel_refinement.cpp") target_link_libraries(geodesic_isolevel_refinement PUBLIC CGAL::Eigen3_support) + if (NanoSVG_FOUND) + create_single_source_cgal_program("trace_svg_example.cpp") + target_link_libraries(trace_svg_example PUBLIC CGAL::Eigen3_support CGAL::NanoSVG_support) + else() + message(STATUS "NOTICE: trace_svg_example requires NanoSVG and will not be compiled.") + endif() + ## create_single_source_cgal_program("interpolated_corrected_curvatures_SM.cpp") target_link_libraries(interpolated_corrected_curvatures_SM PUBLIC CGAL::Eigen3_support) create_single_source_cgal_program("interpolated_corrected_curvatures_PH.cpp") diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_svg_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_svg_example.cpp new file mode 100644 index 000000000000..30a3739725ba --- /dev/null +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_svg_example.cpp @@ -0,0 +1,123 @@ +#include +#include +#include + +#include + +#include + +namespace PMP = CGAL::Polygon_mesh_processing; + +typedef CGAL::Exact_predicates_inexact_constructions_kernel K; +typedef CGAL::Surface_mesh Mesh; +typedef PMP::Face_location Face_location; +typedef PMP::Edge_location Edge_location; + + +int main(int argc, char** argv) +{ + std::string mesh_filename = (argc > 1) ? std::string(argv[1]) + : CGAL::data_file_path("meshes/elephant.off"); + + std::string svg_filename = (argc > 2) ? std::string(argv[2]) + : CGAL::data_file_path("polylines_2/nano.svg"); + + Mesh mesh; + if(!CGAL::IO::read_polygon_mesh(mesh_filename, mesh) || !CGAL::is_triangle_mesh(mesh)) + { + std::cerr << "Invalid input mesh." << std::endl; + return 1; + } + + NSVGimage* g_image = nsvgParseFromFile(svg_filename.c_str(), "px", 96.0f); + if (g_image == NULL) { + printf("Could not open SVG image.\n"); + return 1; + } + + // extract control points + std::vector< std::array > bezier_curves; + CGAL::Bbox_2 bb2; + + // in SVG's the y axis points downward, so we must take the opposite y coordinates + for (NSVGshape* shape = g_image->shapes; shape != NULL; shape = shape->next) + { + for (NSVGpath* path = shape->paths; path != NULL; path = path->next) + { + CGAL::Bbox_2 path_bbox(path->bounds[0], -path->bounds[1], + path->bounds[2], -path->bounds[3]); + bb2+=path_bbox; + + float* pts=path->pts; + int npts=path->npts; + + for (int i=0; i> directions; + std::vector> lengths; + directions.reserve(bezier_curves.size()); + lengths.reserve(bezier_curves.size()); + + for (const std::array& bezier : bezier_curves) + { + std::vector> polar_coords = + PMP::convert_polygon_to_polar_coordinates(bezier, center_2); + + directions.emplace_back(); + lengths.emplace_back(); + + assert(polar_coords.size()==4); + + for (int i=0;i<4; ++i) + { + lengths.back()[i] = scaling * polar_coords[i].first; + directions.back()[i]=K::Vector_2(std::cos(polar_coords[i].second), std::sin(polar_coords[i].second)); + } + } + + // trace bezier curves + std::size_t nb_faces = faces(mesh).size(); + Mesh::Face_index f = *std::next(faces(mesh).begin(), (2154)%nb_faces); + Face_location center(f, CGAL::make_array(0.3,0.3,0.4)); + + PMP::Dual_geodesic_solver solver; + PMP::init_geodesic_dual_solver(solver, mesh); + + std::vector< std::vector > res = + PMP::trace_bezier_curves(center, directions, lengths, 6, mesh, solver); + + // write result + std::ofstream out("svg.polylines.txt"); + out << std::setprecision(17); + for (const auto& b : res) + { + out << b.size(); + for (const K::Point_3& pt : b) + out << " " << pt; + out << "\n"; + } + + return 0; +} diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index 0f12debfa949..f171aa9b1585 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -2955,9 +2955,10 @@ straightest_geodesic(const Face_location &src, } // we don't expect the last point to be duplicated here -template +// TODO: rename the function, it's not a polygon necessarily +template std::vector> -convert_polygon_to_polar_coordinates(const std::vector & polygon, +convert_polygon_to_polar_coordinates(const PointRange_2& polygon, std::optional center = std::nullopt) { std::vector> result; @@ -3272,6 +3273,71 @@ trace_geodesic_label(const Face_location ¢er, } +template +std::vector< std::vector > +trace_bezier_curves(const Face_location ¢er, + const std::vector>& directions, + const std::vector>& lengths, + const int num_subdiv, + const TriangleMesh &tmesh +#ifndef CGAL_BSURF_USE_DIJKSTRA_SP + , const Dual_geodesic_solver& solver = {} +#endif +) +{ + using FT = typename K::FT; + + std::size_t n=directions.size(); + std::vector< std::vector > result(n); + std::vector> vertices(n); + +#ifndef CGAL_BSURF_USE_DIJKSTRA_SP + const Dual_geodesic_solver* solver_ptr=&solver; + Dual_geodesic_solver local_solver; + if (solver.graph.empty()) + { + solver_ptr = &local_solver; + init_geodesic_dual_solver(local_solver, tmesh); + } +#endif + +#ifdef CGAL_DEBUG_BSURF + std::ofstream debug_cp("/tmp/control_points.xyz"); + std::ofstream debug_ep("/tmp/end_points.xyz"); + debug_cp << std::setprecision(17); + debug_ep << std::setprecision(17); +#endif + for (std::size_t i=0; i control_loc; + for (int k=0;k<4; ++k) + { + control_loc[k] = straightest_geodesic(center,directions[i][k],lengths[i][k],tmesh).back(); + } + + #ifdef CGAL_DEBUG_BSURF + debug_ep << construct_point(control_loc[0], tmesh) << "\n"; + debug_ep << construct_point(control_loc[3], tmesh) << "\n"; + debug_cp << construct_point(control_loc[1], tmesh) << "\n"; + debug_cp << construct_point(control_loc[2], tmesh) << "\n"; + #endif + + std::vector> bezier = + recursive_de_Casteljau(tmesh, control_loc, num_subdiv +#ifndef CGAL_BSURF_USE_DIJKSTRA_SP + , *solver_ptr +#endif + ); + + result[i].reserve(bezier.size()); + for(const Face_location& loc : bezier) + result[i].push_back(construct_point(loc,tmesh)); + } + + return result; +} + + template typename K::FT path_length(const std::vector>& path, const Face_location& src, From 7fca80b86c3f8d68c8d96115b746acf576b42085 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 26 Jun 2024 09:34:54 +0200 Subject: [PATCH 071/120] change return type --- .../trace_svg_example.cpp | 6 +++--- .../Bsurf/locally_shortest_path.h | 21 +++++++++---------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_svg_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_svg_example.cpp index 30a3739725ba..2e2c9686f102 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_svg_example.cpp +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_svg_example.cpp @@ -105,7 +105,7 @@ int main(int argc, char** argv) PMP::Dual_geodesic_solver solver; PMP::init_geodesic_dual_solver(solver, mesh); - std::vector< std::vector > res = + std::vector< std::vector > res = PMP::trace_bezier_curves(center, directions, lengths, 6, mesh, solver); // write result @@ -114,8 +114,8 @@ int main(int argc, char** argv) for (const auto& b : res) { out << b.size(); - for (const K::Point_3& pt : b) - out << " " << pt; + for (const Face_location& loc : b) + out << " " << PMP::construct_point(loc,mesh); out << "\n"; } diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index f171aa9b1585..5c4a8719ea0f 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -3107,6 +3107,8 @@ trace_geodesic_polygons(const Face_location ¢e } //TODO add groups of polygons for better rendering +//TODO add a function dedicated to groups of polygons (like a sentence) +// that takes groups of polyons template std::vector> trace_geodesic_label(const Face_location ¢er, @@ -3272,9 +3274,8 @@ trace_geodesic_label(const Face_location ¢er, return result; } - template -std::vector< std::vector > +std::vector< std::vector> > trace_bezier_curves(const Face_location ¢er, const std::vector>& directions, const std::vector>& lengths, @@ -3288,7 +3289,7 @@ trace_bezier_curves(const Face_location ¢er, using FT = typename K::FT; std::size_t n=directions.size(); - std::vector< std::vector > result(n); + std::vector< std::vector> > result(n); std::vector> vertices(n); #ifndef CGAL_BSURF_USE_DIJKSTRA_SP @@ -3331,7 +3332,7 @@ trace_bezier_curves(const Face_location ¢er, result[i].reserve(bezier.size()); for(const Face_location& loc : bezier) - result[i].push_back(construct_point(loc,tmesh)); + result[i].push_back(loc); } return result; @@ -3344,19 +3345,17 @@ typename K::FT path_length(const std::vector& tgt, const TriangleMesh &tmesh) { - if(path.size()==0) - return sqrt(squared_distance(construct_point(src,tmesh),construct_point(tgt,tmesh))); + std::size_t lpath = path.size(); + if(lpath==0) + return sqrt(squared_distance(construct_point(src,tmesh),construct_point(tgt,tmesh))); using VPM = typename boost::property_map::const_type; VPM vpm = get(CGAL::vertex_point, tmesh); typename K::FT len=sqrt(squared_distance(construct_point(src,tmesh),construct_point(path[0],tmesh))); - typename K::FT dist=[&](const Edge_location& p0,const Edge_location& p1) - { - return sqrt(squared_distance(construct_point(p0,tmesh),construct_point(p1,tmesh))); - }; - len=std::accumulate(path.begin(),path.end(),dist); + for (std::size_t i=0; i Date: Wed, 26 Jun 2024 09:35:38 +0200 Subject: [PATCH 072/120] add function to write along a curve given as input --- Data/data/polylines_2/Archimedean_spiral.svg | 40 ++++ .../Polygon_mesh_processing/CMakeLists.txt | 2 + .../trace_polygon_on_svg_curve_example.cpp | 211 ++++++++++++++++++ .../Bsurf/locally_shortest_path.h | 177 +++++++++++++++ 4 files changed, 430 insertions(+) create mode 100644 Data/data/polylines_2/Archimedean_spiral.svg create mode 100644 Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_on_svg_curve_example.cpp diff --git a/Data/data/polylines_2/Archimedean_spiral.svg b/Data/data/polylines_2/Archimedean_spiral.svg new file mode 100644 index 000000000000..c2481821d070 --- /dev/null +++ b/Data/data/polylines_2/Archimedean_spiral.svg @@ -0,0 +1,40 @@ + + + + + + diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt b/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt index 9adcc9babf29..e3c730e32f86 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt @@ -102,6 +102,8 @@ if(TARGET CGAL::Eigen3_support) if (NanoSVG_FOUND) create_single_source_cgal_program("trace_svg_example.cpp") target_link_libraries(trace_svg_example PUBLIC CGAL::Eigen3_support CGAL::NanoSVG_support) + create_single_source_cgal_program("trace_polygon_on_svg_curve_example.cpp") + target_link_libraries(trace_polygon_on_svg_curve_example PUBLIC CGAL::Eigen3_support CGAL::NanoSVG_support) else() message(STATUS "NOTICE: trace_svg_example requires NanoSVG and will not be compiled.") endif() diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_on_svg_curve_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_on_svg_curve_example.cpp new file mode 100644 index 000000000000..80673e296dfd --- /dev/null +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_on_svg_curve_example.cpp @@ -0,0 +1,211 @@ +#include +#include +#include + +#include + +#include + +namespace PMP = CGAL::Polygon_mesh_processing; + +typedef CGAL::Exact_predicates_inexact_constructions_kernel K; +typedef CGAL::Surface_mesh Mesh; +typedef PMP::Face_location Face_location; +typedef PMP::Edge_location Edge_location; + +std::vector +get_supporting_curve(std::string svg_filename, + const Mesh& mesh, + Face_location center, const PMP::Dual_geodesic_solver& solver) +{ + std::vector res; + + NSVGimage* g_image = nsvgParseFromFile(svg_filename.c_str(), "px", 96.0f); + if (g_image == NULL) { + printf("Could not open SVG image.\n"); + return res; + } + + // extract control points + std::vector< std::array > bezier_curves; + CGAL::Bbox_2 bb2; + + // in SVG's the y axis points downward, so we must take the opposite y coordinates + for (NSVGshape* shape = g_image->shapes; shape != NULL; shape = shape->next) + { + for (NSVGpath* path = shape->paths; path != NULL; path = path->next) + { + CGAL::Bbox_2 path_bbox(path->bounds[0], -path->bounds[1], + path->bounds[2], -path->bounds[3]); + bb2+=path_bbox; + + float* pts=path->pts; + int npts=path->npts; + + for (int i=0; i> directions; + std::vector> lengths; + directions.reserve(bezier_curves.size()); + lengths.reserve(bezier_curves.size()); + + for (const std::array& bezier : bezier_curves) + { + std::vector> polar_coords = + PMP::convert_polygon_to_polar_coordinates(bezier, center_2); + + directions.emplace_back(); + lengths.emplace_back(); + + assert(polar_coords.size()==4); + + for (int i=0;i<4; ++i) + { + lengths.back()[i] = scaling * polar_coords[i].first; + directions.back()[i]=K::Vector_2(std::cos(polar_coords[i].second), std::sin(polar_coords[i].second)); + } + } + + // trace bezier curves + std::vector< std::vector > resi = + PMP::trace_bezier_curves(center, directions, lengths, 6, mesh, solver); + + //TODO: here we assume that curves are parameterized in the same order and are consecutives + for (const std::vector& r : resi) + res.insert(res.end(), r.begin(), std::prev(r.end())); + res.push_back(resi.back().back()); + + std::reverse(res.begin(), res.end()); // TODO: should be an option! + + return res; +} + +int main(int argc, char** argv) +{ + std::string filename = (argc > 1) ? std::string(argv[1]) + : CGAL::data_file_path("meshes/elephant.off"); + + std::string svg_filename = (argc > 2) ? std::string(argv[2]) + : CGAL::data_file_path("polylines_2/Archimedean_spiral.svg"); + + std::string filename_poly = (argc > 3) ? std::string(argv[3]) + : CGAL::data_file_path("XXXXX"); + + Mesh mesh; + if(!CGAL::IO::read_polygon_mesh(filename, mesh) || !CGAL::is_triangle_mesh(mesh)) + { + std::cerr << "Invalid input." << std::endl; + return 1; + } + + std::vector> polygons; + std::ifstream in(filename_poly); + if (!in) + { + std::cerr << "Error cannot open " << filename_poly << "\n"; + return 1; + } + + int nb_pt; + K::Point_3 pt; + CGAL::Bbox_2 bb2; + while (in >> nb_pt) + { + polygons.emplace_back(); + polygons.back().reserve(nb_pt-1); + for (int i=0; i> pt; + polygons.back().emplace_back(pt.x(), pt.y()); + bb2+=polygons.back().back().bbox(); + } + in >> pt; + if (!in) + { + std::cerr << "Error reading input polygons\n"; + return 1; + } + // check if last point is duplicated + if (polygons.back().back().x()!=pt.x() || polygons.back().back().y()!=pt.y()) + { + polygons.back().emplace_back(pt.x(), pt.y()); + bb2+=polygons.back().back().bbox(); + } + if (!in) break; + } + + std::cout << polygons.size() << " polygons read\n"; + + // tracing center + std::size_t nb_faces = faces(mesh).size(); + Mesh::Face_index f = *std::next(faces(mesh).begin(), (2154)%nb_faces); + Face_location center(f, CGAL::make_array(0.3,0.3,0.4)); + + K::Point_3 center_pt = PMP::construct_point(center, mesh); + std::cout << "center = " << center_pt << "\n"; + PMP::Dual_geodesic_solver solver; + PMP::init_geodesic_dual_solver(solver, mesh); + + + // get supporting curve + std::vector supporting_curve = get_supporting_curve(svg_filename, mesh, center, solver); + if (supporting_curve.empty()) return 1; + + std::cout <<"supporting_curve generated!\n"; + std::ofstream debug("debug.polylines.txt"); + debug << supporting_curve.size(); + for (auto loc : supporting_curve) + debug << " " << PMP::construct_point(loc, mesh); + debug << "\n"; + debug.close(); + + // convert polygons to polar coordinates + typename K::Point_2 center_2((bb2.xmax()+bb2.xmin())/2., (bb2.ymax()+bb2.ymin())/2.); + double diag = std::sqrt( CGAL::square(bb2.xmin()-bb2.xmax()) + CGAL::square(bb2.xmin()-bb2.xmax()) ); + const double expected_diag = 2.1; // user parameter for scaling + const double scaling = expected_diag/diag; + + + std::ofstream out("label_on_curve.polylines.txt"); + out << std::setprecision(17); + + std::vector> polygons_3; + polygons_3 = PMP::trace_geodesic_label_along_curve(supporting_curve, polygons, scaling, 0., true, mesh, solver); + + for (const auto& polygon : polygons_3) + { + out << polygon.size(); + for (auto p : polygon) + out << " " << p; + out << std::endl; + } + + return 0; +} diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index 5c4a8719ea0f..83b561b6708d 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -3274,6 +3274,183 @@ trace_geodesic_label(const Face_location ¢er, return result; } +template +typename K::FT path_length(const std::vector>& path, + const TriangleMesh &tmesh) +{ + std::size_t lpath = path.size(); + if(lpath<2) + return 0; + + using VPM = typename boost::property_map::const_type; + VPM vpm = get(CGAL::vertex_point, tmesh); + + typename K::FT len(0); + + for (std::size_t i=0; i +std::vector> +trace_geodesic_label_along_curve(const std::vector>& supporting_curve, + const std::vector>& polygons, + const typename K::FT scaling, + const typename K::FT padding, + const bool is_centered, + const TriangleMesh &tmesh, + const Dual_geodesic_solver& solver = {}) +{ + using VPM = typename boost::property_map::const_type; + using Impl = internal::Locally_shortest_path_imp; + VPM vpm = get(CGAL::vertex_point, tmesh); + using Point_2 = typename K::Point_2; + using Vector_2 = typename K::Vector_2; + + + std::vector polygon_bboxes; + polygon_bboxes.reserve(polygons.size()); + Bbox_2 gbox; + for (const std::vector& polygon : polygons) + { + polygon_bboxes.push_back( bbox_2(polygon.begin(), polygon.end()) ); + gbox+=polygon_bboxes.back(); + } + + const Dual_geodesic_solver* solver_ptr=&solver; + Dual_geodesic_solver local_solver; + if (solver.graph.empty()) + { + solver_ptr = &local_solver; + init_geodesic_dual_solver(local_solver, tmesh); + } + + std::vector> result(polygons.size()); + Vector_2 initial_dir(1,0);// TODO: input parameter or 2D PCA of the centers? + + // 1D partition of the letters + Point_2 c2( (gbox.xmin()+gbox.xmax())/2., + (gbox.ymin()+gbox.ymax())/2. ); + Point_2 left_most(gbox.xmin(), c2.y()); + Point_2 right_most(gbox.xmax(), c2.y()); + + std::vector support_len(supporting_curve.size(),0); + for (std::size_t i=0; ipadding + scaling * (gbox.xmax()-gbox.xmin())) || + (is_centered && (support_len.back()> scaling * (gbox.xmax()-gbox.xmin()))) ); + + typename K::FT pad = is_centered ? (support_len.back()-(scaling*(gbox.xmax()-gbox.xmin())))/2 + : padding; + + auto get_polygon_center = [&tmesh, &vpm, &supporting_curve, &support_len](double targetd) + { + // use left + std::size_t k=0; + while(true) // TODO get rid of the while and std::lower_bound + { + double acc = support_len[k]; + if (acc == targetd) + { + double theta=0; + if (k!=0) + { + Face_location loc_k=supporting_curve[k], loc_k1=supporting_curve[k+1]; + CGAL_assertion_code(bool OK=) + locate_in_common_face(loc_k, loc_k1, tmesh); + CGAL_assertion(OK); + std::array flat_triangle = + Impl::init_flat_triangle(halfedge(loc_k.first,tmesh),vpm,tmesh); + Vector_2 src = loc_k.second[0]*flat_triangle[0]+loc_k.second[1]*flat_triangle[1]+loc_k.second[2]*flat_triangle[2]; + Vector_2 tgt = loc_k1.second[0]*flat_triangle[0]+loc_k1.second[1]*flat_triangle[1]+loc_k1.second[2]*flat_triangle[2]; + + Vector_2 dir2 = tgt-src; + theta = atan2(dir2.y(), dir2.x()); + } + return std::make_pair(supporting_curve[k+1], theta); + } + + if (acc > targetd) + { + double excess = acc-targetd; + + Face_location loc_k=supporting_curve[k], loc_k1=supporting_curve[k+1]; + CGAL_assertion_code(bool OK=) + locate_in_common_face(loc_k, loc_k1, tmesh); + // CGAL_assertion(OK); // TODO: check with Claudio how to correctly implement transport along the curve + + Face_location polygon_center; + polygon_center.first=loc_k.first; + double alpha = excess/(support_len[k]-support_len[k-1]); + + for(int ii=0; ii<3;++ii) + polygon_center.second[ii] = loc_k.second[ii]*alpha+loc_k1.second[ii]*(1.-alpha); + + std::array flat_triangle = + Impl::init_flat_triangle(halfedge(polygon_center.first,tmesh),vpm,tmesh); + Vector_2 src = loc_k.second[0]*flat_triangle[0]+loc_k.second[1]*flat_triangle[1]+loc_k.second[2]*flat_triangle[2]; + Vector_2 tgt = loc_k1.second[0]*flat_triangle[0]+loc_k1.second[1]*flat_triangle[1]+loc_k1.second[2]*flat_triangle[2]; + + Vector_2 dir2 = tgt-src; + double theta = atan2(dir2.y(), dir2.x()); + + return std::make_pair(polygon_center, theta); + } + + if (++k==supporting_curve.size()-1) + { + Face_location loc_k=supporting_curve[k-1], loc_k1=supporting_curve[k]; + CGAL_assertion_code(bool OK=) + locate_in_common_face(loc_k, loc_k1, tmesh); + CGAL_assertion(OK); + std::array flat_triangle = + Impl::init_flat_triangle(halfedge(loc_k.first,tmesh),vpm,tmesh); + Vector_2 src = loc_k.second[0]*flat_triangle[0]+loc_k.second[1]*flat_triangle[1]+loc_k.second[2]*flat_triangle[2]; + Vector_2 tgt = loc_k1.second[0]*flat_triangle[0]+loc_k1.second[1]*flat_triangle[1]+loc_k1.second[2]*flat_triangle[2]; + + Vector_2 dir2 = tgt-src; + double theta = atan2(dir2.y(), dir2.x()); + + return std::make_pair(supporting_curve.back(), theta); + } + } + }; + + for(std::size_t i=0;i polygon_center; + double xc = (polygon_bboxes[i].xmin()+polygon_bboxes[i].xmax())/2.; + + double theta; + std::tie(polygon_center, theta)=get_polygon_center(scaling * (xc-gbox.xmin())+pad); + theta=CGAL_PI+theta; + + std::vector> polar_coords = + convert_polygon_to_polar_coordinates(polygons[i], + typename K::Point_2((polygon_bboxes[i].xmin()+polygon_bboxes[i].xmax())/2., + (gbox.ymin()+gbox.ymax())/2.)); + + std::vector directions; + std::vector lens; + lens.reserve(polar_coords.size()); + directions.reserve(polar_coords.size()); + + for (const std::pair& coord : polar_coords) + { + lens.push_back(scaling * coord.first); + directions.emplace_back(std::cos(coord.second+theta), std::sin(coord.second+theta)); + } + result[i] = trace_geodesic_polygon(polygon_center, directions, lens, tmesh, *solver_ptr); + } + + return result; +} + template std::vector< std::vector> > trace_bezier_curves(const Face_location ¢er, From 90ed96f9ab8edc22cd10da796a1500129cadb7ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 28 Jun 2024 14:12:12 +0200 Subject: [PATCH 073/120] more helper functions --- .../CGAL/Polygon_mesh_processing/locate.h | 95 ++++++++++++++++++- 1 file changed, 91 insertions(+), 4 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/locate.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/locate.h index dfd726287833..dd30211f0396 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/locate.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/locate.h @@ -99,6 +99,97 @@ using Edge_location = std::pair::edge_descriptor, std::array>; +/// \ingroup PMP_locate_grp +/// returns `true` if `loc1` and `loc2` indicate the same point on `tm`, and `false` otherwise, +template +bool +are_locations_identical(const Face_location& loc1, + const Face_location& loc2, + const TriangleMesh& tm) +{ + if (loc1==loc2) return true; + + using vertex_descriptor = typename boost::graph_traits::vertex_descriptor; + using halfedge_descriptor = typename boost::graph_traits::halfedge_descriptor; + using face_descriptor = typename boost::graph_traits::face_descriptor; + using Barycentric_coordinates = CGAL::Polygon_mesh_processing::Barycentric_coordinates; + + const face_descriptor fd1 = loc1.first; + const Barycentric_coordinates& bar1 = loc1.second; + const face_descriptor fd2 = loc2.first; + const Barycentric_coordinates& bar2 = loc2.second; + + // the first barycentric coordinate corresponds to source(halfedge(fdX, tm), tm) + halfedge_descriptor hd1 = prev(halfedge(fd1, tm), tm); + halfedge_descriptor hd2 = prev(halfedge(fd2, tm), tm); + + // check if the point is a vertex + for(int i=0; i<3; ++i) + { + if(bar1[i] == FT(1)) + { + vertex_descriptor vd1 = target(hd1, tm); + for(int k=0; k<3; ++k) + { + if(bar2[k] == FT(1)) + return target(hd2, tm) == vd1; + hd2 = next(hd2, tm); + } + } + hd1 = next(hd1, tm); + } + CGAL_assertion(hd1 == prev(halfedge(fd1, tm), tm)); + + // check loc2 is not a vertex + for(int k=0; k<3; ++k) + { + if(bar2[k] == FT(1)) + return false; + hd2 = next(hd2, tm); + } + CGAL_assertion(hd2 == prev(halfedge(fd2, tm), tm)); + + + // check if the point is on an edge + for(int i=0; i<3; ++i) + { + if(bar1[i] == FT(0)) // coordinate at target(hd1, tm) + { + for(int k=0; k<3; ++k) + { + if(bar2[k] == FT(0)) // coordinate at target(hd2, tm) + return prev(hd2, tm) == opposite(prev(hd1, tm), tm); + hd2 = next(hd2, tm); + } + } + hd1 = next(hd1, tm); + } + + return false; +} + +template +Face_location +to_face_location(Edge_location loc, + const TriangleMesh& tm) +{ + auto h = halfedge(loc.first, tm); + if (is_border(h, tm)) + { + h=opposite(h, tm); + std::swap(loc.second[0], loc.second[1]); + } + + auto f = face(h, tm); + auto hf = halfedge(f, tm); + if (hf == h) + return Face_location(f, CGAL::make_array(loc.second[0], loc.second[1], FT(0))); + if (next(hf, tm)==h) + return Face_location(f, CGAL::make_array(FT(0),loc.second[0], loc.second[1])); + CGAL_assertion(prev(hf, tm)==h); + return Face_location(f, CGAL::make_array(loc.second[1],FT(0),loc.second[0])); +} + namespace internal { // The Ray must have the same ambient dimension as the property map's value type (aka, the point type) @@ -533,11 +624,7 @@ random_location_on_mesh(const TriangleMesh& tm, /// template descriptor_variant -#ifdef DOXYGEN_RUNNING // just for convenience because template alias do not allow template deduction get_descriptor_from_location(const Face_location& loc, -#else -get_descriptor_from_location(const Face_location& loc, -#endif const TriangleMesh& tm) { typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; From 9c550522c438ca05b8667e7386bfc2c61bbd62fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 28 Jun 2024 14:14:39 +0200 Subject: [PATCH 074/120] change return type to return location handle duplicated locations in the output add function to refine a mesh along paths --- .../trace_polygon_example.cpp | 22 +- .../Bsurf/locally_shortest_path.h | 485 +++++++++++++++++- 2 files changed, 487 insertions(+), 20 deletions(-) diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_example.cpp index f61e2d4c4f4c..fc1b5ce8a66c 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_example.cpp +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_example.cpp @@ -99,6 +99,7 @@ int main(int argc, char** argv) { std::vector> polar_coords = PMP::convert_polygon_to_polar_coordinates(polygon, center_2); + if (polygon.front()==polygon.back()) polar_coords.pop_back(); std::vector directions; std::vector lens; @@ -112,10 +113,10 @@ int main(int argc, char** argv) } // last point is duplicated - std::vector out_polygon = PMP::trace_geodesic_polygon(center,directions,lens,mesh, solver); + std::vector out_polygon = PMP::trace_geodesic_polygon(center,directions,lens,mesh, solver); out << out_polygon.size(); for (auto p : out_polygon) - out << " " << p; + out << " " << PMP::construct_point(p, mesh); out << std::endl; } @@ -124,14 +125,14 @@ int main(int argc, char** argv) out.open("geodesic_polygons.polylines.txt"); out << std::setprecision(17); - std::vector> polygons_3 + std::vector> polygons_3 = PMP::trace_geodesic_polygons(center, polygons, scaling, mesh, solver); for (const auto& polygon : polygons_3) { out << polygon.size(); for (auto p : polygon) - out << " " << p; + out << " " << PMP::construct_point(p, mesh); out << std::endl; } @@ -147,9 +148,20 @@ int main(int argc, char** argv) { out << polygon.size(); for (auto p : polygon) - out << " " << p; + out << " " << PMP::construct_point(p, mesh); out << std::endl; } + // now refine the input mesh + std::vector cst_hedges; + PMP::refine_mesh_along_paths(polygons_3, mesh, std::back_inserter(cst_hedges)); + + std::ofstream("mesh_refined.off") << std::setprecision(17) << mesh; + + std::ofstream cst_edges("refinement_edges.polylines.txt"); + cst_edges.precision(17); + for (Mesh::Halfedge_index h : cst_hedges) + cst_edges << "2 " << mesh.point(source(h,mesh)) << " " << mesh.point(target(h,mesh)) << "\n"; + return 0; } diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index 83b561b6708d..7a112e1c779d 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -25,6 +25,12 @@ #include #include +#include +#include +#include +#include +#include + #ifdef CGAL_DEBUG_BSURF #include #endif @@ -2969,13 +2975,18 @@ convert_polygon_to_polar_coordinates(const PointRange_2& polygon, center = typename K::Point_2((bb.xmax()+bb.xmin())/2., (bb.ymax()+bb.ymin())/2); } - for (const typename K::Point_2& pt : polygon) + bool is_closed = polygon.front()==polygon.back(); + std::size_t nbp=polygon.size(); + if (is_closed) --nbp; + for (std::size_t i=0; iy())/* /d */, (pt.x()-center->x())/* /d */); result.emplace_back(d, polar); // std::cout << center->x()+d*std::cos(polar) << " " << center->x()+d*std::sin(polar) << " 0\n"; } + if (is_closed) result.push_back(result.front()); return result; } @@ -2983,15 +2994,15 @@ convert_polygon_to_polar_coordinates(const PointRange_2& polygon, //TODO: do we want to handle polylines? // href of center.first is the y axis in 2D template -std::vector +std::vector> trace_geodesic_polygon(const Face_location ¢er, const std::vector& directions, const std::vector& lengths, const TriangleMesh &tmesh, const Dual_geodesic_solver& solver = {}) { - size_t n=directions.size(); - std::vector result; + std::size_t n=directions.size(); + std::vector> result; std::vector> vertices(n); #ifdef CGAL_DEBUG_BSURF std::cout << "trace_geodesic_polygon\n"; @@ -3017,20 +3028,20 @@ trace_geodesic_polygon(const Face_location ¢er { edge_locations.clear(); locally_shortest_path(vertices[i],vertices[(i+1)%n],tmesh, edge_locations, *solver_ptr); - result.push_back(construct_point(vertices[i],tmesh)); + result.push_back(vertices[i]); for(auto& el : edge_locations) { - result.push_back(construct_point(el, tmesh)); + result.push_back(to_face_location(el, tmesh)); } } - result.push_back(construct_point(vertices[0],tmesh)); + result.push_back(vertices[0]); return result; } //TODO add groups of polygons for better rendering template -std::vector> +std::vector>> trace_geodesic_polygons(const Face_location ¢er, const std::vector>& polygons, const typename K::FT scaling, @@ -3060,7 +3071,7 @@ trace_geodesic_polygons(const Face_location ¢e init_geodesic_dual_solver(local_solver, tmesh); } - std::vector> result(polygons.size()); + std::vector>> result(polygons.size()); typename K::Vector_2 initial_dir(1,0);// TODO: input parameter or 2D PCA of the centers? for(std::size_t i=0;i ¢e convert_polygon_to_polar_coordinates(polygons[i], typename K::Point_2((polygon_bboxes[i].xmin()+polygon_bboxes[i].xmax())/2., (polygon_bboxes[i].ymin()+polygon_bboxes[i].ymax())/2.)); - + if (polygons[i].front()==polygons[i].back()) + polar_coords.pop_back(); double theta = atan2(v.y(),v.x()); @@ -3110,7 +3122,7 @@ trace_geodesic_polygons(const Face_location ¢e //TODO add a function dedicated to groups of polygons (like a sentence) // that takes groups of polyons template -std::vector> +std::vector>> trace_geodesic_label(const Face_location ¢er, const std::vector>& polygons, const typename K::FT scaling, @@ -3141,8 +3153,8 @@ trace_geodesic_label(const Face_location ¢er, init_geodesic_dual_solver(local_solver, tmesh); } - std::vector> result(polygons.size()); - Vector_2 initial_dir(1,0);// TODO: input parameter or 2D PCA of the centers? + std::vector>> result(polygons.size()); + // Vector_2 initial_dir(1,0);// TODO: input parameter or 2D PCA of the centers? // 1D partition of the letters Point_2 c2( (gbox.xmin()+gbox.xmax())/2., @@ -3258,6 +3270,9 @@ trace_geodesic_label(const Face_location ¢er, typename K::Point_2((polygon_bboxes[i].xmin()+polygon_bboxes[i].xmax())/2., (gbox.ymin()+gbox.ymax())/2.)); + //already duplicated in trace_geodesic_polygon + if (polar_coords.front()==polar_coords.back()) polar_coords.pop_back(); + std::vector directions; std::vector lens; lens.reserve(polar_coords.size()); @@ -3310,7 +3325,6 @@ trace_geodesic_label_along_curve(const std::vector polygon_bboxes; polygon_bboxes.reserve(polygons.size()); Bbox_2 gbox; @@ -3329,7 +3343,7 @@ trace_geodesic_label_along_curve(const std::vector> result(polygons.size()); - Vector_2 initial_dir(1,0);// TODO: input parameter or 2D PCA of the centers? +// Vector_2 initial_dir(1,0);// TODO: input parameter or 2D PCA of the centers? // 1D partition of the letters Point_2 c2( (gbox.xmin()+gbox.xmax())/2., @@ -3434,6 +3448,8 @@ trace_geodesic_label_along_curve(const std::vector(polygons[i], typename K::Point_2((polygon_bboxes[i].xmin()+polygon_bboxes[i].xmax())/2., (gbox.ymin()+gbox.ymax())/2.)); + //already duplicated in trace_geodesic_polygon + if (polar_coords.front()==polar_coords.back()) polar_coords.pop_back(); std::vector directions; std::vector lens; @@ -3539,6 +3555,445 @@ typename K::FT path_length(const std::vector +// std::vector::vertex_descriptor> +OutputIterator +refine_mesh_along_paths(const std::vector>>& paths, + TriangleMesh& tmesh, + OutputIterator out) +{ + // TODO: nothing is done here to identify identical points + + using vertex_descriptor = typename boost::graph_traits::vertex_descriptor; + using edge_descriptor = typename boost::graph_traits::edge_descriptor; + using halfedge_descriptor = typename boost::graph_traits::halfedge_descriptor; + using face_descriptor = typename boost::graph_traits::face_descriptor; + using Face_loc = Face_location; + using dscrptr_vrnt = descriptor_variant; + using Point_3 = typename K::Point_3; + using EK = CGAL::Exact_predicates_exact_constructions_kernel; + + // 3 types of points: on edges, on faces, and existing vertices + typename boost::property_map>::type + fid_map = get(CGAL::dynamic_face_property_t(), tmesh, std::size_t(-1)); + typename boost::property_map>::type + eid_map = get(CGAL::dynamic_edge_property_t(), tmesh, std::size_t(-1)); + typename boost::property_map>::type + vid_map = get(CGAL::dynamic_vertex_property_t(), tmesh, std::size_t(-1)); + + std::vector< std::vector> > edges_per_face; + std::vector< std::vector > points_per_face; + std::vector< std::vector > points_per_edge; + std::vector< edge_descriptor > edges_to_refine; + std::vector< face_descriptor > faces_to_refine; + std::vector< std::pair > input_vertices; + + std::vector polyline_locations; + + auto register_point = [&](const dscrptr_vrnt& var, std::size_t id) + { + switch(var.index()) + { + case 1: + { + halfedge_descriptor hd=std::get(var); + edge_descriptor ed = edge(hd, tmesh); + if (get(eid_map, ed)==std::size_t(-1)) + { + put(eid_map, ed, edges_to_refine.size()); + points_per_edge.emplace_back(); + edges_to_refine.push_back(ed); + } + points_per_edge[get(eid_map,ed)].push_back(id); + return; + } + case 2: + { + face_descriptor fd=std::get(var); + if (get(fid_map, fd)==std::size_t(-1)) + { + put(fid_map, fd, faces_to_refine.size()); + points_per_face.emplace_back(); + edges_per_face.emplace_back(); + faces_to_refine.push_back(fd); + } + points_per_face[get(fid_map,fd)].push_back(id); + return; + } + default: + input_vertices.emplace_back(id, std::get(var)); + } + }; + + auto register_segment = [&](const dscrptr_vrnt& v1, const dscrptr_vrnt& v2, + const Face_loc& loc1, const Face_loc& loc2, + std::size_t i1, std::size_t i2) + { + if (v1.index()==0 && v2.index()==0) return; + if (v1.index()==1 && v2.index()==1 && v1==v2) return; + if (v1.index()==0 && v2.index()==1) + { + vertex_descriptor vd = std::get(v1); + halfedge_descriptor hd = std::get(v2); + if (vd==source(hd, tmesh) || vd==target(hd, tmesh)) + return; + } + if (v2.index()==0 && v1.index()==1) + { + vertex_descriptor vd = std::get(v2); + halfedge_descriptor hd = std::get(v1); + if (vd==source(hd, tmesh) || vd==target(hd, tmesh)) + return; + } + Face_loc copy1=loc1, copy2=loc2; + CGAL_assertion_code(bool OK=) + locate_in_common_face(copy1, copy2, tmesh); + CGAL_assertion(OK); + if (get(fid_map,copy1.first)==std::size_t(-1)) + { + put(fid_map, copy1.first, faces_to_refine.size()); + points_per_face.emplace_back(); + edges_per_face.emplace_back(); + faces_to_refine.push_back(copy1.first); + } + edges_per_face[get(fid_map, copy1.first)].emplace_back(i1,i2); + }; + + for (const std::vector& path : paths) + { + bool closed = are_locations_identical(path.front(), path.back(), tmesh); + std::size_t nbp = path.size(); + + if (closed) nbp-=1; + + std::size_t prev_id=polyline_locations.size(), first_id=prev_id; + dscrptr_vrnt prev_var = get_descriptor_from_location(path.front(), tmesh), + first_var = prev_var; + + polyline_locations.push_back( path.front() ); + register_point(prev_var, prev_id); + + for (std::size_t i=1; i face_normals(faces_to_refine.size()); + std::vector> face_input_vertices(faces_to_refine.size()); + for (std::size_t fid=0; fid exact_points(polyline_locations.size()); + std::vector polyline_vertices( + polyline_locations.size(), boost::graph_traits::null_vertex()); + + // collect split point on edges + std::vector hedges_to_refine(edges_to_refine.size()); + std::vector< std::vector > > points_on_hedges(edges_to_refine.size()); + for (std::size_t eid=0; eid > coordinates; + coordinates.reserve(points_per_edge[eid].size()); + for (std::size_t pid : points_per_edge[eid]) + { + halfedge_descriptor hd = halfedge(polyline_locations[pid].first, tmesh); + int src_id=0; + if (edge(hd, tmesh)!=e) + { + hd=next(hd, tmesh); ++src_id; + if (edge(hd, tmesh)!=e) + { + hd=next(hd, tmesh); + ++src_id; + } + } + CGAL_assertion(edge(hd, tmesh)==e); + coordinates.emplace_back(polyline_locations[pid].second[src_id], polyline_locations[pid].second[(src_id+1)%3]); + if (hd!=href) + { + CGAL_assertion( opposite(hd, tmesh)==href ); + std::swap( coordinates.back().first, coordinates.back().second ); + } + } + + // now sort coordinates + std::vector ids(coordinates.size()); + std::iota(ids.begin(), ids.end(), 0); + + std::sort(ids.begin(), ids.end(), [&coordinates](std::size_t i, std::size_t j) + { return coordinates[i].first < coordinates[j].first; }); + + points_on_hedges[eid].reserve(ids.size()); + for (std::size_t id : ids) + { + points_on_hedges[eid].emplace_back( + construct_point(polyline_locations[points_per_edge[eid][id]], tmesh), + points_per_edge[eid][id]); + + exact_points[points_per_edge[eid][id]] = + construct_point(polyline_locations[points_per_edge[eid][id]], tmesh, + parameters::vertex_point_map(make_cartesian_converter_property_map(tmesh.points()))); + + } + } + + CGAL::Cartesian_converter to_exact; + + // add new vertices per face + for (std::size_t fid=0; fid::null_vertex() ); + polyline_vertices[vid] = vd; + exact_points[vid] = construct_point(polyline_locations[vid], tmesh, + parameters::vertex_point_map(make_cartesian_converter_property_map(tmesh.points()))); + } + } + + // set existing vertex + for (const std::pair& id_and_vd : input_vertices) + { + polyline_vertices[id_and_vd.first]=id_and_vd.second; + put(vid_map, id_and_vd.second, id_and_vd.first); + exact_points[id_and_vd.first]=to_exact(tmesh.point(id_and_vd.second)); + } + + //Now split edges + for (std::size_t eid=0; eid& pt_and_id : points_on_hedges[eid]) + { + CGAL_assertion( polyline_vertices[pt_and_id.second]==boost::graph_traits::null_vertex() ); + href = ::CGAL::Euler::split_edge(href, tmesh); + polyline_vertices[pt_and_id.second]=target(href, tmesh); + // TODO: use VPM + tmesh.point(target(href,tmesh)) = std::move(pt_and_id.first); + put(vid_map, target(href,tmesh), pt_and_id.second); + } + } + + // triangulate faces + using CDT_traits = Projection_traits_3; + using Vb = Triangulation_vertex_base_with_info_2; + using Fb = Constrained_triangulation_face_base_2; + using TDS_2 = Triangulation_data_structure_2; + using CDT = Constrained_Delaunay_triangulation_2; + using CDT_Vertex_handle = typename CDT::Vertex_handle; + + std::size_t nb_verts = polyline_vertices.size(); + polyline_vertices.resize(polyline_vertices.size()+3); // for input triangle vertices + exact_points.resize(exact_points.size()+3); + + std::vector id_to_cdt_vhandles(nb_verts); + + for (std::size_t fid=0; fid tri_verts = + make_array(face_input_vertices[fid][0], face_input_vertices[fid][1], face_input_vertices[fid][2]); + + std::array vhandles; + vhandles[0]=cdt.insert_outside_affine_hull(to_exact(tmesh.point(tri_verts[0]))); + vhandles[1]=cdt.insert_outside_affine_hull(to_exact(tmesh.point(tri_verts[1]))); + vhandles[2] = cdt.tds().insert_dim_up(cdt.infinite_vertex(), false); + vhandles[2]->set_point(to_exact(tmesh.point(tri_verts[2]))); + + // assign ids to input triangle vertices + std::size_t offset=nb_verts; + for (int i=0;i<3;++i) + { + vhandles[i]->info()=tri_verts[i]; + if (get(vid_map, tri_verts[i])>=nb_verts) // and not simply -1 as previous triangulation could have set another dummy value + { + polyline_vertices[offset]=tri_verts[i]; + put(vid_map, tri_verts[i], offset); + exact_points[offset]=vhandles[i]->point(); + ++offset; + } + else + { + id_to_cdt_vhandles[get(vid_map, tri_verts[i])]=vhandles[i]; + } + } + + + face_descriptor fd = faces_to_refine[fid]; + std::array face_sides; + for (int i=0; i<3; ++i) + { + for (halfedge_descriptor h : halfedges_around_target(face_input_vertices[fid][i], tmesh)) + { + if (face(h, tmesh) == fd) + { + face_sides[i]=h; + break; + } + } + } + + // insert points on edges + CDT_Vertex_handle vinf = cdt.infinite_vertex(); + for (int k=0; k<3; ++k) + { + if (next(face_sides[k], tmesh) != face_sides[(k+1)%3]) + { + CDT_Vertex_handle src = vhandles[k], tgt = vhandles[(k+1)%3]; + halfedge_descriptor hcurr=next(face_sides[k], tmesh); + CGAL_assertion(src->info()==source(hcurr, tmesh)); + CGAL_assertion(tgt->info()==target(face_sides[(k+1)%3], tmesh)); + + CDT_Vertex_handle prev = src; + while(hcurr!=face_sides[(k+1)%3]) + { + typename CDT::Face_handle fh; + CGAL_assertion_code(bool ok =) + cdt.is_face(prev, tgt, vinf, fh); + CGAL_assertion(ok); + CGAL_assertion( get(vid_map, target(hcurr,tmesh)) != std::size_t(-1) ); + prev=cdt.insert_in_edge(exact_points[get(vid_map, target(hcurr,tmesh))], fh, fh->index(vinf)); + id_to_cdt_vhandles[get(vid_map, target(hcurr,tmesh))]=prev; + prev->info() = target(hcurr,tmesh); + cdt.restore_Delaunay(prev); // TODO maybe not each time but one global? + CGAL_assertion(cdt.is_valid()); + hcurr=next(hcurr, tmesh); + } + } + } + + // insert points in the face (TODO: we probably don't need spatial sorting) + for (std::size_t vid : points_per_face[fid]) + { + id_to_cdt_vhandles[vid] = cdt.insert(exact_points[vid]); + id_to_cdt_vhandles[vid]->info()=polyline_vertices[vid]; + } + + + + // insert constrained edges + for (const std::pair& pi : edges_per_face[fid]) + { + cdt.insert_constraint(id_to_cdt_vhandles[pi.first],id_to_cdt_vhandles[pi.second]); + } + + // register cdt edge -> halfedge + halfedge_descriptor hd = halfedge(fd, tmesh); + std::map, halfedge_descriptor> edge_to_hedge; + // triangle boundary first + for (halfedge_descriptor h : halfedges_around_face(hd, tmesh)) + { + edge_to_hedge[std::make_pair(get(vid_map,source(h,tmesh)), get(vid_map,target(h,tmesh)))]=h; + } + //grab edges that are not on the convex hull (these have already been created) + for (typename CDT::Finite_edges_iterator it=cdt.finite_edges_begin(); + it!=cdt.finite_edges_end(); ++it) + { + typename CDT::Vertex_handle cdt_v0=it->first->vertex( cdt.ccw(it->second) ); + typename CDT::Vertex_handle cdt_v1=it->first->vertex( cdt.cw(it->second) ); + + // consider edges not on the convex hull (not on the boundary of the face) + // and create the corresponding halfedges + if ( !cdt.is_infinite(it->first->vertex(it->second)) && + !cdt.is_infinite(cdt.mirror_vertex(it->first,it->second)) ) + { + edge_descriptor e=add_edge(tmesh); + halfedge_descriptor h=halfedge(e,tmesh), h_opp=opposite(h,tmesh); + std::size_t i0=get(vid_map,cdt_v0->info()), i1=get(vid_map,cdt_v1->info()); + vertex_descriptor v0=polyline_vertices[i0], v1=polyline_vertices[i1]; + + set_target(h,v0,tmesh); + set_target(h_opp,v1,tmesh); + set_halfedge(v0,h,tmesh); + set_halfedge(v1,h_opp,tmesh); + + edge_to_hedge[std::make_pair(i0,i1)]=h_opp; + edge_to_hedge[std::make_pair(i1,i0)]=h; + + if (it->first->is_constrained(it->second)) + *out++=h; + } + } + + //grab triangles. + face_descriptor current_face = fd; + for (typename CDT::Finite_faces_iterator it=cdt.finite_faces_begin(), + it_end=cdt.finite_faces_end();;) + { + typename CDT::Vertex_handle cdt_v0=it->vertex(0); + typename CDT::Vertex_handle cdt_v1=it->vertex(1); + typename CDT::Vertex_handle cdt_v2=it->vertex(2); + + std::size_t i0=get(vid_map,cdt_v0->info()), i1=get(vid_map,cdt_v1->info()), i2=get(vid_map,cdt_v2->info()); + + CGAL_assertion(edge_to_hedge.count(std::make_pair(i0,i1))!= 0); + CGAL_assertion(edge_to_hedge.count(std::make_pair(i1,i2))!= 0); + CGAL_assertion(edge_to_hedge.count(std::make_pair(i2,i0))!= 0); + + halfedge_descriptor h01=edge_to_hedge[std::make_pair(i0,i1)]; + halfedge_descriptor h12=edge_to_hedge[std::make_pair(i1,i2)]; + halfedge_descriptor h20=edge_to_hedge[std::make_pair(i2,i0)]; + + set_next(h01,h12,tmesh); + set_next(h12,h20,tmesh); + set_next(h20,h01,tmesh); + + //update face halfedge + set_halfedge(current_face,h01,tmesh); + + //update face of halfedges + set_face(h01,current_face,tmesh); + set_face(h12,current_face,tmesh); + set_face(h20,current_face,tmesh); + + if ( ++it!=it_end ) + { + current_face=add_face(tmesh); + } + else + break; + } + } + + return out; +} + + // template // std::vector // tangent_path_direction(const std::vector>& path, From 019ef75fddf31545f6b49c0a9ea2a0609901b085 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 28 Jun 2024 18:24:18 +0200 Subject: [PATCH 075/120] add basic code for extruding/carving --- .../trace_polygon_example.cpp | 110 +++++++++++++++++- .../Bsurf/locally_shortest_path.h | 14 ++- 2 files changed, 121 insertions(+), 3 deletions(-) diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_example.cpp index fc1b5ce8a66c..7fa8c24d77c3 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_example.cpp +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_example.cpp @@ -1,6 +1,10 @@ #include #include #include +#include +#include +#include +#include #include @@ -154,7 +158,12 @@ int main(int argc, char** argv) // now refine the input mesh std::vector cst_hedges; - PMP::refine_mesh_along_paths(polygons_3, mesh, std::back_inserter(cst_hedges)); + auto vnm = mesh.add_property_map("vnm", K::Vector_3(0,0,0)).first; + auto fnm = mesh.add_property_map("fnm", K::Vector_3(0,0,0)).first; + using VNM = decltype(vnm); + + PMP::compute_normals(mesh, vnm, fnm); + PMP::refine_mesh_along_paths(polygons_3, mesh, vnm, fnm, std::back_inserter(cst_hedges)); std::ofstream("mesh_refined.off") << std::setprecision(17) << mesh; @@ -163,5 +172,104 @@ int main(int argc, char** argv) for (Mesh::Halfedge_index h : cst_hedges) cst_edges << "2 " << mesh.point(source(h,mesh)) << " " << mesh.point(target(h,mesh)) << "\n"; + + auto ecm = mesh.add_property_map("ecm", false).first; + for (Mesh::Halfedge_index h : cst_hedges) + ecm[edge(h, mesh)]=true; + + + // face index for doing flood fill and mark inside-out + Mesh::Face_index out_face(2612); + std::vector in_out(num_faces(mesh), -1); + + bool inorout=false; + std::vector queue, next_queue; + queue.push_back(out_face); + + while(!queue.empty()) + { + Mesh::Face_index f = queue.back(); + queue.pop_back(); + if (in_out[f]==-1) + { + in_out[f]=inorout?1:0; + Mesh::Halfedge_index h=halfedge(f, mesh); + for (int i=0; i<3; ++i) + { + Mesh::Face_index nf = face(opposite(h, mesh), mesh); + if (nf!=boost::graph_traits::null_face() && in_out[nf]==-1) + { + if (ecm[edge(h,mesh)]) + next_queue.push_back(nf); + else + queue.push_back(nf); + } + h=next(h, mesh); + } + } + if (queue.empty()) + { + queue.swap(next_queue); + inorout=!inorout; + } + } + + + + struct Visitor + : public PMP::Corefinement::Default_visitor + { + VNM vnm; + Visitor(VNM vnm) : vnm(vnm) {} + + std::vector > hedge_map; + void after_edge_duplicated(Mesh::Halfedge_index h, Mesh::Halfedge_index new_hedge, const Mesh&) + { + hedge_map.emplace_back(h, new_hedge); + } + + void after_vertex_copy(Mesh::Vertex_index v, const Mesh&, Mesh::Vertex_index nv, const Mesh&) + { + put(vnm, nv, get(vnm, v)); + } + }; + Visitor visitor(vnm); + + PMP::internal::split_along_edges(mesh, ecm, mesh.points(), visitor); + + // TODO should actually only handle interior vertices... + double delta = -0.005; + for (const auto& ph : visitor.hedge_map) + { + Mesh::Halfedge_index h1 = ph.first; + Mesh::Halfedge_index h2 = ph.second; + if (is_border(h1, mesh)) h1=opposite(h1, mesh); + if (is_border(h2, mesh)) h2=opposite(h2, mesh); + Mesh::Halfedge_index h = in_out[face(h1, mesh)]==1 ? h1 : h2; + + Mesh::Vertex_index v = target(h, mesh); + K::Vector_3 n = get(vnm, v); + mesh.point(v) = mesh.point(v)+delta*n; + } + + std::vector b1(visitor.hedge_map.size()); + std::vector b2(visitor.hedge_map.size()); + for (std::size_t i=0; i +//TODO VNM should be optional as well as FNM +template // std::vector::vertex_descriptor> OutputIterator refine_mesh_along_paths(const std::vector>>& paths, TriangleMesh& tmesh, + VNM vnm, + FNM fnm, OutputIterator out) { // TODO: nothing is done here to identify identical points @@ -3770,9 +3772,11 @@ refine_mesh_along_paths(const std::vector& pt_and_id : points_on_hedges[eid]) { CGAL_assertion( polyline_vertices[pt_and_id.second]==boost::graph_traits::null_vertex() ); @@ -3803,6 +3810,7 @@ refine_mesh_along_paths(const std::vector Date: Tue, 9 Jul 2024 16:53:01 +0200 Subject: [PATCH 076/120] accomodate API update --- .../trace_polygon_on_svg_curve_example.cpp | 4 ++-- .../trace_regular_polygons_example.cpp | 4 ++-- .../Polygon_mesh_processing/Bsurf/locally_shortest_path.h | 5 +++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_on_svg_curve_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_on_svg_curve_example.cpp index 80673e296dfd..73b5a3092f86 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_on_svg_curve_example.cpp +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_on_svg_curve_example.cpp @@ -196,14 +196,14 @@ int main(int argc, char** argv) std::ofstream out("label_on_curve.polylines.txt"); out << std::setprecision(17); - std::vector> polygons_3; + std::vector> polygons_3; polygons_3 = PMP::trace_geodesic_label_along_curve(supporting_curve, polygons, scaling, 0., true, mesh, solver); for (const auto& polygon : polygons_3) { out << polygon.size(); for (auto p : polygon) - out << " " << p; + out << " " << PMP::construct_point(p, mesh); out << std::endl; } diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_regular_polygons_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_regular_polygons_example.cpp index 27353bba05d8..a4d7d15107d2 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_regular_polygons_example.cpp +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_regular_polygons_example.cpp @@ -59,10 +59,10 @@ int main(int argc, char** argv) for (double len : lenghts) { std::vector lens(n_sides,len); - std::vector polygon = PMP::trace_geodesic_polygon(center,directions,lens,mesh, solver); + std::vector polygon = PMP::trace_geodesic_polygon(center,directions,lens,mesh, solver); out << polygon.size(); for (auto p : polygon) - out << " " << p; + out << " " << PMP::construct_point(p, mesh); out << "\n"; } diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index f2821fec8bce..4255406e112f 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -3310,7 +3310,7 @@ typename K::FT path_length(const std::vector -std::vector> +std::vector>> trace_geodesic_label_along_curve(const std::vector>& supporting_curve, const std::vector>& polygons, const typename K::FT scaling, @@ -3324,6 +3324,7 @@ trace_geodesic_label_along_curve(const std::vector; std::vector polygon_bboxes; polygon_bboxes.reserve(polygons.size()); @@ -3342,7 +3343,7 @@ trace_geodesic_label_along_curve(const std::vector> result(polygons.size()); + std::vector> result(polygons.size()); // Vector_2 initial_dir(1,0);// TODO: input parameter or 2D PCA of the centers? // 1D partition of the letters From e0d66e9c7d2584856b0deccbcde36f55f671cd37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Tue, 9 Jul 2024 19:16:49 +0200 Subject: [PATCH 077/120] API update --- .../trace_polygon_on_svg_curve_example.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_on_svg_curve_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_on_svg_curve_example.cpp index 73b5a3092f86..0067a9611a48 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_on_svg_curve_example.cpp +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_on_svg_curve_example.cpp @@ -71,21 +71,31 @@ get_supporting_curve(std::string svg_filename, directions.reserve(bezier_curves.size()); lengths.reserve(bezier_curves.size()); + CGAL_assertion_code(K::Point_2 prev = bezier_curves[0][0];) for (const std::array& bezier : bezier_curves) { + CGAL_assertion(bezier[0]==prev); + CGAL_assertion_code(prev=bezier[3];) std::vector> polar_coords = PMP::convert_polygon_to_polar_coordinates(bezier, center_2); + int start_id=directions.empty()?0:1; + directions.emplace_back(); lengths.emplace_back(); assert(polar_coords.size()==4); - for (int i=0;i<4; ++i) + for (int i=start_id;i<4; ++i) { lengths.back()[i] = scaling * polar_coords[i].first; directions.back()[i]=K::Vector_2(std::cos(polar_coords[i].second), std::sin(polar_coords[i].second)); } + if (start_id==1) + { + lengths.back()[0] = lengths[lengths.size()-2][3]; + directions.back()[0] = directions[directions.size()-2][3]; + } } // trace bezier curves @@ -180,7 +190,7 @@ int main(int argc, char** argv) std::cout <<"supporting_curve generated!\n"; std::ofstream debug("debug.polylines.txt"); - debug << supporting_curve.size(); + debug << std::setprecision(17) << supporting_curve.size(); for (auto loc : supporting_curve) debug << " " << PMP::construct_point(loc, mesh); debug << "\n"; From fa2143a4034e8535f686fe65b15d4994dc51718c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Tue, 9 Jul 2024 19:17:05 +0200 Subject: [PATCH 078/120] connect de Casteljau points with geodesic paths --- .../Bsurf/locally_shortest_path.h | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index 4255406e112f..a59fe8b220d5 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -630,6 +630,7 @@ struct Locally_shortest_path_imp // if I hit the source vertex v of h_curr, then h_next has v as source, thus we turn ccw around v in path // Similarly, if I hit the target vertex v of h_curr, then h_next has v as target, thus we turn cw around v in path std::size_t curr_index = index+1; + std::vector new_hedges; if (is_target) @@ -2633,6 +2634,8 @@ void init_geodesic_dual_solver(Dual_geodesic_solver& solver, const TriangleM } #endif +// points can be duplicated in the path in case you hit a vertex of the mesh, +// in order to get continuity of edge locations template void locally_shortest_path(Face_location src, Face_location tgt, @@ -2888,7 +2891,7 @@ recursive_de_Casteljau(const TriangleMesh& mesh, std::vector> segments(1,control_points); std::vector> result; - for (auto subdivision = 0; subdivision < num_subdiv; subdivision++) + for (auto subdivision = 0; subdivision < num_subdiv; ++subdivision) { result.clear(); result.reserve(segments.size() * 2); @@ -3397,7 +3400,7 @@ trace_geodesic_label_along_curve(const std::vector loc_k=supporting_curve[k], loc_k1=supporting_curve[k+1]; CGAL_assertion_code(bool OK=) locate_in_common_face(loc_k, loc_k1, tmesh); - // CGAL_assertion(OK); // TODO: check with Claudio how to correctly implement transport along the curve + CGAL_assertion(OK); Face_location polygon_center; polygon_center.first=loc_k.first; @@ -3525,8 +3528,23 @@ trace_bezier_curves(const Face_location ¢er, ); result[i].reserve(bezier.size()); - for(const Face_location& loc : bezier) - result[i].push_back(loc); + result[i].push_back(bezier[0]); + for(std::size_t b=0; b& loc = bezier[b]; + const Face_location& loc1 = bezier[b+1]; + + // connect the two face locations with shortest path is they are in different faces + if (loc.first!=loc1.first) + { + std::vector> edge_locations; + locally_shortest_path(loc, loc1, tmesh, edge_locations, solver); + result[i].reserve(result[i].size()+edge_locations.size()); + for (const Edge_location& e : edge_locations) + result[i].push_back(to_face_location(e, tmesh)); + } + result[i].push_back(loc1); + } } return result; From e59b726c287137b18b2fbc300aadcea047e4ba4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 12 Jul 2024 11:30:11 +0200 Subject: [PATCH 079/120] avoid duplicated points in the output of de Casteljau + remove unneeded check + cosmetics --- .../trace_svg_example.cpp | 2 +- .../Bsurf/locally_shortest_path.h | 79 +++++++++++-------- 2 files changed, 49 insertions(+), 32 deletions(-) diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_svg_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_svg_example.cpp index 2e2c9686f102..334ef596260f 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_svg_example.cpp +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_svg_example.cpp @@ -106,7 +106,7 @@ int main(int argc, char** argv) PMP::init_geodesic_dual_solver(solver, mesh); std::vector< std::vector > res = - PMP::trace_bezier_curves(center, directions, lengths, 6, mesh, solver); + PMP::trace_bezier_curves(center, directions, lengths, 4, mesh, solver); // write result std::ofstream out("svg.polylines.txt"); diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index a59fe8b220d5..3e86310e90c3 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -926,9 +926,9 @@ struct Locally_shortest_path_imp #ifdef CGAL_DEBUG_BSURF std::cout<<" k is "< src, } } + +//TODO: do we want to also have a way to return Bezier segments? +// the output is actually bezier segments subdivided. template std::vector> recursive_de_Casteljau(const TriangleMesh& mesh, @@ -2901,13 +2904,28 @@ recursive_de_Casteljau(const TriangleMesh& mesh, result.push_back(split0); result.push_back(split1); } + + CGAL_assertion( 2*segments.size()==result.size() ); + std::swap(segments, result); } + std::vector> final_result; + final_result.reserve(result.size() * 3 + 1); + final_result.push_back(std::move(result.front()[0])); + for (Bezier_segment& bs : result) + { + for (int i=1;i<4;++i) + final_result.push_back(std::move(bs[i])); + } + +#if 0 // nasty trick to build the vector from a pair of iterators // using the fact that data in array and vector are contiguous return {(Face_location*)segments.data(), (Face_location*)segments.data() + segments.size() * 4}; +#endif + return final_result; } template @@ -3192,21 +3210,21 @@ trace_geodesic_label(const Face_location ¢er, acc+=len; if (acc == targetd) { + // TODO: if you land here and path[k] is on an input vertex, it might be that loc_k==loc_k1 + double theta=0; - if (k!=0) - { - Face_location loc_k=path[k], loc_k1=path[k+1]; - CGAL_assertion_code(bool OK=) - locate_in_common_face(loc_k, loc_k1, tmesh); - CGAL_assertion(OK); - std::array flat_triangle = - Impl::init_flat_triangle(halfedge(loc_k.first,tmesh),vpm,tmesh); - Vector_2 src = loc_k.second[0]*flat_triangle[0]+loc_k.second[1]*flat_triangle[1]+loc_k.second[2]*flat_triangle[2]; - Vector_2 tgt = loc_k1.second[0]*flat_triangle[0]+loc_k1.second[1]*flat_triangle[1]+loc_k1.second[2]*flat_triangle[2]; - - Vector_2 dir2 = tgt-src; - theta = atan2(dir2.y(), dir2.x()); - } + Face_location loc_k=path[k], loc_k1=path[k+1]; + CGAL_assertion_code(bool OK=) + locate_in_common_face(loc_k, loc_k1, tmesh); + CGAL_assertion(OK); + std::array flat_triangle = + Impl::init_flat_triangle(halfedge(loc_k.first,tmesh),vpm,tmesh); + Vector_2 src = loc_k.second[0]*flat_triangle[0]+loc_k.second[1]*flat_triangle[1]+loc_k.second[2]*flat_triangle[2]; + Vector_2 tgt = loc_k1.second[0]*flat_triangle[0]+loc_k1.second[1]*flat_triangle[1]+loc_k1.second[2]*flat_triangle[2]; + + Vector_2 dir2 = tgt-src; + theta = atan2(dir2.y(), dir2.x()); + return std::make_pair(path[k+1], theta); } @@ -3375,21 +3393,21 @@ trace_geodesic_label_along_curve(const std::vector loc_k=supporting_curve[k], loc_k1=supporting_curve[k+1]; - CGAL_assertion_code(bool OK=) - locate_in_common_face(loc_k, loc_k1, tmesh); - CGAL_assertion(OK); - std::array flat_triangle = - Impl::init_flat_triangle(halfedge(loc_k.first,tmesh),vpm,tmesh); - Vector_2 src = loc_k.second[0]*flat_triangle[0]+loc_k.second[1]*flat_triangle[1]+loc_k.second[2]*flat_triangle[2]; - Vector_2 tgt = loc_k1.second[0]*flat_triangle[0]+loc_k1.second[1]*flat_triangle[1]+loc_k1.second[2]*flat_triangle[2]; + Face_location loc_k=supporting_curve[k], loc_k1=supporting_curve[k+1]; + CGAL_assertion_code(bool OK=) + locate_in_common_face(loc_k, loc_k1, tmesh); + CGAL_assertion(OK); + std::array flat_triangle = + Impl::init_flat_triangle(halfedge(loc_k.first,tmesh),vpm,tmesh); + Vector_2 src = loc_k.second[0]*flat_triangle[0]+loc_k.second[1]*flat_triangle[1]+loc_k.second[2]*flat_triangle[2]; + Vector_2 tgt = loc_k1.second[0]*flat_triangle[0]+loc_k1.second[1]*flat_triangle[1]+loc_k1.second[2]*flat_triangle[2]; + + Vector_2 dir2 = tgt-src; + theta = atan2(dir2.y(), dir2.x()); - Vector_2 dir2 = tgt-src; - theta = atan2(dir2.y(), dir2.x()); - } return std::make_pair(supporting_curve[k+1], theta); } @@ -3446,7 +3464,6 @@ trace_geodesic_label_along_curve(const std::vector> polar_coords = convert_polygon_to_polar_coordinates(polygons[i], From 3f8506d4e725ad64dd57bf4e5656d4e6298a75af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 12 Jul 2024 13:54:52 +0200 Subject: [PATCH 080/120] add functions to get vertex from location --- .../CGAL/Polygon_mesh_processing/locate.h | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/locate.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/locate.h index dd30211f0396..e2445ef237e1 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/locate.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/locate.h @@ -662,6 +662,80 @@ get_descriptor_from_location(const Face_location& loc, return fd; } +/// \ingroup PMP_locate_grp +/// +/// \brief Given a location, if it designates a vertex, returns the corresponding vertex descriptor and `std::nullopt` otherwise. +/// +/// \tparam FT must be a model of `FieldNumberType` +/// \tparam TriangleMesh must be a model of `FaceGraph` +/// +/// \param loc a location with `loc.first` a face of `tm` +/// \param tm a triangulated surface mesh +/// +/// \pre `loc.first` is a face descriptor corresponding to a face of `tm`. +/// \pre `loc` describes the barycentric coordinates of a point that lives within the face (boundary included), +/// meaning the barycentric coordinates are all positive. +/// +template +std::optional::vertex_descriptor> +vertex_descriptor_from_location(const Face_location& loc, + const TriangleMesh& tm) +{ + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + typedef typename boost::graph_traits::face_descriptor face_descriptor; + + typedef Barycentric_coordinates Barycentric_coordinates; + + const face_descriptor fd = loc.first; + const Barycentric_coordinates& bar = loc.second; + + CGAL_precondition(is_valid_face_descriptor(fd, tm)); + CGAL_precondition(is_triangle(halfedge(fd, tm), tm)); + CGAL_precondition(is_in_face(loc, tm)); + + // the first barycentric coordinate corresponds to source(halfedge(fd, tm), tm) + halfedge_descriptor hd = prev(halfedge(fd, tm), tm); + + // check if the point is a vertex + for(int i=0; i<3; ++i) + { + if(bar[i] == FT(1)) // coordinate at target(hd, tm) + return target(hd, tm); + hd = next(hd, tm); + } + return std::nullopt; +} + +/// \ingroup PMP_locate_grp +/// +/// \brief Given a location, if it designates a vertex, returns the corresponding vertex descriptor and `std::nullopt` otherwise. +/// +/// \tparam FT must be a model of `FieldNumberType` +/// \tparam TriangleMesh must be a model of `FaceGraph` +/// +/// \param loc a location with `loc.first` a face of `tm` +/// \param tm a triangulated surface mesh +/// +/// \pre `loc.first` is a face descriptor corresponding to a face of `tm`. +/// \pre `loc` describes the barycentric coordinates of a point that lives within the face (boundary included), +/// meaning the barycentric coordinates are all positive. +/// +template +std::optional::vertex_descriptor> +vertex_descriptor_from_location(const Edge_location& loc, + const TriangleMesh& tm) +{ + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + + vertex_descriptor src = source(loc.first, tm), tgt = target(loc.first, tm); + const auto& bar = loc.second; + + if (bar[1]==0) return src; + if (bar[0]==0) return tgt; + + return std::nullopt; +} + /// \ingroup PMP_locate_grp /// From dcf46c5a9675f584358ac34fe71d7f512a80c98e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 12 Jul 2024 13:55:05 +0200 Subject: [PATCH 081/120] add function to get unique points from a path --- .../locally_shortest_path_sm_example.cpp | 15 ++++-- .../straightest_geodesic_sm_example.cpp | 17 +++---- .../trace_polygon_example.cpp | 12 +++-- .../trace_polygon_on_svg_curve_example.cpp | 29 +++++++---- .../trace_regular_polygons_example.cpp | 13 +++-- .../trace_svg_example.cpp | 11 ++-- .../Bsurf/locally_shortest_path.h | 51 +++++++++++++++++++ 7 files changed, 111 insertions(+), 37 deletions(-) diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/locally_shortest_path_sm_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/locally_shortest_path_sm_example.cpp index 7b8c397f076e..3f606f0da257 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/locally_shortest_path_sm_example.cpp +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/locally_shortest_path_sm_example.cpp @@ -67,11 +67,16 @@ int main(int argc, char** argv) std::vector edge_locations; CGAL::Polygon_mesh_processing::locally_shortest_path(src, tgt, mesh, edge_locations); + + std::vector poly; + poly.reserve(edge_locations.size()+2); + PMP::convert_path_to_polyline(src, edge_locations, tgt, mesh, std::back_inserter(poly)); + std::ofstream out("locally_shortest_path.polylines.txt"); - out << edge_locations.size()+2; - out << " " << PMP::construct_point(src, mesh); - for (auto el : edge_locations) - out << " " << PMP::construct_point(el, mesh); - out << " " << PMP::construct_point(tgt, mesh) << "\n"; + out << poly.size(); + for (auto p : poly) + out << " " << p; + out << "\n"; + return 0; } diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/straightest_geodesic_sm_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/straightest_geodesic_sm_example.cpp index 16da75321312..7136bcd4ece6 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/straightest_geodesic_sm_example.cpp +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/straightest_geodesic_sm_example.cpp @@ -47,22 +47,17 @@ int main(int argc, char** argv) K::Vector_2 dir(1,1); -#if 0 - std::vector path; - PMP::walk_and_intersection_point_collection(mesh, src_pt, src.first, - K::Plane_3(src_pt, K::Vector_3(0,1,0)), - K::Plane_3(src_pt, K::Vector_3(1,0,0)), - target_distance, - path); -#else std::vector path = PMP::straightest_geodesic(src, dir, target_distance, mesh); -#endif + + std::vector poly; + poly.reserve(path.size()); + PMP::convert_path_to_polyline(path, mesh, std::back_inserter(poly)); std::ofstream out("straightest_geodesic_path.polylines.txt"); out << path.size() << " "; - for (auto fl : path) - out << " " << PMP::construct_point(fl, mesh); + for (auto p : poly) + out << " " << p; out << "\n"; return 0; diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_example.cpp index 7fa8c24d77c3..5a1feab2a472 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_example.cpp +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_example.cpp @@ -117,10 +117,14 @@ int main(int argc, char** argv) } // last point is duplicated - std::vector out_polygon = PMP::trace_geodesic_polygon(center,directions,lens,mesh, solver); - out << out_polygon.size(); - for (auto p : out_polygon) - out << " " << PMP::construct_point(p, mesh); + std::vector out_polygon_path = PMP::trace_geodesic_polygon(center,directions,lens,mesh, solver); + std::vector poly; + poly.reserve(out_polygon_path.size()); + PMP::convert_path_to_polyline(out_polygon_path, mesh, std::back_inserter(poly)); + + out << poly.size(); + for (auto p : poly) + out << " " << p; out << std::endl; } diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_on_svg_curve_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_on_svg_curve_example.cpp index 0067a9611a48..e777569b22fc 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_on_svg_curve_example.cpp +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_on_svg_curve_example.cpp @@ -189,12 +189,17 @@ int main(int argc, char** argv) if (supporting_curve.empty()) return 1; std::cout <<"supporting_curve generated!\n"; - std::ofstream debug("debug.polylines.txt"); - debug << std::setprecision(17) << supporting_curve.size(); - for (auto loc : supporting_curve) - debug << " " << PMP::construct_point(loc, mesh); - debug << "\n"; - debug.close(); + std::ofstream support_out("svg_support.polylines.txt"); + + std::vector support_poly; + support_poly.reserve(supporting_curve.size()); + PMP::convert_path_to_polyline(supporting_curve, mesh, std::back_inserter(support_poly)); + + support_out << std::setprecision(17) << support_poly.size(); + for (auto p : support_poly) + support_out << " " << p; + support_out << "\n"; + support_out.close(); // convert polygons to polar coordinates typename K::Point_2 center_2((bb2.xmax()+bb2.xmin())/2., (bb2.ymax()+bb2.ymin())/2.); @@ -209,11 +214,15 @@ int main(int argc, char** argv) std::vector> polygons_3; polygons_3 = PMP::trace_geodesic_label_along_curve(supporting_curve, polygons, scaling, 0., true, mesh, solver); - for (const auto& polygon : polygons_3) + for (const auto& polygon_path : polygons_3) { - out << polygon.size(); - for (auto p : polygon) - out << " " << PMP::construct_point(p, mesh); + std::vector poly; + poly.reserve(polygon_path.size()); + PMP::convert_path_to_polyline(polygon_path, mesh, std::back_inserter(poly)); + + out << poly.size(); + for (auto p : poly) + out << " " << p; out << std::endl; } diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_regular_polygons_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_regular_polygons_example.cpp index a4d7d15107d2..bf97c4079e74 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_regular_polygons_example.cpp +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_regular_polygons_example.cpp @@ -59,10 +59,15 @@ int main(int argc, char** argv) for (double len : lenghts) { std::vector lens(n_sides,len); - std::vector polygon = PMP::trace_geodesic_polygon(center,directions,lens,mesh, solver); - out << polygon.size(); - for (auto p : polygon) - out << " " << PMP::construct_point(p, mesh); + std::vector polygon_path = PMP::trace_geodesic_polygon(center,directions,lens,mesh, solver); + + std::vector poly; + poly.reserve(polygon_path.size()); + PMP::convert_path_to_polyline(polygon_path, mesh, std::back_inserter(poly)); + + out << poly.size(); + for (auto p : poly) + out << " " << p; out << "\n"; } diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_svg_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_svg_example.cpp index 334ef596260f..0c2225b0c129 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_svg_example.cpp +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_svg_example.cpp @@ -113,9 +113,14 @@ int main(int argc, char** argv) out << std::setprecision(17); for (const auto& b : res) { - out << b.size(); - for (const Face_location& loc : b) - out << " " << PMP::construct_point(loc,mesh); + std::vector poly; + poly.reserve(b.size()); + PMP::convert_path_to_polyline(b, mesh, std::back_inserter(poly)); + + + out << poly.size(); + for (const K::Point_3& p : poly) + out << " " << p; out << "\n"; } diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index 3e86310e90c3..4f9c9871c366 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -4039,6 +4039,57 @@ refine_mesh_along_paths(const std::vector +OutputIterator +convert_path_to_polyline(const std::vector>& path, + const TriangleMesh& tmesh, + OutputIterator poly_out) +{ + using vertex_descriptor = typename boost::graph_traits::vertex_descriptor; + + vertex_descriptor vd = boost::graph_traits::null_vertex(); + for (const Face_location& loc : path) + { + std::optional ov = vertex_descriptor_from_location(loc, tmesh); + if (ov.has_value()) + { + if (vd==*ov) continue; + vd=*ov; + } + *poly_out++=construct_point(loc, tmesh); + } + return poly_out; +} + +// same as above but for edge locations +template +OutputIterator +convert_path_to_polyline(const Face_location& src, + const std::vector>& path, + const Face_location& tgt, + const TriangleMesh& tmesh, + OutputIterator poly_out) +{ + using vertex_descriptor = typename boost::graph_traits::vertex_descriptor; + + *poly_out++=construct_point(src, tmesh); + vertex_descriptor vd = boost::graph_traits::null_vertex(); + for (const Edge_location& loc : path) + { + std::optional ov = vertex_descriptor_from_location(loc, tmesh); + if (ov.has_value()) + { + if (vd==*ov) continue; + vd=*ov; + } + *poly_out++=construct_point(loc, tmesh); + } + *poly_out++=construct_point(tgt, tmesh); + return poly_out; +} // template // std::vector From 514c87a4e24de21ce31b5223d91d2d4e4aa1ddd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 12 Jul 2024 14:32:33 +0200 Subject: [PATCH 082/120] remove unused files --- .../walk_in_polygon_mesh.h | 556 --------- .../Polygon_mesh_processing/walk_to_select.h | 1105 ----------------- .../include/CGAL/Polyhedron_simplex_type.h | 24 - 3 files changed, 1685 deletions(-) delete mode 100644 Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/walk_in_polygon_mesh.h delete mode 100644 Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/walk_to_select.h delete mode 100644 Polygon_mesh_processing/include/CGAL/Polyhedron_simplex_type.h diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/walk_in_polygon_mesh.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/walk_in_polygon_mesh.h deleted file mode 100644 index e56bd7d15725..000000000000 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/walk_in_polygon_mesh.h +++ /dev/null @@ -1,556 +0,0 @@ -// Copyright (c) 2014 GeometryFactory (France). -// All rights reserved. -// -// This file is part of CGAL (www.cgal.org). -// -// $URL$ -// $Id$ -// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial -// -// Author(s) : Sébastien Loriot - -#ifndef CGAL_WALK_IN_POLYGON_MESH_H -#define CGAL_WALK_IN_POLYGON_MESH_H - -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include - -#ifdef CGAL_DEBUG_WALK_IN_POLYGON_MESH -#include -#define CGAL_WALKER_TRACE(X) std::cout << X; -#else -#define CGAL_WALKER_TRACE(X) -#endif - -namespace CGAL{ - -struct Walk_is_cycling_exception : std::logic_error -{ - Walk_is_cycling_exception() - : std::logic_error("Loop detected during the walk") - {} -}; - -namespace walker_impl{ - - /// \todo I think this was used in the clipping-snapping branch - struct Walk_on_polyhedron_border_crossing_exception : std::logic_error - { - Walk_on_polyhedron_border_crossing_exception() - : std::logic_error("Trying to walking outside of the polyhedron") - {} - }; - - /// get the set of faces incident to a given simplex - template - void get_incident_faces(Polyhedron_simplex_type simplex_type, - typename boost::graph_traits::halfedge_descriptor h, - std::set::face_descriptor>& incident_faces, - const PM& pm) - { - switch(simplex_type) - { - case POLYHEDRON_FACET: - incident_faces.insert(face(h, pm)); - break; - case POLYHEDRON_EDGE: - if (!is_border(h, pm)) incident_faces.insert(face(h, pm)); - if (!is_border(opposite(h, pm), pm)) incident_faces.insert(face(opposite(h, pm), pm)); - break; - case POLYHEDRON_VERTEX: - { - BOOST_FOREACH(typename boost::graph_traits::halfedge_descriptor hd, - halfedges_around_target(h, pm)) - { - if (!is_border(hd, pm)) - incident_faces.insert(face(hd, pm)); - } - } - break; - default: - break; - } - } - - template - typename boost::graph_traits::halfedge_descriptor - canonical_hedge(typename boost::graph_traits::halfedge_descriptor h, - const PM& pm) - { - return h - typename boost::graph_traits::halfedge_descriptor - common_edge( typename boost::graph_traits::face_descriptor f1, - typename boost::graph_traits::face_descriptor f2, - const PM& pm) - { - CGAL_assertion(f1!=boost::graph_traits::null_face()); - CGAL_assertion(f2!=boost::graph_traits::null_face()); - BOOST_FOREACH(typename boost::graph_traits::halfedge_descriptor h, - halfedges_around_face(halfedge(f1, pm), pm)) - { - if ( face(opposite(h, pm), pm)==f2 ) - return h; - } - return boost::graph_traits::null_halfedge(); - } -} // end of internal namespace - -/// \todo document me -/// We walk from (src, source_type) to (tgt, target_type) intersecting -/// all edges encountered. The predicate inside_edge_pred is responsible to indicate the correct -/// direction to move. -/// \param source_type is different from POLYHEDRON_NONE -template< class PolygonMesh, - class EdgeIntersectionPredicate, - class EdgeIntersectionVisitor > -bool walk_in_polygon_mesh(const PolygonMesh& pm, - typename boost::graph_traits::halfedge_descriptor src, - Polyhedron_simplex_type source_type, - typename boost::graph_traits::halfedge_descriptor tgt, - Polyhedron_simplex_type target_type, - const EdgeIntersectionPredicate& inside_edge_pred, - EdgeIntersectionVisitor& visitor) -{ - typedef boost::graph_traits graph_traits; - typedef typename graph_traits::vertex_descriptor vertex_descriptor; - typedef typename graph_traits::halfedge_descriptor halfedge_descriptor; - typedef typename graph_traits::face_descriptor face_descriptor; - - using namespace walker_impl; - - /// info returned by the predicate when an edge of the input triangle mesh - /// is intersected in its interior - typedef typename EdgeIntersectionPredicate::Barycentric_NT Barycentric_NT; - -/// First check that the projected vertices do not fall in a common simplex - halfedge_descriptor common_face = graph_traits::null_halfedge(); - bool new_edge_to_add = true; - switch( source_type ) - { - case POLYHEDRON_FACET: - CGAL_WALKER_TRACE("\n1-POLYHEDRON_FACET\n") - switch(target_type) - { - case POLYHEDRON_FACET: - CGAL_WALKER_TRACE("2-POLYHEDRON_FACET\n") - if (face(src, pm) == face(tgt, pm)) - common_face=src; - break; - case POLYHEDRON_EDGE: - CGAL_WALKER_TRACE("2-POLYHEDRON_EDGE\n") - if ( face(src, pm)==face(tgt, pm) || - face(src, pm)==face(opposite(tgt, pm), pm) ) - common_face=src; - break; - case POLYHEDRON_VERTEX: - { - CGAL_WALKER_TRACE("2-POLYHEDRON_VERTEX\n") - BOOST_FOREACH(halfedge_descriptor hd, halfedges_around_target(tgt, pm)) - if (face(hd, pm)==face(src, pm)) - { - common_face=src; - break; - } - } - break; - case POLYHEDRON_NONE: - CGAL_WALKER_TRACE("2-POLYHEDRON_NONE\n") - break; - } - break; - case POLYHEDRON_EDGE: - CGAL_WALKER_TRACE("\n1-POLYHEDRON_EDGE\n") - switch(target_type) - { - case POLYHEDRON_FACET: - CGAL_WALKER_TRACE("2-POLYHEDRON_FACET\n") - if ( face(tgt, pm)==face(src, pm) || - face(tgt, pm)==face(opposite(src, pm), pm) ) - common_face=tgt; - break; - case POLYHEDRON_EDGE: - CGAL_WALKER_TRACE("2-POLYHEDRON_EDGE\n") - if (canonical_hedge(src, pm)==canonical_hedge(tgt, pm)) - { - /// we explicitly do not update common_face - /// so that the visitor knows src and tgt are completly - /// inside an edge - new_edge_to_add=false; - } - else - { - if ( !is_border(src, pm) && - ( face(src, pm) == face(tgt, pm) || - face(src, pm) == face(opposite(tgt, pm), pm) ) ) - common_face=src; - else{ - if (!is_border(opposite(src, pm), pm)){ - if ( face(tgt, pm) == face(opposite(src, pm), pm) ) - common_face=tgt; - else - if ( face(opposite(tgt, pm), pm) == face(opposite(src, pm), pm) ) - common_face=opposite(tgt, pm); - } - } - } - break; - case POLYHEDRON_VERTEX: - CGAL_WALKER_TRACE("2-POLYHEDRON_VERTEX\n") - if ( target(src, pm)==target(tgt, pm) || - source(src, pm)==target(tgt, pm) ) - { - common_face = target(src, pm)==target(tgt, pm)?src:opposite(src, pm); - new_edge_to_add=false; - } - else - { - if (!is_border(src, pm) && - target(next(src, pm), pm) == target(tgt, pm)) - common_face=src; - else - if (!is_border(opposite(src, pm), pm) && - target(next(opposite(src, pm), pm), pm) == target(tgt, pm)) - common_face=opposite(src, pm); - } - break; - case POLYHEDRON_NONE: - CGAL_WALKER_TRACE("2-POLYHEDRON_NONE\n") - break; - } - break; - case POLYHEDRON_VERTEX: - CGAL_WALKER_TRACE("\n1-POLYHEDRON_VERTEX\n") - switch(target_type) - { - case POLYHEDRON_FACET: - { - CGAL_WALKER_TRACE("2-POLYHEDRON_FACET\n") - BOOST_FOREACH(halfedge_descriptor hd, halfedges_around_target(src, pm)) - if (face(hd, pm)==face(tgt, pm)) - { - common_face=tgt; - break; - } - } - break; - case POLYHEDRON_EDGE: - CGAL_WALKER_TRACE("2-POLYHEDRON_EDGE\n") - if ( target(tgt, pm)==target(src, pm) || - source(tgt, pm)==target(src, pm) ) - { - common_face=target(tgt, pm)==target(src, pm)? - opposite(tgt, pm):tgt; - new_edge_to_add=false; - } - else - { - if (!is_border(tgt, pm) && - target(next(tgt, pm), pm) == target(src, pm)) - common_face=tgt; - else - if (!is_border(opposite(tgt, pm), pm) && - target(next(opposite(tgt, pm), pm), pm) == target(src, pm)) - common_face=opposite(tgt, pm); - } - break; - case POLYHEDRON_VERTEX: - { - /// @TODO Handle the case of when src and tgt are the same vertices - // in case of identical vertices, it might indicate that we want to walk along a cycle. - // If not, the predicate should return false for all candidates - if (target(src, pm) == target(tgt, pm) ) break; - - CGAL_WALKER_TRACE("2-POLYHEDRON_VERTEX\n") - BOOST_FOREACH(halfedge_descriptor hd, halfedges_around_target(src, pm)) - if (source(hd, pm)==target(tgt, pm)) - { - common_face=opposite(hd, pm); - new_edge_to_add=false; - break; - } - } - break; - case POLYHEDRON_NONE: - CGAL_WALKER_TRACE("2-POLYHEDRON_NONE\n") - break; - } - break; - case POLYHEDRON_NONE: - CGAL_assertion("Should never be here\n"); - } - - if (!new_edge_to_add) - { - // In this case, the edge to add is already on an edge of the input mesh - visitor.on_walk_end_on_edge(common_face); - return true; - } - if ( common_face != graph_traits::null_halfedge() ) - { - CGAL_assertion( !is_border(common_face, pm) ); - visitor.on_walk_end(face(common_face, pm)); - return true; - } - - -/// We now do the walk in the triangulation and find edge-edge intersection points - std::set target_faces; /// \todo try using a boost::flat_set instead - get_incident_faces(target_type, tgt, target_faces, pm); - std::set faces_already_visited; -// halfedge_descriptor first_halfedge_intersected; - - while( true ) - { - std::vector hedges_to_test; - switch( source_type ) - { - case POLYHEDRON_FACET: - CGAL_WALKER_TRACE(" i-POLYHEDRON_FACET\n") - hedges_to_test.push_back( src ); - hedges_to_test.push_back( next(src, pm) ); - hedges_to_test.push_back( prev(src, pm) ); - break; - case POLYHEDRON_EDGE: - CGAL_WALKER_TRACE(" i-POLYHEDRON_EDGE\n") - // if the v_src is on an edge, we don't know in which direction to go - // This is only true the first time of the loop - if ( faces_already_visited.empty() && - !is_border(opposite(src, pm), pm) ) - { - hedges_to_test.push_back( next(opposite(src, pm), pm) ); - hedges_to_test.push_back( prev(opposite(src, pm), pm) ); - } - /// @TODO what if src is a border edge? - hedges_to_test.push_back( next(src, pm) ); - hedges_to_test.push_back( prev(src, pm) ); - break; - case POLYHEDRON_VERTEX: - { - CGAL_WALKER_TRACE(" i-POLYHEDRON_VERTEX\n") - BOOST_FOREACH(halfedge_descriptor hd, halfedges_around_target(src, pm)) - { - if ( faces_already_visited.count(face(hd, pm))==0 //we don't want to go back, so we filter faces - && !is_border(hd, pm)){ - hedges_to_test.push_back( prev(hd, pm) ); - } - } - } - break; - case POLYHEDRON_NONE: - CGAL_assertion("Should never be here\n"); - } - - halfedge_descriptor intersected_edge = graph_traits::null_halfedge(); - boost::optional< std::pair > > inter_res; - - CGAL_WALKER_TRACE(" Looking for intersections hedges_to_test.size()="<< hedges_to_test.size() << "\n") - std::size_t k=0; - for (;kfirst); - CGAL_WALKER_TRACE(" intersection found\n") -// if (first_halfedge_intersected==graph_traits::null_halfedge()) -// first_halfedge_intersected=canonical_hedge(intersected_edge, pm); -// else -// if (first_halfedge_intersected==canonical_hedge(intersected_edge, pm)) -// { -// CGAL_WALKER_TRACE(" ERROR: loop detected. Abording.\n"); -// throw Walk_is_cycling_exception(); -// } -#if 0 - if ( is_border_edge(intersected_edge, pm) ) - throw Walk_on_polyhedron_border_crossing_exception(); - // \todo use the boolean in the API about incomplete overlay -#endif - break; - } - } - - if ( k==hedges_to_test.size() ) - { - // this handles the case when the src is on the border - // and the walk directly goes out - if (target_type==POLYHEDRON_NONE) break; - // this handle the case when the src is on the border - // and the tgt is inside or on the boundary of the supporting mesh - // but the path is going out - CGAL_WALKER_TRACE("WARNING: Halting but tgt not reached!\n") - return false; - - - CGAL_WALKER_TRACE("ERROR DID NOT FIND THE INTERSECTION!!!!!!!!!!!!!!!\n") - CGAL_assertion(!"NO INTERSECTION FOUND"); - break; - } - - std::set current_incident_facets; - get_incident_faces(source_type, src, current_incident_facets, pm); - faces_already_visited.clear(); - - const Barycentric_NT* barycentric_coord = - boost::get( &(inter_res->second) ); - if (!barycentric_coord) - { - // we reached a vertex - halfedge_descriptor hedge=canonical_hedge(intersected_edge, pm); - bool is_target = boost::get( inter_res->second ); - if (!is_target) hedge=opposite(hedge, pm); - vertex_descriptor vh=target(hedge, pm); - - Polyhedron_simplex_type prev_type=source_type; - halfedge_descriptor prev_source=src; - source_type=POLYHEDRON_VERTEX; - src=hedge; - - std::vector common_faces; - - bool did_break=false; - - BOOST_FOREACH(halfedge_descriptor hd, halfedges_around_target(hedge, pm)) - { - if ( !is_border(hd, pm) ){ //note that target_faces and current_incident_facets do not contain any NULL face - if ( !did_break && target_faces.count(face(hd, pm))!=0 ) - did_break=true; - if ( current_incident_facets.count( face(hd, pm) )!=0 ) - { - common_faces.push_back(hd); - faces_already_visited.insert(face(hd, pm)); - } - } - } - - //insert current edge - if (common_faces.size()==1) - { - // vertex-vertex case (on the mesh border) - if (prev_type==POLYHEDRON_VERTEX){ - CGAL_assertion(target(common_faces[0], pm)==vh); - visitor.on_input_edge( - source(common_faces[0], pm)==target(prev_source, pm) - ? common_faces[0] - : opposite(next(common_faces[0], pm), pm)); - } - else{ - // edge->vertex case (on the mesh border) - if (prev_type==POLYHEDRON_EDGE && (target(prev_source, pm)==vh || source(prev_source, pm)==vh)) - visitor.on_input_edge(target(prev_source, pm)==vh - ? prev_source:opposite(prev_source, pm)); - else - // we reached a vertex opposite to the edge or in a face - visitor.found_vertex_intersection(common_faces[0]); - } - } - else - { - CGAL_assertion(common_faces.size()==2); - //this is a vertex-vertex case or a edge-vertex - halfedge_descriptor edge = - common_edge(face(common_faces[0], pm), face(common_faces[1], pm), pm); - visitor.on_input_edge(target(edge, pm)==vh?edge:opposite(edge, pm)); - } - - if ( did_break ) - { - CGAL_assertion((hedge==canonical_hedge(intersected_edge, pm)) == is_target); - common_faces.clear(); - - BOOST_FOREACH(halfedge_descriptor hd, halfedges_around_target(hedge, pm)) - { - if ( !is_border(hd, pm) && target_faces.count( face(hd, pm) )!=0 ) - common_faces.push_back(hd); - } - - //insert the last edge - if (common_faces.size()==1){ - // the last visited edge is a complete border edge - if (target_type==POLYHEDRON_VERTEX){ - CGAL_assertion(target(common_faces[0], pm)==vh); - visitor.on_walk_end_on_edge( - source(common_faces[0], pm)==target(tgt, pm) - ? opposite(common_faces[0], pm) - : next(common_faces[0], pm) ); - } - else - { - // the walk ends on a border edge - if(target_type==POLYHEDRON_EDGE && - (target(tgt, pm)==vh || source(tgt, pm)==vh) ) - { - visitor.on_walk_end_on_edge(target(tgt, pm)==vh - ? opposite(tgt, pm):tgt); - } - else - visitor.on_walk_end(face(common_faces[0], pm)); - } - } - else - { - CGAL_assertion(common_faces.size()==2); - //this is a vertex-vertex case or a edge-vertex - halfedge_descriptor edge = - common_edge(face(common_faces[0], pm), face(common_faces[1], pm), pm); - visitor.on_walk_end_on_edge(target(edge, pm)==vh?opposite(edge, pm):edge); - } - break; - } - } - else{ - // we crossed the interior of an edge - faces_already_visited.insert(face(src, pm)); - source_type=POLYHEDRON_EDGE; - src=opposite(intersected_edge, pm); - // barycentric_coord must have been computed using `canonical_hedge(intersected_edge, pm)` - visitor.found_edge_intersection( intersected_edge, *barycentric_coord ); - - if ( target_faces.count( face(src, pm) )!=0 ) - { - CGAL_assertion ( target_type != POLYHEDRON_VERTEX || - ( target(src, pm) != target(tgt, pm) && - source(src, pm) != target(tgt, pm)) ); - //insert the last edge - if ( !is_border(opposite(intersected_edge, pm), pm) ) - visitor.on_walk_end(face(opposite(intersected_edge, pm), pm)); - break; - } - } - if ( is_border_edge(intersected_edge, pm) ){ - if (target_type!=POLYHEDRON_NONE){ - CGAL_WALKER_TRACE("WARNING: Halting but tgt not reached!\n") - return false; - } - break; // the walk reached the boundary of the domain - } - - if(visitor.do_break()) - break; - - } - return true; -} - -} - -#undef CGAL_WALKER_TRACE - -#endif // CGAL_WALK_IN_POLYGON_MESH_H diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/walk_to_select.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/walk_to_select.h deleted file mode 100644 index 1ea703d948e3..000000000000 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/walk_to_select.h +++ /dev/null @@ -1,1105 +0,0 @@ -// Copyright (c) 2014 GeometryFactory (France). -// All rights reserved. -// -// This file is part of CGAL (www.cgal.org). -// -// $URL$ -// $Id$ -// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial -// -// Author(s) : Maxime Gimero and Sébastien Loriot - -#ifndef CGAL_POLYGON_MESH_PROCESSING_WALK_TO_SELECT_H -#define CGAL_POLYGON_MESH_PROCESSING_WALK_TO_SELECT_H - -// #include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include - -//todo: if no point is between the planes, nothing is selected. -namespace CGAL { - -namespace Polygon_mesh_processing { - -namespace walker_internal{ -template -class Edge_intersection_with_plane -{ - typedef typename Kernel_traits< - typename boost::property_traits::value_type - >::Kernel K; - - typedef typename K::Point_3 Point_3; - typedef typename K::Vector_3 Vector_3; - typedef typename K::FT FT; - - typedef boost::variant Variant; - typedef boost::optional< std::pair > result_type; - - Point_3 m_point; - Vector_3 m_normal; - const PolygonMesh* m_pm_ptr; - VertexPointMap m_vpm; - Vector_3 m_other_normal; - mutable FT res_scalar; - mutable int step; - - typedef typename boost::graph_traits::halfedge_descriptor - halfedge_descriptor; - -public: - - typedef FT Barycentric_NT; - - Edge_intersection_with_plane(const Point_3& pt, - const Vector_3& n, - const Vector_3& other_n, - const PolygonMesh& pm, - const VertexPointMap& vpm) - : m_point(pt) - , m_normal(n) - , m_pm_ptr(&pm) - , m_vpm(vpm) - , m_other_normal(other_n) - , res_scalar(1) - , step(0) - {} - - result_type operator()(halfedge_descriptor h) const - { - typename K::Oriented_side_3 oriented_side; - Point_3 b0 = get(m_vpm, source(h, *m_pm_ptr) ); - Point_3 b1 = get(m_vpm, target(h, *m_pm_ptr) ); - - Oriented_side s0 = oriented_side(m_point, m_normal, b0); - Oriented_side s1 = oriented_side(m_point, m_normal, b1); - - //the path is jagged, so we take the biggest projection of 2 steps to - //get the walking direction - if(step == 0){ - res_scalar = scalar_prod(h); - } - if(step == 1){ - FT new_dir= scalar_prod(h); - if(std::abs(new_dir) > std::abs(res_scalar)) - res_scalar = new_dir; - - } - if (s0 == ON_ORIENTED_BOUNDARY){return std::make_pair(0.5, Variant(false));} - if (s1 == ON_ORIENTED_BOUNDARY){return std::make_pair(0.5, Variant(true)); } - if(s0==s1) return boost::none; - FT num = ( (m_point - b1) * m_normal ); - FT denum = ((b0-b1) * m_normal); - - step++; - // the edge is almost in the plane. This is an arbitrary choice - if (denum==0) - return std::make_pair(0.5, Variant( 0.5 )); - - FT alpha = num / denum; - - if (alpha<=0) std::make_pair(0.5, Variant(false)); - if (alpha>=1) std::make_pair(0.5, Variant(true)); - return std::make_pair(0.5, Variant( alpha )); - } - - FT scalar_prod(const halfedge_descriptor& input) const - { - Point_3 b0 = get(m_vpm, source(input, *m_pm_ptr) ); - Point_3 b1 = get(m_vpm, target(input, *m_pm_ptr) ); - Vector_3 dir(b0, b1); - return dir*m_other_normal; - } - - bool wrong_direction() const - { - return res_scalar <= 0; - } - - void reset() - { - res_scalar = 1; - step = 0; - } - - bool do_break() const - { - return step > 1 && wrong_direction(); - } -}; - -template -class Intersection_info -{ - // typedefs - typedef typename Kernel::FT FT; - typedef typename Kernel::Point_3 Point_3; - typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; - - // data members - bool m_is_vertex; - halfedge_descriptor m_hedge; - Point_3 m_point; - - // internal function - template - Point_3 - compute_point(const FT& alpha, const PolygonMesh& pm, VertexPointMap vpm) - { - CGAL_assertion(!m_is_vertex); - CGAL_assertion(alpha<1 && alpha >0); - // get canonical_edge - halfedge_descriptor hedge = m_hedge < opposite(m_hedge, pm) ? - m_hedge : opposite(m_hedge, pm); - - const Point_3 b0 = get(vpm, source(hedge, pm)); - const Point_3 b1 = get(vpm, target(hedge, pm)); - - return b1+alpha*(b0-b1); - } - -public: - - Intersection_info() - : m_is_vertex(true) - {} - - template - Intersection_info( halfedge_descriptor h, - const PolygonMesh& pm, - VertexPointMap vpm) - : m_is_vertex(true) - , m_hedge(h) - , m_point(get(vpm, target(h, pm))) - {} - - template - Intersection_info( halfedge_descriptor h, - const FT& alpha, - const PolygonMesh& pm, - VertexPointMap vpm) - : m_is_vertex(false) - , m_hedge(h) - , m_point(compute_point(alpha, pm, vpm)) - {} - - - Intersection_info( halfedge_descriptor h, - const Point_3& p) - : m_is_vertex(false) - , m_hedge(h) - , m_point(p) - {} - - const Point_3& point() const - { - return m_point; - } - - bool is_vertex() const - { - return m_is_vertex; - } - - halfedge_descriptor halfedge() const - { - return m_hedge; - } - -}; - -template -class Walk_in_polygon_mesh_visitor { - typedef boost::graph_traits BGT; - typedef typename BGT::vertex_descriptor vertex_descriptor; - typedef typename BGT::halfedge_descriptor halfedge_descriptor; - typedef typename BGT::face_descriptor face_descriptor; - - typedef typename boost::property_traits::value_type Point_3; - typedef typename Kernel_traits::Kernel K; - typedef typename K::FT FT; - - typedef Intersection_info Inter_info; - // to collect the target vertices of the polyline during the walk (but the last one) - std::vector target_vertices; - // the set of halfedges showing the walk - std::vector< halfedge_descriptor > m_walked_halfedges; - // the set of faces modified - std::vector< face_descriptor > m_split_faces; - // the set of faces created - std::vector< face_descriptor > m_new_faces; - // the face where the walk ended (if not on an edge) - face_descriptor m_last_face; - // The triangle mesh to be refined - const TriangleMesh* m_tm_ptr; - //The point property map - VertexPointMap m_vpm; - //the distance to reach whiPoint_set5le walking - FT m_dist; - //the distance currently reached - FT cur_dist; - //the last value of cur_dist (used to find the end_point) - FT last_dist; - //the initial center - Point_3 text_center; - -public: - - Walk_in_polygon_mesh_visitor(const TriangleMesh& tm, - VertexPointMap vpm, - FT dist, - const Point_3& center) - : m_tm_ptr(&tm) - , m_vpm(vpm) - , m_dist(dist) - , cur_dist(0) - , last_dist(0) - , text_center(center) - {} - - // intersected_edge points in the face into which the edge should be added - void found_edge_intersection(halfedge_descriptor intersected_edge, - const FT& barycentric_coord ) - { - m_walked_halfedges.push_back( intersected_edge ); - target_vertices.push_back( Inter_info(intersected_edge, barycentric_coord, *m_tm_ptr, m_vpm) ); - Point_3 new_p = target_vertices.back().point(); - - //update distance - if(target_vertices.size() == 1) - { - cur_dist += CGAL::sqrt(CGAL::squared_distance(text_center,new_p)); - } - else { - Point_3 last_point = target_vertices[target_vertices.size() -2].point(); - last_dist = cur_dist; - cur_dist += CGAL::sqrt(CGAL::squared_distance(last_point,new_p)); - } - } - - void found_vertex_intersection(halfedge_descriptor h) - { - m_walked_halfedges.push_back( h ); - target_vertices.push_back( Inter_info(h, *m_tm_ptr, m_vpm) ); - Point_3 new_p = target_vertices.back().point(); - //update distance - if(target_vertices.size() == 1) - { - cur_dist += CGAL::sqrt(CGAL::squared_distance(text_center,new_p)); - } - else { - Point_3 last_point = target_vertices[target_vertices.size() -2].point(); - last_dist = cur_dist; - cur_dist += CGAL::sqrt(CGAL::squared_distance(last_point,new_p)); - } - } - - void register_initial_intersection(const Inter_info& i) - { - target_vertices.push_back(i); - Point_3 new_p = target_vertices.back().point(); - //update distance - CGAL_assertion(target_vertices.size() == 1); - cur_dist = CGAL::sqrt(CGAL::squared_distance(text_center,new_p)); - } - - void on_walk_end(face_descriptor f) - { - m_last_face = f; - } - - template - void set_beta(T) - {} - - //called when the walk is over but the last segment is on an existing edge - void on_walk_end_on_edge(halfedge_descriptor) {} - - /// indicates that hp1 is on m_hp2, with the same orientation. - /// if hp1 is not initialized, it indicates that m_source and m_target - /// or both on the edge and a geometric predicates need to be used - /// to get a correct hp1 - void on_input_edge(halfedge_descriptor h) - { - m_walked_halfedges.push_back(h); - target_vertices.push_back(Inter_info()); - } - - const std::vector& - walked_halfedges() const - { - return m_walked_halfedges; - } - - bool do_break() - { -// if (cur_dist >= m_dist) -// std::cout << "stop at " << cur_dist << " (vs. " << m_dist << ")\n"; - return cur_dist >= m_dist; - } - - Point_3 end_point() - { - FT alpha = 0; - if(is_wrapping()) - { - alpha = 1; - } - else - { - FT wanted_dist = m_dist - last_dist; - FT gotten_dist = cur_dist - last_dist; - alpha = wanted_dist / gotten_dist; - } - - CGAL_assertion(alpha<=1 && alpha >=0); - Point_3 b0; - if(target_vertices.size() <2) - b0 = text_center; - else - b0 = target_vertices[target_vertices.size() - 2].point(); - Point_3 b1; - if(target_vertices.size() == 0) - b1 = text_center; - else - b1= target_vertices.back().point(); - return b0+alpha*(b1-b0); - } - - bool is_wrapping() - { - return (cur_dist > 0 && (m_dist > cur_dist)); - } - - template - OutputIterator get_intersection_points(OutputIterator out) const - { - for (const Inter_info& i : target_vertices) - *out++=i.point(); - return out; - } -}; - -} // end of Remove_caps_impl - - -// 0: negative side of axis_2 and 1: positive side of axis_2 -template -boost::optional< std::array::halfedge_descriptor, 2> > -get_sorted_intersected_halfedges(typename boost::graph_traits::face_descriptor start_face, - const TriangleMesh& tm, - const Vpm& vpm, - const typename K::Plane_3& axis_1, - const typename K::Plane_3& axis_2) -{ - typedef typename boost::graph_traits BGT; - typedef typename BGT::halfedge_descriptor halfedge_descriptor; - - halfedge_descriptor h = halfedge(start_face, tm); - std::array triangle_points = { - get(vpm, source(h, tm)), - get(vpm, target(h, tm)), - get(vpm, target(next(h, tm), tm)) - }; - std::array oriented_sides; - for (int i=0; i<3; ++i) - { - oriented_sides[i] = axis_1.oriented_side( triangle_points[i] ); - // TODO: handle when the plane goes through a vertex/edge - if (oriented_sides[i] == ON_ORIENTED_BOUNDARY) return boost::none; - } - - std::array int_edges; - int k=-1; - if (oriented_sides[0]!=oriented_sides[1]) int_edges[++k]=h; - if (oriented_sides[1]!=oriented_sides[2]) int_edges[++k]=next(h, tm); - if (oriented_sides[2]!=oriented_sides[0]) int_edges[++k]=prev(h, tm); - - if (k!=1) return boost::none; - - // TODO: avoid the constructions - std::optional > - inter_res = intersection( typename K::Segment_3(get(vpm, source(int_edges[0], tm)), - get(vpm, target(int_edges[0], tm))), - axis_1 ); - CGAL_assertion( inter_res != std::nullopt ); - const typename K::Point_3* pt = std::get_if(&(*inter_res)); - CGAL_assertion( pt!=NULL ); - - if (axis_2.oriented_side( *pt )!= ON_NEGATIVE_SIDE) - std::swap(int_edges[0], int_edges[1]); - return int_edges; -} - -template -std::size_t -two_side_walk_and_intersection_point_collection(const TriangleMesh& tm, - const typename K::Point_3& text_center, - typename boost::graph_traits::face_descriptor start_face, - const typename K::Plane_3& axis_1, - const typename K::Plane_3& axis_2, - double width, - std::vector& inter_points, - const NamedParameters& np) -{ - typedef boost::graph_traits BGT; - typedef typename BGT::halfedge_descriptor halfedge_descriptor; - using parameters::choose_parameter; - using parameters::get_parameter; - - // Vertex point maps - typedef typename GetVertexPointMap::const_type Vpm; - Vpm vpm = choose_parameter(get_parameter(np, internal_np::vertex_point), - get_const_property_map(boost::vertex_point, tm)); - - typedef walker_internal::Edge_intersection_with_plane Predicate; - typedef walker_internal::Walk_in_polygon_mesh_visitor Visitor; - - boost::optional< std::array > opt_int_edges = - get_sorted_intersected_halfedges(start_face, tm, vpm, axis_1, axis_2); - - if (opt_int_edges == boost::none) - return 0; - - // 1) walk width/2 to the left - // first compute the out point from the center triangle (TODO: make more robust) - boost::optional > - inter_res = intersection( typename K::Segment_3(get(vpm, source((*opt_int_edges)[0], tm)), - get(vpm, target((*opt_int_edges)[0], tm))), - axis_1 ); - if ( inter_res == boost::none ) - { - CGAL_warning(!"Intersection 1 not found"); - return 0; - } - const typename K::Point_3* pt = boost::get(&(*inter_res)); - if ( pt == nullptr ) - { - CGAL_warning(!"Intersection 1 is not a point"); - return 0; - } - - // walk only if needed - if ( CGAL::compare_squared_distance(text_center, *pt, width*width / 4.0) == SMALLER ) - { - Predicate predicate(text_center, axis_1.orthogonal_vector(), - axis_2.orthogonal_vector(), tm, vpm); - Visitor visitor = Visitor(tm, vpm, width/2.0, text_center); - visitor.register_initial_intersection({(*opt_int_edges)[0], *pt}); - walk_in_polygon_mesh(tm, - (*opt_int_edges)[0], CGAL::POLYHEDRON_EDGE, - BGT::null_halfedge(), CGAL::POLYHEDRON_NONE, - predicate, visitor); - visitor.get_intersection_points(std::back_inserter(inter_points)); - inter_points.back() = visitor.end_point(); // replace the last point by the real endpoint of the walk - // reverse sequence - std::reverse(inter_points.begin(), inter_points.end()); - } - else - inter_points.push_back(*pt); - - // add the center + save its position - std::size_t center_pos = inter_points.size(); - if (inter_points.back()!=text_center) - inter_points.push_back(text_center); - else - --center_pos; - - //2)walk width/2 to the right - // first compute the out point from the center triangle, other direction (TODO: make more robust) - inter_res = intersection( typename K::Segment_3(get(vpm, source((*opt_int_edges)[1], tm)), - get(vpm, target((*opt_int_edges)[1], tm))), - axis_1 ); - if ( inter_res == boost::none ) - { - CGAL_warning(!"Intersection 2 not found"); - return 0; - } - pt = boost::get(&(*inter_res)); - if ( pt == nullptr ) - { - CGAL_warning(!"Intersection 2 is not a point"); - return 0; - } - - // walk only if needed - if ( CGAL::compare_squared_distance(text_center, *pt, width*width / 4.0) == SMALLER ) - { - Predicate predicate(text_center, axis_1.orthogonal_vector(), - -axis_2.orthogonal_vector(), tm, vpm); - Visitor visitor(tm, vpm, width/2.0, text_center); - visitor.register_initial_intersection({(*opt_int_edges)[1], *pt}); - - walk_in_polygon_mesh(tm, - (*opt_int_edges)[1], CGAL::POLYHEDRON_EDGE, - BGT::null_halfedge(), CGAL::POLYHEDRON_NONE, - predicate, visitor); - visitor.get_intersection_points(std::back_inserter(inter_points)); - inter_points.back() = visitor.end_point(); // replace the last point by the real endpoint of the walk - } - else - inter_points.push_back(*pt); - - return center_pos; -} - -template -std::size_t -walk_and_intersection_point_collection(const TriangleMesh& tm, - const typename K::Point_3& center, - typename boost::graph_traits::face_descriptor start_face, - const typename K::Plane_3& axis_1, - const typename K::Plane_3& axis_2, - double target_distance, - std::vector& inter_points, - const NamedParameters& np = parameters::default_values()) -{ - typedef boost::graph_traits BGT; - typedef typename BGT::halfedge_descriptor halfedge_descriptor; - using parameters::choose_parameter; - using parameters::get_parameter; - - // Vertex point maps - typedef typename GetVertexPointMap::const_type Vpm; - Vpm vpm = choose_parameter(get_parameter(np, internal_np::vertex_point), - get_const_property_map(boost::vertex_point, tm)); - - typedef walker_internal::Edge_intersection_with_plane Predicate; - typedef walker_internal::Walk_in_polygon_mesh_visitor Visitor; - - boost::optional< std::array > opt_int_edges = - get_sorted_intersected_halfedges(start_face, tm, vpm, axis_1, axis_2); - - if (opt_int_edges == boost::none) - { - std::cout << "no intersected edge, nothing is done\n"; - return 0; - } - - // first compute the out point from the center triangle (TODO: make more robust) - std::optional > - inter_res = intersection( typename K::Segment_3(get(vpm, source((*opt_int_edges)[0], tm)), - get(vpm, target((*opt_int_edges)[0], tm))), - axis_1 ); - if ( inter_res == std::nullopt) - { - CGAL_warning(!"Intersection 1 not found"); - return 0; - } - const typename K::Point_3* pt = std::get_if(&(*inter_res)); - if ( pt == nullptr ) - { - CGAL_warning(!"Intersection 1 is not a point"); - return 0; - } - - // walk only if needed - if ( CGAL::compare_squared_distance(center, *pt, target_distance*target_distance) == SMALLER ) - { - Predicate predicate(center, axis_1.orthogonal_vector(), - axis_2.orthogonal_vector(), tm, vpm); - Visitor visitor = Visitor(tm, vpm, target_distance, center); - visitor.register_initial_intersection({(*opt_int_edges)[0], *pt}); - walk_in_polygon_mesh(tm, - (*opt_int_edges)[0], CGAL::POLYHEDRON_EDGE, - BGT::null_halfedge(), CGAL::POLYHEDRON_NONE, - predicate, visitor); - visitor.get_intersection_points(std::back_inserter(inter_points)); - inter_points.back() = visitor.end_point(); // replace the last point by the real endpoint of the walk - // reverse sequence - std::reverse(inter_points.begin(), inter_points.end()); - } - else - inter_points.push_back(*pt); - - // add the center + save its position - std::size_t center_pos = inter_points.size(); - if (inter_points.back()!=center) - inter_points.push_back(center); - else - --center_pos; - - return center_pos; -} - -template -struct Weight_map{ - const TriangleMesh& tm; - const ECM& ecm; - typedef double value_type; - typedef double reference; - typedef boost::readable_property_map_tag category; - typedef typename boost::graph_traits::edge_descriptor key_type; - - Weight_map(const TriangleMesh& tm, const ECM& ecm) - : tm(tm) - , ecm(ecm) - {} - - friend - double get(const Weight_map& wm, key_type ed) - { - if (get(wm.ecm, ed)) return std::numeric_limits::max(); // do not use constrained edges (no walk back); - return std::sqrt( squared_distance(wm.tm.point(source(ed, wm.tm)), wm.tm.point(target(ed, wm.tm))) ); - } -}; - -template -OutputIterator -select_to_walk( const TriangleMesh& tm, - const Tree& aabb_tree, - typename K::Point_3 text_center, - typename K::Plane_3 axis_1, - typename K::Plane_3 axis_2, - double width, - double height, - OutputIterator out, - const NamedParameters& np) -{ - typedef boost::graph_traits BGT; - typedef typename BGT::halfedge_descriptor halfedge_descriptor; - typedef typename BGT::face_descriptor face_descriptor; - typedef typename BGT::vertex_descriptor vertex_descriptor; - typedef typename BGT::edge_descriptor edge_descriptor; - using boost::choose_param; - using boost::get_param; - - // Vertex point maps - typedef typename GetVertexPointMap::const_type Vpm; - Vpm vpm = choose_param(get_param(np, internal_np::vertex_point), - get_const_property_map(boost::vertex_point, tm)); - - typedef walker_internal::Edge_intersection_with_plane Predicate; - typedef walker_internal::Walk_in_polygon_mesh_visitor Visitor; - - face_descriptor start_face = aabb_tree.closest_point_and_primitive(text_center).second; - - // code use to get the top/bottom, left/right points surrounding the letter - // on the input mesh -#if 0 - typedef typename K::Plane_3 Plane_3; - typedef typename K::Point_3 Point_3; - - std::array int_edges = - get_sorted_intersected_halfedges(start_face, tm, vpm, axis_1, axis_2); - - // 1) walk width/2 to the right - Predicate predicate(text_center, axis_1.orthogonal_vector(), - axis_2.orthogonal_vector(), tm, vpm); - Visitor visitor = Visitor(tm, vpm, width/2.0, text_center); - Point_3 new_p; - walk_in_polygon_mesh(tm, - int_edges[1], CGAL::POLYHEDRON_EDGE, - BGT::null_halfedge(), CGAL::POLYHEDRON_NONE, - predicate, visitor); - new_p= visitor.end_point(); - std::cout << new_p << "\n"; - Plane_3 r_plane(new_p, -axis_2.orthogonal_vector()); - //2)walk width/2 to the left - predicate = Predicate(text_center, axis_1.orthogonal_vector(), - -axis_2.orthogonal_vector(), tm, vpm); - visitor = Visitor(tm, vpm, width/2.0, text_center); - //get last point orientation to know where to walk next. - walk_in_polygon_mesh(tm, - int_edges[0], CGAL::POLYHEDRON_EDGE, - BGT::null_halfedge(), CGAL::POLYHEDRON_NONE, - predicate, visitor); - - new_p= visitor.end_point(); - std::cout << new_p << "\n"; - //bool is_l_wrapping = visitor.is_wrapping(); - Plane_3 l_plane(new_p, axis_2.orthogonal_vector()); - - int_edges = get_sorted_intersected_halfedges(start_face, tm, vpm, axis_2, axis_1); - //3)walk height /2 to the top - predicate = Predicate(text_center, axis_2.orthogonal_vector(), - axis_1.orthogonal_vector(),tm, vpm); - visitor = Visitor(tm, vpm, height/2.0, text_center); - walk_in_polygon_mesh(tm, - int_edges[1], CGAL::POLYHEDRON_EDGE, - BGT::null_halfedge(), CGAL::POLYHEDRON_NONE, - predicate, visitor); - new_p= visitor.end_point(); - std::cout << new_p << "\n"; - //bool is_t_wrapping = visitor.is_wrapping(); - Plane_3 t_plane(new_p, -axis_1.orthogonal_vector()); - //4)walk height /2 to the bottom - predicate = Predicate(text_center, axis_2.orthogonal_vector(), - -axis_1.orthogonal_vector(), tm, vpm); - visitor = Visitor(tm, vpm, height/2.0, text_center); - walk_in_polygon_mesh(tm, - int_edges[0], CGAL::POLYHEDRON_EDGE, - BGT::null_halfedge(), CGAL::POLYHEDRON_NONE, - predicate, visitor); - new_p= visitor.end_point(); - std::cout << new_p << "\n"; - //bool is_b_wrapping = visitor.is_wrapping(); - Plane_3 b_plane(new_p, axis_1.orthogonal_vector()); - CGAL_assertion( l_plane.has_on_positive_side(r_plane.point()) && - t_plane.has_on_positive_side(b_plane.point()) ); -#endif - - // walk in the input mesh to get the 4 corners of the bbox of the letter - // Then using, these corner we extract the next halfedge that would have - // been intersected if the walk wasn't ended. Using these edges, we are - // trying to use Dijkstra to close the selection area. - // The result is not satisfactory on meshes with low triangle resolution - std::vector targets(4), sources(4); - - typename K::Vector_3 tmp_axis_1 = axis_1.orthogonal_vector(), - tmp_axis_2 = axis_2.orthogonal_vector(); - tmp_axis_1 /= std::sqrt(tmp_axis_1*tmp_axis_1); - tmp_axis_2 /= std::sqrt(tmp_axis_2*tmp_axis_2); - - typename K::Vector_3 v1 = tmp_axis_1 + tmp_axis_2; - typename K::Vector_3 v2 = -tmp_axis_1 + tmp_axis_2; - - std::array int_edges = - get_sorted_intersected_halfedges(start_face, tm, vpm, typename K::Plane_3(text_center,v1), typename K::Plane_3(text_center,v2)); - - double D = std::sqrt( ( width * width + height * height )/ 4. ); - - halfedge_descriptor hedge = boost::graph_traits::null_halfedge(); - - typedef typename boost::property_map >::const_type ECM; - ECM ecm = get(dynamic_edge_property_t(), tm); - for (edge_descriptor ed : edges(tm)) - put(ecm, ed, false); - - // 1) walk to the corner 0 - Predicate predicate(text_center, v1, - v2, tm, vpm); - Visitor visitor = Visitor(tm, vpm, D, text_center); - walk_in_polygon_mesh(tm, - int_edges[1], CGAL::POLYHEDRON_EDGE, - BGT::null_halfedge(), CGAL::POLYHEDRON_NONE, - predicate, visitor); - hedge = visitor.walked_halfedges().back(); - put(ecm, edge(hedge, tm), true); - targets[0] = target(hedge, tm); - sources[3] = source(hedge, tm); -#ifndef NO_DEBUG - std::cout << "* " << visitor.end_point() << "\n"; - std::cout << "2 " << tm.point(source(hedge, tm)) << " " << tm.point(target(hedge, tm)) << "\n"; -#endif - //2)walk to the corner 2 - predicate = Predicate(text_center, v1, - -v2, tm, vpm); - visitor = Visitor(tm, vpm, D, text_center); - walk_in_polygon_mesh(tm, - int_edges[0], CGAL::POLYHEDRON_EDGE, - BGT::null_halfedge(), CGAL::POLYHEDRON_NONE, - predicate, visitor); - hedge = visitor.walked_halfedges().back(); - put(ecm, edge(hedge, tm), true); - targets[2] = target(hedge, tm); - sources[1] = source(hedge, tm); -#ifndef NO_DEBUG - std::cout << "* " << visitor.end_point() << "\n"; - std::cout << "2 " << tm.point(source(hedge, tm)) << " " << tm.point(target(hedge, tm)) << "\n"; -#endif - int_edges = get_sorted_intersected_halfedges(start_face, tm, vpm, typename K::Plane_3(text_center,v2), typename K::Plane_3(text_center,v1)); - //3)walk to the corner 1 - predicate = Predicate(text_center, v2, - v1,tm, vpm); - visitor = Visitor(tm, vpm, D, text_center); - walk_in_polygon_mesh(tm, - int_edges[1], CGAL::POLYHEDRON_EDGE, - BGT::null_halfedge(), CGAL::POLYHEDRON_NONE, - predicate, visitor); - hedge = visitor.walked_halfedges().back(); - put(ecm, edge(hedge, tm), true); - targets[1] = target(hedge, tm); - sources[0] = source(hedge, tm); -#ifndef NO_DEBUG - std::cout << "* " << visitor.end_point() << "\n"; - std::cout << "2 " << tm.point(source(hedge, tm)) << " " << tm.point(target(hedge, tm)) << "\n"; -#endif - //4)walk to the corner 3 - predicate = Predicate(text_center, v2, - -v1, tm, vpm); - visitor = Visitor(tm, vpm, D, text_center); - walk_in_polygon_mesh(tm, - int_edges[0], CGAL::POLYHEDRON_EDGE, - BGT::null_halfedge(), CGAL::POLYHEDRON_NONE, - predicate, visitor); - hedge = visitor.walked_halfedges().back(); - put(ecm, edge(hedge, tm), true); - targets[3] = target(hedge, tm); - sources[2] = source(hedge, tm); -#ifndef NO_DEBUG - std::cout << "* " << visitor.end_point() << "\n"; - std::cout << "2 " << tm.point(source(hedge, tm)) << " " << tm.point(target(hedge, tm)) << "\n"; -#endif - - class Dijkstra_end_exception : public std::exception - { - const char* what() const throw () - { - return "Dijkstra shortest path: reached the target vertex."; - } - }; - - class Stop_at_target_Dijkstra_visitor : boost::default_dijkstra_visitor - { - vertex_descriptor destination_vd; - - public: - Stop_at_target_Dijkstra_visitor(vertex_descriptor destination_vd) - : destination_vd(destination_vd) - { } - - void initialize_vertex(const vertex_descriptor& /*s*/, const TriangleMesh& /*mesh*/) const { } - void examine_vertex(const vertex_descriptor& /*s*/, const TriangleMesh& /*mesh*/) const { } - void examine_edge(const edge_descriptor& /*e*/, const TriangleMesh& /*mesh*/) const { } - void edge_relaxed(const edge_descriptor& /*e*/, const TriangleMesh& /*mesh*/) const { } - void discover_vertex(const vertex_descriptor& /*s*/, const TriangleMesh& /*mesh*/) const { } - void edge_not_relaxed(const edge_descriptor& /*e*/, const TriangleMesh& /*mesh*/) const { } - void finish_vertex(const vertex_descriptor &vd, const TriangleMesh& /* mesh*/) const - { - if(vd == destination_vd) - throw Dijkstra_end_exception(); - } - }; - - Weight_map wm(tm, ecm); - -// close the loop - for (std::size_t i=0; i<4; ++i) - { - typedef boost::unordered_map Pred_umap; - typedef boost::associative_property_map Pred_pmap; - vertex_descriptor src = sources[i], tgt=targets[i]; - if (src==tgt) continue; // TODO: another case to handle is when hedge was the same for 2 consecutive corners - Pred_umap pred_umap; - Pred_pmap pred_pmap(pred_umap); - Stop_at_target_Dijkstra_visitor vis(tgt); - - try{ - boost::dijkstra_shortest_paths(tm, src, - boost::predecessor_map(pred_pmap).visitor(vis).weight_map(wm)); - } - catch(const Dijkstra_end_exception&) - {} - - vertex_descriptor prev = tgt; - do{ - CGAL_assertion( pred_umap.count(prev) == 1 ); - vertex_descriptor vd = pred_umap[prev]; - halfedge_descriptor hd = halfedge(vd, prev, tm).first; - CGAL_assertion( hd != boost::graph_traits::null_halfedge() ); - put(ecm, edge(hd, tm), true); -#ifndef NO_DEBUG - std::cout << "2 " << tm.point(prev) << " " << tm.point(vd) << "\n"; -#endif - prev = vd; - } - while(prev != src); - std::cout << "--\n"; - } - -#ifndef NO_DEBUG - for (edge_descriptor ed : edges(tm)) - if (get(ecm, ed)) - std::cout << "2 " << tm.point(source(ed, tm)) << " " << tm.point(target(ed, tm)) << "\n"; -#endif - connected_component(start_face, tm, out, parameters::edge_is_constrained_map(ecm)); - -#if 1 - -#if 0 - - -// select face by expension from the center face, cross an edge if one of its endpoint is inside the selection - //TODO: use dynamic maps - - std::array planes = { &r_plane, &l_plane, &t_plane, &b_plane }; - - std::vector visited_faces(num_faces(tm),false), - visited_vertices(num_vertices(tm), false); - std::vector < std::bitset<4> > - vertices_pos(vertices(tm).size()); - - std::vector queue; - visited_faces[start_face]=true; - *out++=start_face; - queue.push_back( halfedge(start_face, tm) ); - queue.push_back( next(queue.back(), tm) ); - queue.push_back( next(queue.back(), tm) ); - - while(!queue.empty()) - { - halfedge_descriptor h = opposite(queue.back(), tm); - queue.pop_back(); - face_descriptor f = face(h, tm); - if (f==BGT::null_face()) continue; - if (visited_faces[f]) continue; - visited_faces[f] = true; - - std::array, 3> f_v_selected; - for (int i=0; i<3; ++i) - { - vertex_descriptor v = source(h, tm); - if ( !visited_vertices[v] ) - { - for (int k=0;k<4;++k) - vertices_pos[v][k] = !planes[k]->has_on_negative_side(get(vpm,v)); - visited_vertices[v] = true; - } - f_v_selected[i] = vertices_pos[v]; - h = next(h, tm); - } - *out++=f; - - // cross any edge having a point in the plane intersection - // or if it is intersected by at least two consecutive planes - std::bitset<4> bs = f_v_selected[1] ^ f_v_selected[2]; - if ( f_v_selected[1].all() || f_v_selected[2].all() | - (bs[0] && bs[2]) || (bs[0] && bs[3]) || (bs[1] && bs[2]) || (bs[1] && bs[3]) ) - { - queue.push_back(next(h,tm)); - } - bs = f_v_selected[0] ^ f_v_selected[2]; - if ( f_v_selected[0].all() || f_v_selected[2].all() || - (bs[0] && bs[2]) || (bs[0] && bs[3]) || (bs[1] && bs[2]) || (bs[1] && bs[3]) ) - { - queue.push_back(prev(h,tm)); - } - } -#endif -#else - bool w_planes_are_reversed = l_plane.has_on_negative_side(r_plane.point()); - bool h_planes_are_reversed = t_plane.has_on_negative_side(b_plane.point()); - - //if a walk on 1 direction made a whole turn of a cc (e.g. wrapping a sphere ), - //disable the corresponding planes test, as they are combined - bool w_wrapping = false; - bool h_wrapping = false; - - std::vector all_selected_faces; - CGAL::Triangle_from_face_descriptor_map triangulator(&tm, vpm) ; - //first pass : all inside 4 planes - for(auto fd : faces(tm)) - { - if (fd==start_face) - { - all_selected_faces.push_back(fd); - continue; - } - bool do_select = false; - //get faces with at least 1 point inside planes - for(auto vd : CGAL::vertices_around_face(halfedge(fd, tm), tm)) - { - Point_3 p = get(vpm, vd); - if( - (w_wrapping || - (w_planes_are_reversed - ? (r_plane.has_on_negative_side(p) && l_plane.has_on_negative_side(p)) - : (r_plane.has_on_positive_side(p) && l_plane.has_on_positive_side(p))) - ) && (h_wrapping || - (h_planes_are_reversed - ? (t_plane.has_on_negative_side(p) && b_plane.has_on_negative_side(p)) - : (t_plane.has_on_positive_side(p) && b_plane.has_on_positive_side(p)) ))) - { - do_select = true; - break; - } - } - //if face is not selected : check if intersecting a plane (to avoid no point between point -> no selection) - if(!do_select) - { - if(!w_wrapping && - (CGAL::do_intersect(l_plane, get(triangulator, fd)) || - CGAL::do_intersect(r_plane, get(triangulator, fd)))) - { - for(auto vd : CGAL::vertices_around_face(halfedge(fd, tm), tm)){ - Point_3 p = get(vpm, vd); - if( h_planes_are_reversed - ? (t_plane.has_on_negative_side(p) && b_plane.has_on_negative_side(p)) - : (t_plane.has_on_positive_side(p) && b_plane.has_on_positive_side(p)) ){ - do_select = true; - break; - } - } - } - if(!h_wrapping && - (CGAL::do_intersect(b_plane, get(triangulator, fd)) || - CGAL::do_intersect(t_plane, get(triangulator, fd)))) - { - for(auto vd : CGAL::vertices_around_face(halfedge(fd, tm), tm)){ - Point_3 p = get(vpm, vd); - if( w_planes_are_reversed - ? (r_plane.has_on_negative_side(p) && l_plane.has_on_negative_side(p)) - : (r_plane.has_on_positive_side(p) && l_plane.has_on_positive_side(p)) ){ - do_select = true; - break; - } - } - } - } - if(do_select) - all_selected_faces.push_back(fd); - } - //add a "same-CC" constraint:the one that contains start_face - - //create a mesh from the selected faces - CGAL::Face_filtered_graph ffg(tm, all_selected_faces); - TriangleMesh subsm; - face_descriptor subsm_center_face; - CGAL::copy_face_graph(ffg, subsm, - CGAL::Emptyset_iterator(), CGAL::Emptyset_iterator(), - boost::make_function_output_iterator( - [&subsm_center_face, start_face, tm]( - const std::pair& p) - { - if (p.first==start_face){ subsm_center_face=p.second; } - })); - //get fcc map - std::vector fccmap(num_faces(tm)); // TODO: use dynamic map - CGAL::Polygon_mesh_processing::connected_components(ffg,CGAL::make_property_map(fccmap)); - - //create ffg from CC containing the start_face and give its faces as output - CGAL::Face_filtered_graph > selected_cc(ffg,fccmap[start_face], CGAL::make_property_map(fccmap)); - - //cut the band - bool take_r = (width > height); - //filter out the rest - for( auto fd : faces(selected_cc)) - { - bool do_select = true; - //to avoid non sphere topo - Plane_3 test_plane(text_center, r_plane.orthogonal_vector()); - if(!take_r) - test_plane = Plane_3(text_center, t_plane.orthogonal_vector()); - - if(do_select) - *out++ = fd; - } -#endif - return out; -} - -} -} -#endif // CGAL_POLYGON_MESH_PROCESSING_WALK_TO_SELECT_H diff --git a/Polygon_mesh_processing/include/CGAL/Polyhedron_simplex_type.h b/Polygon_mesh_processing/include/CGAL/Polyhedron_simplex_type.h deleted file mode 100644 index 3e2171ed6290..000000000000 --- a/Polygon_mesh_processing/include/CGAL/Polyhedron_simplex_type.h +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) 2014 GeometryFactory (France). -// All rights reserved. -// -// This file is part of CGAL (www.cgal.org). -// -// $URL$ -// $Id$ -// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial -// -// Author(s) : Sébastien Loriot - -#ifndef CGAL_POLYHEDRON_SIMPLEX_TYPE_H -#define CGAL_POLYHEDRON_SIMPLEX_TYPE_H - -namespace CGAL{ - -enum Polyhedron_simplex_type { - POLYHEDRON_VERTEX, - POLYHEDRON_EDGE, - POLYHEDRON_FACET, - POLYHEDRON_NONE}; -} - -#endif // CGAL_POLYHEDRON_SIMPLEX_TYPE_H From acd226d0360f87efd9262d230186caa521ce73fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Sat, 13 Jul 2024 12:02:39 +0200 Subject: [PATCH 083/120] new example to write on a polygon curve with location snapping --- .../Polygon_mesh_processing/CMakeLists.txt | 2 + ...ace_polygon_on_polygonal_curve_example.cpp | 297 ++++++++++++++++++ 2 files changed, 299 insertions(+) create mode 100644 Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_on_polygonal_curve_example.cpp diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt b/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt index e3c730e32f86..0a0f5c745e1e 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt @@ -99,6 +99,8 @@ if(TARGET CGAL::Eigen3_support) target_link_libraries(trace_regular_polygons_example PUBLIC CGAL::Eigen3_support) create_single_source_cgal_program("geodesic_isolevel_refinement.cpp") target_link_libraries(geodesic_isolevel_refinement PUBLIC CGAL::Eigen3_support) + create_single_source_cgal_program("trace_polygon_on_polygonal_curve_example.cpp") + target_link_libraries(trace_polygon_on_polygonal_curve_example PUBLIC CGAL::Eigen3_support) if (NanoSVG_FOUND) create_single_source_cgal_program("trace_svg_example.cpp") target_link_libraries(trace_svg_example PUBLIC CGAL::Eigen3_support CGAL::NanoSVG_support) diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_on_polygonal_curve_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_on_polygonal_curve_example.cpp new file mode 100644 index 000000000000..11d4329838bc --- /dev/null +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_on_polygonal_curve_example.cpp @@ -0,0 +1,297 @@ +#include +#include +#include + +#include + +namespace PMP = CGAL::Polygon_mesh_processing; + +typedef CGAL::Exact_predicates_inexact_constructions_kernel K; +typedef CGAL::Surface_mesh Mesh; +typedef PMP::Face_location Face_location; +typedef PMP::Edge_location Edge_location; + +std::vector +get_supporting_curve(std::string filename, + const Mesh& mesh) +{ + std::ifstream in(filename); + if (!in) + { + std::cerr << "ERROR: cannot open " << filename << "\n"; + exit(1); + } + + int nbp; + in >> nbp; + std::cout << "Support polyline size: " << nbp << "\n"; + std::vector polyline(nbp); + + for (int i=0;i> polyline[i]; + + std::vector face_locations(nbp); + + using AABB_face_graph_primitive = CGAL::AABB_face_graph_triangle_primitive; + using AABB_face_graph_traits = CGAL::AABB_traits_3; + + CGAL::AABB_tree tree; + PMP::build_AABB_tree(mesh, tree); + + //Remarks on snapping_tolerance of locate: it is actually a value used as is in barycentric coordinates with no relation to the size of the triangle + // and snapping is only done when at least one coordinate is negative (clamping would be a better name, snapping is what I'm doing below). + //TODO: the following works only if we don't have a lfs smaller than snap_tol + double snap_tol = 0.00001; + for(int i=0; i primitives; + tree.all_intersected_primitives(query, std::back_inserter(primitives)); + + switch (primitives.size()) + { + case 0: + std::cerr <<"ERROR points too far!\n"; + exit(1); + break; + case 1: + face_locations[i] = PMP::locate_in_face(polyline[i],primitives[0], mesh); + break; + case 2: + { + // TODO: we assume we don't have any boundary to it's clear it's on an edge + Mesh::Halfedge_index h1 = halfedge(primitives[0], mesh), + h2 = halfedge(primitives[1], mesh); + bool found=false; + for (int j=0;j<3;++j) + { + for (int k=0;k<3; ++k) + { + if (h1==opposite(h2, mesh)) + { + found = true; + break; + } + h2=next(h2, mesh); + } + if (found) break; + h1=next(h1, mesh); + } + CGAL_assertion(h1 == opposite(h2, mesh)); + + const K::Point_3& src = mesh.point(source(h1, mesh)); + const K::Point_3& tgt= mesh.point(target(h1, mesh)); + double t = std::sqrt(CGAL::squared_distance(polyline[i], src)/CGAL::squared_distance(src,tgt)); + face_locations[i] = PMP::locate_on_halfedge(h1, t, mesh); + } + break; + default: + { + std::vector vrts; + Mesh::Halfedge_index h=halfedge(primitives[0], mesh); + vrts.push_back(source(h, mesh)); + vrts.push_back(target(h, mesh)); + vrts.push_back(target(next(h, mesh), mesh)); + std::sort(vrts.begin(), vrts.end()); + for(std::size_t k=1;k tmp; + h=halfedge(primitives[k], mesh); + for (int j=0;j<3;++j) + { + Mesh::Vertex_index v=target(h, mesh); + if (std::binary_search(vrts.begin(), vrts.end(), v)) + tmp.push_back(v); + h=next(h, mesh); + } + tmp.swap(vrts); + std::sort(vrts.begin(), vrts.end()); + } + CGAL_assertion(vrts.size()==1); + + int offset=0; + h=halfedge(primitives[0], mesh); + if (target(h, mesh)==vrts[0]) + offset=1; + else + if (target(next(h, mesh), mesh)==vrts[0]) + offset=2; + else + CGAL_assertion(source(h, mesh)==vrts[0]); + std::array bary = CGAL::make_array(0.,0.,0.); + bary[offset]=1.; + face_locations[i] = std::make_pair(primitives[0], bary); + } + } + // face_locations[i] = PMP::locate_with_AABB_tree(polyline[i], tree, mesh, CGAL::parameters::snapping_tolerance(snap_tol)); + for (int k=0;k<3;++k) + if (face_locations[i].second[k]<0) face_locations[i].second[k]=0; + } + + // DEBUG code to check that the polyline has correctly been converted to face locations (it's not a path as if vertex is hit we don't have the face continuity property) + for (int i=0;i 1) ? std::string(argv[1]) + : CGAL::data_file_path("meshes/elephant.off"); + + std::string support_filename = (argc > 2) ? std::string(argv[2]) + : CGAL::data_file_path("XXXXX"); + + std::string filename_poly = (argc > 3) ? std::string(argv[3]) + : CGAL::data_file_path("XXXXX"); + + std::size_t nb_copies = atoi(argv[4]); + + Mesh mesh; + if(!CGAL::IO::read_polygon_mesh(filename, mesh) || !CGAL::is_triangle_mesh(mesh)) + { + std::cerr << "Invalid input." << std::endl; + return 1; + } + + std::vector> polygons; + std::ifstream in(filename_poly); + if (!in) + { + std::cerr << "Error cannot open " << filename_poly << "\n"; + return 1; + } + + int nb_pt; + K::Point_3 pt; + CGAL::Bbox_2 bb2; + while (in >> nb_pt) + { + polygons.emplace_back(); + polygons.back().reserve(nb_pt-1); + for (int i=0; i> pt; + polygons.back().emplace_back(pt.x(), pt.y()); + bb2+=polygons.back().back().bbox(); + } + in >> pt; + if (!in) + { + std::cerr << "Error reading input polygons\n"; + return 1; + } + // check if last point is duplicated + if (polygons.back().back().x()!=pt.x() || polygons.back().back().y()!=pt.y()) + { + polygons.back().emplace_back(pt.x(), pt.y()); + bb2+=polygons.back().back().bbox(); + } + if (!in) break; + } + + std::cout << polygons.size() << " polygons read\n"; + + + // duplicate them a bit to get longer text while getting another one + CGAL::Bbox_2 gbox; + for (const auto& polygon : polygons) + gbox+=CGAL::bbox_2(polygon.begin(), polygon.end()); + K::Vector_2 delta(gbox.xmax()-gbox.xmin(), 0); + std::size_t nbpoly=polygons.size(); + polygons.reserve(nbpoly*(nb_copies+1)); + for (std::size_t c=0; c solver; + PMP::init_geodesic_dual_solver(solver, mesh); + + // get supporting curve + std::vector supporting_curve = get_supporting_curve(support_filename, mesh); + if (supporting_curve.empty()) return 1; + + + std::ofstream support_out("support.polylines.txt"); + + std::vector support_poly; + support_poly.reserve(supporting_curve.size()); + PMP::convert_path_to_polyline(supporting_curve, mesh, std::back_inserter(support_poly)); + + support_out << std::setprecision(17) << support_poly.size(); + for (auto p : support_poly) + support_out << " " << p; + support_out << "\n"; + support_out.close(); + std::cout <<"supporting_curve generated!\n"; + + // convert polygons to polar coordinates + const double expected_height = 0.025; // user parameter for scaling + const double scaling = expected_height/(gbox.ymax()-gbox.ymin()); + + + std::ofstream out("label_on_curve.polylines.txt"); + out << std::setprecision(17); + + std::vector> polygons_3; + polygons_3 = PMP::trace_geodesic_label_along_curve(supporting_curve, polygons, scaling, 0., true, mesh, solver); + + for (const auto& polygon_path : polygons_3) + { + std::vector poly; + poly.reserve(polygon_path.size()); + PMP::convert_path_to_polyline(polygon_path, mesh, std::back_inserter(poly)); + + out << poly.size(); + for (auto p : poly) + out << " " << p; + out << std::endl; + } + + return 0; +} From c0b87a0e9320df813440dbc736a38adcf6fc469c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Sat, 13 Jul 2024 15:25:55 +0200 Subject: [PATCH 084/120] WIP bug fix straightest geodesic starting on an edge but going in the opposite direction vertex case needs to be done --- .../straightest_geodesic_sm_example.cpp | 10 +- .../Bsurf/locally_shortest_path.h | 101 +++++++++++++++--- 2 files changed, 94 insertions(+), 17 deletions(-) diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/straightest_geodesic_sm_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/straightest_geodesic_sm_example.cpp index 7136bcd4ece6..39f1eebbe098 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/straightest_geodesic_sm_example.cpp +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/straightest_geodesic_sm_example.cpp @@ -38,14 +38,20 @@ int main(int argc, char** argv) std::cout << "seed " << rnd.get_seed() << std::endl; Mesh::Face_index f = *std::next(faces(mesh).begin(), rnd.get_int(0, nb_faces)); - Face_location src(f, CGAL::make_array(0.3,0.3,0.4)); +//case opposite edge (for testsuite) +// Mesh::Face_index f = *std::next(faces(mesh).begin(), 1031); +// Face_location src(f, CGAL::make_array(0, 0.65258992669550031, 0.34741007330449963)); + K::Point_3 src_pt = PMP::construct_point(src, mesh); std::cout << "src = " << src_pt << "\n"; - double target_distance = 0.5; +//case opposite edge (for testsuite) +// double target_distance = 0.0072766352463858128; +// K::Vector_2 dir(0.079665117730984697, 0.99682168366107904); + double target_distance = 0.5; K::Vector_2 dir(1,1); std::vector path = PMP::straightest_geodesic(src, dir, target_distance, mesh); diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index 4f9c9871c366..45585cf1d4d3 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -922,7 +922,6 @@ struct Locally_shortest_path_imp Vector_2 prev_flat_p=prev_coords[0]*flat_prev[0]+prev_coords[1]*flat_prev[1]+prev_coords[2]*flat_prev[2]; Vector_2 curr_flat_p=curr_coords[0]*flat_curr[0]+curr_coords[1]*flat_curr[1]+curr_coords[2]*flat_curr[2]; - #ifdef CGAL_DEBUG_BSURF std::cout<<" k is "<> - straightest_geodesic(const Face_location& p, + straightest_geodesic(const Face_location& start_loc, const TriangleMesh& mesh, const VertexPointMap &vpm, - const Vector_2& dir,const FT& len) + Vector_2 dir,const FT& len) { +#ifdef CGAL_DEBUG_BSURF + { + std::ofstream log("/tmp/log.txt"); + log << std::setprecision(17); + log << start_loc.first << " " << start_loc.second[0] << " " << start_loc.second[1] << " " << start_loc.second[2] << "\n"; + log << "dir = " << dir << "\n"; + log << "len = " << len << "\n"; + } +#endif + auto get_halfedge_offset=[&mesh](const halfedge_descriptor& h_ref,const halfedge_descriptor& h_curr) { if( h_ref == h_curr) return 0; @@ -1287,15 +1302,15 @@ struct Locally_shortest_path_imp }; #ifdef CGAL_DEBUG_BSURF - auto fn = compute_face_normal(p.first, mesh); + auto fn = compute_face_normal(start_loc.first, mesh); double theta= std::acos(dir.y()/sqrt(dir*dir)); if(dir.x()<0) theta*=-1; - auto dir3 = rotate_vector(mesh.point(target(halfedge(p.first, mesh), mesh)) - - mesh.point(source(halfedge(p.first, mesh), mesh)), + auto dir3 = rotate_vector(mesh.point(target(halfedge(start_loc.first, mesh), mesh)) - + mesh.point(source(halfedge(start_loc.first, mesh), mesh)), fn, theta); - std::cout << "direction " << construct_point(p, mesh) << " " << construct_point(p, mesh)+dir3 << "\n"; + std::cout << "direction " << construct_point(start_loc, mesh) << " " << construct_point(start_loc, mesh)+dir3 << "\n"; #endif auto get_vid_offset=[&mesh](const halfedge_descriptor& h_ref,const vertex_descriptor& vid) @@ -1365,30 +1380,86 @@ struct Locally_shortest_path_imp std::vector> result; FT accumulated=0.; - face_descriptor curr_tid=p.first; + face_descriptor curr_tid=start_loc.first; std::array curr_flat_tid=init_flat_triangle(halfedge(curr_tid,mesh),vpm,mesh); - Vector_2 flat_p= p.second[0]*curr_flat_tid[0]+p.second[1]*curr_flat_tid[1]+p.second[2]*curr_flat_tid[2]; - Face_location curr_p=p; + Vector_2 flat_p= start_loc.second[0]*curr_flat_tid[0]+start_loc.second[1]*curr_flat_tid[1]+start_loc.second[2]*curr_flat_tid[2]; + Face_location curr_p=start_loc; + + descriptor_variant var_des = get_descriptor_from_location(start_loc,mesh); + switch(var_des.index()) + { + case 0: + { + // TODO! + } + break; + case 1: + { + halfedge_descriptor h = std::get(var_des); + halfedge_descriptor href = halfedge(face(h,mesh), mesh); + int opp_id = h==href?2:(next(href,mesh)==h?0:1); + Vector_2 opp = curr_flat_tid[opp_id]; + + // check if the line starts in the current face (TODO should be a predicate) + if ( (opp-flat_p) * dir < 0 ) + { + // take the same point but in the opposite face + halfedge_descriptor h_start=h; + h=opposite(h, mesh); + curr_tid=face(h, mesh); + curr_p.first=curr_tid; + href=halfedge(curr_tid, mesh); + std::array bary2 = CGAL::make_array(start_loc.second[(opp_id+2)%3], start_loc.second[(opp_id+1)%3]); // swap done here + int opp_id = h==href?2:(next(href,mesh)==h?0:1); + curr_p.second[0]=FT(0); + curr_p.second[(opp_id+1)%3]=bary2[0]; + curr_p.second[(opp_id+2)%3]=bary2[1]; + + + // dir must also be updated: + // unfold the new triangle into the basis of the input one + // compute the angle between the new triangle ref halfedge and dir + // compute the new dir thanks to that angle. + halfedge_descriptor start_href=halfedge(start_loc.first, mesh); + int k=h_start==prev(start_href,mesh)?2 :( h_start==next(start_href,mesh)?1:0); + std::array new_flat_tid_in_curr_basis=unfold_face(h_start,vpm, mesh,curr_flat_tid, k); + + Vector_2 ybase = new_flat_tid_in_curr_basis[1]-new_flat_tid_in_curr_basis[0]; + double cos_theta = ybase * dir / std::sqrt(ybase*ybase) / std::sqrt(dir*dir); + double theta = std::acos(cos_theta); + dir = Vector_2(std::cos(CGAL_PI/2.-theta), std::sin(CGAL_PI/2.-theta)); + + curr_flat_tid=init_flat_triangle(halfedge(curr_tid,mesh),vpm,mesh); + flat_p= curr_p.second[0]*curr_flat_tid[0]+curr_p.second[1]*curr_flat_tid[1]+curr_p.second[2]*curr_flat_tid[2]; + } + } + break; + default: + break; + } + Face_location prev_p; Vector_2 curr_dir=dir; halfedge_descriptor h_ref=halfedge(curr_tid,mesh); halfedge_descriptor h_curr=h_ref; - result.push_back(p); + + result.push_back(curr_p); #ifdef CGAL_DEBUG_BSURF - std::cout << "p= " << construct_point(p,mesh) << ")\n"; + std::cout << "p= " << construct_point(curr_p,mesh) << ")\n"; #endif - auto [is_vert, kv] = point_is_vert(p); - auto [is_edge, ke] = point_is_edge(p); + //TODO not sure why we need that + auto [is_vert, kv] = point_is_vert(curr_p); + auto [is_edge, ke] = point_is_edge(curr_p); if (is_vert) h_curr=get_halfedge(kv,h_ref); else if (is_edge) h_curr=get_halfedge(ke,h_ref); #ifdef CGAL_DEBUG_BSURF - std::cout << "Accululated loop starts\n"; + std::cout << "Accumulated loop starts\n"; #endif while (accumulated < len) { From ed33392a4e32cac86742cc51a1c4668b10ee6f83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Mon, 15 Jul 2024 17:30:07 +0200 Subject: [PATCH 085/120] fix invalid index --- .../straightest_geodesic_sm_example.cpp | 6 ------ ...trace_polygon_on_polygonal_curve_example.cpp | 2 +- .../Bsurf/locally_shortest_path.h | 17 ++++++++++++----- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/straightest_geodesic_sm_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/straightest_geodesic_sm_example.cpp index 39f1eebbe098..3ca0f7edbcf3 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/straightest_geodesic_sm_example.cpp +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/straightest_geodesic_sm_example.cpp @@ -4,12 +4,6 @@ #include -#if 0 -#include -#else - -#endif - namespace PMP = CGAL::Polygon_mesh_processing; typedef CGAL::Exact_predicates_inexact_constructions_kernel K; diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_on_polygonal_curve_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_on_polygonal_curve_example.cpp index 11d4329838bc..c92fb24b2a78 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_on_polygonal_curve_example.cpp +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_on_polygonal_curve_example.cpp @@ -279,7 +279,7 @@ int main(int argc, char** argv) out << std::setprecision(17); std::vector> polygons_3; - polygons_3 = PMP::trace_geodesic_label_along_curve(supporting_curve, polygons, scaling, 0., true, mesh, solver); + polygons_3 = PMP::trace_geodesic_label_along_curve(supporting_curve, polygons, scaling, 0., false, mesh, solver); for (const auto& polygon_path : polygons_3) { diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index 45585cf1d4d3..57e1b5b9e8de 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -3263,6 +3263,8 @@ trace_geodesic_label(const Face_location ¢er, CGAL_assertion(left_path.size() >=2); CGAL_assertion(right_path.size() >=2); + // TODO: precompute distances along supporting curve + stop when exceeding max distance needed + for(std::size_t i=0;i polygon_center; @@ -3464,9 +3466,12 @@ trace_geodesic_label_along_curve(const std::vector loc_k=supporting_curve[k], loc_k1=supporting_curve[k+1]; CGAL_assertion_code(bool OK=) locate_in_common_face(loc_k, loc_k1, tmesh); @@ -3479,14 +3484,14 @@ trace_geodesic_label_along_curve(const std::vector targetd) { double excess = acc-targetd; - Face_location loc_k=supporting_curve[k], loc_k1=supporting_curve[k+1]; + Face_location loc_k=supporting_curve[k-1], loc_k1=supporting_curve[k]; CGAL_assertion_code(bool OK=) locate_in_common_face(loc_k, loc_k1, tmesh); CGAL_assertion(OK); @@ -3509,9 +3514,11 @@ trace_geodesic_label_along_curve(const std::vector loc_k=supporting_curve[k-1], loc_k1=supporting_curve[k]; + //TODO: shall we throw an exception instead or return false? + + Face_location loc_k=supporting_curve[k-2], loc_k1=supporting_curve[k-1]; CGAL_assertion_code(bool OK=) locate_in_common_face(loc_k, loc_k1, tmesh); CGAL_assertion(OK); From a5553d8f2f1997adfb723e7401a2fb4cf0110ad9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Tue, 16 Jul 2024 18:09:38 +0200 Subject: [PATCH 086/120] handle case when starting at a vertex in straightest path --- .../Bsurf/locally_shortest_path.h | 96 ++++++++++++++++++- 1 file changed, 95 insertions(+), 1 deletion(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index 57e1b5b9e8de..fe0550b9c1c3 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -1390,7 +1390,101 @@ struct Locally_shortest_path_imp { case 0: { - // TODO! + vertex_descriptor src_vertex = std::get(var_des); + halfedge_descriptor href = halfedge(curr_tid, mesh); + int src_index = source(href, mesh)==src_vertex?0:(target(href,mesh)==src_vertex?1:2); + // first get the direction in the basis of the triangle to get its angle with an edge incident to the vertex + + Vector_2 v02 = curr_flat_tid[(src_index+2)%3] - curr_flat_tid[src_index]; + Vector_2 v01 = curr_flat_tid[(src_index+1)%3] - curr_flat_tid[src_index]; + double unnorm_cos_theta_1 = v02 * dir; + double unnorm_cos_theta_2 = v02 * v01; + + // 2 orientation tests to check if the angle is larger or smaller than Pi + // 2 orientation tests are needed as curr_flat_tid orientation could be inverted during flattening + Orientation dir_ori=orientation(v02,dir), v01_ori=orientation(v02,v01); + + std::cout << "dir_ori!=v01_ori ? " << (dir_ori!=v01_ori) << "\n"; + + // check if dir is the cone centered at curr_flat_tid[src_vertex] (TODO: should be a predicate!) + if ( dir_ori!=v01_ori || + unnorm_cos_theta_2 / std::sqrt(v01*v01) > unnorm_cos_theta_1 / std::sqrt(dir*dir)) // > because cos is decreasing from 0 -> Pi, should be < for the angle` + { + //TODO: should be made robust with snapping and predicates or something à la robust construction traits + + // compute all the angles around the vertex from where the path starts + std::vector angles; + double acc_angle=0; + halfedge_descriptor hloop=href; + while (target(hloop, mesh)!=src_vertex) hloop=next(hloop, mesh); + halfedge_descriptor hstart=hloop; + Vector_3 n(get(vpm,target(hloop, mesh)), get(vpm,source(hloop, mesh))); + n /= std::sqrt(n*n); + do{ + Vector_3 nv(get(vpm,target(hloop, mesh)), get(vpm,target(next(hloop, mesh), mesh))); + nv /= std::sqrt(nv*nv); + angles.push_back( std::acos(n * nv) ); + acc_angle+=angles.back(); + n = nv; + hloop=opposite(next(hloop, mesh), mesh); + }while(hloop!=hstart); + + std::cout << std::acos(unnorm_cos_theta_2 / std::sqrt((v01*v01) * (v02*v02))) << " vs " << angles[0] << "\n"; + + CGAL_assertion( std::acos(unnorm_cos_theta_2 / std::sqrt((v01*v01) * (v02*v02))) == angles[0]); // will not be true because of rounding + + // normalise the angle to bring them in [0,1] + for (double& angle : angles) + angle /= acc_angle; + + // compute and normalise the target angle + double target_theta = std::acos(unnorm_cos_theta_1 / std::sqrt((dir*dir) * (v02*v02))); + if ( dir_ori!=v01_ori ) target_theta = 2 * CGAL_PI - target_theta; + target_theta /= acc_angle; + + double curr_angle = 0; + int ia = 0; + do{ + curr_angle+=angles[ia]; + if (curr_angle >= target_theta) break; + hloop=opposite(next(hloop, mesh), mesh); + ++ia; + }while(hloop!=hstart); + + CGAL_assertion(hloop!=hstart); + + // angle in target face wrt hloop + double delta = (target_theta - angles[ia-1]) * acc_angle; + + // using the law of the sinus we have: + CGAL_assertion(target(hloop, mesh) == src_vertex); + Vector_3 vloop(get(vpm, source(hloop, mesh)),get(vpm, target(hloop, mesh))); + Vector_3 vprev(get(vpm, source(hloop, mesh)),get(vpm, target(next(hloop, mesh), mesh))); + double dvloop = std::sqrt(vloop*vloop); + double dvprev = std::sqrt(vprev*vprev); + double theta = std::acos( vloop*vprev / dvloop / dvprev ); + double d_opp_delta = dvloop * sin(delta) / sin(CGAL_PI-delta-theta); + + double alpha = d_opp_delta/dvprev; + + halfedge_descriptor href = halfedge(face(hloop,mesh),mesh); + curr_flat_tid=init_flat_triangle(href,vpm,mesh); + int src_id = source(href, mesh)==src_vertex?0:(target(href,mesh)==src_vertex?1:2); + +#ifdef CGAL_DEBUG_BSURF + std::cout << "new_face= " << face(href, mesh) << "\n"; + std::array debug_pt; + debug_pt[0]=mesh.point(source(href,mesh)) - Point_3(0.,0.,0.); + debug_pt[1]=mesh.point(target(href,mesh)) - Point_3(0.,0.,0.); + debug_pt[2]=mesh.point(target(next(href,mesh),mesh)) - Point_3(0.,0.,0.); + std::cout << "direction inter pt: " << (alpha) * debug_pt[(src_id+1)%3] + (1-alpha) * debug_pt[(src_id+2)%3] << "\n"; +#endif + // point on the opposite edge of src_vertex in face(href, mesh) + Vector_2 ip = (alpha) * curr_flat_tid[(src_id+1)%3] + (1-alpha) * curr_flat_tid[(src_id+2)%3]; + dir = ip - curr_flat_tid[src_id]; + curr_tid=face(href, mesh); + flat_p = curr_flat_tid[src_id]; + } } break; case 1: From 792e1de9f35fbe584bb4fe289077e7af2b75953b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 17 Jul 2024 10:30:53 +0200 Subject: [PATCH 087/120] missing update of location --- .../Bsurf/locally_shortest_path.h | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index fe0550b9c1c3..e00b4f7532a9 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -924,9 +924,9 @@ struct Locally_shortest_path_imp #ifdef CGAL_DEBUG_BSURF std::cout<<" k is "< unnorm_cos_theta_1 / std::sqrt(dir*dir)) // > because cos is decreasing from 0 -> Pi, should be < for the angle` @@ -1429,8 +1427,6 @@ struct Locally_shortest_path_imp hloop=opposite(next(hloop, mesh), mesh); }while(hloop!=hstart); - std::cout << std::acos(unnorm_cos_theta_2 / std::sqrt((v01*v01) * (v02*v02))) << " vs " << angles[0] << "\n"; - CGAL_assertion( std::acos(unnorm_cos_theta_2 / std::sqrt((v01*v01) * (v02*v02))) == angles[0]); // will not be true because of rounding // normalise the angle to bring them in [0,1] From de4bb02b8bea2da72239898d71393a67852971a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 24 Jul 2024 18:52:38 +0200 Subject: [PATCH 090/120] fix case of a path of length 0 --- .../Polygon_mesh_processing/Bsurf/locally_shortest_path.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index 7b5f28b2797a..ac5de3afbe08 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -1289,6 +1289,8 @@ struct Locally_shortest_path_imp } #endif + if (len == 0) return {start_loc}; + auto get_halfedge_offset=[&mesh](const halfedge_descriptor& h_ref,const halfedge_descriptor& h_curr) { if( h_ref == h_curr) return 0; @@ -3269,6 +3271,7 @@ trace_geodesic_polygons(const Face_location ¢e straightest_geodesic(center, dir, scaling * std::sqrt(dir.squared_length()),tmesh); Face_location polygon_center = spath.back(); + // TODO: avoid using the shortest path and directly use the straightest! std::vector> shortest_path; locally_shortest_path(center, polygon_center, tmesh, shortest_path, *solver_ptr); @@ -3677,7 +3680,6 @@ trace_bezier_curves(const Face_location ¢er, std::size_t n=directions.size(); std::vector< std::vector> > result(n); - std::vector> vertices(n); #ifndef CGAL_BSURF_USE_DIJKSTRA_SP const Dual_geodesic_solver* solver_ptr=&solver; From 6eaa1ab5b625db05a02ed04e9002433358941d6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 24 Jul 2024 18:53:03 +0200 Subject: [PATCH 091/120] add code for tracing a polyline of adjacent bezier curves --- .../Bsurf/locally_shortest_path.h | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index ac5de3afbe08..2f7d46f6635a 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -3742,6 +3742,104 @@ trace_bezier_curves(const Face_location ¢er, return result; } +//TODO: make sure it is consistent with the rest to not duplicate the last point if closed +template +std::vector> +trace_polyline_of_bezier_curves(const Face_location ¢er, + const std::vector& directions, + const std::vector& lengths, + bool is_closed, // use [directions/lengths].front as last control point? + const int num_subdiv, + const TriangleMesh &tmesh +#ifndef CGAL_BSURF_USE_DIJKSTRA_SP + , const Dual_geodesic_solver& solver = {} +#endif +) +{ + using FT = typename K::FT; + + std::size_t n = (directions.size() - (is_closed?0:1))/3; + CGAL_assertion( n * 3 + (is_closed?0:1) == directions.size() ); + + std::vector> result; + + // n is the number of quadruple of control points + // After num_subdiv steps, we have 2^num_subdiv * n quadruples of control points + + + // even if closed we will duplicate the last point + // (this is a lower bound without taking into account shortest path between points) + result.reserve( (1<* solver_ptr=&solver; + Dual_geodesic_solver local_solver; + if (solver.graph.empty()) + { + solver_ptr = &local_solver; + init_geodesic_dual_solver(local_solver, tmesh); + } +#endif + +#ifdef CGAL_DEBUG_BSURF + std::ofstream debug_cp("/tmp/control_points.xyz"); + std::ofstream debug_ep("/tmp/end_points.xyz"); + debug_cp << std::setprecision(17); + debug_ep << std::setprecision(17); +#endif + + Face_location prev_loc = straightest_geodesic(center,directions[0],lengths[0],tmesh).back(), + first_loc = prev_loc; + + for (std::size_t i=0; i control_loc; + control_loc[0]=prev_loc; + for (int k=1;k<4; ++k) + { + if (k!=3 || !is_closed || 3*i+k!=directions.size()) + control_loc[k] = straightest_geodesic(center,directions[3*i+k],lengths[3*i+k],tmesh).back(); + else + control_loc[k] = first_loc; + } + prev_loc=control_loc[3]; + + #ifdef CGAL_DEBUG_BSURF + debug_ep << construct_point(control_loc[0], tmesh) << "\n"; + debug_ep << construct_point(control_loc[3], tmesh) << "\n"; + debug_cp << construct_point(control_loc[1], tmesh) << "\n"; + debug_cp << construct_point(control_loc[2], tmesh) << "\n"; + #endif + + std::vector> bezier = + recursive_de_Casteljau(tmesh, control_loc, num_subdiv +#ifndef CGAL_BSURF_USE_DIJKSTRA_SP + , *solver_ptr +#endif + ); + + if (i==0) + result.push_back(bezier[0]); + for(std::size_t b=0; b& loc = bezier[b]; + const Face_location& loc1 = bezier[b+1]; + + // connect the two face locations with shortest path is they are in different faces + if (loc.first!=loc1.first) + { + std::vector> edge_locations; + locally_shortest_path(loc, loc1, tmesh, edge_locations, solver); + for (const Edge_location& e : edge_locations) + result.push_back(to_face_location(e, tmesh)); + } + result.push_back(loc1); + } + } + + return result; +} + template typename K::FT path_length(const std::vector>& path, From e91fc756f492a95aaf2ec76e76e83b92d771ba7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 24 Jul 2024 18:53:29 +0200 Subject: [PATCH 092/120] update example code to also trace svg with different center after regrouping control curves --- .../trace_svg_example.cpp | 283 +++++++++++++++--- 1 file changed, 242 insertions(+), 41 deletions(-) diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_svg_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_svg_example.cpp index 0c2225b0c129..87a1978838af 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_svg_example.cpp +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_svg_example.cpp @@ -1,8 +1,12 @@ #include #include #include +#include #include +#include + +#include #include @@ -22,6 +26,11 @@ int main(int argc, char** argv) std::string svg_filename = (argc > 2) ? std::string(argv[2]) : CGAL::data_file_path("polylines_2/nano.svg"); + int face_index = (argc>3) ? atoi(argv[3]) : 2154; + double b0 = (argc>4) ? atof(argv[4]) : 0.3; + double b1 = (argc>5) ? atof(argv[5]) : 0.3; + double b2 = (argc>6) ? atof(argv[6]) : 0.4; + Mesh mesh; if(!CGAL::IO::read_polygon_mesh(mesh_filename, mesh) || !CGAL::is_triangle_mesh(mesh)) { @@ -36,7 +45,7 @@ int main(int argc, char** argv) } // extract control points - std::vector< std::array > bezier_curves; + std::vector< std::array > bezier_control_points; CGAL::Bbox_2 bb2; // in SVG's the y axis points downward, so we must take the opposite y coordinates @@ -53,75 +62,267 @@ int main(int argc, char** argv) for (int i=0; i> directions; - std::vector> lengths; - directions.reserve(bezier_curves.size()); - lengths.reserve(bezier_curves.size()); + PMP::Dual_geodesic_solver solver; + PMP::init_geodesic_dual_solver(solver, mesh); - for (const std::array& bezier : bezier_curves) + std::size_t nb_faces = faces(mesh).size(); + Mesh::Face_index f = *std::next(faces(mesh).begin(), (face_index)%nb_faces); + Face_location center(f, CGAL::make_array(b0,b1,b2)); + + std::size_t num_subdiv = 3; + + // option 1: trace bezier curves with a single center { - std::vector> polar_coords = - PMP::convert_polygon_to_polar_coordinates(bezier, center_2); + CGAL::Real_timer time; + time.start(); + std::vector> directions; + std::vector> lengths; + directions.reserve(bezier_control_points.size()); + lengths.reserve(bezier_control_points.size()); + + for (const std::array& bezier : bezier_control_points) + { + std::vector> polar_coords = + PMP::convert_polygon_to_polar_coordinates(bezier, center_2); - directions.emplace_back(); - lengths.emplace_back(); + directions.emplace_back(); + lengths.emplace_back(); - assert(polar_coords.size()==4); + assert(polar_coords.size()==4); - for (int i=0;i<4; ++i) + for (int i=0;i<4; ++i) + { + lengths.back()[i] = scaling * polar_coords[i].first; + directions.back()[i]=K::Vector_2(std::cos(polar_coords[i].second), std::sin(polar_coords[i].second)); + } + } + + std::vector< std::vector > res = + PMP::trace_bezier_curves(center, directions, lengths, num_subdiv, mesh, solver); + + // write result + std::ofstream out("svg_option1.polylines.txt"); + out << std::setprecision(17); + for (const auto& b : res) { - lengths.back()[i] = scaling * polar_coords[i].first; - directions.back()[i]=K::Vector_2(std::cos(polar_coords[i].second), std::sin(polar_coords[i].second)); + std::vector poly; + poly.reserve(b.size()); + PMP::convert_path_to_polyline(b, mesh, std::back_inserter(poly)); + + + out << poly.size(); + for (const K::Point_3& p : poly) + out << " " << p; + out << "\n"; } + time.stop(); + std::cout << "option 1 took: " << time.time() << "\n"; } - // trace bezier curves - std::size_t nb_faces = faces(mesh).size(); - Mesh::Face_index f = *std::next(faces(mesh).begin(), (2154)%nb_faces); - Face_location center(f, CGAL::make_array(0.3,0.3,0.4)); + // option 2: regroup control polygon and trace bezier curves with a center per group + { + CGAL::Real_timer time; + time.start(); - PMP::Dual_geodesic_solver solver; - PMP::init_geodesic_dual_solver(solver, mesh); + typedef boost::adjacency_list< boost::vecS, boost::vecS, + boost::undirectedS, + K::Point_2> Graph; + using Graph_vertex = Graph::vertex_descriptor; + + Graph graph; + + std::vector vrts; + std::map pt_map; + Graph_vertex null_vertex = boost::graph_traits::null_vertex(); + + // build bezier control segment graph + for (std::size_t eid=0; eid& bezier = bezier_control_points[eid]; + + auto insert_res_0 = pt_map.emplace(bezier[0], null_vertex); + if (insert_res_0.second) + { + vrts.push_back(add_vertex(graph)); + graph[vrts.back()]=bezier[0]; + insert_res_0.first->second = vrts.back(); + } + + auto insert_res_3 = pt_map.emplace(bezier[3], null_vertex); + if (insert_res_3.second) + { + vrts.push_back(add_vertex(graph)); + graph[vrts.back()]=bezier[3]; + insert_res_3.first->second = vrts.back(); + } + + Graph_vertex v1 = add_vertex(graph); + Graph_vertex v2 = add_vertex(graph); + graph[v1]=bezier[1]; + graph[v2]=bezier[2]; + + add_edge(insert_res_0.first->second, v1, graph); + add_edge(v1, v2, graph); + add_edge(v2, insert_res_3.first->second, graph); + } + + // visitor for split_graph_into_polylines that will also take care of + // the drawing and writing in the output file + struct Draw_visitor + { + std::ofstream out; + const Graph& graph; + const Face_location& center; + const K::Point_2& center_2; + double scaling, num_subdiv; + const Mesh& mesh; + PMP::Dual_geodesic_solver& solver; + + Draw_visitor(const Graph& g, const Face_location& c, const K::Point_2& c2, double s, double ns, + const Mesh& tm, PMP::Dual_geodesic_solver& sol) + : graph(g) + , center(c) + , center_2(c2) + , scaling(s) + , num_subdiv(ns) + , mesh(tm) + , solver(sol) + { + out.open("svg_option2.polylines.txt"); + out << std::setprecision(17); + } + + std::vector current_polyline; + + void start_new_polyline() + { + current_polyline.clear(); + } + + void add_node(Graph_vertex v) + { + current_polyline.push_back(v); + } + + void end_polyline() + { + //TODO: we should collect polylines and use union-find to group them when their bbox overlaps + // this would save some center computations + make sure close polylines have the same center + std::vector directions; + std::vector lengths; + directions.reserve(current_polyline.size()); + lengths.reserve(current_polyline.size()); + + // recover polyline of control points + std::vector poly2; + poly2.reserve(current_polyline.size()); + for (Graph_vertex v : current_polyline) + poly2.push_back(graph[v]); + + // center of the polyline + CGAL::Bbox_2 poly_bb=CGAL::bbox_2(poly2.begin(), poly2.end()); + K::Point_2 poly_center((poly_bb.xmin()+poly_bb.xmax())/2., (poly_bb.ymin()+poly_bb.ymax())/2.); + + // path from the general center to the polyline center + K::Vector_2 dir(center_2, poly_center); + std::vector path_2_center = + PMP::straightest_geodesic(center, dir, scaling * std::sqrt(dir.squared_length()), mesh); + Face_location poly_center_loc = path_2_center.back(); + + // update initial angle from the global center to the polyline center + using Impl=PMP::internal::Locally_shortest_path_imp; + K::Vector_2 initial_dir(1,0);// TODO: this is arbitray and could be a user input or from PCA... + + + // TODO: avoid using the shortest path and directly use the straightest! + std::vector shortest_path; + PMP::locally_shortest_path(center, poly_center_loc, mesh, shortest_path, solver); + + typename K::Vector_2 v = initial_dir; + // TODO: the code uses the straightest path but since the code expects Edge_location, + // it is not working directly. We need to extract the edge encoded by the face location + // for(std::size_t i=1;i> polar_coords = + PMP::convert_polygon_to_polar_coordinates(poly2, poly_center); + + for (const std::pair& polar_coord : polar_coords) + { + lengths.push_back(scaling * polar_coord.first); + directions.push_back(K::Vector_2(std::cos(polar_coord.second+theta), std::sin(polar_coord.second+theta))); + } + + bool is_closed=current_polyline.front()==current_polyline.back(); + if (is_closed) + { + lengths.pop_back(); + directions.pop_back(); + } + + + std::vector res = + PMP::trace_polyline_of_bezier_curves(poly_center_loc, directions, lengths, + is_closed, // use [directions/lengths].front as last control point? + num_subdiv, mesh, solver); + // write result + std::vector poly3; + poly3.reserve(res.size()); + PMP::convert_path_to_polyline(res, mesh, std::back_inserter(poly3)); + + out << poly3.size(); + for (const K::Point_3& p : poly3) + out << " " << p; + out << "\n"; + } + }; + + Draw_visitor svisitor(graph, center, center_2, scaling, num_subdiv, mesh, solver); + CGAL::split_graph_into_polylines(graph, svisitor); + + // for loop + + std::vector directions; + std::vector lengths; - std::vector< std::vector > res = - PMP::trace_bezier_curves(center, directions, lengths, 4, mesh, solver); - // write result - std::ofstream out("svg.polylines.txt"); - out << std::setprecision(17); - for (const auto& b : res) - { - std::vector poly; - poly.reserve(b.size()); - PMP::convert_path_to_polyline(b, mesh, std::back_inserter(poly)); - out << poly.size(); - for (const K::Point_3& p : poly) - out << " " << p; - out << "\n"; + time.stop(); + std::cout << "option 2 took: " << time.time() << "\n"; } return 0; From c2d9c7827d218ce9682e6e82a861137f53a63ef2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Thu, 25 Jul 2024 12:23:25 +0200 Subject: [PATCH 093/120] add extrude/carving for svg drawing --- .../trace_polygon_example.cpp | 23 ++- .../trace_svg_example.cpp | 166 +++++++++++++++++- 2 files changed, 178 insertions(+), 11 deletions(-) diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_example.cpp index 5a1feab2a472..ed97206bfea8 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_example.cpp +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_polygon_example.cpp @@ -4,7 +4,6 @@ #include #include #include -#include #include @@ -241,7 +240,7 @@ int main(int argc, char** argv) PMP::internal::split_along_edges(mesh, ecm, mesh.points(), visitor); - // TODO should actually only handle interior vertices... + double delta = -0.005; for (const auto& ph : visitor.hedge_map) { @@ -256,6 +255,26 @@ int main(int argc, char** argv) mesh.point(v) = mesh.point(v)+delta*n; } + // interior vertices + for (Mesh::Vertex_index v : vertices(mesh)) + { + bool skip=false; + Mesh::Halfedge_index h = halfedge(v, mesh); + for (Mesh::Halfedge_index h : CGAL::halfedges_around_target(v, mesh)) + { + if (is_border(h, mesh) || in_out[face(h, mesh)]==0) + { + skip=true; + break; + } + } + if (!skip) + { + K::Vector_3 n = get(vnm, v); + mesh.point(v) = mesh.point(v)+delta*n; + } + } + std::vector b1(visitor.hedge_map.size()); std::vector b2(visitor.hedge_map.size()); for (std::size_t i=0; i #include #include +#include +#include #include #include @@ -26,10 +28,11 @@ int main(int argc, char** argv) std::string svg_filename = (argc > 2) ? std::string(argv[2]) : CGAL::data_file_path("polylines_2/nano.svg"); - int face_index = (argc>3) ? atoi(argv[3]) : 2154; - double b0 = (argc>4) ? atof(argv[4]) : 0.3; - double b1 = (argc>5) ? atof(argv[5]) : 0.3; - double b2 = (argc>6) ? atof(argv[6]) : 0.4; + const double expected_diag = (argc>3) ? atof(argv[3]) : 0.6; // user parameter for scaling + int face_index = (argc>4) ? atoi(argv[4]) : 2154; + double b0 = (argc>5) ? atof(argv[5]) : 0.3; + double b1 = (argc>6) ? atof(argv[6]) : 0.3; + double b2 = (argc>7) ? atof(argv[7]) : 0.4; Mesh mesh; if(!CGAL::IO::read_polygon_mesh(mesh_filename, mesh) || !CGAL::is_triangle_mesh(mesh)) @@ -79,8 +82,6 @@ int main(int argc, char** argv) // convert control points to polar coordinates typename K::Point_2 center_2((bb2.xmax()+bb2.xmin())/2., (bb2.ymax()+bb2.ymin())/2.); double diag = std::sqrt( CGAL::square(bb2.xmin()-bb2.xmax()) + CGAL::square(bb2.xmin()-bb2.xmax()) ); - //~ const double expected_diag = 0.45; // user parameter for scaling - const double expected_diag = 0.6; // user parameter for scaling const double scaling = expected_diag/diag; //TODO: do the scaling at read time! @@ -200,9 +201,12 @@ int main(int argc, char** argv) double scaling, num_subdiv; const Mesh& mesh; PMP::Dual_geodesic_solver& solver; + std::vector>& polygons_3; + bool all_closed=true; Draw_visitor(const Graph& g, const Face_location& c, const K::Point_2& c2, double s, double ns, - const Mesh& tm, PMP::Dual_geodesic_solver& sol) + const Mesh& tm, PMP::Dual_geodesic_solver& sol, + std::vector>& ps3) : graph(g) , center(c) , center_2(c2) @@ -210,6 +214,7 @@ int main(int argc, char** argv) , num_subdiv(ns) , mesh(tm) , solver(sol) + , polygons_3(ps3) { out.open("svg_option2.polylines.txt"); out << std::setprecision(17); @@ -292,6 +297,8 @@ int main(int argc, char** argv) lengths.pop_back(); directions.pop_back(); } + else + all_closed=false; std::vector res = @@ -307,22 +314,163 @@ int main(int argc, char** argv) for (const K::Point_3& p : poly3) out << " " << p; out << "\n"; + + polygons_3.push_back(std::move(res)); } }; - Draw_visitor svisitor(graph, center, center_2, scaling, num_subdiv, mesh, solver); + std::vector> polygons_3; + Draw_visitor svisitor(graph, center, center_2, scaling, num_subdiv, mesh, solver, polygons_3); CGAL::split_graph_into_polylines(graph, svisitor); // for loop std::vector directions; std::vector lengths; - + svisitor.out.close(); time.stop(); std::cout << "option 2 took: " << time.time() << "\n"; + + + if (svisitor.all_closed) + { + std::cout << "Now carve the input mesh\n"; + // copy/pasted from trace_polygon_example.cpp + // now refine the input mesh + std::vector cst_hedges; + auto vnm = mesh.add_property_map("vnm", K::Vector_3(0,0,0)).first; + auto fnm = mesh.add_property_map("fnm", K::Vector_3(0,0,0)).first; + using VNM = decltype(vnm); + + PMP::compute_normals(mesh, vnm, fnm); + PMP::refine_mesh_along_paths(polygons_3, mesh, vnm, fnm, std::back_inserter(cst_hedges)); + + std::ofstream("mesh_refined.off") << std::setprecision(17) << mesh; + + std::ofstream cst_edges("refinement_edges.polylines.txt"); + cst_edges.precision(17); + for (Mesh::Halfedge_index h : cst_hedges) + cst_edges << "2 " << mesh.point(source(h,mesh)) << " " << mesh.point(target(h,mesh)) << "\n"; + + + auto ecm = mesh.add_property_map("ecm", false).first; + for (Mesh::Halfedge_index h : cst_hedges) + ecm[edge(h, mesh)]=true; + + + // face index for doing flood fill and mark inside-out + Mesh::Face_index out_face(2612); + std::vector in_out(num_faces(mesh), -1); + + bool inorout=false; + std::vector queue, next_queue; + queue.push_back(out_face); + + while(!queue.empty()) + { + Mesh::Face_index f = queue.back(); + queue.pop_back(); + if (in_out[f]==-1) + { + in_out[f]=inorout?1:0; + Mesh::Halfedge_index h=halfedge(f, mesh); + for (int i=0; i<3; ++i) + { + Mesh::Face_index nf = face(opposite(h, mesh), mesh); + if (nf!=boost::graph_traits::null_face() && in_out[nf]==-1) + { + if (ecm[edge(h,mesh)]) + next_queue.push_back(nf); + else + queue.push_back(nf); + } + h=next(h, mesh); + } + } + if (queue.empty()) + { + queue.swap(next_queue); + inorout=!inorout; + } + } + + struct Visitor + : public PMP::Corefinement::Default_visitor + { + VNM vnm; + Visitor(VNM vnm) : vnm(vnm) {} + + std::vector > hedge_map; + void after_edge_duplicated(Mesh::Halfedge_index h, Mesh::Halfedge_index new_hedge, const Mesh&) + { + hedge_map.emplace_back(h, new_hedge); + } + + void after_vertex_copy(Mesh::Vertex_index v, const Mesh&, Mesh::Vertex_index nv, const Mesh&) + { + put(vnm, nv, get(vnm, v)); + } + }; + Visitor visitor(vnm); + + PMP::internal::split_along_edges(mesh, ecm, mesh.points(), visitor); + + double delta = -0.005; + for (const auto& ph : visitor.hedge_map) + { + Mesh::Halfedge_index h1 = ph.first; + Mesh::Halfedge_index h2 = ph.second; + if (is_border(h1, mesh)) h1=opposite(h1, mesh); + if (is_border(h2, mesh)) h2=opposite(h2, mesh); + Mesh::Halfedge_index h = in_out[face(h1, mesh)]==1 ? h1 : h2; + + Mesh::Vertex_index v = target(h, mesh); + K::Vector_3 n = get(vnm, v); + mesh.point(v) = mesh.point(v)+delta*n; + } + + // interior vertices + for (Mesh::Vertex_index v : vertices(mesh)) + { + bool skip=false; + Mesh::Halfedge_index h = halfedge(v, mesh); + for (Mesh::Halfedge_index h : CGAL::halfedges_around_target(v, mesh)) + { + if (is_border(h, mesh) || in_out[face(h, mesh)]==0) + { + skip=true; + break; + } + } + if (!skip) + { + K::Vector_3 n = get(vnm, v); + mesh.point(v) = mesh.point(v)+delta*n; + } + } + + std::vector b1(visitor.hedge_map.size()); + std::vector b2(visitor.hedge_map.size()); + for (std::size_t i=0; i Date: Thu, 25 Jul 2024 15:07:40 +0200 Subject: [PATCH 094/120] regroup control polylines when overlapping --- .../trace_svg_example.cpp | 198 ++++++++++-------- 1 file changed, 111 insertions(+), 87 deletions(-) diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_svg_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_svg_example.cpp index ab0f87b00804..faaa43a4e03f 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_svg_example.cpp +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/trace_svg_example.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -192,36 +193,17 @@ int main(int argc, char** argv) // visitor for split_graph_into_polylines that will also take care of // the drawing and writing in the output file - struct Draw_visitor + struct Collect_visitor { - std::ofstream out; const Graph& graph; - const Face_location& center; - const K::Point_2& center_2; - double scaling, num_subdiv; - const Mesh& mesh; - PMP::Dual_geodesic_solver& solver; - std::vector>& polygons_3; - bool all_closed=true; - - Draw_visitor(const Graph& g, const Face_location& c, const K::Point_2& c2, double s, double ns, - const Mesh& tm, PMP::Dual_geodesic_solver& sol, - std::vector>& ps3) - : graph(g) - , center(c) - , center_2(c2) - , scaling(s) - , num_subdiv(ns) - , mesh(tm) - , solver(sol) - , polygons_3(ps3) - { - out.open("svg_option2.polylines.txt"); - out << std::setprecision(17); - } - + std::vector>& polylines_2; std::vector current_polyline; + Collect_visitor(const Graph& g, std::vector>& poly2) + : graph(g) + , polylines_2(poly2) + {} + void start_new_polyline() { current_polyline.clear(); @@ -234,56 +216,109 @@ int main(int argc, char** argv) void end_polyline() { - //TODO: we should collect polylines and use union-find to group them when their bbox overlaps - // this would save some center computations + make sure close polylines have the same center - std::vector directions; - std::vector lengths; - directions.reserve(current_polyline.size()); - lengths.reserve(current_polyline.size()); - // recover polyline of control points - std::vector poly2; - poly2.reserve(current_polyline.size()); + polylines_2.emplace_back(); + polylines_2.back().reserve(current_polyline.size()); for (Graph_vertex v : current_polyline) - poly2.push_back(graph[v]); - - // center of the polyline - CGAL::Bbox_2 poly_bb=CGAL::bbox_2(poly2.begin(), poly2.end()); - K::Point_2 poly_center((poly_bb.xmin()+poly_bb.xmax())/2., (poly_bb.ymin()+poly_bb.ymax())/2.); - - // path from the general center to the polyline center - K::Vector_2 dir(center_2, poly_center); - std::vector path_2_center = - PMP::straightest_geodesic(center, dir, scaling * std::sqrt(dir.squared_length()), mesh); - Face_location poly_center_loc = path_2_center.back(); - - // update initial angle from the global center to the polyline center - using Impl=PMP::internal::Locally_shortest_path_imp; - K::Vector_2 initial_dir(1,0);// TODO: this is arbitray and could be a user input or from PCA... - - - // TODO: avoid using the shortest path and directly use the straightest! - std::vector shortest_path; - PMP::locally_shortest_path(center, poly_center_loc, mesh, shortest_path, solver); - - typename K::Vector_2 v = initial_dir; - // TODO: the code uses the straightest path but since the code expects Edge_location, - // it is not working directly. We need to extract the edge encoded by the face location - // for(std::size_t i=1;i> polylines_2; + Collect_visitor svisitor(graph, polylines_2); + CGAL::split_graph_into_polylines(graph, svisitor); + std::size_t nb_poly = polylines_2.size(); + + // regroup polylines if they have overlapping bboxes + std::vector bboxes_2(nb_poly); + using UF = CGAL::Union_find; + std::vector uf_handles(nb_poly); + UF uf; + for(std::size_t i=0;i partition_id(nb_poly,-1); + std::vector< std::vector > partition; + partition.reserve(uf.number_of_sets()); + + for (std::size_t i=0; i> polygons_3; + std::ofstream out("svg_option2.polylines.txt"); + out << std::setprecision(17); + bool all_closed=true; + for (const std::vector& pids : partition) + { + // center of the polyline + CGAL::Bbox_2 poly_bb; + for (std::size_t i : pids) + poly_bb+=bboxes_2[i]; + K::Point_2 poly_center((poly_bb.xmin()+poly_bb.xmax())/2., (poly_bb.ymin()+poly_bb.ymax())/2.); + + // path from the general center to the polyline center + K::Vector_2 dir(center_2, poly_center); + std::vector path_2_center = + PMP::straightest_geodesic(center, dir, scaling * std::sqrt(dir.squared_length()), mesh); + Face_location poly_center_loc = path_2_center.back(); + + // update initial angle from the global center to the polyline center + using Impl=PMP::internal::Locally_shortest_path_imp; + K::Vector_2 initial_dir(1,0);// TODO: this is arbitray and could be a user input or from PCA... + + + // TODO: avoid using the shortest path and directly use the straightest! + std::vector shortest_path; + PMP::locally_shortest_path(center, poly_center_loc, mesh, shortest_path, solver); + + typename K::Vector_2 v = initial_dir; + // TODO: the code uses the straightest path but since the code expects Edge_location, + // it is not working directly. We need to extract the edge encoded by the face location + // for(std::size_t i=1;i directions; + std::vector lengths; + directions.reserve(polylines_2[pid].size()); + lengths.reserve(polylines_2[pid].size()); // polar coordinates from the polyline center and convertion to lengths and directions std::vector> polar_coords = - PMP::convert_polygon_to_polar_coordinates(poly2, poly_center); + PMP::convert_polygon_to_polar_coordinates(polylines_2[pid], poly_center); for (const std::pair& polar_coord : polar_coords) { @@ -291,7 +326,7 @@ int main(int argc, char** argv) directions.push_back(K::Vector_2(std::cos(polar_coord.second+theta), std::sin(polar_coord.second+theta))); } - bool is_closed=current_polyline.front()==current_polyline.back(); + bool is_closed=polylines_2[pid].front()==polylines_2[pid].back(); if (is_closed) { lengths.pop_back(); @@ -317,25 +352,14 @@ int main(int argc, char** argv) polygons_3.push_back(std::move(res)); } - }; - - std::vector> polygons_3; - Draw_visitor svisitor(graph, center, center_2, scaling, num_subdiv, mesh, solver, polygons_3); - CGAL::split_graph_into_polylines(graph, svisitor); - - // for loop - - std::vector directions; - std::vector lengths; - svisitor.out.close(); - - + } + out.close(); time.stop(); std::cout << "option 2 took: " << time.time() << "\n"; - if (svisitor.all_closed) + if (all_closed) { std::cout << "Now carve the input mesh\n"; // copy/pasted from trace_polygon_example.cpp @@ -418,7 +442,7 @@ int main(int argc, char** argv) PMP::internal::split_along_edges(mesh, ecm, mesh.points(), visitor); - double delta = -0.005; + double delta = -0.0005; for (const auto& ph : visitor.hedge_map) { Mesh::Halfedge_index h1 = ph.first; From 0045843cadf3e1e546744ea454d8bbfd58a3209a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 21 Aug 2024 17:55:28 +0200 Subject: [PATCH 095/120] create skeleton of a new package for Bsurf --- Documentation/doc/Documentation/packages.txt | 1 + .../Polygon_mesh_processing/CMakeLists.txt | 27 ------------- .../Vector_graphics_on_surfaces/Doxyfile.in | 3 ++ .../PackageDescription.txt | 35 ++++++++++++++++ .../Vector_graphics_on_surfaces.txt | 22 ++++++++++ .../Vector_graphics_on_surfaces/dependencies | 7 ++++ .../Vector_graphics_on_surfaces/examples.txt | 0 .../fig/vgos-small.png | Bin 0 -> 18036 bytes .../CMakeLists.txt | 38 ++++++++++++++++++ .../geodesic_circles_sm_example.cpp | 0 .../locally_shortest_path_sm_example.cpp | 0 .../straightest_geodesic_sm_example.cpp | 0 .../trace_bezier_segment_sm_example.cpp | 0 .../trace_polygon_example.cpp | 0 ...ace_polygon_on_polygonal_curve_example.cpp | 0 .../trace_polygon_on_svg_curve_example.cpp | 0 .../trace_regular_polygons_example.cpp | 0 .../trace_svg_example.cpp | 0 .../Bsurf/locally_shortest_path.h | 0 .../Vector_graphics_on_surfaces/copyright | 1 + .../description.txt | 2 + .../Vector_graphics_on_surfaces/license.txt | 1 + .../Vector_graphics_on_surfaces/maintainer | 1 + 23 files changed, 111 insertions(+), 27 deletions(-) create mode 100644 Vector_graphics_on_surfaces/doc/Vector_graphics_on_surfaces/Doxyfile.in create mode 100644 Vector_graphics_on_surfaces/doc/Vector_graphics_on_surfaces/PackageDescription.txt create mode 100644 Vector_graphics_on_surfaces/doc/Vector_graphics_on_surfaces/Vector_graphics_on_surfaces.txt create mode 100644 Vector_graphics_on_surfaces/doc/Vector_graphics_on_surfaces/dependencies create mode 100644 Vector_graphics_on_surfaces/doc/Vector_graphics_on_surfaces/examples.txt create mode 100644 Vector_graphics_on_surfaces/doc/Vector_graphics_on_surfaces/fig/vgos-small.png create mode 100644 Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/CMakeLists.txt rename {Polygon_mesh_processing/examples/Polygon_mesh_processing => Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces}/geodesic_circles_sm_example.cpp (100%) rename {Polygon_mesh_processing/examples/Polygon_mesh_processing => Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces}/locally_shortest_path_sm_example.cpp (100%) rename {Polygon_mesh_processing/examples/Polygon_mesh_processing => Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces}/straightest_geodesic_sm_example.cpp (100%) rename {Polygon_mesh_processing/examples/Polygon_mesh_processing => Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces}/trace_bezier_segment_sm_example.cpp (100%) rename {Polygon_mesh_processing/examples/Polygon_mesh_processing => Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces}/trace_polygon_example.cpp (100%) rename {Polygon_mesh_processing/examples/Polygon_mesh_processing => Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces}/trace_polygon_on_polygonal_curve_example.cpp (100%) rename {Polygon_mesh_processing/examples/Polygon_mesh_processing => Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces}/trace_polygon_on_svg_curve_example.cpp (100%) rename {Polygon_mesh_processing/examples/Polygon_mesh_processing => Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces}/trace_regular_polygons_example.cpp (100%) rename {Polygon_mesh_processing/examples/Polygon_mesh_processing => Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces}/trace_svg_example.cpp (100%) rename {Polygon_mesh_processing => Vector_graphics_on_surfaces}/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h (100%) create mode 100644 Vector_graphics_on_surfaces/package_info/Vector_graphics_on_surfaces/copyright create mode 100644 Vector_graphics_on_surfaces/package_info/Vector_graphics_on_surfaces/description.txt create mode 100644 Vector_graphics_on_surfaces/package_info/Vector_graphics_on_surfaces/license.txt create mode 100644 Vector_graphics_on_surfaces/package_info/Vector_graphics_on_surfaces/maintainer diff --git a/Documentation/doc/Documentation/packages.txt b/Documentation/doc/Documentation/packages.txt index 9b1fa73107e4..e8a1738d1ee2 100644 --- a/Documentation/doc/Documentation/packages.txt +++ b/Documentation/doc/Documentation/packages.txt @@ -118,6 +118,7 @@ \package_listing{Surface_mesh_shortest_path} \package_listing{Surface_mesh_skeletonization} \package_listing{Surface_mesh_approximation} +\package_listing{Vector_graphics_on_surfaces} \package_listing{Ridges_3} \package_listing{Jet_fitting_3} \package_listing{Point_set_3} diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt b/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt index 0a0f5c745e1e..718ab1096a9d 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt @@ -58,9 +58,6 @@ create_single_source_cgal_program("isotropic_remeshing_with_custom_sizing_exampl create_single_source_cgal_program("triangle_mesh_autorefinement.cpp") create_single_source_cgal_program("soup_autorefinement.cpp") -find_package(NanoSVG) -include(CGAL_NanoSVG_support) - find_package(Eigen3 3.2.0 QUIET) #(requires 3.2.0 or greater) include(CGAL_Eigen3_support) if(TARGET CGAL::Eigen3_support) @@ -84,32 +81,8 @@ if(TARGET CGAL::Eigen3_support) target_link_libraries(delaunay_remeshing_example PUBLIC CGAL::Eigen3_support) create_single_source_cgal_program("remesh_almost_planar_patches.cpp") target_link_libraries(remesh_almost_planar_patches PUBLIC CGAL::Eigen3_support) - create_single_source_cgal_program("locally_shortest_path_sm_example.cpp") - target_link_libraries(locally_shortest_path_sm_example PUBLIC CGAL::Eigen3_support) - #b/surf - create_single_source_cgal_program("trace_bezier_segment_sm_example.cpp") - target_link_libraries(trace_bezier_segment_sm_example PUBLIC CGAL::Eigen3_support) - create_single_source_cgal_program("geodesic_circles_sm_example.cpp") - target_link_libraries(geodesic_circles_sm_example PUBLIC CGAL::Eigen3_support) - create_single_source_cgal_program("straightest_geodesic_sm_example.cpp") - target_link_libraries(straightest_geodesic_sm_example PUBLIC CGAL::Eigen3_support) - create_single_source_cgal_program("trace_polygon_example.cpp") - target_link_libraries(trace_polygon_example PUBLIC CGAL::Eigen3_support) - create_single_source_cgal_program("trace_regular_polygons_example.cpp") - target_link_libraries(trace_regular_polygons_example PUBLIC CGAL::Eigen3_support) create_single_source_cgal_program("geodesic_isolevel_refinement.cpp") target_link_libraries(geodesic_isolevel_refinement PUBLIC CGAL::Eigen3_support) - create_single_source_cgal_program("trace_polygon_on_polygonal_curve_example.cpp") - target_link_libraries(trace_polygon_on_polygonal_curve_example PUBLIC CGAL::Eigen3_support) - if (NanoSVG_FOUND) - create_single_source_cgal_program("trace_svg_example.cpp") - target_link_libraries(trace_svg_example PUBLIC CGAL::Eigen3_support CGAL::NanoSVG_support) - create_single_source_cgal_program("trace_polygon_on_svg_curve_example.cpp") - target_link_libraries(trace_polygon_on_svg_curve_example PUBLIC CGAL::Eigen3_support CGAL::NanoSVG_support) - else() - message(STATUS "NOTICE: trace_svg_example requires NanoSVG and will not be compiled.") - endif() - ## create_single_source_cgal_program("interpolated_corrected_curvatures_SM.cpp") target_link_libraries(interpolated_corrected_curvatures_SM PUBLIC CGAL::Eigen3_support) create_single_source_cgal_program("interpolated_corrected_curvatures_PH.cpp") diff --git a/Vector_graphics_on_surfaces/doc/Vector_graphics_on_surfaces/Doxyfile.in b/Vector_graphics_on_surfaces/doc/Vector_graphics_on_surfaces/Doxyfile.in new file mode 100644 index 000000000000..e1d1b533f789 --- /dev/null +++ b/Vector_graphics_on_surfaces/doc/Vector_graphics_on_surfaces/Doxyfile.in @@ -0,0 +1,3 @@ +@INCLUDE = ${CGAL_DOC_PACKAGE_DEFAULTS} + +PROJECT_NAME = "CGAL ${CGAL_DOC_VERSION} - Vector Graphics on Triangulated Surface Meshes" diff --git a/Vector_graphics_on_surfaces/doc/Vector_graphics_on_surfaces/PackageDescription.txt b/Vector_graphics_on_surfaces/doc/Vector_graphics_on_surfaces/PackageDescription.txt new file mode 100644 index 000000000000..10e917796a0a --- /dev/null +++ b/Vector_graphics_on_surfaces/doc/Vector_graphics_on_surfaces/PackageDescription.txt @@ -0,0 +1,35 @@ +/// \defgroup PkgVGoS_Ref Vector Graphics on Triangulated Surface Meshes Reference +/// \defgroup VGSConcepts Concepts +/// \ingroup PkgVGoS_Ref + +/// \defgroup VGSAlgorithmClasses Algorithm Classes +/// \ingroup PkgVGoS_Ref + +/// \defgroup VGSTraitsClasses Traits Classes +/// \ingroup PkgVGoS_Ref + +/// \defgroup VGSMiscellaneous Miscellaneous +/// \ingroup PkgVGoS_Ref + +/*! +\cgalPkgDescriptionBegin{Vector Graphics on Triangulated Surface Meshes,PkgVGoS} +\cgalPkgPicture{vgos-small.png} + +\cgalPkgSummaryBegin +\cgalPkgAuthors{Claudio Mancinelli and Sébastien Loriot} +\cgalPkgDesc{The package provides functions to draw and manipulate basic geometric primitives such as geodesics, Bézier and B-Spline curves + on triangulated surface meshes.} +\cgalPkgManuals{Chapter_VGoS,PkgVGoS_Ref} +\cgalPkgSummaryEnd + +\cgalPkgShortInfoBegin +\cgalPkgSince{6.2} +\cgalPkgDependsOn{\ref PkgPolygonMeshProcessing} +\cgalPkgBib{cgal:ml-vgos} +\cgalPkgLicense{\ref licensesGPL "GPL"} +\cgalPkgDemo{CGAL Lab,CGALlab.zip} +\cgalPkgShortInfoEnd + +\cgalPkgDescriptionEnd + +*/ diff --git a/Vector_graphics_on_surfaces/doc/Vector_graphics_on_surfaces/Vector_graphics_on_surfaces.txt b/Vector_graphics_on_surfaces/doc/Vector_graphics_on_surfaces/Vector_graphics_on_surfaces.txt new file mode 100644 index 000000000000..0a50533331f7 --- /dev/null +++ b/Vector_graphics_on_surfaces/doc/Vector_graphics_on_surfaces/Vector_graphics_on_surfaces.txt @@ -0,0 +1,22 @@ +namespace CGAL { +/*! + +\mainpage User Manual +\anchor Chapter_VGoS +\cgalAutoToc +\author Claudio Mancinelli and Sébastien Loriot + +This chapter describes the ... + +\section vgos_definitions Definitions + +Section on definitions here ... + +\section vgos_examples Examples + +\subsection vgos_examples_first First Example + +The following example shows ... + +*/ +} /* namespace CGAL */ diff --git a/Vector_graphics_on_surfaces/doc/Vector_graphics_on_surfaces/dependencies b/Vector_graphics_on_surfaces/doc/Vector_graphics_on_surfaces/dependencies new file mode 100644 index 000000000000..1972566b0b5b --- /dev/null +++ b/Vector_graphics_on_surfaces/doc/Vector_graphics_on_surfaces/dependencies @@ -0,0 +1,7 @@ +Manual +Kernel_23 +STL_Extension +Algebraic_foundations +Circulator +Stream_support +Polygon_mesh_processing diff --git a/Vector_graphics_on_surfaces/doc/Vector_graphics_on_surfaces/examples.txt b/Vector_graphics_on_surfaces/doc/Vector_graphics_on_surfaces/examples.txt new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/Vector_graphics_on_surfaces/doc/Vector_graphics_on_surfaces/fig/vgos-small.png b/Vector_graphics_on_surfaces/doc/Vector_graphics_on_surfaces/fig/vgos-small.png new file mode 100644 index 0000000000000000000000000000000000000000..aab567220e559ea5a21aea1035e1d2896e3be101 GIT binary patch literal 18036 zcmeHubx@p5^B@p{h2X*6oy8Xq!2)FQh2Xlt0*e#eHE8ez0t654?j9gO&grc@_uog=vd=!#(=*-EGTlA%UQI>eITkq<5)#sL#n-YLh;NTS9}G0ae-R6z z3B*^Im)2WX4O4eoM`s61s2zmX)zc9|3-N$jA^_)LYU(&Wv>5kWL}EcG?9>D0hSe1N z*D_rN<=*dc*W+1G2Da{l6wu|-ROFmTL|7Z0`wHHp%1}@~!KG%^jIyOtL9UY}A1qI3 z^PgFRnq)&$xaCh>P4z>E4tb>HtpjglC6*SxHsfbwjz3TH1IP`$ySqb6cwC9Hb>@*bczDVr4UN;Q`xuM)h)*!;oYJ1v0cp-Pwex5gGJ5)yI< zR7OTkQAXw;#2`rc>>Drfx<`#DR8=!yPEYi$bT6HZTXj?>V0?9aa?d?l(|UB@)NRvc5unPbQrT>Z?|?UE zr{^|$+pDgf0$z1l#SA8A^jmLUJ*C)6=s_8aCZGK-?v(VF^P+O1r_|26ce0lkJjrJK zt*O*`akUgoQ98-uZ~FC11*c1f4G|ls876@&tM!*4cFtZXT#+|E`z$ZqzxPCWUE>=V znPC9M<_E2q-?7xSgnkWBaECcG;pJl#%s(1A(D2Jb?ZJ`xynPnZgVnOq(Ty#a$z|fX zrA`zTay)E#wYh}NyK@5vaHo~}d^Y}0>s1v-Yu32%Mqrn|iOEQY{uX5+?#mR*}Z3vs12gIGcBCFqY@I_PPk783M2f*=6MQ3hfSeeLB8(ezT$GWW7I7qOt1 zlEf1C5Jez>L0nB~Jz#eBE}|Y1^nc+MMLhp`%|lQ77l^B^1pQl(8m)|jGlW)vTYwwD zCFcQs$44)TMJw)XVJWI1EB|*Ch+h)))~>FOqC7nA?(W>~{M-)CRy@2SA|gBhJ{~?k zE(8RZi>JM-sRx(63&S52f5RaQaWQv>I=Vs~>}mhtG&OT@bCsZ{N3_%aV}3A45a^%i z?Op!P1q45MJWL&Vc)0;QFc{Cj_i%BQdxt>s_X+(Ud$?#Jbd*N};^N@uYz~oo2eEf$ z_;(Z*=Kt*P=;mzq7daN@JP zL=Y$l1n>%TnTr4fxdcS`%(zTNAbebAWEL|As=<8H$ifQ@ekk)gM$A z2vh<9b6ydCVL>j4sksQ30FY0R%T&M&$ORCB@FK<#5ds3t|3YP9E-LTf3^PS6ClqFC z1>teDxB9E$55q;J)f6S@`M3f9dZT7%>S~GTAVL2IYVYRpuLdnB45I03`iGmm!h8aJ z0zyIn0YO0l0b$_3fV3gbE(l5dgOeA)%`fm*%b#TtMG%8vtmz*)MIiX=J%Scd8E1&8 ztAn$agM*y}{huk({^|M8W)MQ1EKFTZWldcn2&4c$0Z{-@l#f@7S5TCXUzAUP1HdN= z_&0b53#g^%{|)+&^3aO^ZOLClT@d4Y{`Kl_8l?$w`rEI+{j`JrrAoB4e<=lG4gW@h zi|IRv#b5J8;QCvWxwWai6$D{D{w}Wncn|$Qhys957|0K>5aKd3=e6V#5C8~si2#Hx zxh#du1kE9Qg1kb&ze&?SvAZ}}y1JV>L!_+`d_?dHA)bHnik9`Sg<|{9xwu|BWZ&e*yfPkwNtP+dG8uLfAr{e;UKT^9(WH|DS(< zUyJ{rL(tOxcaZ-PfB(y_|FY|U#DV`2@PDQ2zwG)Sao~Rh{9oz%f5tAXe}z*Jd&Dlt z9T6<43cW!_1X^fj$_lc7guv{bo z(`>@)L1#wkc-lSyC2hwUgH%Xo1Ty-2SZ81iZCC~p)_!M7>7WzADE+KOltjG5qGxfj zzRNV(W$VmPT-{i?{)m0Gq9W>TvG&?Koh9$xsis@QJ&MJn`*x;i-mqY|it@(=5f8Mh zOHrh2q~%~Yq~k8clL-})PbChLZLGDD~`#Yf5z%)Mk#d0v=KVUne z!0S&T>eOFObsE>>hS7QZE8lJ5$m#M)h{<#;Nxj@M67BL#QBPs&CpGd~d77kwzWqxX z$)U7NGl_|Ug2K+uuDY(yEnu|GEGLE+8|}$Z&P!X6Sgfv*o~6BgxhF_BY27mxF96Rv zA0MTbw=p&t4+rDemxgTvxmrpEiDiw~6mR)f(vOrr-@?WGNq_*T$@h#{*x{FnrB|AL zg#u;6_#6r+UJ^JISqq$B0&to2ENKc!^v+%CBi8|47qv9A_URa4X8Zib+C zN{W+Vc)D;oFZuK9AL#x;O5oRHJx-J%yp0+;G4iO$bJj^3zuG;c2n$tKfup!Ea0uQT z587P8Ul)&hdz)@bY+CthdUTZ)<4UG!SIBt4*x)VC*KF~bwOLCKD{apmbyG~R4^&8Q zIZ;hXovk3BnwoTtyf?(OAcJM;!c8@`y#rDwnr}wF{|x6sMe$Bxd3+6*Qyx4#}0t|j$sl?VzYX+n=2|<=wGZNof z5#@%aWJl*r+tB5Qk0UzoW|f?XQ$HZ0?iwGCPO^CK;JPniGuK@T1AY z+Kw&7CW`H^(`GI%i89Zg)y~5V228!Z3E^&R!wD;nsdaT=uY1WiNbe|Z;L#kY@)$w1 z%R~9Y`}>D@Xmqb4`lS@qpTV8pF1;N%^H~MTi{i}r(GFEJD)vVYU_w(8wnX$OCIq_9 z#Di$9RkCK%PCUL_3)qyZHB8cB$V7?{I2nqMF~pkJ*3a}^`L@J_1Z!Z8oxOegQ9zF06(!U3@_+H zTmhqMtXCL1Y{=c?^m)_Duyxz%bX^gLMlyOlZQT0%%`Umi+1dB8G5y?AII^QIQFP+E zAt7#07gE`a;bG-GsC>zE4*baF@UE%B_a3q2o+(wWtuY4$GKk>~1V4vI}tpG`}rSVRuG*x*?_p7U`yKinp_4W1b?ClTF z%7VL)=upe|ggQbTcWGmNap9!FyyP`k6s%lnsc!;&Sssg%Y>fDMf9u&!2yD#N*?tyv zE1o5lGjiNnl@k)0@m%f>r5Q3FKH4DBNzuMelv2S60{LwmNy=8hZ`&Oj+nV|ufnQj$ zYPGe%gZ($6R->7o^8tmdSnT=RuA;R$#IZFb(b(ZK^+LkOcSteMy~2VGLmXdEBz0aP z6b^W}(###Z_A}8Sr;iBGzfR_#ic`i_UxHKqhsKS#~U!1wCp4}02tfkyul96 z_t;(u_gns=-X3heJ|euhJWXP3c81#?>-DH>Z{TqDEjP|@N%+_7tUQNY%YwWTZV0Wi z<_vu1qw!nohXiO^b$xUH;V4c`O-+dOkLxeR#g2x9jz9|_C0>IVYP+gB5%A(F-1N0> z%7uJ&VBB6a>C0yBZhVi-I2H*CWD1#OMPd>X5ua_mo|TXD$_-6UYoftj0gQ^515eSg z3xwK^e4H0=tP1@6ZjEIL4LdGw^H@Z@)>#Wxv$8pr^OAaRXX5n{TF+r z<{sl|q@q$?OGtehG=-BSoL3o~tLF%e17p?i3t9Z0bDL>fk57ap>Fz z*Tcv@XE$zPJ;?MRb<+@dWV}W@hhs3n!qPIOXoswMfxDkHueK?x%LK2nJeVOwLN{@_ zfEA(4Na{qqDg^dR?TVRO*M0%TM0&Q7)tydEVl84ACRXkw15Vqu^F)yMno=Ja6@OA; ze<)W*s2ZnoHK(nRfo{r(WpsBykzX9b(it>9BA-^%g81v}>%UbeRkM;!*;|QI9}S1r z)gxyf-JW~3^*5++DpL-4WGaw`$*MiI7B!xJ=<EuV3u`@NWTv|c&fcd+3^*dLEA9vj_ed4CY+<&yI>RNmsdBZOzX60{* zM}Ue9P18tuI`?rC?~O+G`{7S_MxM0m>tf!wDK~4q@%qr})ytO&s zHdoyiDI3h%&qH~RYos2}-he>&%Ls4ofOm~NImyo`oC?Um=|6K^ILcCoIXL7NN9NBr zyu?t+5Cw7A2*wx20P7^WVxPPMP{)wQ4bCFa`fBSORo1mBlFAFIK7VDP zqd_d9n^Mzau39-xZV0xRpC>Kbxww@zwGPxwdHcEsd+F@hH>=w3OF!wUcwy;6uy!%p z@I=NMBb~CQ_|ky&$Y5gO_PZAAvcYO>04rD>%#=HhL5J$%;zDZvdiVJT5GPr~!;Skh zgbb`}w!8a@7p-9wV`$DtwKT1clTIEZGN<#*7l2kKSf(9S;kUuzbF<}lc)K}xTE^mN z>yikF#OpS#`J6v}l-nKA<@gc|KDrJAgPk^3nY#Z7YZz;!b;G=cXa7tFEN^e((DgVk z@0*}Bo=}UvQtG(1EMADvqrS1#TXaeFpWPMrcBEl;<@$Oq?pEk!M43lxw~}SrCCDaP zgt+ZT^WPf%JUlZsiR9MMS>$5BJUplr_DoF?fx+=4eeIfG>+J7N87&dpg6iUrpEBf{ z?c1c4Y^iE3ft0J>)PW=b6~5^4Jl#k@!61jJ%v-B5ldvv>{G^WRe4z@=SD5TZo_-%;N=ES z%_DLmj2w7iPKK!}EhE+fX0|RhziR|Qc(I!4nk4yWb6c|+667&FR{kyA^|8ZZgmX9DOXjaW+&^p;a>u$7+Fn@=h&23$O8pU<<0Iq;LmY`e9f zN}H5`V}px?6^P`7&{GLkfcrRT(F1G!TlIQe;!9{=XdfmRBK1)HUaDgmc@-%Xw)Oa4 zxqUk;ofRNFWr{Wbb>q3-6!XGI%HDbkp;iG$o?1%ya$N2A6Uv`OeVgfQ+Ic!D_V%vZ zRf**6?3mw-ym6B4tHq&_CL2L)$=D);c6N5s?D_v}3fk*qS(uz!V7_4*DZb83x|UF7 z7tpnCiU+$O?{A%(*Iw6;PjqBu$zc^toIT>QKEX&$e|_zZ86 zjEwbj*|8EBAs4X&MRAlp_wJ8z8_+t_VmJd8%dbI~=o!}h_{{Zh*)r=*PvzPAtOXcH z_~d)&%Cw_-)5M1-2!JJs#qg;?!#xw{B?#wPI9xNgkOF&rra99s;k5 zu;8JbAjc3H(mso#eD2(@eWUNfb&L*<+Xv84vxB=7j%#UVOPQv#x^mTroj_!fo}Gd0$VRsD9IMGCgsUGK>yhVF9>OC;&MiOEr4*_1%+0xHHV+xwvU~kW2f>MTUdEh>?qy+%$?sM2&2m# zM*73x%(?I}iK!2J7 z{31RxkG%|nnHzDCL6uY4cXJa^qT`^MQux{>)ndP@NrYM1#Q>f?U$%W|aF`y4M=y(! zev=H*Z3+HNi=nQh31&Jwt0{G81gayuJB64CP^%|1AuJT-OVThcw+H*(gs;U# zpbDW$a>Oox#MmP+>t?EOQ79(a8~>QNeC-2-&HeD#%1sQ?4)aIN!jJ8K8QWdYw3Jpb z(07z5;Teu6DXkSxNZFW3Bj%|R7_$)$&XbyhV;HfiBo9z2j5@j%DlUB_=vhHB$Br6^ zt*LA3sw>gic5VJF;!drewQnKbJr(*;OkfTBD)6gSy{aa|`R-?c0`+-(9A)-od`O_e zvIwfiS}v~`gE$+0c~pGDgcEJ-i9xCW89zA-s%c!=NWuY3`j_ZnItfSfLV~uI<7r6v zQNsrfjVWo;P7i@z(jRUj0GmEp^=G-DwF#uT1K#0b%x7n&8Bsc{g5^;!F6+wo%%V-* z-5at6B*q+mR#g)VlETQBD~y}E=XTkjh2ymGce)>zF8_8L`jWl0=Xk9aw=l3Yw%Kt< zqD8`u$Mc;KE&KV|S+w{V88%vJ#r$V29dJf&IXyz)!=8%(`1zG}wXMq{*4~}1)M=M4 z>e3DKBMb;HbsXz(!lzl?dLf}kuML85Tp9>uhZv$Ru#@bFTC!K|XYxdaM@cVM!TSTj zmdIgUw3$`<$tK#yrI2SzZU~Ypy`iESQE$8I);9h9?Wm?JiSB9Kmknm67!jYmzR+iy zloA?vVYHB+l@Phi6|5g%xy*b}d6t!k`qV2ZC?(%IU%lDPYQX-%NZ4dUC4F|bb@2}7 zv&1rXCQqrR{le2|87P^YqLtq>`ZAGGL|4z$HFT@Rn(B0J4<>ouMS*JCxmVHgFp6Yb zh6v&5W9f^Pkh#Gk7TyXgSKb!5;xDg%xg3AzkCtaJfSX!bg_n&ym+!$>;wUI6NdM3= zf5iWDfoaTHdC<+fcAjcz4jWSePa$Qc$@5GU;a<0Yn4XF=kg@bSrRxiq^Z%MPHHRf9 z>o*{bYz*7tf-@WcNYx<~F$1VIL9?~7+uA%Bf~?yS$1kj4b;3HiCH1;N3e++E%8S|$ zZ&{REH=(kvYWQqvHsAL|Xh5ia^!+E@ZdKn%AJfb)FVtU{zpG!qejoADi)elxK)M>I zphmpS$)bTpRtIR0gJX?5p|2^Tu;FJjf?!_j=*UWB#Y1!$^f|1S?(V%F`w0bfdlWJW zPO)oPwZx6~VR)EjDa`iXy=9RqoNC3dnkAt^o!e0u1s}?X`{2fFgSbAO)#eUQe~R7_ zP{|U>g=qxU)p2cZ%}0mH4BZ~B%{*RKeAN3`_&Jk)th!(~uyWK-0CF61lh7~R zDRG!R-}u0>!)5xyvkMDyO3F-!hld{u*wl&QqX&dOCM4jgKT>AZiM{ZGM(Nhcv*-`- zhNWxz7SHOo_Vt9|8q!9XTk6B~%2GyJ5TT^qil^kn*RjzJM!UrfC9YpN4?NjH<^6!EMrEtP7?e?WW28F(|T)+lmc zOzbTy#c_i0R807hOqCnu8^t|V@N(7VTVU_M?!n7XmSu1cwgzH&Ediv*8)N>le*Td; z-`JE#HijKOCMW6Od=?&&8)7`)cI* z^hoN_DyYw#v!cR1;_E_{NUhZlTnQS2Ty{W5w!dHf&OBXgsdZ?4 zS_gQstf;*@@LHAoN5&I#R9$MPyN~&rH3D)Ra=&&E@(Tvfjalq(&GdiRf+o^q+ObVq z_gH^+vU(azEjY98-f}lo@vz%&qKsna0E?d~3cG*mc_olQ#@#PdISEoSgOnCxtNrCLkIAok(QEq;m?G-RP6N zK4N?QjH)gsEfS*=glp6bh9)EYOnW;6p%Y^Noh+|8{=ZLmc4U&)c^mcZuTHk~9NuX_V8XlWJ+h!{M(r zj_wUO~30Jl(<%5jA~^cMPhUZ$Kw=w%oCR?c(l40;$MOYppbf z>gd;OtXO$#A1uf+X_jlviGp(2@yczR+{R>}=w&W>uDZth5O41m%w6&T7Po1|LkJ!= z8fH&6vVa~(9u1!VI)lMu&13J$yXdo@xo<8CJEGKdF1&%Uy=M}6=es^C2?cC&@re+! zo)tWEY|Y6@wX<&BloiroT_ekfI=F7L^QYuo1vOQP5Q5d{Ye~GvX`*&F#s>TvQ zXAhGY>a%=CBbXkU;ag$`?auG@8?>)Cj3r03I)XJ`DDw4_2JT(%NL?VKZH+peq%VEB z9eZGN>3E><7$vLDqffz$k#8(1S#L-st>EN;YY6#hWucU95Y9LJzLqXh|9T*9()${} zx=A;Bi!K*$sew09D5c*(*Ju&F643tF=^xH4ra#LRXsS~G-z#CMu7*!DO?lknI#i?IA1#M z-(kU!bPYUYksKWa%6rYUMWiug9Z$useW~raypX#YIQ-FkBtFl<(R$i9=CZ!;88kp+ z1+*LA(Q|j-m|!{;Z(!2yyl-2&ZCm2+udHsB?O0-orq@IG>xQ^x?*u>Nd88cfA=|Q( zAf#BDtb_^` z1`l)a2>%Nmk@L>`#?0^uEO;^^Zxc@v^E#XT1v`e?m_)eGHa?|JPF;|mpJvL_T>R@y z#^^7cWufe6C}l}6XjtXk%?Y>8vesfMd_D~qs%mD4$XR`G+1|k&PtWYf&`(Xtv?@`! z;1@u{qo$yMwO@zMTl^M^isD+ePhVcgJZ!yTFXLo6qAZ-BuG14(JO5PU+Pu(4NQsIp zjW=QM4$IK&v8{M`npp^Rx6kzB>2=~W&{=Ixpsn2eL1}1laN}0re*IMCvd~)M#IHJ6 zfvu-<2ZeQLV$*)2=Yp}h^msU7XXV4{v`*7a_p@K(?cX$|Jkx%Mo?W+F3Cn)coDRJf zAA`9Tz0hmHK&VufJpTsJ-f)%;GZ#ZgNnCZc5ofK+v{^Gd%R6&8b;)9-CW|R;cRn_v z8QXfAnmTvo0|0BBy&BhJ1tV2kvLCUl5f($WbKL`~fMm3ZJcG8Nc}ZGwP`Rgigh@;b zM?T@h#y12yV;QEqsebR*?%CexCK%o|04escr-_V~bCc-Do+&%ns0~$Cjay!*bh|#H zWj*YmfV=wO?joKA3O^%7&d(?9d{>@*J!f4r!Zj}qFtfFdVyIh{K>?NzkB$;6%_cV2 zwDtLJ36-f@hDVV+hs;{Os~;F7q?_cYGJtCb^cHDiTUmN+^gr+B@qfSODSh=_cRjx6 zhrQOwL0vQW(p0s)eL*b*`%j~f7O1m=6>vB zf7oI8{vEAXRibaVu6^8@6*2N#(XU6qs=6mQJz*+!I=w_K>DO(l+wAV}tU9f<}Zgui}_704kOp?QmfwKMtME{t> z3&Vf)HZ+_lXz^g)?r}Ej%$J6@l;^c$jQUmg;NrY#)>cyl~U<0sc$`@va$-9y{Qqw*IL5XNT2feM!)aQsrFWpHZ zm?hF>>ovSE+$)%5IpmH;)05&P`uF2=G2nEFNXkp>z1Dqag>5yai8&_4KC6Mir)bPq z$~7t)O7r#ffM#0NE*GYg2?7rPYjgw8)qBdeBfq;FZ`n377SQ2=N}p=~V{Ex68u&Np zMG>6idKsO;AbI?@|IDDxCw^LiCR>nSW}xOYyO^DfldI;ejK{_Lf^mF>fhd(HF+);U zYT&db>bVo;*tzIKRzd<4_n8~Y+m#5wO=?Nj9EZg+g`y=86{`awaDMaQ1u|xs=0(>qmI>KFf zDK;HTSH8AbMe_tud4f)?Y|&=6lh z``%>Rsi<(dk?EB=4WxXjLO4SI@h*D#JM@$xT-Wb}i8#|g@arT~>+wr-&!zfhDSrOp zqlbI{PN~PjM?%P_#4R>m%a3>WEUKnQfpbexx%9@v09&}8R`Gz=QUO{^aP&yo=bfVG zD8>Nl^vx-dtGu4h?VF0P>4VHjT6G39PO^k;W{n&}wYXjyg_$3yQYr(>Ui4glV*!dv zJc~_kcT5#EL%GgN@5INEe4z|*p-F=vO19}4FitATFBcK+B1Ut z(*2%9t$r>ZKI>|5@e3gY`h;`&fN)nvbu5=lQQ|{_yScs9mRVjJ#q+V{irTx2JMX;10j#O<< zs}V6NCIBd7N#kJUm|L$=zkItFoY=Mg=s%2AhJ5=@VUNrE6FI1qDii9UsO^vZ_X(9^{+{__taQr#+*04 zCGYCN)t3zLW3b3#9$mzAyJMdVwBxjsAy1su*NGtZ3^u9>IAvNw z$^^rN(F5P|IigiF?WQObgW?vhnbuA!Q^=aG{a#K*zBRC0`z<-)HaM~G?Jk5t=dte_ z=d#)>wLbQ69{2l<$2VK@(z_^fCe6q{GO=*%$KDm@6K{6W1#TsmIRf3CFse zimskgMe#lxeyd@6I4#DL0xJXy3ZZiNx#JPZ`SaSLYfts_-~HvJsZ z=!pahB)GPqq@r|xtoUqFbqUJ2P8>QBLT5)j%y=_6+{?8h19wVwWXiPr=m%2`s2o5??!bfKD+dDeCL_wNH z{s-WPYuv{td$t@gp4IJ@4|8sq`uKV!dJd)EYCaoWSGt5(f7?qmfxb3m1B4aVIt8pv zz0r=>%*Pxrsc`x|A*Uzx=#sMey+1mP&65rTWo$RKE=S)k9`(s;pBzP(SWwRQ_z9V! zp&?Vl24>sh&6=F&i-!TPM)^q@Yt5C<(iV{AI-Y5mg=C1)?`fx3bE+r5@|uWk{*lJ0 zh$!b(EH$L-fa^*XTU#DX2An*u;}x!t{Ej-TyuH&A)zyZ1Zyi{4A{9v(zSO=mWjHX_ zb9E5c@$k;~uH^B;*!lOWtvlwHLCN8HF!MFV^YD61%5Vzk_xHo8Y}DSUWV6h!3XQTE z#85u@ouM?V>;pC>O-m!saPbtaYb~46JcxCt#bdg--qN^Y@tWm*a>wwcq)Ez zjgeo91i%n=`@xK1g`KYIpR`Z0WU=Y_o?j{)!{<$kI%h>?bJO5yxA^H!9IuNta{klu zy_&iOOP89LLrk%hC~QW$*>-zKmSS+u*w`u%N0JKMq(T5)k}6nC;9 zvc-HbwfEhiFV&YL-2keA6%xM_euEb0(2F{;Xa$+Pr1Kkz99E0UmSO88)93+2RWFiW ze96kjCajwEC)K_NQ6&AIjh>I!F=kTXUzKySL!!kM_KKv2x>adjp^9?U=-M@H={zzDh(OnwU(4ST(Vk1DBXrojJ3^h{0y4@3cD znS@JLN2635#f>I&Xl;#ZXuo54qxY(2JYsZ)c!i_F8B&aHHM$IUWd_qMO1;j`P;28MdYDEvaId3obSTl71l3{-+g1Yi2rXL%)@FO~S}%2>F5tkmJj_Jtpos-d#WUaiA)r zf9EcsD_XFyGZ8u8sF4soVCUeVACzN@p4+Igs>Re?ov%)3?Xj--0!oiNRQ*|?#IAjA z=L@xWlD=o`pi3=-qj(%G2Bx8+E?a%Q@5I!PJ$rzJX2au@`q}(~k>C!}Y=uyti27u-RkVo}+zJ??VimAKd#F*d zPmx)0#Y`}ruLi@8bvzb-pG4hf*f8ciV$wOTw3mY(+x}jng^jcg+VDs!Dk={mI(Bg-HckFTh+%T0^@PEE5awCEs8K zP6Tz84_I?r47#ua%x&<@^X^`?wS`ydXv6Jb_l1z~3`S7rXuVdXlSnn8!ydDH88~rL z#p@G{JxQ=f(-p?RQsG32uFyC_>L;ezJCjynD5a>54fL~%X_V-81G%8EKx4|qXs$Ym zZXbpXeWwY}#Ebgd-T2?k1u$nIPE1VfU5WUuuD3lup61ZGJ|$^?E1Wwo4#buTqP3Gf z*BELR)}1eM_G~_Scsfs9v*bEQ?+bpya(Lq#Ji`>F&=D*>js>C;7Ku6khGuhn`}MNc z!oe!A^zK_x1qOiw*(*u1j<1M=)t90|V;LPCJKq~wkhvp+9DMF=lOVBcjjp1GGuk>B zJs$+>dVcDaHBx`y6k6~%CC$08$SEmelWteufKqIi?#ckoj7T9|8^d zocGp2u+AGFVYhyYb=BWW3oKn}vrUE6Psy)|xNWd2cL*yS8=*CDDjWGccosYJz2&T` z8y7XZ^j*3n*&x$seoZC>sb?h9Opya6d0en*w7OY*W)cyIxRH!#kUVd(lvec$J`!qk zSXuZOfy_d%+(vs`=KgAbB))-Vj$P6aUZ!uc;Wz%!hT)h3G3f9L6`-2$hruD@}PVKTY zBHZM<^W5H`MP7IAn;^D~&l2L$;{q74l>S5n8dc>M(ze2Txn9(ekCz@h&_KGxe9^QQ zpsHuxtM3CndlooO`?&$t#0^#IlbHZAd?x%}p6~#OqnP&V7OFuxAbxqhCVq3hW(_{M zDXBOOe|&hXcA?@B`vT!Zbv-qCBaz=`T2|XsGTgm?7N1ygwbWNhQ`^!Z^zo>OBrD=9 z)X~m|_7|F?`WVJ1>kdwiEunT@c;b0v_o6i++DaSl@dECG8&ly}X0GAb@aJnC39?~* z+=y{K!Hh)jrsBq1aX+00n@9NQ8D1S(R!Cc-G{k+$7C0W3CE}iWOWkNvxC{?oYQIV5 zIm<{j?sc;E%3ywQNKg@3a!=vl2Y_jf^BB)Og{G(joeYevn$0V58E z*OGz3=&JiUPw-I@QTYU6c($%SIQCKfa8?Zwv>#Z~|ti%J9qBG%g^l)9(~ z=XujCi;hLFeMB7Le=rd1L=UF(m(8Rq}%FVbuS`_WV}U|A1!v>WSp}N*p5S zOCR}6B!%(84UaS=8&rhn{>jbO)Bp}S`H4yGb-m&(47xG0^|qRyh(63C$V+2(8asoj zCbS4oDsl9!o)F&MI6Ta_4;np)!q2~Bw{Ho%h_~HjMgXFBzo^Y4Req;{rSR52-fa1= zkYKilKekyRx4*k!H@>;s+P)}oGz_n1OOEu&h{{>E^?!IF3iGZ}ES&7Yb*?S8DT>MHg6z(lP8lvBm#-o0q+ec^yEwJe z{Vv!xO#*jk&U7P@gTT(Fa90IwUNnvI5@(G;fn;fVtg#SjadxpE|DARHG^D9^?VazA zbIB()Vm)>Xx#I9g>8iVwaAvV6_BR4%47&25+nF4o%Mu(9QXyI`U5NA zxqFW6v2zJC@_KmMydWtTFPNK}CtQR+X>dx4`tJOAW^?nQvy`~?@vC+w#En=?TzTQz$4i#Hf zqUIEh>vA*4gNW3~&G{#qovnvXbgACK$->`rS^l9VsobUly>4qCnaqC`sL@FbBG#LP z+UMgymPgJ?*`qv?cvIK>*}nDDz&7t3R_E%QYOZ{N+!Z8Uei^;Ju*z>yLB_wQjn}qU zSL4oI=A*K4Pn5InUnDTD>~0LVo@S(y#VBBW)ub02xp%lZ*VPbn_QV}|G)(KT*iBTiMOBOHk2nfn9xk@wxb)YOYzf2#Nk z3rZ(&GUyXu>>ADP*#aR_aGU4dJWt!VIu4z$Ly<*0P|E@+q{sD1K Date: Wed, 9 Oct 2024 17:02:43 +0200 Subject: [PATCH 096/120] start handling border in straightest geodesic --- .../Bsurf/locally_shortest_path.h | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/Vector_graphics_on_surfaces/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Vector_graphics_on_surfaces/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index 2f7d46f6635a..57c0632fdadb 100644 --- a/Vector_graphics_on_surfaces/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Vector_graphics_on_surfaces/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -1392,6 +1392,7 @@ struct Locally_shortest_path_imp { case 0: { + // TODO: handle is src_vertex is a border vertex: does not necessarily mean early exist as it depends on the direction vertex_descriptor src_vertex = std::get(var_des); halfedge_descriptor href = halfedge(curr_tid, mesh); int src_index = source(href, mesh)==src_vertex?0:(target(href,mesh)==src_vertex?1:2); @@ -1490,6 +1491,7 @@ struct Locally_shortest_path_imp break; case 1: { + //TODO: handle if h is on the boundary (does not necessarily mean early exit, depends on direction) halfedge_descriptor h = std::get(var_des); halfedge_descriptor href = halfedge(face(h,mesh), mesh); int opp_id = h==href?2:(next(href,mesh)==h?0:1); @@ -1590,6 +1592,12 @@ struct Locally_shortest_path_imp accumulated += sqrt(squared_distance(construct_point(curr_p,mesh), get(vpm,vid))); + // stop if hitting the boundary + for (halfedge_descriptor h : halfedges_around_target(vid, mesh)) + if (is_border(h, mesh)) + break; + + // Point_3 vert = get(vpm,vid); // Point_3 vert_adj=get(vpm,source(h,mesh)); // Vector_3 v = vert - vert_adj; @@ -1618,6 +1626,10 @@ struct Locally_shortest_path_imp { h_curr=opposite(get_halfedge(k, h_ref),mesh); + // break if hitting the boundary + if (is_border(h_curr, mesh)) + break; + face_descriptor adj = face(h_curr,mesh); std::array curr_alpha=make_array(t1,1-t1); //reversed because will switch face new_bary=edge_barycentric_coordinate(h_curr,halfedge(adj,mesh),curr_alpha); @@ -1650,6 +1662,15 @@ struct Locally_shortest_path_imp } double excess = accumulated - len; + + if (excess <= 0) + { +#ifdef CGAL_DEBUG_BSURF + std::cout << "excess " << excess << std::endl; +#endif + return result; + } + Point_3 prev_pos = construct_point(*std::next(result.rbegin()),mesh); Point_3 last_pos = construct_point(result.back(),mesh); double alpha = excess / sqrt((last_pos - prev_pos).squared_length()); From 60823599103bac3d6dde169e409304eb726c83b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 9 Oct 2024 17:31:27 +0200 Subject: [PATCH 097/120] bad break --- .../CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Vector_graphics_on_surfaces/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Vector_graphics_on_surfaces/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index 57c0632fdadb..f6e1fd9a292f 100644 --- a/Vector_graphics_on_surfaces/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Vector_graphics_on_surfaces/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -1595,7 +1595,7 @@ struct Locally_shortest_path_imp // stop if hitting the boundary for (halfedge_descriptor h : halfedges_around_target(vid, mesh)) if (is_border(h, mesh)) - break; + return result; // Point_3 vert = get(vpm,vid); From 79f0baa9f9db7550ff3ac949778098080e9e7fc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 16 Oct 2024 18:03:02 +0200 Subject: [PATCH 098/120] first fixes for cases when reaching the boundary --- .../Bsurf/locally_shortest_path.h | 43 +++++++++++++++---- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/Vector_graphics_on_surfaces/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Vector_graphics_on_surfaces/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index f6e1fd9a292f..d19e3f9878a3 100644 --- a/Vector_graphics_on_surfaces/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Vector_graphics_on_surfaces/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -1491,24 +1491,30 @@ struct Locally_shortest_path_imp break; case 1: { - //TODO: handle if h is on the boundary (does not necessarily mean early exit, depends on direction) halfedge_descriptor h = std::get(var_des); halfedge_descriptor href = halfedge(face(h,mesh), mesh); int opp_id = h==href?2:(next(href,mesh)==h?0:1); - Vector_2 opp = curr_flat_tid[opp_id]; + Vector_2 h_2d = curr_flat_tid[(opp_id+2)%3] - curr_flat_tid[(opp_id+1)%3]; // check if the line starts in the current face (TODO should be a predicate) - if ( (opp-flat_p) * dir < 0 ) + if ( orientation(h_2d, dir) == RIGHT_TURN ) { + if ( is_border(opposite(h, mesh), mesh) ) + { + result.push_back(curr_p); + return result; + } + // take the same point but in the opposite face halfedge_descriptor h_start=h; h=opposite(h, mesh); curr_tid=face(h, mesh); curr_p.first=curr_tid; href=halfedge(curr_tid, mesh); + CGAL_assertion(start_loc.second[opp_id]==0); std::array bary2 = CGAL::make_array(start_loc.second[(opp_id+2)%3], start_loc.second[(opp_id+1)%3]); // swap done here int opp_id = h==href?2:(next(href,mesh)==h?0:1); - curr_p.second[0]=FT(0); + curr_p.second[opp_id]=FT(0); curr_p.second[(opp_id+1)%3]=bary2[0]; curr_p.second[(opp_id+2)%3]=bary2[1]; @@ -1524,7 +1530,7 @@ struct Locally_shortest_path_imp Vector_2 ybase = new_flat_tid_in_curr_basis[1]-new_flat_tid_in_curr_basis[0]; double cos_theta = ybase * dir / std::sqrt(ybase*ybase) / std::sqrt(dir*dir); double theta = std::acos(cos_theta); - dir = Vector_2(std::cos(CGAL_PI/2.-theta), std::sin(CGAL_PI/2.-theta)); + dir = Vector_2(-std::sin(theta), std::cos(theta)); curr_flat_tid=init_flat_triangle(halfedge(curr_tid,mesh),vpm,mesh); flat_p= curr_p.second[0]*curr_flat_tid[0]+curr_p.second[1]*curr_flat_tid[1]+curr_p.second[2]*curr_flat_tid[2]; @@ -1583,6 +1589,8 @@ struct Locally_shortest_path_imp #endif if (is_vert) { + point_on_edge.second = make_array(0.,0.,0.); + point_on_edge.second[kv]=1; vertex_descriptor vid = target(get_halfedge(kv, prev(h_ref,mesh)), mesh); #ifdef CGAL_DEBUG_BSURF std::cout<< "hit vertex "<< vid < len TODO: what about below for edges? + break; + } + if (border_reached) break; // Point_3 vert = get(vpm,vid); @@ -1628,7 +1643,12 @@ struct Locally_shortest_path_imp // break if hitting the boundary if (is_border(h_curr, mesh)) + { + result.push_back(point_on_edge); + accumulated += sqrt(squared_distance(construct_point(point_on_edge,mesh), construct_point(curr_p,mesh))); + prev_p=point_on_edge; //if accumulated > len we need to be in the common face break; + } face_descriptor adj = face(h_curr,mesh); std::array curr_alpha=make_array(t1,1-t1); //reversed because will switch face @@ -1680,15 +1700,20 @@ struct Locally_shortest_path_imp std::cout << "prev_pos " << prev_pos << std::endl; std::cout << "last_pos " << last_pos << std::endl; - std::cout << "curr_tri "<< prev_p.first << std::endl; + std::cout << "prev_p "<< prev_p.first << std::endl; std::cout << "pos " << pos << std::endl; #endif auto [inside, bary] = - point_in_triangle(vpm,mesh,prev_p.first,pos); + point_in_triangle(vpm,mesh,prev_p.first,pos); // TODO replace with function in PMP/locate.h if (!inside) - std::cout << "error!This point should be in the triangle" << std::endl; + { + std::cout << "prev_pos " << prev_pos << "\n"; + std::cout << "last_pos " << last_pos << "\n"; + std::cout << "pos " << pos << "\n"; + std::cout << "error!This point should be in the triangle" << std::endl; //TODO this is a debug + } result.pop_back(); prev_p.second=bary; From 9bcf6eb2c36fa855582b9f071f293a9c3c6d9f18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 16 Oct 2024 18:03:43 +0200 Subject: [PATCH 099/120] add test for straightest geodesic --- .../CMakeLists.txt | 13 ++++ .../test_straightest_geodesic.cpp | 67 +++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 Vector_graphics_on_surfaces/test/Vector_graphics_on_surfaces/CMakeLists.txt create mode 100644 Vector_graphics_on_surfaces/test/Vector_graphics_on_surfaces/test_straightest_geodesic.cpp diff --git a/Vector_graphics_on_surfaces/test/Vector_graphics_on_surfaces/CMakeLists.txt b/Vector_graphics_on_surfaces/test/Vector_graphics_on_surfaces/CMakeLists.txt new file mode 100644 index 000000000000..a8baaba3f80f --- /dev/null +++ b/Vector_graphics_on_surfaces/test/Vector_graphics_on_surfaces/CMakeLists.txt @@ -0,0 +1,13 @@ +# Created by the script cgal_create_cmake_script +# This is the CMake script for compiling a CGAL application. + +cmake_minimum_required(VERSION 3.12...3.29) +project( Vector_graphics_on_surfaces ) + +find_package(CGAL REQUIRED) +find_package(Eigen3 REQUIRED) + +include(CGAL_Eigen3_support) + +create_single_source_cgal_program( "test_straightest_geodesic.cpp" ) +target_link_libraries(test_straightest_geodesic PRIVATE CGAL::Eigen3_support) diff --git a/Vector_graphics_on_surfaces/test/Vector_graphics_on_surfaces/test_straightest_geodesic.cpp b/Vector_graphics_on_surfaces/test/Vector_graphics_on_surfaces/test_straightest_geodesic.cpp new file mode 100644 index 000000000000..342017410641 --- /dev/null +++ b/Vector_graphics_on_surfaces/test/Vector_graphics_on_surfaces/test_straightest_geodesic.cpp @@ -0,0 +1,67 @@ +#include +#include +#include +#include +#include + +#include + +namespace PMP = CGAL::Polygon_mesh_processing; + +using K = CGAL::Exact_predicates_inexact_constructions_kernel; +using Mesh = CGAL::Surface_mesh; +using Face_location = PMP::Face_location; +using Edge_location = PMP::Edge_location; + +int main() +{ + Mesh mesh; + Mesh::Halfedge_index h = CGAL::make_quad(K::Point_3(0,0,0), K::Point_3(1,0,0), + K::Point_3(1,1,0),K::Point_3(0,1,0), + mesh); + CGAL::Euler::split_face(h, next(next(h,mesh), mesh), mesh); + PMP::isotropic_remeshing(faces(mesh), 0.15, mesh); + + std::ofstream("square.off") << std::setprecision(17) << mesh; + + // test starting on edges + //int runid=0; + for (Mesh::Halfedge_index h : halfedges(mesh)) + { + if (is_border(h,mesh)) continue; + + Mesh::Face_index f = face(h, mesh); + Face_location src(f, CGAL::make_array(0.5, 0.5, 0)); + + // K::Point_3 src_pt = PMP::construct_point(src, mesh); + // std::cout << "src = " << src_pt << "\n"; + + double target_distance = 0.3; + + //std::ofstream out("straightest_geodesic_path_"+std::to_string(runid)+".polylines.txt"); + for (double n=0; n<8; ++n) + { + double theta = 2*CGAL_PI/8*n; + K::Vector_2 dir(std::cos(theta), std::sin(theta)); + std::vector path = PMP::straightest_geodesic(src, dir, target_distance, mesh); + + //TODO: check the output is of correct length + +/* + std::vector poly; + poly.reserve(path.size()); + PMP::convert_path_to_polyline(path, mesh, std::back_inserter(poly)); + + + out << path.size() << " "; + + for (auto p : poly) + out << " " << p; + out << "\n"; +*/ + } +// ++runid; + } + + return 0; +} From 0576fb6db93900c573ad767dfbba5cd0a35365a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 18 Oct 2024 18:32:24 +0200 Subject: [PATCH 100/120] start fixing vertex case --- .../Bsurf/locally_shortest_path.h | 15 +++++-- .../test_straightest_geodesic.cpp | 41 +++++++++++++++++++ 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/Vector_graphics_on_surfaces/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Vector_graphics_on_surfaces/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index d19e3f9878a3..423e8573e294 100644 --- a/Vector_graphics_on_surfaces/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Vector_graphics_on_surfaces/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -1408,8 +1408,7 @@ struct Locally_shortest_path_imp Orientation dir_ori=orientation(v02,dir), v01_ori=orientation(v02,v01); // check if dir is the cone centered at curr_flat_tid[src_vertex] (TODO: should be a predicate!) - if ( dir_ori!=v01_ori || - unnorm_cos_theta_2 / std::sqrt(v01*v01) > unnorm_cos_theta_1 / std::sqrt(dir*dir)) // > because cos is decreasing from 0 -> Pi, should be < for the angle` + if (orientation(dir, v01)==LEFT_TURN || orientation(dir, v02)==RIGHT_TURN) { //TODO: should be made robust with snapping and predicates or something à la robust construction traits @@ -1422,12 +1421,12 @@ struct Locally_shortest_path_imp Vector_3 n(get(vpm,target(hloop, mesh)), get(vpm,source(hloop, mesh))); n /= std::sqrt(n*n); do{ - Vector_3 nv(get(vpm,target(hloop, mesh)), get(vpm,target(next(hloop, mesh), mesh))); + hloop=opposite(next(hloop, mesh), mesh); + Vector_3 nv(get(vpm,target(hloop, mesh)), get(vpm,source(hloop, mesh))); nv /= std::sqrt(nv*nv); angles.push_back( std::acos(n * nv) ); acc_angle+=angles.back(); n = nv; - hloop=opposite(next(hloop, mesh), mesh); }while(hloop!=hstart); CGAL_assertion( std::acos(unnorm_cos_theta_2 / std::sqrt((v01*v01) * (v02*v02))) == angles[0]); // will not be true because of rounding @@ -1466,6 +1465,13 @@ struct Locally_shortest_path_imp double alpha = d_opp_delta/dvprev; + if (is_border(hloop,mesh)) + { + // border case falling outside of the mesh TODO: check with Claudio + result.push_back(curr_p); + return result; + } + halfedge_descriptor href = halfedge(face(hloop,mesh),mesh); curr_flat_tid=init_flat_triangle(href,vpm,mesh); int src_id = source(href, mesh)==src_vertex?0:(target(href,mesh)==src_vertex?1:2); @@ -1605,6 +1611,7 @@ struct Locally_shortest_path_imp for (halfedge_descriptor h : halfedges_around_target(vid, mesh)) if (is_border(h, mesh)) { + //TODO: that's not necessarily a break! depends on dir result.push_back(point_on_edge); border_reached=true; prev_p=point_on_edge; //useful if accumulated > len TODO: what about below for edges? diff --git a/Vector_graphics_on_surfaces/test/Vector_graphics_on_surfaces/test_straightest_geodesic.cpp b/Vector_graphics_on_surfaces/test/Vector_graphics_on_surfaces/test_straightest_geodesic.cpp index 342017410641..786ba6163e7f 100644 --- a/Vector_graphics_on_surfaces/test/Vector_graphics_on_surfaces/test_straightest_geodesic.cpp +++ b/Vector_graphics_on_surfaces/test/Vector_graphics_on_surfaces/test_straightest_geodesic.cpp @@ -63,5 +63,46 @@ int main() // ++runid; } + // test starting on vertices + //int runid=0; + for (Mesh::Halfedge_index h : halfedges(mesh)) + { + if (is_border(h,mesh)) continue; + + Mesh::Face_index f = face(h, mesh); + Face_location src(f, CGAL::make_array(0., 1., 0.)); + + // K::Point_3 src_pt = PMP::construct_point(src, mesh); + // std::cout << "src = " << src_pt << "\n"; + + double target_distance = 0.3; + + //std::ofstream out("straightest_geodesic_path_"+std::to_string(runid)+".polylines.txt"); + for (double n=0; n<8; ++n) + { + double theta = 2*CGAL_PI/16*n; + K::Vector_2 dir(std::cos(theta), std::sin(theta)); + std::vector path = PMP::straightest_geodesic(src, dir, target_distance, mesh); + + //TODO: check the output is of correct length + +/* + std::vector poly; + poly.reserve(path.size()); + PMP::convert_path_to_polyline(path, mesh, std::back_inserter(poly)); + + + out << path.size() << " "; + + for (auto p : poly) + out << " " << p; + out << "\n"; +*/ + } +// ++runid; + } + + + return 0; } From 54d72a998296e7cba1d204a5b319de095f064b8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 3 Jan 2025 16:02:41 +0100 Subject: [PATCH 101/120] fix ref landing page --- .../doc/Vector_graphics_on_surfaces/PackageDescription.txt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Vector_graphics_on_surfaces/doc/Vector_graphics_on_surfaces/PackageDescription.txt b/Vector_graphics_on_surfaces/doc/Vector_graphics_on_surfaces/PackageDescription.txt index 10e917796a0a..0e33ab8dd509 100644 --- a/Vector_graphics_on_surfaces/doc/Vector_graphics_on_surfaces/PackageDescription.txt +++ b/Vector_graphics_on_surfaces/doc/Vector_graphics_on_surfaces/PackageDescription.txt @@ -12,16 +12,15 @@ /// \ingroup PkgVGoS_Ref /*! +\addtogroup PkgVGoS_Ref \cgalPkgDescriptionBegin{Vector Graphics on Triangulated Surface Meshes,PkgVGoS} \cgalPkgPicture{vgos-small.png} - \cgalPkgSummaryBegin \cgalPkgAuthors{Claudio Mancinelli and Sébastien Loriot} \cgalPkgDesc{The package provides functions to draw and manipulate basic geometric primitives such as geodesics, Bézier and B-Spline curves on triangulated surface meshes.} \cgalPkgManuals{Chapter_VGoS,PkgVGoS_Ref} \cgalPkgSummaryEnd - \cgalPkgShortInfoBegin \cgalPkgSince{6.2} \cgalPkgDependsOn{\ref PkgPolygonMeshProcessing} @@ -29,7 +28,6 @@ \cgalPkgLicense{\ref licensesGPL "GPL"} \cgalPkgDemo{CGAL Lab,CGALlab.zip} \cgalPkgShortInfoEnd - \cgalPkgDescriptionEnd */ From 40c7d250e62fd533e463192ec3e1315d9bcad6e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 3 Jan 2025 16:20:23 +0100 Subject: [PATCH 102/120] tmp change to not forget to fix the code for the test --- .../test_straightest_geodesic.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Vector_graphics_on_surfaces/test/Vector_graphics_on_surfaces/test_straightest_geodesic.cpp b/Vector_graphics_on_surfaces/test/Vector_graphics_on_surfaces/test_straightest_geodesic.cpp index 786ba6163e7f..229375e78bd6 100644 --- a/Vector_graphics_on_surfaces/test/Vector_graphics_on_surfaces/test_straightest_geodesic.cpp +++ b/Vector_graphics_on_surfaces/test/Vector_graphics_on_surfaces/test_straightest_geodesic.cpp @@ -102,7 +102,6 @@ int main() // ++runid; } - - - return 0; + //TODO: we still need to check that the output is correct (which is currently not the case at vertex) + return 1; } From a372da361c71765469e50eaf9cafe89fc351ef57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 3 Jan 2025 19:19:22 +0100 Subject: [PATCH 103/120] start adding function doc --- .../CGAL/Polygon_mesh_processing/locate.h | 79 +++++++++- .../PackageDescription.txt | 3 + .../Bsurf/locally_shortest_path.h | 136 +++++++++++------- 3 files changed, 168 insertions(+), 50 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/locate.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/locate.h index e2445ef237e1..184c66b27825 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/locate.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/locate.h @@ -780,7 +780,7 @@ get_descriptor_from_location(const Edge_location& loc, /// \ingroup PMP_locate_grp /// -/// \brief Given a location in a face, returns the geometric position described +/// \brief returns, given a location in a face, the geometric position described /// by these coordinates, as a point. /// /// \tparam FT must be a model of `FieldNumberType` @@ -849,6 +849,83 @@ construct_point(const Face_location& loc, return bp_constructor(p0, loc.second[0], p1, loc.second[1], p2, loc.second[2], gt); } +/// \ingroup PMP_locate_grp +/// +/// \brief returns, given a location in an edge, the geometric position described +/// by these coordinates, as a point. +/// +/// \tparam FT must be a model of `FieldNumberType` +/// \tparam TriangleMesh must be a model of `FaceGraph` +/// \tparam NamedParameters a sequence of \ref bgl_namedparameters "Named Parameters" +/// +/// \param loc the location from which a point is constructed +/// \param tm a triangulated surface mesh +/// \param np an optional sequence of \ref bgl_namedparameters "Named Parameters" among the ones listed below +/// +/// \cgalNamedParamsBegin +/// \cgalParamNBegin{vertex_point_map} +/// \cgalParamDescription{a property map associating points to the vertices of `tm`} +/// \cgalParamType{a class model of `ReadablePropertyMap` with `boost::graph_traits::%vertex_descriptor` +/// as key type and `%Point_3` as value type} +/// \cgalParamDefault{`boost::get(CGAL::vertex_point, tm)`} +/// \cgalParamNEnd +/// +/// \cgalParamNBegin{geom_traits} +/// \cgalParamDescription{an instance of a geometric traits class} +/// \cgalParamType{a class model of `Kernel`} +/// \cgalParamDefault{a \cgal Kernel deduced from the point type, using `CGAL::Kernel_traits`} +/// \cgalParamExtra{The geometric traits class must be compatible with the vertex point type.} +/// \cgalParamExtra{If such traits class is provided, its type `FT` must be identical +/// to the template parameter `FT` of this function.} +/// \cgalParamNEnd +/// \cgalNamedParamsEnd +/// +/// \pre `loc.first` is a face descriptor corresponding to a face of `tm`. +/// +/// \returns a point whose type is the same as the value type of the vertex point property map +/// provided by the user or via named parameters, or the internal point map of the mesh `tm`. +/// +template +#ifdef DOXYGEN_RUNNING +Point construct_point( + const Edge_location &loc, +#else +typename internal::Location_traits::Point +construct_point(const Edge_location &loc, +#endif + const TriangleMesh &tm, + const NamedParameters &np = parameters::default_values()) { + typedef typename boost::graph_traits::edge_descriptor + edge_descriptor; + typedef + typename GetGeomTraits::type Geom_traits; + + typedef typename GetVertexPointMap::const_type + VertexPointMap; + typedef typename boost::property_traits::value_type Point; + typedef typename boost::property_traits::reference + Point_reference; + + using parameters::choose_parameter; + using parameters::get_parameter; + + CGAL_precondition(CGAL::is_triangle_mesh(tm)); + + VertexPointMap vpm = parameters::choose_parameter( + parameters::get_parameter(np, internal_np::vertex_point), + get_const_property_map(boost::vertex_point, tm)); + Geom_traits gt = choose_parameter( + get_parameter(np, internal_np::geom_traits)); + + edge_descriptor ed = loc.first; + const Point_reference p0 = get(vpm, source(ed, tm)); + const Point_reference p1 = get(vpm, target(ed, tm)); + + internal::Barycentric_point_constructor bp_constructor; + return bp_constructor(p0, loc.second[0], p1, loc.second[1], gt); +} + /// \name Location Predicates /// @{ diff --git a/Vector_graphics_on_surfaces/doc/Vector_graphics_on_surfaces/PackageDescription.txt b/Vector_graphics_on_surfaces/doc/Vector_graphics_on_surfaces/PackageDescription.txt index 0e33ab8dd509..4e3969d0edc3 100644 --- a/Vector_graphics_on_surfaces/doc/Vector_graphics_on_surfaces/PackageDescription.txt +++ b/Vector_graphics_on_surfaces/doc/Vector_graphics_on_surfaces/PackageDescription.txt @@ -5,6 +5,9 @@ /// \defgroup VGSAlgorithmClasses Algorithm Classes /// \ingroup PkgVGoS_Ref +/// \defgroup VGSFunctions Functions +/// \ingroup PkgVGoS_Ref + /// \defgroup VGSTraitsClasses Traits Classes /// \ingroup PkgVGoS_Ref diff --git a/Vector_graphics_on_surfaces/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Vector_graphics_on_surfaces/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index 423e8573e294..943db935dec1 100644 --- a/Vector_graphics_on_surfaces/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Vector_graphics_on_surfaces/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -53,51 +53,16 @@ void locally_shortest_path(Face_location src, #endif ); - +/*! + * \ingroup VGSMiscellaneous + * array containing the locations of the four control points of a Bézier segment on a triangle mesh + * `init_geodesic_dual_solver()`. + * \tparam FT floating point number type (float or double) + * \tparam TriangleMesh a model of `FaceListGraph` and `EdgeListGraph` + */ template using Bezier_segment = std::array, 4>; -template -#ifdef DOXYGEN_RUNNING -Point construct_point( - const Edge_location &loc, -#else -typename internal::Location_traits::Point -construct_point(const Edge_location &loc, -#endif - const TriangleMesh &tm, - const NamedParameters &np = parameters::default_values()) { - typedef typename boost::graph_traits::edge_descriptor - edge_descriptor; - typedef - typename GetGeomTraits::type Geom_traits; - - typedef typename GetVertexPointMap::const_type - VertexPointMap; - typedef typename boost::property_traits::value_type Point; - typedef typename boost::property_traits::reference - Point_reference; - - using parameters::choose_parameter; - using parameters::get_parameter; - - CGAL_precondition(CGAL::is_triangle_mesh(tm)); - - VertexPointMap vpm = parameters::choose_parameter( - parameters::get_parameter(np, internal_np::vertex_point), - get_const_property_map(boost::vertex_point, tm)); - Geom_traits gt = choose_parameter( - get_parameter(np, internal_np::geom_traits)); - - edge_descriptor ed = loc.first; - const Point_reference p0 = get(vpm, source(ed, tm)); - const Point_reference p1 = get(vpm, target(ed, tm)); - - internal::Barycentric_point_constructor bp_constructor; - return bp_constructor(p0, loc.second[0], p1, loc.second[1], gt); -} - namespace internal { template @@ -2391,7 +2356,7 @@ struct Geodesic_circle_impl Stop &&stop, Exit &&exit) { /* - This algortithm uses the heuristic Small Label Fisrt and Large Label Last + This algorithm uses the heuristic Small Label Fisrt and Large Label Last https://en.wikipedia.org/wiki/Shortest_Path_Faster_Algorithm Large Label Last (LLL): When extracting nodes from the queue, pick the @@ -2488,7 +2453,7 @@ struct Geodesic_circle_impl Exit &&exit) { /* - This algortithm uses the heuristic Small Label Fisrt and Large Label Last + This algorithm uses the heuristic Small Label Fisrt and Large Label Last https://en.wikipedia.org/wiki/Shortest_Path_Faster_Algorithm Large Label Last (LLL): When extracting nodes from the queue, pick the @@ -2828,7 +2793,15 @@ struct Geodesic_circle_impl } // namespace internal + #ifndef CGAL_BSURF_USE_DIJKSTRA_SP +/*! + * \ingroup VGSMiscellaneous + * Geodesic solver class used to store pre-computed information of a given mesh for + * approximate geodesic compution. Those information are computed by the function + * `init_geodesic_dual_solver()`. + * \tparam FT floating point number type (float or double) + */ template struct Dual_geodesic_solver { @@ -2839,6 +2812,18 @@ struct Dual_geodesic_solver std::vector> graph = {}; }; +/*! + * \ingroup VGSMiscellaneous + * fills `solver` for a given mesh `tmesh`. It is the user responsability to + * call again this function if `tmesh` or the points of its vertices are modified. + * If `solver` was used in a previous call to this function, information will be ovewritten. + * \tparam TriangleMesh a model of `FaceListGraph` and `EdgeListGraph` + * \tparam FT floating point number type (float or double) + * \param `solver` the container for the pre-computed information + * \param `tmesh` triangle mesh to be considered for the precomputations + * \todo add named parameters + * \todo make sure solver.graph is cleared before filling it + */ template void init_geodesic_dual_solver(Dual_geodesic_solver& solver, const TriangleMesh& tmesh) { @@ -2855,8 +2840,28 @@ void init_geodesic_dual_solver(Dual_geodesic_solver& solver, const TriangleM } #endif -// points can be duplicated in the path in case you hit a vertex of the mesh, -// in order to get continuity of edge locations +/*! + * \ingroup VGSFunctions + * computes an approximated geodesic shortest path between two locations on a + * triangle mesh. `src` and `tgt` must be on the same connected component. + * \tparam TriangleMesh a model of `FaceListGraph` and `EdgeListGraph` + * \tparam FT floating point number type (float or double) + * \tparam EdgeLocationRange a model of `BackInsertionSequence` whose value type `Edge_location`. + * \param src source of the path + * \param tgt target of the path + * \param tmesh input triangle mesh to compute the path on + * \param edge_locations contains the path as a sequence of edge locations. + * Two consecutives edges `e_k` and `e_kp1` stored in `edge_locations` are such that + * `face(halfedge(e_k, tmesh), tmesh) == face(opposite(halfedge(e_kp1, tmesh), tmesh), tmesh))`. + * In parcular, it means that if the path goes through a vertex of `tmesh`, several + * edge locations will be reported to maintain this property. + * Additionally, if `src` is in the interior of a face `f`, then the first edge location `e_0` + * of `edge_locations` is such that `f == face(opposite(halfedge(e_0, tmesh), tmesh), tmesh))`. + * Similarly, if `tgt` is in the interior of a face `f`, then the last edge location `e_n` + * of `edge_locations` is such that `f == face(halfedge(e_n, tmesh), tmesh)`. + * \todo add named parameters + * \todo should we have halfedge location instead? + */ template void locally_shortest_path(Face_location src, Face_location tgt, @@ -3087,8 +3092,20 @@ void locally_shortest_path(Face_location src, } -//TODO: do we want to also have a way to return Bezier segments? -// the output is actually bezier segments subdivided. +/*! + * \ingroup VGSFunctions + * computes a discretization of a Bézier segment defined by the location of four control points on `tmesh`. + * All control points must be on the same connected component. This functions applies several iterations of + * the de Casteljau algorithm, and geodesic shortest path are drawn between the control points. + * \tparam TriangleMesh a model of `FaceListGraph` and `EdgeListGraph` + * \tparam FT floating point number type (float or double) + * \param mesh input triangle mesh to compute the path on + * \param control_points control points of the Bézier segment + * \param num_subdiv the number of iterations of the subdivision algorithm + * \return descretization of the Bézier segment as face locations + * \todo add named parameters + * \todo do we want to also have a way to return Bezier segments? The output is actually bezier segments subdivided. + */ template std::vector> recursive_de_Casteljau(const TriangleMesh& mesh, @@ -3149,6 +3166,11 @@ recursive_de_Casteljau(const TriangleMesh& mesh, return final_result; } +/*! + * \ingroup VGSMiscellaneous + * computes the approximate geodesic distances of `center` to all the vertices of the mesh (only one connected component is expected) + * and put the distance in `distance_map` + */ template void approximate_geodesic_distance_field(const Face_location& center, VertexDistanceMap distance_map, @@ -3185,7 +3207,23 @@ void approximate_geodesic_distance_field(const Face_location& } } -// TODO: center is put is output, shall we keep it like that? +/*! + * \ingroup VGSFunctions + * computes a path on a triangle mesh that is computed by starting a walk on `tmesh` + * given a direction and a maximum distance. The distance will not be achieved is a border edge + * is reached before. + * \tparam TriangleMesh a model of `FaceListGraph` and `EdgeListGraph` + * \tparam FT floating point number type (float or double) + * \tparam num_subdiv the number of iterations of the subdivision algorithm + * \param tmesh input triangle mesh to compute the path on + * \param src the source of the path + * \param len the distance to walk along the straightest + * \param dir the initial direction of the walk, given as a 2D vector in the face of src, the halfedge of the face being the y-axis. + * \return the straightest path (not containing `src`) + * \todo add named parameters + * \todo do we want to also have a way to return Bezier segments? The output is actually bezier segments subdivided. + * \todo offer something better than a 2D vector for the direction + */ template std::vector> straightest_geodesic(const Face_location &src, From 937003c04fd42035d83ff61317334ed0e5b87d2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Mon, 6 Jan 2025 18:45:17 +0100 Subject: [PATCH 104/120] add more doc --- .../trace_polygon_example.cpp | 2 +- .../trace_polygon_on_svg_curve_example.cpp | 2 +- .../trace_svg_example.cpp | 4 +- .../Bsurf/locally_shortest_path.h | 293 +++++++++++++++--- 4 files changed, 249 insertions(+), 52 deletions(-) diff --git a/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_polygon_example.cpp b/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_polygon_example.cpp index ed97206bfea8..679917e1b27c 100644 --- a/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_polygon_example.cpp +++ b/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_polygon_example.cpp @@ -101,7 +101,7 @@ int main(int argc, char** argv) for (const std::vector& polygon : polygons) { std::vector> polar_coords = - PMP::convert_polygon_to_polar_coordinates(polygon, center_2); + PMP::convert_to_polar_coordinates(polygon, center_2); if (polygon.front()==polygon.back()) polar_coords.pop_back(); std::vector directions; diff --git a/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_polygon_on_svg_curve_example.cpp b/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_polygon_on_svg_curve_example.cpp index e777569b22fc..b1be86293ac4 100644 --- a/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_polygon_on_svg_curve_example.cpp +++ b/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_polygon_on_svg_curve_example.cpp @@ -77,7 +77,7 @@ get_supporting_curve(std::string svg_filename, CGAL_assertion(bezier[0]==prev); CGAL_assertion_code(prev=bezier[3];) std::vector> polar_coords = - PMP::convert_polygon_to_polar_coordinates(bezier, center_2); + PMP::convert_to_polar_coordinates(bezier, center_2); int start_id=directions.empty()?0:1; diff --git a/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_svg_example.cpp b/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_svg_example.cpp index faaa43a4e03f..bc63a9e2ea54 100644 --- a/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_svg_example.cpp +++ b/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_svg_example.cpp @@ -108,7 +108,7 @@ int main(int argc, char** argv) for (const std::array& bezier : bezier_control_points) { std::vector> polar_coords = - PMP::convert_polygon_to_polar_coordinates(bezier, center_2); + PMP::convert_to_polar_coordinates(bezier, center_2); directions.emplace_back(); lengths.emplace_back(); @@ -318,7 +318,7 @@ int main(int argc, char** argv) // polar coordinates from the polyline center and convertion to lengths and directions std::vector> polar_coords = - PMP::convert_polygon_to_polar_coordinates(polylines_2[pid], poly_center); + PMP::convert_to_polar_coordinates(polylines_2[pid], poly_center); for (const std::pair& polar_coord : polar_coords) { diff --git a/Vector_graphics_on_surfaces/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Vector_graphics_on_surfaces/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index 943db935dec1..bc6c7e7e3a4a 100644 --- a/Vector_graphics_on_surfaces/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Vector_graphics_on_surfaces/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -3214,7 +3214,6 @@ void approximate_geodesic_distance_field(const Face_location& * is reached before. * \tparam TriangleMesh a model of `FaceListGraph` and `EdgeListGraph` * \tparam FT floating point number type (float or double) - * \tparam num_subdiv the number of iterations of the subdivision algorithm * \param tmesh input triangle mesh to compute the path on * \param src the source of the path * \param len the distance to walk along the straightest @@ -3240,22 +3239,31 @@ straightest_geodesic(const Face_location &src, return Impl::straightest_geodesic(src, tmesh, vpm, dir, len); } -// we don't expect the last point to be duplicated here -// TODO: rename the function, it's not a polygon necessarily + +/*! + * \ingroup VGSMiscellaneous + * converts the coordinates of a range of points into polar coordinates w.r.t. a given center + * \tparam K a model of `PolygonTraits_2` + * \tparam PointRange_2 a model of the concept `RandomAccessContainer` with `K::Point_2` as value type + * \param points the input points to convert. + * \param center the point of reference for the polar coordinates. + * If omitted, then centroid of `polygon` will be used. + * \return the polar coordinates of the points as a pair (distance, angle) + */ template std::vector> -convert_polygon_to_polar_coordinates(const PointRange_2& polygon, - std::optional center = std::nullopt) +convert_to_polar_coordinates(const PointRange_2& points, + std::optional center = std::nullopt) { std::vector> result; // compute naively the center if (center==std::nullopt) { - Bbox_2 bb = bbox_2(polygon.begin(), polygon.end()); + Bbox_2 bb = bbox_2(points.begin(), points.end()); center = typename K::Point_2((bb.xmax()+bb.xmin())/2., (bb.ymax()+bb.ymin())/2); } - bool is_closed = polygon.front()==polygon.back(); + bool is_closed = points.front()==points.back(); std::size_t nbp=polygon.size(); if (is_closed) --nbp; for (std::size_t i=0; i std::vector> trace_geodesic_polygon(const Face_location ¢er, @@ -3319,7 +3344,23 @@ trace_geodesic_polygon(const Face_location ¢er return result; } -//TODO add groups of polygons for better rendering +/*! + * \ingroup VGSFunctions + * computes for each vertex of each polygon in `polygons` a face locations on `tmesh`, `center` representing the center of the 2D bounding box of the polygons. + * This method computes the location of the center of the bounding box of each polygon on the mesh w.r.t. `center` and call `trace_geodesic_polygon()` with that center with + * appropriate directions and distances to have a consistant orientation for the polygons. + * \tparam TriangleMesh a model of `FaceListGraph` and `EdgeListGraph` + * \tparam K a model of `Kernel` with `K::FT` being a floating point number type (float or double) + * \param center the location on `tmesh` corresponding to the center of the 2D bounding box of the polygons. + * \param polygons 2D polygons + * \param scaling a scaling factor to scale the polygons on `tmesh` (considering geodesic distances on `tmesh`) + * \param tmesh input triangle mesh supporting the vertices of the output polygon + * \return a face location for each vertex of each polygon + * \todo add named parameters + * \todo polygon orientation is not handled in the function and should be done outside of the function for now + * \todo for better rendering we can group polygons to have one center for the same group of polygon (useful for letters that are not simply connected) + * \todo what if boundary is reached + */ template std::vector>> trace_geodesic_polygons(const Face_location ¢er, @@ -3375,7 +3416,7 @@ trace_geodesic_polygons(const Face_location ¢e } std::vector> polar_coords = - convert_polygon_to_polar_coordinates(polygons[i], + convert_to_polar_coordinates(polygons[i], typename K::Point_2((polygon_bboxes[i].xmin()+polygon_bboxes[i].xmax())/2., (polygon_bboxes[i].ymin()+polygon_bboxes[i].ymax())/2.)); if (polygons[i].front()==polygons[i].back()) @@ -3399,9 +3440,27 @@ trace_geodesic_polygons(const Face_location ¢e return result; } -//TODO add groups of polygons for better rendering -//TODO add a function dedicated to groups of polygons (like a sentence) -// that takes groups of polyons + + +/*! + * \ingroup VGSFunctions + * computes for each vertex of each polygon in `polygons` a face locations on `tmesh`, `center` representing the center of the 2D bounding box of the polygons. + * This method starts by considering the segment splitting in two halves along the y-axis the bounding box of the polygons. 2D centers for each polygon are + * computed on this segment as the intersection with the line splitting the bounding box of the polygon in two halves along the x-axis. + * The splitting segment is then drawn on `tmesh` and the face location of the 2D centers is found. + * `trace_geodesic_polygon()` is then called for each polygon and center, with appropriate directions and distances to have a consistant orientation for the polygons. + * \tparam TriangleMesh a model of `FaceListGraph` and `EdgeListGraph` + * \tparam K a model of `Kernel` with `K::FT` being a floating point number type (float or double) + * \param center the location on `tmesh` corresponding to the center of the 2D bounding box of the polygons. + * \param polygons 2D polygons + * \param scaling a scaling factor to scale the polygons on `tmesh` (considering geodesic distances on `tmesh`) + * \param tmesh input triangle mesh supporting the vertices of the output polygon + * \return a face location for each vertex of each polygon + * \todo add named parameters + * \todo polygon orientation is not handled in the function and should be done outside of the function for now + * \todo for better rendering we can group polygons to have one center for the same group of polygon (useful for letters that are not simply connected) + * \todo what if boundary is reached + */ template std::vector>> trace_geodesic_label(const Face_location ¢er, @@ -3549,7 +3608,7 @@ trace_geodesic_label(const Face_location ¢er, std::vector> polar_coords = - convert_polygon_to_polar_coordinates(polygons[i], + convert_to_polar_coordinates(polygons[i], typename K::Point_2((polygon_bboxes[i].xmin()+polygon_bboxes[i].xmax())/2., (gbox.ymin()+gbox.ymax())/2.)); @@ -3572,9 +3631,21 @@ trace_geodesic_label(const Face_location ¢er, return result; } -template -typename K::FT path_length(const std::vector>& path, - const TriangleMesh &tmesh) + +/*! + * \ingroup VGSMiscellaneous + * computes the length of a path on a triangle mesh. + * \tparam FT floating point number type (float or double) + * \tparam TriangleMesh a model of `FaceGraph` + * \param path a path described as a range of face locations, with the property that + for two consecutive face locations, there exist a face in `tmesh` containing the two corresponding points. + * \param tmesh the triangle mesh supporing the path + * \todo add named parameters + * \todo generic range + */ +template +typename FT path_length(const std::vector>& path, + const TriangleMesh &tmesh) { std::size_t lpath = path.size(); if(lpath<2) @@ -3591,7 +3662,66 @@ typename K::FT path_length(const std::vector +typename K::FT path_length(const std::vector>& path, + const Face_location& src, + const Face_location& tgt, + const TriangleMesh &tmesh) +{ + std::size_t lpath = path.size(); + if(lpath==0) + return sqrt(squared_distance(construct_point(src,tmesh),construct_point(tgt,tmesh))); + + using VPM = typename boost::property_map::const_type; + VPM vpm = get(CGAL::vertex_point, tmesh); + + typename K::FT len=sqrt(squared_distance(construct_point(src,tmesh),construct_point(path[0],tmesh))); + + for (std::size_t i=0; i std::vector>> trace_geodesic_label_along_curve(const std::vector>& supporting_curve, @@ -3656,7 +3786,7 @@ trace_geodesic_label_along_curve(const std::vector> polar_coords = - convert_polygon_to_polar_coordinates(polygons[i], + convert_to_polar_coordinates(polygons[i], typename K::Point_2((polygon_bboxes[i].xmin()+polygon_bboxes[i].xmax())/2., (gbox.ymin()+gbox.ymax())/2.)); //already duplicated in trace_geodesic_polygon @@ -3755,6 +3885,25 @@ trace_geodesic_label_along_curve(const std::vector std::vector< std::vector> > trace_bezier_curves(const Face_location ¢er, @@ -3833,6 +3982,30 @@ trace_bezier_curves(const Face_location ¢er, return result; } + +/*! + * \ingroup VGSFunctions + * computes a path representing a bezier polyline (a sequence of bezier curves having an identical control points, + * that is the fourth control point of the nth curve is the first control point of the (n+1)th curve). + * Control points are defined by the endpoints of straightest geodesic curves starting from `center` along given directions and distances. + * The iterative de Casteljau subdivision algorithm is applied to create more control points that are then connected with locally shortest paths. + * The output path is such that for two consecutive face locations, there must exist a face in `tmesh` containing the two corresponding points. + * The first portion of the curve is defined by the 4 first values in `directions` and `lengths`. The second portion is defined by the 4'th value + * and the next 3, and so on until the end is reached. If `is_closed` is true, then the first value will be used with the last three to define the last portion. + * \tparam TriangleMesh a model of `FaceListGraph` and `EdgeListGraph` + * \tparam K a model of `Kernel` with `K::FT` being a floating point number type (float or double) + * \param center the location on `tmesh` where straightest geodesic for the placement of control points starts. The y-axis used is `halfedge(center.first, tmesh)`. + * \param directions contains the direction of the straightest geodesic for each control point + * \param lengths contains the length of the straightest geodesic for each control point + * \param num_subdiv the number of iterations of the de Casteljau subdivision algorithm + * \param is_closed if true [directions/lengths].front() will be used as additional last point, generating a closed path + * \param tmesh input triangle mesh supporting the vertices of the output polygon + * \return a face location for each vertex of each polygon + * \todo add named parameters + * \todo polygon orientation is not handled in the function and should be done outside of the function for now + * \todo for better rendering we can group polygons to have one center for the same group of polygon (useful for letters that are not simply connected) + * \todo what if boundary is reached + */ //TODO: make sure it is consistent with the rest to not duplicate the last point if closed template std::vector> @@ -3932,30 +4105,28 @@ trace_polyline_of_bezier_curves(const Face_location -typename K::FT path_length(const std::vector>& path, - const Face_location& src, - const Face_location& tgt, - const TriangleMesh &tmesh) -{ - std::size_t lpath = path.size(); - if(lpath==0) - return sqrt(squared_distance(construct_point(src,tmesh),construct_point(tgt,tmesh))); - - using VPM = typename boost::property_map::const_type; - VPM vpm = get(CGAL::vertex_point, tmesh); - - typename K::FT len=sqrt(squared_distance(construct_point(src,tmesh),construct_point(path[0],tmesh))); - - for (std::size_t i=0; i::%vertex_descriptor` as key type and `Kernel::Vector_3` as value type. + * \tparam FNM a model of `ReadWritePropertyMap` with `boost::graph_traits::%face_descriptor` as key type and `Kernel::Vector_3` as value type. + * \tparam OutputIterator an output iterator accepting `boost::graph_traits::%halfedge_descriptor` to be put + * \param tmesh the triangle mesh to be refined + * \param paths a path described as a range of edge locations, with the property that + * for two consecutive edge locations, there exist a face in `tmesh` containing the two corresponding points. + * \param vnm property map associating a normal to each vertex of `tmesh` that is updated by this function + * \param fnm property map associating a normal to each face of `tmesh` that is updated by this function + * \param out output iterator where created halfedges are put + * \todo add named parameters + * \todo vnm and fnm are optional + * \todo intersection between path or self-intersections are not handle. Should it be? If not what do we do? + * \todo out should contain edges, and also existing edges already part of a path + * \todo shall we also have the edges in the order of the input rather than all at once + */ template // std::vector::vertex_descriptor> OutputIterator @@ -4403,9 +4574,20 @@ refine_mesh_along_paths(const std::vector OutputIterator convert_path_to_polyline(const std::vector>& path, @@ -4428,7 +4610,22 @@ convert_path_to_polyline(const std::vector>& pat return poly_out; } -// same as above but for edge locations +/*! + * \ingroup VGSMiscellaneous + * converts a path on a triangle mesh to the corresponding polyline of points. + * If `path` contains identical consecutive vertices, only one point will be put in `poly_out` for this vertex. + * \tparam FT floating point number type (float or double) + * \tparam TriangleMesh a model of `FaceGraph` + * \tparam OutputIterator an output iterator accepting points from `tmesh` + * \param src source of the path + * \param tgt target of the path + * \param path a path described as a range of edge locations, with the property that + for two consecutive edge locations, there exist a edge in `tmesh` containing the two corresponding points. + * \param tmesh the triangle mesh supporing the path + * \param poly_out output iterator where points of the polyline are put. + * \todo add named parameters + * \todo generic range + */ template OutputIterator convert_path_to_polyline(const Face_location& src, From e0572b9946fef55804c905063240accb756be999 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Tue, 7 Jan 2025 10:28:49 +0100 Subject: [PATCH 105/120] fix license and copyright --- .../license/Vector_graphics_on_surfaces.h | 54 +++++++++++++++++++ .../include/CGAL/license/gpl_package_list.txt | 1 + .../Bsurf/locally_shortest_path.h | 4 +- 3 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 Installation/include/CGAL/license/Vector_graphics_on_surfaces.h diff --git a/Installation/include/CGAL/license/Vector_graphics_on_surfaces.h b/Installation/include/CGAL/license/Vector_graphics_on_surfaces.h new file mode 100644 index 000000000000..32ca4b440c5f --- /dev/null +++ b/Installation/include/CGAL/license/Vector_graphics_on_surfaces.h @@ -0,0 +1,54 @@ +// Copyright (c) 2016 GeometryFactory SARL (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org) +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s) : Andreas Fabri +// +// Warning: this file is generated, see include/CGAL/license/README.md + +#ifndef CGAL_LICENSE_VECTOR_GRAPHICS_ON_SURFACES_H +#define CGAL_LICENSE_VECTOR_GRAPHICS_ON_SURFACES_H + +#include +#include + +#ifdef CGAL_VECTOR_GRAPHICS_ON_SURFACES_COMMERCIAL_LICENSE + +# if CGAL_VECTOR_GRAPHICS_ON_SURFACES_COMMERCIAL_LICENSE < CGAL_RELEASE_DATE + +# if defined(CGAL_LICENSE_WARNING) + + CGAL_pragma_warning("Your commercial license for CGAL does not cover " + "this release of the Vector_graphics_on_surfaces package.") +# endif + +# ifdef CGAL_LICENSE_ERROR +# error "Your commercial license for CGAL does not cover this release \ + of the Vector_graphics_on_surfaces package. \ + You get this error, as you defined CGAL_LICENSE_ERROR." +# endif // CGAL_LICENSE_ERROR + +# endif // CGAL_VECTOR_GRAPHICS_ON_SURFACES_COMMERCIAL_LICENSE < CGAL_RELEASE_DATE + +#else // no CGAL_VECTOR_GRAPHICS_ON_SURFACES_COMMERCIAL_LICENSE + +# if defined(CGAL_LICENSE_WARNING) + CGAL_pragma_warning("\nThe macro CGAL_VECTOR_GRAPHICS_ON_SURFACES_COMMERCIAL_LICENSE is not defined." + "\nYou use the CGAL Vector_graphics_on_surfaces package under " + "the terms of the GPLv3+.") +# endif // CGAL_LICENSE_WARNING + +# ifdef CGAL_LICENSE_ERROR +# error "The macro CGAL_VECTOR_GRAPHICS_ON_SURFACES_COMMERCIAL_LICENSE is not defined.\ + You use the CGAL Vector_graphics_on_surfaces package under the terms of \ + the GPLv3+. You get this error, as you defined CGAL_LICENSE_ERROR." +# endif // CGAL_LICENSE_ERROR + +#endif // no CGAL_VECTOR_GRAPHICS_ON_SURFACES_COMMERCIAL_LICENSE + +#endif // CGAL_LICENSE_VECTOR_GRAPHICS_ON_SURFACES_H diff --git a/Installation/include/CGAL/license/gpl_package_list.txt b/Installation/include/CGAL/license/gpl_package_list.txt index 75ff9d5c9ed9..6d7bd6ea9783 100644 --- a/Installation/include/CGAL/license/gpl_package_list.txt +++ b/Installation/include/CGAL/license/gpl_package_list.txt @@ -106,5 +106,6 @@ Triangulation_2 2D Triangulation Triangulation_3 3D Triangulations Triangulation dD Triangulations Triangulation_on_sphere_2 2D Triangulation on Sphere +Vector_graphics_on_surfaces Visibility_2 2D Visibility Computation Voronoi_diagram_2 2D Voronoi Diagram Adaptor diff --git a/Vector_graphics_on_surfaces/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Vector_graphics_on_surfaces/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h index bc6c7e7e3a4a..4f1581613e96 100644 --- a/Vector_graphics_on_surfaces/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Vector_graphics_on_surfaces/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h @@ -1,4 +1,4 @@ -// Copyright (c) 2023 University of Genova (Italy). +// Copyright (c) 2023 GeometryFactory and Claudio Mancinelli. // All rights reserved. // // This file is part of CGAL (www.cgal.org). @@ -13,7 +13,7 @@ #ifndef CGAL_POLYGON_MESH_PROCESSING_BSURF_LOCALLY_SHORTEST_PATH_H #define CGAL_POLYGON_MESH_PROCESSING_BSURF_LOCALLY_SHORTEST_PATH_H -// #include +#include #include #include From b3d7ce40512e0e85902804bf59a09bf9305de118 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Tue, 7 Jan 2025 13:34:26 +0100 Subject: [PATCH 106/120] move header + fix compilation issues --- .../CMakeLists.txt | 2 ++ .../geodesic_circles_sm_example.cpp | 2 +- .../locally_shortest_path_sm_example.cpp | 2 +- .../straightest_geodesic_sm_example.cpp | 2 +- .../trace_bezier_segment_sm_example.cpp | 2 +- .../trace_polygon_example.cpp | 2 +- ...ace_polygon_on_polygonal_curve_example.cpp | 2 +- .../trace_polygon_on_svg_curve_example.cpp | 2 +- .../trace_regular_polygons_example.cpp | 2 +- .../trace_svg_example.cpp | 2 +- .../locally_shortest_path.h | 22 +++++++++---------- .../test_straightest_geodesic.cpp | 2 +- 12 files changed, 23 insertions(+), 21 deletions(-) rename Vector_graphics_on_surfaces/include/CGAL/{Polygon_mesh_processing/Bsurf => Vector_graphics_on_surfaces}/locally_shortest_path.h (99%) diff --git a/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/CMakeLists.txt b/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/CMakeLists.txt index f247273368fb..3f3cc61f1586 100644 --- a/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/CMakeLists.txt +++ b/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/CMakeLists.txt @@ -25,6 +25,8 @@ if(TARGET CGAL::Eigen3_support) target_link_libraries(trace_regular_polygons_example PUBLIC CGAL::Eigen3_support) create_single_source_cgal_program("trace_polygon_on_polygonal_curve_example.cpp") target_link_libraries(trace_polygon_on_polygonal_curve_example PUBLIC CGAL::Eigen3_support) + create_single_source_cgal_program("locally_shortest_path_sm_example.cpp") + target_link_libraries(locally_shortest_path_sm_example PUBLIC CGAL::Eigen3_support) if (NanoSVG_FOUND) create_single_source_cgal_program("trace_svg_example.cpp") target_link_libraries(trace_svg_example PUBLIC CGAL::Eigen3_support CGAL::NanoSVG_support) diff --git a/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/geodesic_circles_sm_example.cpp b/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/geodesic_circles_sm_example.cpp index fac91379a0f2..978b6601d9ed 100644 --- a/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/geodesic_circles_sm_example.cpp +++ b/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/geodesic_circles_sm_example.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include diff --git a/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/locally_shortest_path_sm_example.cpp b/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/locally_shortest_path_sm_example.cpp index 3f606f0da257..3ebc7e626634 100644 --- a/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/locally_shortest_path_sm_example.cpp +++ b/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/locally_shortest_path_sm_example.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include diff --git a/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/straightest_geodesic_sm_example.cpp b/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/straightest_geodesic_sm_example.cpp index 3ca0f7edbcf3..ef5806fa06ac 100644 --- a/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/straightest_geodesic_sm_example.cpp +++ b/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/straightest_geodesic_sm_example.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include diff --git a/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_bezier_segment_sm_example.cpp b/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_bezier_segment_sm_example.cpp index 1a64cb285a12..2426e163e9a9 100644 --- a/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_bezier_segment_sm_example.cpp +++ b/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_bezier_segment_sm_example.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include diff --git a/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_polygon_example.cpp b/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_polygon_example.cpp index 679917e1b27c..b233d415007a 100644 --- a/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_polygon_example.cpp +++ b/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_polygon_example.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include #include #include diff --git a/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_polygon_on_polygonal_curve_example.cpp b/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_polygon_on_polygonal_curve_example.cpp index c92fb24b2a78..019ec6e045fa 100644 --- a/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_polygon_on_polygonal_curve_example.cpp +++ b/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_polygon_on_polygonal_curve_example.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include diff --git a/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_polygon_on_svg_curve_example.cpp b/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_polygon_on_svg_curve_example.cpp index b1be86293ac4..b5a180ce7a60 100644 --- a/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_polygon_on_svg_curve_example.cpp +++ b/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_polygon_on_svg_curve_example.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include diff --git a/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_regular_polygons_example.cpp b/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_regular_polygons_example.cpp index bf97c4079e74..3162496cd372 100644 --- a/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_regular_polygons_example.cpp +++ b/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_regular_polygons_example.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include diff --git a/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_svg_example.cpp b/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_svg_example.cpp index bc63a9e2ea54..016513b87413 100644 --- a/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_svg_example.cpp +++ b/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_svg_example.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include #include #include diff --git a/Vector_graphics_on_surfaces/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h b/Vector_graphics_on_surfaces/include/CGAL/Vector_graphics_on_surfaces/locally_shortest_path.h similarity index 99% rename from Vector_graphics_on_surfaces/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h rename to Vector_graphics_on_surfaces/include/CGAL/Vector_graphics_on_surfaces/locally_shortest_path.h index 4f1581613e96..bb90ac34488b 100644 --- a/Vector_graphics_on_surfaces/include/CGAL/Polygon_mesh_processing/Bsurf/locally_shortest_path.h +++ b/Vector_graphics_on_surfaces/include/CGAL/Vector_graphics_on_surfaces/locally_shortest_path.h @@ -3264,11 +3264,11 @@ convert_to_polar_coordinates(const PointRange_2& points, } bool is_closed = points.front()==points.back(); - std::size_t nbp=polygon.size(); + std::size_t nbp=points.size(); if (is_closed) --nbp; for (std::size_t i=0; iy())/* /d */, (pt.x()-center->x())/* /d */); result.emplace_back(d, polar); @@ -3644,8 +3644,8 @@ trace_geodesic_label(const Face_location ¢er, * \todo generic range */ template -typename FT path_length(const std::vector>& path, - const TriangleMesh &tmesh) +FT path_length(const std::vector>& path, + const TriangleMesh &tmesh) { std::size_t lpath = path.size(); if(lpath<2) @@ -3654,7 +3654,7 @@ typename FT path_length(const std::vector>& path using VPM = typename boost::property_map::const_type; VPM vpm = get(CGAL::vertex_point, tmesh); - typename K::FT len(0); + FT len(0); for (std::size_t i=0; i>& path * \todo add named parameters * \todo generic range */ -template -typename K::FT path_length(const std::vector>& path, - const Face_location& src, - const Face_location& tgt, - const TriangleMesh &tmesh) +template +FT path_length(const std::vector>& path, + const Face_location& src, + const Face_location& tgt, + const TriangleMesh &tmesh) { std::size_t lpath = path.size(); if(lpath==0) @@ -3688,7 +3688,7 @@ typename K::FT path_length(const std::vector::const_type; VPM vpm = get(CGAL::vertex_point, tmesh); - typename K::FT len=sqrt(squared_distance(construct_point(src,tmesh),construct_point(path[0],tmesh))); + FT len=sqrt(squared_distance(construct_point(src,tmesh),construct_point(path[0],tmesh))); for (std::size_t i=0; i #include -#include +#include #include #include From 3cb40d03bea29a61a581ecff5a60fe78ece4082a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Tue, 7 Jan 2025 15:03:24 +0100 Subject: [PATCH 107/120] change namespace --- .../Bsurf/Locally_shortest_path_item.cpp | 18 +- .../geodesic_circles_sm_example.cpp | 3 +- .../locally_shortest_path_sm_example.cpp | 6 +- .../straightest_geodesic_sm_example.cpp | 5 +- .../trace_bezier_segment_sm_example.cpp | 6 +- .../trace_polygon_example.cpp | 17 +- ...ace_polygon_on_polygonal_curve_example.cpp | 11 +- .../trace_polygon_on_svg_curve_example.cpp | 17 +- .../trace_regular_polygons_example.cpp | 9 +- .../trace_svg_example.cpp | 29 +- .../locally_shortest_path.h | 463 ++++++++++-------- .../CMakeLists.txt | 2 +- .../test_straightest_geodesic.cpp | 9 +- 13 files changed, 331 insertions(+), 264 deletions(-) diff --git a/Lab/demo/Lab/Plugins/Bsurf/Locally_shortest_path_item.cpp b/Lab/demo/Lab/Plugins/Bsurf/Locally_shortest_path_item.cpp index 8a31774331f3..0945534b0dd3 100644 --- a/Lab/demo/Lab/Plugins/Bsurf/Locally_shortest_path_item.cpp +++ b/Lab/demo/Lab/Plugins/Bsurf/Locally_shortest_path_item.cpp @@ -16,11 +16,13 @@ #include #include -#include +#include #include using namespace CGAL::Three; + +namespace VGoS = CGAL::Vector_graphics_on_surfaces; namespace PMP = CGAL::Polygon_mesh_processing; typedef Viewer_interface Vi; @@ -222,7 +224,7 @@ struct Locally_shortest_path_item_priv{ QCursor rotate_cursor; bool path_invalidated=true; #ifndef CGAL_BSURF_USE_DIJKSTRA_SP - PMP::Dual_geodesic_solver geodesic_solver; + VGoS::Dual_geodesic_solver geodesic_solver; #endif }; @@ -325,7 +327,7 @@ void Locally_shortest_path_item::drawPath() const d->locations[0] = PMP::locate_with_AABB_tree(src_pt, d->aabb_tree, mesh); d->locations[1] = PMP::locate_with_AABB_tree(tgt_pt, d->aabb_tree, mesh); - PMP::locally_shortest_path(d->locations[0], d->locations[1], mesh, edge_locations, d->geodesic_solver); + VGoS::locally_shortest_path(d->locations[0], d->locations[1], mesh, edge_locations, d->geodesic_solver); d->spath_item->polylines.back().clear(); d->spath_item->polylines.back().push_back(src_pt); for (auto el : edge_locations) @@ -348,13 +350,13 @@ void Locally_shortest_path_item::drawPath() const d->locations[2] = PMP::locate_with_AABB_tree(c3_pt, d->aabb_tree, mesh); d->locations[3] = PMP::locate_with_AABB_tree(c4_pt, d->aabb_tree, mesh); - PMP::Bezier_segment control_points=CGAL::make_array(d->locations[0], - d->locations[1], - d->locations[2], - d->locations[3]); + VGoS::Bezier_segment control_points=CGAL::make_array(d->locations[0], + d->locations[1], + d->locations[2], + d->locations[3]); std::vector face_locations = - PMP::recursive_de_Casteljau(mesh, control_points, 8, d->geodesic_solver); + VGoS::recursive_de_Casteljau(mesh, control_points, 8, d->geodesic_solver); // TODO: we should connect points with geodesics and not segments d->spath_item->polylines.back().clear(); diff --git a/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/geodesic_circles_sm_example.cpp b/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/geodesic_circles_sm_example.cpp index 978b6601d9ed..c24d173bc389 100644 --- a/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/geodesic_circles_sm_example.cpp +++ b/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/geodesic_circles_sm_example.cpp @@ -4,6 +4,7 @@ #include +namespace VGoS = CGAL::Vector_graphics_on_surfaces; namespace PMP = CGAL::Polygon_mesh_processing; typedef CGAL::Exact_predicates_inexact_constructions_kernel K; @@ -42,7 +43,7 @@ int main(int argc, char** argv) std::cout << "src = " << PMP::construct_point(src, mesh) << "\n"; auto distance_map = mesh.add_property_map("v:dstmap").first; - CGAL::Polygon_mesh_processing::approximate_geodesic_distance_field(src, distance_map, mesh); + VGoS::approximate_geodesic_distance_field(src, distance_map, mesh); std::ofstream out("circles.polylines.txt"); diff --git a/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/locally_shortest_path_sm_example.cpp b/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/locally_shortest_path_sm_example.cpp index 3ebc7e626634..0347c57b78a7 100644 --- a/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/locally_shortest_path_sm_example.cpp +++ b/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/locally_shortest_path_sm_example.cpp @@ -4,7 +4,7 @@ #include - +namespace VGoS = CGAL::Vector_graphics_on_surfaces; namespace PMP = CGAL::Polygon_mesh_processing; typedef CGAL::Exact_predicates_inexact_constructions_kernel K; @@ -65,12 +65,12 @@ int main(int argc, char** argv) std::cout << "tgt = " << PMP::construct_point(tgt, mesh) << "\n"; std::vector edge_locations; - CGAL::Polygon_mesh_processing::locally_shortest_path(src, tgt, mesh, edge_locations); + VGoS::locally_shortest_path(src, tgt, mesh, edge_locations); std::vector poly; poly.reserve(edge_locations.size()+2); - PMP::convert_path_to_polyline(src, edge_locations, tgt, mesh, std::back_inserter(poly)); + VGoS::convert_path_to_polyline(src, edge_locations, tgt, mesh, std::back_inserter(poly)); std::ofstream out("locally_shortest_path.polylines.txt"); out << poly.size(); diff --git a/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/straightest_geodesic_sm_example.cpp b/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/straightest_geodesic_sm_example.cpp index ef5806fa06ac..0a7390c08bc6 100644 --- a/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/straightest_geodesic_sm_example.cpp +++ b/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/straightest_geodesic_sm_example.cpp @@ -4,6 +4,7 @@ #include +namespace VGoS = CGAL::Vector_graphics_on_surfaces; namespace PMP = CGAL::Polygon_mesh_processing; typedef CGAL::Exact_predicates_inexact_constructions_kernel K; @@ -47,11 +48,11 @@ int main(int argc, char** argv) double target_distance = 0.5; K::Vector_2 dir(1,1); - std::vector path = PMP::straightest_geodesic(src, dir, target_distance, mesh); + std::vector path = VGoS::straightest_geodesic(src, dir, target_distance, mesh); std::vector poly; poly.reserve(path.size()); - PMP::convert_path_to_polyline(path, mesh, std::back_inserter(poly)); + VGoS::convert_path_to_polyline(path, mesh, std::back_inserter(poly)); std::ofstream out("straightest_geodesic_path.polylines.txt"); out << path.size() << " "; diff --git a/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_bezier_segment_sm_example.cpp b/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_bezier_segment_sm_example.cpp index 2426e163e9a9..e585fb8b1377 100644 --- a/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_bezier_segment_sm_example.cpp +++ b/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_bezier_segment_sm_example.cpp @@ -4,7 +4,7 @@ #include - +namespace VGoS = CGAL::Vector_graphics_on_surfaces; namespace PMP = CGAL::Polygon_mesh_processing; typedef CGAL::Exact_predicates_inexact_constructions_kernel K; @@ -42,7 +42,7 @@ int main(int argc, char** argv) b(f2, CGAL::make_array(0.3,0.3,0.4)), c(f3, CGAL::make_array(0.3,0.3,0.4)), d(f4, CGAL::make_array(0.3,0.3,0.4)); - PMP::Bezier_segment control_points=CGAL::make_array(a, b, c, d); + VGoS::Bezier_segment control_points=CGAL::make_array(a, b, c, d); std::cout << "Using the following control points:\n"; std::cout << PMP::construct_point(a, mesh) << "\n"; @@ -51,7 +51,7 @@ int main(int argc, char** argv) std::cout << PMP::construct_point(d, mesh) << "\n"; std::vector face_locations = - PMP::recursive_de_Casteljau(mesh, control_points, 8); + VGoS::recursive_de_Casteljau(mesh, control_points, 8); std::ofstream out("bezier.polylines.txt"); diff --git a/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_polygon_example.cpp b/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_polygon_example.cpp index b233d415007a..f1a42ab43e1f 100644 --- a/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_polygon_example.cpp +++ b/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_polygon_example.cpp @@ -13,6 +13,7 @@ #endif +namespace VGoS = CGAL::Vector_graphics_on_surfaces; namespace PMP = CGAL::Polygon_mesh_processing; typedef CGAL::Exact_predicates_inexact_constructions_kernel K; @@ -95,13 +96,13 @@ int main(int argc, char** argv) K::Point_3 center_pt = PMP::construct_point(center, mesh); std::cout << "center = " << center_pt << "\n"; - PMP::Dual_geodesic_solver solver; - PMP::init_geodesic_dual_solver(solver, mesh); + VGoS::Dual_geodesic_solver solver; + VGoS::init_geodesic_dual_solver(solver, mesh); for (const std::vector& polygon : polygons) { std::vector> polar_coords = - PMP::convert_to_polar_coordinates(polygon, center_2); + VGoS::convert_to_polar_coordinates(polygon, center_2); if (polygon.front()==polygon.back()) polar_coords.pop_back(); std::vector directions; @@ -116,10 +117,10 @@ int main(int argc, char** argv) } // last point is duplicated - std::vector out_polygon_path = PMP::trace_geodesic_polygon(center,directions,lens,mesh, solver); + std::vector out_polygon_path = VGoS::trace_geodesic_polygon(center,directions,lens,mesh, solver); std::vector poly; poly.reserve(out_polygon_path.size()); - PMP::convert_path_to_polyline(out_polygon_path, mesh, std::back_inserter(poly)); + VGoS::convert_path_to_polyline(out_polygon_path, mesh, std::back_inserter(poly)); out << poly.size(); for (auto p : poly) @@ -133,7 +134,7 @@ int main(int argc, char** argv) out << std::setprecision(17); std::vector> polygons_3 - = PMP::trace_geodesic_polygons(center, polygons, scaling, mesh, solver); + = VGoS::trace_geodesic_polygons(center, polygons, scaling, mesh, solver); for (const auto& polygon : polygons_3) { @@ -149,7 +150,7 @@ int main(int argc, char** argv) out << std::setprecision(17); polygons_3.clear(); - polygons_3 = PMP::trace_geodesic_label(center, polygons, scaling, mesh, solver); + polygons_3 = VGoS::trace_geodesic_label(center, polygons, scaling, mesh, solver); for (const auto& polygon : polygons_3) { @@ -166,7 +167,7 @@ int main(int argc, char** argv) using VNM = decltype(vnm); PMP::compute_normals(mesh, vnm, fnm); - PMP::refine_mesh_along_paths(polygons_3, mesh, vnm, fnm, std::back_inserter(cst_hedges)); + VGoS::refine_mesh_along_paths(polygons_3, mesh, vnm, fnm, std::back_inserter(cst_hedges)); std::ofstream("mesh_refined.off") << std::setprecision(17) << mesh; diff --git a/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_polygon_on_polygonal_curve_example.cpp b/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_polygon_on_polygonal_curve_example.cpp index 019ec6e045fa..08afc66e60b2 100644 --- a/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_polygon_on_polygonal_curve_example.cpp +++ b/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_polygon_on_polygonal_curve_example.cpp @@ -4,6 +4,7 @@ #include +namespace VGoS = CGAL::Vector_graphics_on_surfaces; namespace PMP = CGAL::Polygon_mesh_processing; typedef CGAL::Exact_predicates_inexact_constructions_kernel K; @@ -249,8 +250,8 @@ int main(int argc, char** argv) std::cout << "Total number of polygons (after copy): " << polygons.size() << "\n"; - PMP::Dual_geodesic_solver solver; - PMP::init_geodesic_dual_solver(solver, mesh); + VGoS::Dual_geodesic_solver solver; + VGoS::init_geodesic_dual_solver(solver, mesh); // get supporting curve std::vector supporting_curve = get_supporting_curve(support_filename, mesh); @@ -261,7 +262,7 @@ int main(int argc, char** argv) std::vector support_poly; support_poly.reserve(supporting_curve.size()); - PMP::convert_path_to_polyline(supporting_curve, mesh, std::back_inserter(support_poly)); + VGoS::convert_path_to_polyline(supporting_curve, mesh, std::back_inserter(support_poly)); support_out << std::setprecision(17) << support_poly.size(); for (auto p : support_poly) @@ -279,13 +280,13 @@ int main(int argc, char** argv) out << std::setprecision(17); std::vector> polygons_3; - polygons_3 = PMP::trace_geodesic_label_along_curve(supporting_curve, polygons, scaling, 0., false, mesh, solver); + polygons_3 = VGoS::trace_geodesic_label_along_curve(supporting_curve, polygons, scaling, 0., false, mesh, solver); for (const auto& polygon_path : polygons_3) { std::vector poly; poly.reserve(polygon_path.size()); - PMP::convert_path_to_polyline(polygon_path, mesh, std::back_inserter(poly)); + VGoS::convert_path_to_polyline(polygon_path, mesh, std::back_inserter(poly)); out << poly.size(); for (auto p : poly) diff --git a/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_polygon_on_svg_curve_example.cpp b/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_polygon_on_svg_curve_example.cpp index b5a180ce7a60..5acab114bf11 100644 --- a/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_polygon_on_svg_curve_example.cpp +++ b/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_polygon_on_svg_curve_example.cpp @@ -6,6 +6,7 @@ #include +namespace VGoS = CGAL::Vector_graphics_on_surfaces; namespace PMP = CGAL::Polygon_mesh_processing; typedef CGAL::Exact_predicates_inexact_constructions_kernel K; @@ -16,7 +17,7 @@ typedef PMP::Edge_location Edge_location; std::vector get_supporting_curve(std::string svg_filename, const Mesh& mesh, - Face_location center, const PMP::Dual_geodesic_solver& solver) + Face_location center, const VGoS::Dual_geodesic_solver& solver) { std::vector res; @@ -77,7 +78,7 @@ get_supporting_curve(std::string svg_filename, CGAL_assertion(bezier[0]==prev); CGAL_assertion_code(prev=bezier[3];) std::vector> polar_coords = - PMP::convert_to_polar_coordinates(bezier, center_2); + VGoS::convert_to_polar_coordinates(bezier, center_2); int start_id=directions.empty()?0:1; @@ -100,7 +101,7 @@ get_supporting_curve(std::string svg_filename, // trace bezier curves std::vector< std::vector > resi = - PMP::trace_bezier_curves(center, directions, lengths, 6, mesh, solver); + VGoS::trace_bezier_curves(center, directions, lengths, 6, mesh, solver); //TODO: here we assume that curves are parameterized in the same order and are consecutives for (const std::vector& r : resi) @@ -180,8 +181,8 @@ int main(int argc, char** argv) K::Point_3 center_pt = PMP::construct_point(center, mesh); std::cout << "center = " << center_pt << "\n"; - PMP::Dual_geodesic_solver solver; - PMP::init_geodesic_dual_solver(solver, mesh); + VGoS::Dual_geodesic_solver solver; + VGoS::init_geodesic_dual_solver(solver, mesh); // get supporting curve @@ -193,7 +194,7 @@ int main(int argc, char** argv) std::vector support_poly; support_poly.reserve(supporting_curve.size()); - PMP::convert_path_to_polyline(supporting_curve, mesh, std::back_inserter(support_poly)); + VGoS::convert_path_to_polyline(supporting_curve, mesh, std::back_inserter(support_poly)); support_out << std::setprecision(17) << support_poly.size(); for (auto p : support_poly) @@ -212,13 +213,13 @@ int main(int argc, char** argv) out << std::setprecision(17); std::vector> polygons_3; - polygons_3 = PMP::trace_geodesic_label_along_curve(supporting_curve, polygons, scaling, 0., true, mesh, solver); + polygons_3 = VGoS::trace_geodesic_label_along_curve(supporting_curve, polygons, scaling, 0., true, mesh, solver); for (const auto& polygon_path : polygons_3) { std::vector poly; poly.reserve(polygon_path.size()); - PMP::convert_path_to_polyline(polygon_path, mesh, std::back_inserter(poly)); + VGoS::convert_path_to_polyline(polygon_path, mesh, std::back_inserter(poly)); out << poly.size(); for (auto p : poly) diff --git a/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_regular_polygons_example.cpp b/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_regular_polygons_example.cpp index 3162496cd372..b85ef4efd8ef 100644 --- a/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_regular_polygons_example.cpp +++ b/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_regular_polygons_example.cpp @@ -10,6 +10,7 @@ #endif +namespace VGoS = CGAL::Vector_graphics_on_surfaces; namespace PMP = CGAL::Polygon_mesh_processing; typedef CGAL::Exact_predicates_inexact_constructions_kernel K; @@ -54,16 +55,16 @@ int main(int argc, char** argv) std::ofstream out("geodesic_polygon.polylines.txt"); out << std::setprecision(17); - PMP::Dual_geodesic_solver solver; - PMP::init_geodesic_dual_solver(solver, mesh); + VGoS::Dual_geodesic_solver solver; + VGoS::init_geodesic_dual_solver(solver, mesh); for (double len : lenghts) { std::vector lens(n_sides,len); - std::vector polygon_path = PMP::trace_geodesic_polygon(center,directions,lens,mesh, solver); + std::vector polygon_path = VGoS::trace_geodesic_polygon(center,directions,lens,mesh, solver); std::vector poly; poly.reserve(polygon_path.size()); - PMP::convert_path_to_polyline(polygon_path, mesh, std::back_inserter(poly)); + VGoS::convert_path_to_polyline(polygon_path, mesh, std::back_inserter(poly)); out << poly.size(); for (auto p : poly) diff --git a/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_svg_example.cpp b/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_svg_example.cpp index 016513b87413..bf45a889ec12 100644 --- a/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_svg_example.cpp +++ b/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/trace_svg_example.cpp @@ -13,6 +13,7 @@ #include +namespace VGoS = CGAL::Vector_graphics_on_surfaces; namespace PMP = CGAL::Polygon_mesh_processing; typedef CGAL::Exact_predicates_inexact_constructions_kernel K; @@ -87,8 +88,8 @@ int main(int argc, char** argv) //TODO: do the scaling at read time! - PMP::Dual_geodesic_solver solver; - PMP::init_geodesic_dual_solver(solver, mesh); + VGoS::Dual_geodesic_solver solver; + VGoS::init_geodesic_dual_solver(solver, mesh); std::size_t nb_faces = faces(mesh).size(); Mesh::Face_index f = *std::next(faces(mesh).begin(), (face_index)%nb_faces); @@ -108,7 +109,7 @@ int main(int argc, char** argv) for (const std::array& bezier : bezier_control_points) { std::vector> polar_coords = - PMP::convert_to_polar_coordinates(bezier, center_2); + VGoS::convert_to_polar_coordinates(bezier, center_2); directions.emplace_back(); lengths.emplace_back(); @@ -123,7 +124,7 @@ int main(int argc, char** argv) } std::vector< std::vector > res = - PMP::trace_bezier_curves(center, directions, lengths, num_subdiv, mesh, solver); + VGoS::trace_bezier_curves(center, directions, lengths, num_subdiv, mesh, solver); // write result std::ofstream out("svg_option1.polylines.txt"); @@ -132,7 +133,7 @@ int main(int argc, char** argv) { std::vector poly; poly.reserve(b.size()); - PMP::convert_path_to_polyline(b, mesh, std::back_inserter(poly)); + VGoS::convert_path_to_polyline(b, mesh, std::back_inserter(poly)); out << poly.size(); @@ -282,17 +283,17 @@ int main(int argc, char** argv) // path from the general center to the polyline center K::Vector_2 dir(center_2, poly_center); std::vector path_2_center = - PMP::straightest_geodesic(center, dir, scaling * std::sqrt(dir.squared_length()), mesh); + VGoS::straightest_geodesic(center, dir, scaling * std::sqrt(dir.squared_length()), mesh); Face_location poly_center_loc = path_2_center.back(); // update initial angle from the global center to the polyline center - using Impl=PMP::internal::Locally_shortest_path_imp; + using Impl=VGoS::internal::Locally_shortest_path_imp; K::Vector_2 initial_dir(1,0);// TODO: this is arbitray and could be a user input or from PCA... // TODO: avoid using the shortest path and directly use the straightest! std::vector shortest_path; - PMP::locally_shortest_path(center, poly_center_loc, mesh, shortest_path, solver); + VGoS::locally_shortest_path(center, poly_center_loc, mesh, shortest_path, solver); typename K::Vector_2 v = initial_dir; // TODO: the code uses the straightest path but since the code expects Edge_location, @@ -318,7 +319,7 @@ int main(int argc, char** argv) // polar coordinates from the polyline center and convertion to lengths and directions std::vector> polar_coords = - PMP::convert_to_polar_coordinates(polylines_2[pid], poly_center); + VGoS::convert_to_polar_coordinates(polylines_2[pid], poly_center); for (const std::pair& polar_coord : polar_coords) { @@ -337,13 +338,13 @@ int main(int argc, char** argv) std::vector res = - PMP::trace_polyline_of_bezier_curves(poly_center_loc, directions, lengths, - is_closed, // use [directions/lengths].front as last control point? - num_subdiv, mesh, solver); + VGoS::trace_polyline_of_bezier_curves(poly_center_loc, directions, lengths, + is_closed, // use [directions/lengths].front as last control point? + num_subdiv, mesh, solver); // write result std::vector poly3; poly3.reserve(res.size()); - PMP::convert_path_to_polyline(res, mesh, std::back_inserter(poly3)); + VGoS::convert_path_to_polyline(res, mesh, std::back_inserter(poly3)); out << poly3.size(); for (const K::Point_3& p : poly3) @@ -370,7 +371,7 @@ int main(int argc, char** argv) using VNM = decltype(vnm); PMP::compute_normals(mesh, vnm, fnm); - PMP::refine_mesh_along_paths(polygons_3, mesh, vnm, fnm, std::back_inserter(cst_hedges)); + VGoS::refine_mesh_along_paths(polygons_3, mesh, vnm, fnm, std::back_inserter(cst_hedges)); std::ofstream("mesh_refined.off") << std::setprecision(17) << mesh; diff --git a/Vector_graphics_on_surfaces/include/CGAL/Vector_graphics_on_surfaces/locally_shortest_path.h b/Vector_graphics_on_surfaces/include/CGAL/Vector_graphics_on_surfaces/locally_shortest_path.h index bb90ac34488b..4bc21608a740 100644 --- a/Vector_graphics_on_surfaces/include/CGAL/Vector_graphics_on_surfaces/locally_shortest_path.h +++ b/Vector_graphics_on_surfaces/include/CGAL/Vector_graphics_on_surfaces/locally_shortest_path.h @@ -36,7 +36,7 @@ #endif namespace CGAL { -namespace Polygon_mesh_processing { +namespace Vector_graphics_on_surfaces { #ifndef CGAL_BSURF_USE_DIJKSTRA_SP template @@ -44,8 +44,8 @@ struct Dual_geodesic_solver; #endif template -void locally_shortest_path(Face_location src, - Face_location tgt, +void locally_shortest_path(CGAL::Polygon_mesh_processing::Face_location src, + CGAL::Polygon_mesh_processing::Face_location tgt, const TriangleMesh &tmesh, EdgeLocationRange &edge_locations #ifndef CGAL_BSURF_USE_DIJKSTRA_SP @@ -61,7 +61,7 @@ void locally_shortest_path(Face_location src, * \tparam TriangleMesh a model of `FaceListGraph` and `EdgeListGraph` */ template -using Bezier_segment = std::array, 4>; +using Bezier_segment = std::array, 4>; namespace internal { @@ -85,17 +85,18 @@ struct Locally_shortest_path_imp static void dump_path(const std::vector& path, const std::vector& lerps, - const Face_location& src, - const Face_location& tgt, + const CGAL::Polygon_mesh_processing::Face_location& src, + const CGAL::Polygon_mesh_processing::Face_location& tgt, const TriangleMesh& mesh) { + namespace PMP = CGAL::Polygon_mesh_processing; static int i = -1; std::cout << "dump current path in path_"+std::to_string(i+1)+".polylines.txt\n"; std::ofstream out("path_"+std::to_string(++i)+".polylines.txt"); - out << path.size()+2 << " " << construct_point(src, mesh); + out << path.size()+2 << " " << PMP::construct_point(src, mesh); for(std::size_t i=0; i(path[i], make_array(lerps[i], 1.-lerps[i])), mesh); - out << " " << construct_point(tgt, mesh) << "\n"; + out << " " << PMP::construct_point(PMP::Edge_location(path[i], make_array(lerps[i], 1.-lerps[i])), mesh); + out << " " << PMP::construct_point(tgt, mesh) << "\n"; } #endif @@ -144,7 +145,7 @@ struct Locally_shortest_path_imp init_source_triangle(halfedge_descriptor hopp, const VertexPointMap &vpm, const TriangleMesh &mesh, - Face_location src) + CGAL::Polygon_mesh_processing::Face_location src) { halfedge_descriptor h = opposite(hopp, mesh); std::array triangle_vertices = make_array( @@ -194,7 +195,7 @@ struct Locally_shortest_path_imp init_target_triangle(halfedge_descriptor h, const std::array& flat_tid, const VertexPointMap &vpm, const TriangleMesh &mesh, - Face_location tgt) + CGAL::Polygon_mesh_processing::Face_location tgt) { std::array triangle_vertices = make_array( source(h, mesh), target(h, mesh), target(next(h, mesh), mesh)); @@ -335,8 +336,8 @@ struct Locally_shortest_path_imp static std::vector< std::array> unfold_strip(const std::vector& initial_path, - const Face_location& src, - const Face_location& tgt, + const CGAL::Polygon_mesh_processing::Face_location& src, + const CGAL::Polygon_mesh_processing::Face_location& tgt, const VertexPointMap &vpm, const TriangleMesh &mesh) { std::size_t s=initial_path.size(); @@ -544,10 +545,12 @@ struct Locally_shortest_path_imp void straighten_path(std::vector< std::array>& portals, std::vector& lerps, std::vector& path, - Face_location& src, - Face_location& tgt, + CGAL::Polygon_mesh_processing::Face_location& src, + CGAL::Polygon_mesh_processing::Face_location& tgt, const VertexPointMap &vpm, const TriangleMesh &mesh, std::size_t index) { + namespace PMP = CGAL::Polygon_mesh_processing; + #ifdef CGAL_DEBUG_BSURF dump_path(path, lerps, src, tgt, mesh); #endif @@ -560,8 +563,8 @@ struct Locally_shortest_path_imp { #ifdef CGAL_DEBUG_BSURF std::cout << "Improving path " << path.size() << " hedges\n"; - std::cout << "src = " << construct_point(src, mesh) << "\n"; - std::cout << "tgt = " << construct_point(tgt, mesh) << "\n"; + std::cout << "src = " << PMP::construct_point(src, mesh) << "\n"; + std::cout << "tgt = " << PMP::construct_point(tgt, mesh) << "\n"; #endif vertex_descriptor new_vertex=BGT::null_vertex(); @@ -955,11 +958,11 @@ struct Locally_shortest_path_imp } - Vector_3 parallel_transport_along_path(const std::vector>& edge_locations, + Vector_3 parallel_transport_along_path(const std::vector>& edge_locations, const VertexPointMap &vpm, const TriangleMesh& mesh, - const Face_location& src, - const Face_location& tgt, + const CGAL::Polygon_mesh_processing::Face_location& src, + const CGAL::Polygon_mesh_processing::Face_location& tgt, const Vector_3& v) { if(edge_locations.size()==0) @@ -1014,7 +1017,7 @@ struct Locally_shortest_path_imp static std::vector - get_3D_basis_at_point(const Face_location& p, + get_3D_basis_at_point(const CGAL::Polygon_mesh_processing::Face_location& p, const TriangleMesh& mesh, const VertexPointMap &vpm) { @@ -1035,7 +1038,7 @@ struct Locally_shortest_path_imp static std::tuple - point_is_edge(const Face_location& p, + point_is_edge(const CGAL::Polygon_mesh_processing::Face_location& p, const FT& tol=1e-5) { auto bary=p.second; @@ -1053,7 +1056,7 @@ struct Locally_shortest_path_imp // static std::tuple - point_is_vert(const Face_location& p,const FT& tol=1e-5) + point_is_vert(const CGAL::Polygon_mesh_processing::Face_location& p,const FT& tol=1e-5) { auto bary=p.second; if (bary[0] > tol && bary[1] <= tol && bary[2] <= tol) @@ -1238,12 +1241,14 @@ struct Locally_shortest_path_imp // dir is defined wrt the halfedge of start_loc.first that is used as y axis static - std::vector> - straightest_geodesic(const Face_location& start_loc, + std::vector> + straightest_geodesic(const CGAL::Polygon_mesh_processing::Face_location& start_loc, const TriangleMesh& mesh, const VertexPointMap &vpm, Vector_2 dir,const FT& len) { + namespace PMP = CGAL::Polygon_mesh_processing; + #ifdef CGAL_DEBUG_BSURF { std::ofstream log("/tmp/log.txt"); @@ -1277,7 +1282,7 @@ struct Locally_shortest_path_imp mesh.point(source(halfedge(start_loc.first, mesh), mesh)), fn, theta); - std::cout << "direction " << construct_point(start_loc, mesh) << " " << construct_point(start_loc, mesh)+dir3 << "\n"; + std::cout << "direction " << PMP::construct_point(start_loc, mesh) << " " << PMP::construct_point(start_loc, mesh)+dir3 << "\n"; #endif auto get_vid_offset=[&mesh](const halfedge_descriptor& h_ref,const vertex_descriptor& vid) @@ -1345,14 +1350,14 @@ struct Locally_shortest_path_imp return bary_edge_in_face; }; - std::vector> result; + std::vector> result; FT accumulated=0.; face_descriptor curr_tid=start_loc.first; std::array curr_flat_tid=init_flat_triangle(halfedge(curr_tid,mesh),vpm,mesh); Vector_2 flat_p= start_loc.second[0]*curr_flat_tid[0]+start_loc.second[1]*curr_flat_tid[1]+start_loc.second[2]*curr_flat_tid[2]; - Face_location curr_p=start_loc; + PMP::Face_location curr_p=start_loc; - descriptor_variant var_des = get_descriptor_from_location(start_loc,mesh); + PMP::descriptor_variant var_des = PMP::get_descriptor_from_location(start_loc,mesh); switch(var_des.index()) { case 0: @@ -1512,7 +1517,7 @@ struct Locally_shortest_path_imp break; } - Face_location prev_p; + PMP::Face_location prev_p; Vector_2 curr_dir=dir; halfedge_descriptor h_ref=halfedge(curr_tid,mesh); halfedge_descriptor h_curr=h_ref; @@ -1521,7 +1526,7 @@ struct Locally_shortest_path_imp result.push_back(curr_p); #ifdef CGAL_DEBUG_BSURF - std::cout << "p= " << construct_point(curr_p,mesh) << ")\n"; + std::cout << "p= " << PMP::construct_point(curr_p,mesh) << ")\n"; #endif //TODO not sure why we need that @@ -1548,7 +1553,7 @@ struct Locally_shortest_path_imp #endif CGAL_assertion(k!=-1); std::array new_bary=make_array(0.,0.,0.); - Face_location point_on_edge; + PMP::Face_location point_on_edge; new_bary[k] = 1 - t1; new_bary[(k + 1) % 3] = t1; @@ -1569,7 +1574,7 @@ struct Locally_shortest_path_imp std::cout<< "kv "<< kv <(curr_tid,tmp_bary); + curr_p=PMP::Face_location(curr_tid,tmp_bary); int k=get_vid_offset(h_ref,target(h_curr,mesh)); flat_p=curr_flat_tid[k]; } @@ -1617,7 +1622,8 @@ struct Locally_shortest_path_imp if (is_border(h_curr, mesh)) { result.push_back(point_on_edge); - accumulated += sqrt(squared_distance(construct_point(point_on_edge,mesh), construct_point(curr_p,mesh))); + accumulated += sqrt(squared_distance(PMP::construct_point(point_on_edge,mesh), + PMP::construct_point(curr_p,mesh))); prev_p=point_on_edge; //if accumulated > len we need to be in the common face break; } @@ -1628,7 +1634,8 @@ struct Locally_shortest_path_imp prev_p = curr_p; curr_p.first=adj; curr_p.second= new_bary; - accumulated += sqrt(squared_distance(construct_point(curr_p,mesh), construct_point(prev_p,mesh))); + accumulated += sqrt(squared_distance(PMP::construct_point(curr_p,mesh), + PMP::construct_point(prev_p,mesh))); curr_tid = adj; h_ref=halfedge(curr_tid,mesh); //TODO curr_dir should be normalized everytime (to try to avoid numerical errors) @@ -1649,7 +1656,7 @@ struct Locally_shortest_path_imp } result.push_back(curr_p); #ifdef CGAL_DEBUG_BSURF - std::cout << " inter pt: " << construct_point(curr_p, mesh) << "\n"; + std::cout << " inter pt: " << PMP::construct_point(curr_p, mesh) << "\n"; #endif } @@ -1663,8 +1670,8 @@ struct Locally_shortest_path_imp return result; } - Point_3 prev_pos = construct_point(*std::next(result.rbegin()),mesh); - Point_3 last_pos = construct_point(result.back(),mesh); + Point_3 prev_pos = PMP::construct_point(*std::next(result.rbegin()),mesh); + Point_3 last_pos = PMP::construct_point(result.back(),mesh); double alpha = excess / sqrt((last_pos - prev_pos).squared_length()); Point_3 pos = barycenter(prev_pos, alpha, last_pos, 1-alpha); #ifdef CGAL_DEBUG_BSURF @@ -1814,12 +1821,14 @@ struct Locally_shortest_path_imp static void strip_path(const TriangleMesh& tmesh, - Face_location& src, - Face_location& tgt, + CGAL::Polygon_mesh_processing::Face_location& src, + CGAL::Polygon_mesh_processing::Face_location& tgt, std::vector& initial_path) { + namespace PMP = CGAL::Polygon_mesh_processing; + // retrieve the vertex id of a face location describing a vertex - auto is_vertex = [](const Face_location& fl) + auto is_vertex = [](const PMP::Face_location& fl) { if (fl.second[0]==0 && fl.second[1]==0) return 2; @@ -1831,7 +1840,7 @@ struct Locally_shortest_path_imp }; // retrieve the source vertex id of a face location describing a point on a halfedge - auto is_edge = [](const Face_location& fl) + auto is_edge = [](const PMP::Face_location& fl) { if (fl.second[0]==0 && fl.second[1]!=0 && fl.second[2]!=0) return 2; @@ -1985,21 +1994,26 @@ struct Bezier_tracing_impl using Vector_3 = typename K::Vector_3; using FT = typename K::FT; + using Face_location = Polygon_mesh_processing::Face_location; + using Edge_location = Polygon_mesh_processing::Edge_location; + template static std::vector get_positions(const EdgeLocationRange& edge_locations, const TriangleMesh& mesh, - const Face_location& src, - const Face_location& tgt) + const Face_location& src, + const Face_location& tgt) { + namespace PMP = CGAL::Polygon_mesh_processing; + std::vector result; result.reserve(edge_locations.size()+2); - result.push_back(construct_point(src,mesh)); + result.push_back(PMP::construct_point(src,mesh)); for(auto& e: edge_locations) - result.push_back(construct_point(e,mesh)); + result.push_back(PMP::construct_point(e,mesh)); - result.push_back(construct_point(tgt,mesh)); + result.push_back(PMP::construct_point(tgt,mesh)); //TODO: we must guarantee that result is sorted and unique (rounding issue?) return result; } @@ -2009,8 +2023,8 @@ struct Bezier_tracing_impl std::vector path_parameters(const EdgeLocationRange& edge_locations, const TriangleMesh& mesh, - const Face_location& src, - const Face_location& tgt) + const Face_location& src, + const Face_location& tgt) { std::vector pos=get_positions(edge_locations,mesh,src,tgt); FT L=0.; @@ -2028,11 +2042,11 @@ struct Bezier_tracing_impl template static - Face_location + Face_location eval_point_on_geodesic(const EdgeLocationRange& edge_locations, const TriangleMesh& mesh, - const Face_location& src, - const Face_location& tgt, + const Face_location& src, + const Face_location& tgt, const std::vector& parameters,/// edge length parameterization of the path from src to tgt through edge_locations const FT& t) { @@ -2122,20 +2136,19 @@ struct Bezier_tracing_impl } static - Face_location + Face_location geodesic_lerp(const TriangleMesh &mesh, - const Face_location& src, - const Face_location& tgt,const FT& t + const Face_location& src, + const Face_location& tgt,const FT& t #ifndef CGAL_BSURF_USE_DIJKSTRA_SP , const Dual_geodesic_solver& solver #endif ) { - std::vector> edge_locations; + std::vector edge_locations; locally_shortest_path(src,tgt,mesh, edge_locations, solver); std::vector parameters=path_parameters(edge_locations,mesh,src,tgt); - Face_location point = - eval_point_on_geodesic(edge_locations,mesh,src,tgt,parameters,t); + Face_location point = eval_point_on_geodesic(edge_locations,mesh,src,tgt,parameters,t); return point; } @@ -2151,19 +2164,19 @@ struct Bezier_tracing_impl ) { #ifndef CGAL_BSURF_USE_DIJKSTRA_SP - Face_location Q0 = geodesic_lerp(mesh, polygon[0], polygon[1], t, solver); - Face_location Q1 = geodesic_lerp(mesh, polygon[1], polygon[2], t, solver); - Face_location Q2 = geodesic_lerp(mesh, polygon[2], polygon[3], t, solver); - Face_location R0 = geodesic_lerp(mesh, Q0, Q1, t, solver); - Face_location R1 = geodesic_lerp(mesh, Q1, Q2, t, solver); - Face_location S = geodesic_lerp(mesh, R0, R1, t, solver); + Face_location Q0 = geodesic_lerp(mesh, polygon[0], polygon[1], t, solver); + Face_location Q1 = geodesic_lerp(mesh, polygon[1], polygon[2], t, solver); + Face_location Q2 = geodesic_lerp(mesh, polygon[2], polygon[3], t, solver); + Face_location R0 = geodesic_lerp(mesh, Q0, Q1, t, solver); + Face_location R1 = geodesic_lerp(mesh, Q1, Q2, t, solver); + Face_location S = geodesic_lerp(mesh, R0, R1, t, solver); #else - Face_location Q0 = geodesic_lerp(mesh, polygon[0], polygon[1], t); - Face_location Q1 = geodesic_lerp(mesh, polygon[1], polygon[2], t); - Face_location Q2 = geodesic_lerp(mesh, polygon[2], polygon[3], t); - Face_location R0 = geodesic_lerp(mesh, Q0, Q1, t); - Face_location R1 = geodesic_lerp(mesh, Q1, Q2, t); - Face_location S = geodesic_lerp(mesh, R0, R1, t); + Face_location Q0 = geodesic_lerp(mesh, polygon[0], polygon[1], t); + Face_location Q1 = geodesic_lerp(mesh, polygon[1], polygon[2], t); + Face_location Q2 = geodesic_lerp(mesh, polygon[2], polygon[3], t); + Face_location R0 = geodesic_lerp(mesh, Q0, Q1, t); + Face_location R1 = geodesic_lerp(mesh, Q1, Q2, t); + Face_location S = geodesic_lerp(mesh, R0, R1, t); #endif return {{polygon[0], Q0, R0, S}, {S, R1, Q2, polygon[3]}}; } @@ -2186,6 +2199,9 @@ struct Geodesic_circle_impl using Vector_3 = typename K::Vector_3; using FT = typename K::FT; + using Face_location = Polygon_mesh_processing::Face_location; + using Edge_location = Polygon_mesh_processing::Edge_location; + struct geodesic_solver { struct graph_edge { int node = -1; @@ -2612,7 +2628,7 @@ struct Geodesic_circle_impl static Point_3 eval_position(const VertexPointMap &vpm, const TriangleMesh &mesh, - const Face_location& p) + const Face_location& p) { halfedge_descriptor h=halfedge(p.first,mesh); return CGAL::barycenter(get(vpm, source(h,mesh)), p.second[0], get(vpm, target(h,mesh)), p.second[1], get(vpm, target(next(h,mesh),mesh)), p.second[2]); @@ -2625,7 +2641,7 @@ struct Geodesic_circle_impl std::vector> nodes_around_point(const VertexPointMap &vpm, const TriangleMesh &mesh, - const Face_location& p) + const Face_location& p) { auto get_vid=[&mesh](const int k,const face_descriptor& tid) { @@ -2677,7 +2693,7 @@ struct Geodesic_circle_impl const VertexPointMap& vpm, const VertexIndexMap& vim, const TriangleMesh &mesh, - const Face_location& p) + const Face_location& p) { std::vector> source_nodes=nodes_around_point(vpm,mesh,p); @@ -2692,8 +2708,8 @@ struct Geodesic_circle_impl const VertexPointMap &vpm, const VertexIndexMap& vidmap, const TriangleMesh &mesh, - const Face_location& src, - const Face_location& tgt) + const Face_location& src, + const Face_location& tgt) { std::vector> source_nodes = nodes_around_point(vpm,mesh,src); @@ -2846,7 +2862,7 @@ void init_geodesic_dual_solver(Dual_geodesic_solver& solver, const TriangleM * triangle mesh. `src` and `tgt` must be on the same connected component. * \tparam TriangleMesh a model of `FaceListGraph` and `EdgeListGraph` * \tparam FT floating point number type (float or double) - * \tparam EdgeLocationRange a model of `BackInsertionSequence` whose value type `Edge_location`. + * \tparam EdgeLocationRange a model of `BackInsertionSequence` whose value type `CGAL::Polygon_mesh_processing::Edge_location`. * \param src source of the path * \param tgt target of the path * \param tmesh input triangle mesh to compute the path on @@ -2859,12 +2875,13 @@ void init_geodesic_dual_solver(Dual_geodesic_solver& solver, const TriangleM * of `edge_locations` is such that `f == face(opposite(halfedge(e_0, tmesh), tmesh), tmesh))`. * Similarly, if `tgt` is in the interior of a face `f`, then the last edge location `e_n` * of `edge_locations` is such that `f == face(halfedge(e_n, tmesh), tmesh)`. + * \param `solver` container for the pre-computed information. If not initialized, it will initialized internally. * \todo add named parameters * \todo should we have halfedge location instead? */ template -void locally_shortest_path(Face_location src, - Face_location tgt, +void locally_shortest_path(CGAL::Polygon_mesh_processing::Face_location src, + CGAL::Polygon_mesh_processing::Face_location tgt, const TriangleMesh &tmesh, EdgeLocationRange &edge_locations #ifndef CGAL_BSURF_USE_DIJKSTRA_SP @@ -2872,6 +2889,8 @@ void locally_shortest_path(Face_location src, #endif ) { + namespace PMP = CGAL::Polygon_mesh_processing; + typedef boost::graph_traits BGT; typedef typename BGT::halfedge_descriptor halfedge_descriptor; typedef typename BGT::vertex_descriptor vertex_descriptor; @@ -2880,8 +2899,8 @@ void locally_shortest_path(Face_location src, // start by checking if it is not a trivial path if (src.first == tgt.first) return; - auto variant_src = get_descriptor_from_location(src,tmesh); - auto variant_tgt = get_descriptor_from_location(tgt,tmesh); + auto variant_src = PMP::get_descriptor_from_location(src,tmesh); + auto variant_tgt = PMP::get_descriptor_from_location(tgt,tmesh); std::vector src_visible_face; if (const face_descriptor* f_ptr = std::get_if(&variant_src)) @@ -3069,7 +3088,7 @@ void locally_shortest_path(Face_location src, } std::cout << " src=" << src.first << " ("<< src.second[0] << "," << src.second[1] << "," << src.second[2] << ")\n"; std::cout << " tgt=" << tgt.first << " ("<< tgt.second[0] << "," << tgt.second[1] << "," << tgt.second[2] << ")\n"; - std::cout << " Updated src/tgt " << construct_point(src, tmesh) << " | " << construct_point(tgt, tmesh) << "\n"; + std::cout << " Updated src/tgt " << PMP::construct_point(src, tmesh) << " | " << PMP::construct_point(tgt, tmesh) << "\n"; } #endif @@ -3102,12 +3121,13 @@ void locally_shortest_path(Face_location src, * \param mesh input triangle mesh to compute the path on * \param control_points control points of the Bézier segment * \param num_subdiv the number of iterations of the subdivision algorithm + * \param `solver` container for the pre-computed information. If not initialized, it will initialized internally. * \return descretization of the Bézier segment as face locations * \todo add named parameters * \todo do we want to also have a way to return Bezier segments? The output is actually bezier segments subdivided. */ template -std::vector> +std::vector> recursive_de_Casteljau(const TriangleMesh& mesh, const Bezier_segment& control_points, const int num_subdiv @@ -3116,6 +3136,8 @@ recursive_de_Casteljau(const TriangleMesh& mesh, #endif ) { + namespace PMP = CGAL::Polygon_mesh_processing; + //TODO replace with named parameter using VPM = typename boost::property_map::const_type; using K = typename Kernel_traits::value_type>::type; @@ -3148,7 +3170,7 @@ recursive_de_Casteljau(const TriangleMesh& mesh, std::swap(segments, result); } - std::vector> final_result; + std::vector> final_result; final_result.reserve(result.size() * 3 + 1); final_result.push_back(std::move(result.front()[0])); for (Bezier_segment& bs : result) @@ -3160,8 +3182,8 @@ recursive_de_Casteljau(const TriangleMesh& mesh, #if 0 // nasty trick to build the vector from a pair of iterators // using the fact that data in array and vector are contiguous - return {(Face_location*)segments.data(), - (Face_location*)segments.data() + segments.size() * 4}; + return {(PMP::Face_location*)segments.data(), + (PMP::Face_location*)segments.data() + segments.size() * 4}; #endif return final_result; } @@ -3172,7 +3194,7 @@ recursive_de_Casteljau(const TriangleMesh& mesh, * and put the distance in `distance_map` */ template -void approximate_geodesic_distance_field(const Face_location& center, +void approximate_geodesic_distance_field(const CGAL::Polygon_mesh_processing::Face_location& center, VertexDistanceMap distance_map, const TriangleMesh& tmesh) { @@ -3224,8 +3246,8 @@ void approximate_geodesic_distance_field(const Face_location& * \todo offer something better than a 2D vector for the direction */ template -std::vector> -straightest_geodesic(const Face_location &src, +std::vector> +straightest_geodesic(const CGAL::Polygon_mesh_processing::Face_location &src, const typename K::Vector_2& dir, const typename K::FT len, const TriangleMesh &tmesh) @@ -3291,6 +3313,7 @@ convert_to_polar_coordinates(const PointRange_2& points, * \param directions contains the direction one need to move from `center` to reach each vertex of the polygon. * \param lengths the distance one need to move from `center` along the direction at the same position in `directions` to reach each vertex of the polygon. * \param tmesh input triangle mesh supporting the vertices of the output polygon + * \param `solver` container for the pre-computed information. If not initialized, it will initialized internally. * \return a face location for each vertex of the polygon * \todo add named parameters * \todo polygon orientation is not handled in the function and should be done outside of the function for now @@ -3299,16 +3322,18 @@ convert_to_polar_coordinates(const PointRange_2& points, * \todo why the first polygon vertex is duplicated by the function? (most probably for the example but it shouldn't be done here) */ template -std::vector> -trace_geodesic_polygon(const Face_location ¢er, +std::vector> +trace_geodesic_polygon(const CGAL::Polygon_mesh_processing::Face_location ¢er, const std::vector& directions, const std::vector& lengths, const TriangleMesh &tmesh, const Dual_geodesic_solver& solver = {}) { + namespace PMP = CGAL::Polygon_mesh_processing; + std::size_t n=directions.size(); - std::vector> result; - std::vector> vertices(n); + std::vector> result; + std::vector> vertices(n); #ifdef CGAL_DEBUG_BSURF std::cout << "trace_geodesic_polygon\n"; #endif @@ -3316,10 +3341,10 @@ trace_geodesic_polygon(const Face_location ¢er { vertices[i]= straightest_geodesic(center,directions[i],lengths[i],tmesh).back(); #ifdef CGAL_DEBUG_BSURF - std::cout << construct_point(vertices[i], tmesh) << "\n"; + std::cout << PMP::construct_point(vertices[i], tmesh) << "\n"; #endif } - std::vector> edge_locations; + std::vector> edge_locations; const Dual_geodesic_solver* solver_ptr=&solver; Dual_geodesic_solver local_solver; @@ -3336,7 +3361,7 @@ trace_geodesic_polygon(const Face_location ¢er result.push_back(vertices[i]); for(auto& el : edge_locations) { - result.push_back(to_face_location(el, tmesh)); + result.push_back(PMP::to_face_location(el, tmesh)); } } result.push_back(vertices[0]); @@ -3355,6 +3380,7 @@ trace_geodesic_polygon(const Face_location ¢er * \param polygons 2D polygons * \param scaling a scaling factor to scale the polygons on `tmesh` (considering geodesic distances on `tmesh`) * \param tmesh input triangle mesh supporting the vertices of the output polygon + * \param `solver` container for the pre-computed information. If not initialized, it will initialized internally. * \return a face location for each vertex of each polygon * \todo add named parameters * \todo polygon orientation is not handled in the function and should be done outside of the function for now @@ -3362,13 +3388,15 @@ trace_geodesic_polygon(const Face_location ¢er * \todo what if boundary is reached */ template -std::vector>> -trace_geodesic_polygons(const Face_location ¢er, +std::vector>> +trace_geodesic_polygons(const CGAL::Polygon_mesh_processing::Face_location ¢er, const std::vector>& polygons, const typename K::FT scaling, const TriangleMesh &tmesh, const Dual_geodesic_solver& solver = {}) { + namespace PMP = CGAL::Polygon_mesh_processing; + using VPM = typename boost::property_map::const_type; using Impl = internal::Locally_shortest_path_imp; VPM vpm = get(CGAL::vertex_point, tmesh); @@ -3392,19 +3420,19 @@ trace_geodesic_polygons(const Face_location ¢e init_geodesic_dual_solver(local_solver, tmesh); } - std::vector>> result(polygons.size()); + std::vector>> result(polygons.size()); typename K::Vector_2 initial_dir(1,0);// TODO: input parameter or 2D PCA of the centers? for(std::size_t i=0;i > spath = + std::vector< PMP::Face_location > spath = straightest_geodesic(center, dir, scaling * std::sqrt(dir.squared_length()),tmesh); - Face_location polygon_center = spath.back(); + PMP::Face_location polygon_center = spath.back(); // TODO: avoid using the shortest path and directly use the straightest! - std::vector> shortest_path; + std::vector> shortest_path; locally_shortest_path(center, polygon_center, tmesh, shortest_path, *solver_ptr); // update direction @@ -3455,6 +3483,7 @@ trace_geodesic_polygons(const Face_location ¢e * \param polygons 2D polygons * \param scaling a scaling factor to scale the polygons on `tmesh` (considering geodesic distances on `tmesh`) * \param tmesh input triangle mesh supporting the vertices of the output polygon + * \param `solver` container for the pre-computed information. If not initialized, it will initialized internally. * \return a face location for each vertex of each polygon * \todo add named parameters * \todo polygon orientation is not handled in the function and should be done outside of the function for now @@ -3462,13 +3491,15 @@ trace_geodesic_polygons(const Face_location ¢e * \todo what if boundary is reached */ template -std::vector>> -trace_geodesic_label(const Face_location ¢er, +std::vector>> +trace_geodesic_label(const CGAL::Polygon_mesh_processing::Face_location ¢er, const std::vector>& polygons, const typename K::FT scaling, const TriangleMesh &tmesh, const Dual_geodesic_solver& solver = {}) { + namespace PMP = CGAL::Polygon_mesh_processing; + using VPM = typename boost::property_map::const_type; using Impl = internal::Locally_shortest_path_imp; VPM vpm = get(CGAL::vertex_point, tmesh); @@ -3493,7 +3524,7 @@ trace_geodesic_label(const Face_location ¢er, init_geodesic_dual_solver(local_solver, tmesh); } - std::vector>> result(polygons.size()); + std::vector>> result(polygons.size()); // Vector_2 initial_dir(1,0);// TODO: input parameter or 2D PCA of the centers? // 1D partition of the letters @@ -3503,9 +3534,9 @@ trace_geodesic_label(const Face_location ¢er, Point_2 right_most(gbox.xmax(), c2.y()); double len = (gbox.xmax()-gbox.xmin())/2.; - std::vector< Face_location > left_path = + std::vector< PMP::Face_location > left_path = straightest_geodesic(center, left_most-c2, scaling * len, tmesh); - std::vector< Face_location > right_path = + std::vector< PMP::Face_location > right_path = straightest_geodesic(center, right_most-c2, scaling * len, tmesh); CGAL_assertion(left_path.size() >=2); @@ -3515,10 +3546,10 @@ trace_geodesic_label(const Face_location ¢er, for(std::size_t i=0;i polygon_center; + PMP::Face_location polygon_center; double xc = (polygon_bboxes[i].xmin()+polygon_bboxes[i].xmax())/2.; - auto get_polygon_center = [&tmesh, &vpm](const std::vector>& path, + auto get_polygon_center = [&tmesh, &vpm](const std::vector>& path, double targetd) { // use left @@ -3526,17 +3557,17 @@ trace_geodesic_label(const Face_location ¢er, std::size_t k=0; while(true) { - double len = std::sqrt(squared_distance(construct_point(path[k], tmesh), - construct_point(path[k+1], tmesh))); + double len = std::sqrt(squared_distance(PMP::construct_point(path[k], tmesh), + PMP::construct_point(path[k+1], tmesh))); acc+=len; if (acc == targetd) { // TODO: if you land here and path[k] is on an input vertex, it might be that loc_k==loc_k1 double theta=0; - Face_location loc_k=path[k], loc_k1=path[k+1]; + PMP::Face_location loc_k=path[k], loc_k1=path[k+1]; CGAL_assertion_code(bool OK=) - locate_in_common_face(loc_k, loc_k1, tmesh); + PMP::locate_in_common_face(loc_k, loc_k1, tmesh); CGAL_assertion(OK); std::array flat_triangle = Impl::init_flat_triangle(halfedge(loc_k.first,tmesh),vpm,tmesh); @@ -3553,12 +3584,12 @@ trace_geodesic_label(const Face_location ¢er, { double excess = acc-targetd; - Face_location loc_k=path[k], loc_k1=path[k+1]; + PMP::Face_location loc_k=path[k], loc_k1=path[k+1]; CGAL_assertion_code(bool OK=) - locate_in_common_face(loc_k, loc_k1, tmesh); + PMP::locate_in_common_face(loc_k, loc_k1, tmesh); CGAL_assertion(OK); - Face_location polygon_center; + PMP::Face_location polygon_center; polygon_center.first=loc_k.first; double alpha = excess/len; @@ -3578,9 +3609,9 @@ trace_geodesic_label(const Face_location ¢er, if (++k==path.size()-1) { - Face_location loc_k=path[k-1], loc_k1=path[k]; + PMP::Face_location loc_k=path[k-1], loc_k1=path[k]; CGAL_assertion_code(bool OK=) - locate_in_common_face(loc_k, loc_k1, tmesh); + PMP::locate_in_common_face(loc_k, loc_k1, tmesh); CGAL_assertion(OK); std::array flat_triangle = Impl::init_flat_triangle(halfedge(loc_k.first,tmesh),vpm,tmesh); @@ -3603,7 +3634,7 @@ trace_geodesic_label(const Face_location ¢er, else std::tie(polygon_center, theta)=get_polygon_center(right_path, scaling * (xc-c2.x())); - std::vector> shortest_path; + std::vector> shortest_path; locally_shortest_path(center, polygon_center, tmesh, shortest_path, *solver_ptr); @@ -3644,9 +3675,11 @@ trace_geodesic_label(const Face_location ¢er, * \todo generic range */ template -FT path_length(const std::vector>& path, +FT path_length(const std::vector>& path, const TriangleMesh &tmesh) { + namespace PMP = CGAL::Polygon_mesh_processing; + std::size_t lpath = path.size(); if(lpath<2) return 0; @@ -3657,7 +3690,8 @@ FT path_length(const std::vector>& path, FT len(0); for (std::size_t i=0; i>& path, * \todo generic range */ template -FT path_length(const std::vector>& path, - const Face_location& src, - const Face_location& tgt, +FT path_length(const std::vector>& path, + const CGAL::Polygon_mesh_processing::Face_location& src, + const CGAL::Polygon_mesh_processing::Face_location& tgt, const TriangleMesh &tmesh) { + namespace PMP = CGAL::Polygon_mesh_processing; + std::size_t lpath = path.size(); if(lpath==0) - return sqrt(squared_distance(construct_point(src,tmesh),construct_point(tgt,tmesh))); + return sqrt(squared_distance(PMP::construct_point(src,tmesh), + PMP::construct_point(tgt,tmesh))); using VPM = typename boost::property_map::const_type; VPM vpm = get(CGAL::vertex_point, tmesh); - FT len=sqrt(squared_distance(construct_point(src,tmesh),construct_point(path[0],tmesh))); + FT len=sqrt(squared_distance(PMP::construct_point(src,tmesh), + PMP::construct_point(path[0],tmesh))); for (std::size_t i=0; i>& path, * \param padding padding applied at the beggining of supporting curve to start the drawing * \param is_centered is `true`, `padding` is ignored and the bounding box of the polygon is centered on the supporting curve (in 1D) * \param tmesh input triangle mesh supporting the vertices of the output polygon + * \param `solver` container for the pre-computed information. If not initialized, it will initialized internally. * \return a face location for each vertex of each polygon * \todo add named parameters * \todo polygon orientation is not handled in the function and should be done outside of the function for now @@ -3723,8 +3764,8 @@ FT path_length(const std::vector>& path, * \todo doc what happens if supporting curve is not long enough + boundary reached */ template -std::vector>> -trace_geodesic_label_along_curve(const std::vector>& supporting_curve, +std::vector>> +trace_geodesic_label_along_curve(const std::vector>& supporting_curve, const std::vector>& polygons, const typename K::FT scaling, const typename K::FT padding, @@ -3732,12 +3773,14 @@ trace_geodesic_label_along_curve(const std::vector& solver = {}) { + namespace PMP = CGAL::Polygon_mesh_processing; + using VPM = typename boost::property_map::const_type; using Impl = internal::Locally_shortest_path_imp; VPM vpm = get(CGAL::vertex_point, tmesh); using Point_2 = typename K::Point_2; using Vector_2 = typename K::Vector_2; - using Face_loc = Face_location; + using Face_loc = PMP::Face_location; std::vector polygon_bboxes; polygon_bboxes.reserve(polygons.size()); @@ -3767,8 +3810,8 @@ trace_geodesic_label_along_curve(const std::vector support_len(supporting_curve.size(),0); for (std::size_t i=0; ipadding + scaling * (gbox.xmax()-gbox.xmin())) || (is_centered && (support_len.back()> scaling * (gbox.xmax()-gbox.xmin()))) ); @@ -3791,9 +3834,9 @@ trace_geodesic_label_along_curve(const std::vector loc_k=supporting_curve[k], loc_k1=supporting_curve[k+1]; + PMP::Face_location loc_k=supporting_curve[k], loc_k1=supporting_curve[k+1]; CGAL_assertion_code(bool OK=) - locate_in_common_face(loc_k, loc_k1, tmesh); + PMP::locate_in_common_face(loc_k, loc_k1, tmesh); CGAL_assertion(OK); std::array flat_triangle = Impl::init_flat_triangle(halfedge(loc_k.first,tmesh),vpm,tmesh); @@ -3810,12 +3853,12 @@ trace_geodesic_label_along_curve(const std::vector loc_k=supporting_curve[k-1], loc_k1=supporting_curve[k]; + PMP::Face_location loc_k=supporting_curve[k-1], loc_k1=supporting_curve[k]; CGAL_assertion_code(bool OK=) - locate_in_common_face(loc_k, loc_k1, tmesh); + PMP::locate_in_common_face(loc_k, loc_k1, tmesh); CGAL_assertion(OK); - Face_location polygon_center; + PMP::Face_location polygon_center; polygon_center.first=loc_k.first; double alpha = excess/(support_len[k]-support_len[k-1]); @@ -3837,9 +3880,9 @@ trace_geodesic_label_along_curve(const std::vector loc_k=supporting_curve[k-2], loc_k1=supporting_curve[k-1]; + PMP::Face_location loc_k=supporting_curve[k-2], loc_k1=supporting_curve[k-1]; CGAL_assertion_code(bool OK=) - locate_in_common_face(loc_k, loc_k1, tmesh); + PMP::locate_in_common_face(loc_k, loc_k1, tmesh); CGAL_assertion(OK); std::array flat_triangle = Impl::init_flat_triangle(halfedge(loc_k.first,tmesh),vpm,tmesh); @@ -3856,7 +3899,7 @@ trace_geodesic_label_along_curve(const std::vector polygon_center; + PMP::Face_location polygon_center; double xc = (polygon_bboxes[i].xmin()+polygon_bboxes[i].xmax())/2.; double theta; @@ -3898,6 +3941,7 @@ trace_geodesic_label_along_curve(const std::vector -std::vector< std::vector> > -trace_bezier_curves(const Face_location ¢er, +std::vector< std::vector> > +trace_bezier_curves(const CGAL::Polygon_mesh_processing::Face_location ¢er, const std::vector>& directions, const std::vector>& lengths, const int num_subdiv, @@ -3916,10 +3960,12 @@ trace_bezier_curves(const Face_location ¢er, #endif ) { + namespace PMP = CGAL::Polygon_mesh_processing; + using FT = typename K::FT; std::size_t n=directions.size(); - std::vector< std::vector> > result(n); + std::vector< std::vector> > result(n); #ifndef CGAL_BSURF_USE_DIJKSTRA_SP const Dual_geodesic_solver* solver_ptr=&solver; @@ -3946,13 +3992,13 @@ trace_bezier_curves(const Face_location ¢er, } #ifdef CGAL_DEBUG_BSURF - debug_ep << construct_point(control_loc[0], tmesh) << "\n"; - debug_ep << construct_point(control_loc[3], tmesh) << "\n"; - debug_cp << construct_point(control_loc[1], tmesh) << "\n"; - debug_cp << construct_point(control_loc[2], tmesh) << "\n"; + debug_ep << PMP::construct_point(control_loc[0], tmesh) << "\n"; + debug_ep << PMP::construct_point(control_loc[3], tmesh) << "\n"; + debug_cp << PMP::construct_point(control_loc[1], tmesh) << "\n"; + debug_cp << PMP::construct_point(control_loc[2], tmesh) << "\n"; #endif - std::vector> bezier = + std::vector> bezier = recursive_de_Casteljau(tmesh, control_loc, num_subdiv #ifndef CGAL_BSURF_USE_DIJKSTRA_SP , *solver_ptr @@ -3963,17 +4009,17 @@ trace_bezier_curves(const Face_location ¢er, result[i].push_back(bezier[0]); for(std::size_t b=0; b& loc = bezier[b]; - const Face_location& loc1 = bezier[b+1]; + const PMP::Face_location& loc = bezier[b]; + const PMP::Face_location& loc1 = bezier[b+1]; // connect the two face locations with shortest path is they are in different faces if (loc.first!=loc1.first) { - std::vector> edge_locations; + std::vector> edge_locations; locally_shortest_path(loc, loc1, tmesh, edge_locations, solver); result[i].reserve(result[i].size()+edge_locations.size()); - for (const Edge_location& e : edge_locations) - result[i].push_back(to_face_location(e, tmesh)); + for (const PMP::Edge_location& e : edge_locations) + result[i].push_back(PMP::to_face_location(e, tmesh)); } result[i].push_back(loc1); } @@ -4000,6 +4046,7 @@ trace_bezier_curves(const Face_location ¢er, * \param num_subdiv the number of iterations of the de Casteljau subdivision algorithm * \param is_closed if true [directions/lengths].front() will be used as additional last point, generating a closed path * \param tmesh input triangle mesh supporting the vertices of the output polygon + * \param `solver` container for the pre-computed information. If not initialized, it will initialized internally. * \return a face location for each vertex of each polygon * \todo add named parameters * \todo polygon orientation is not handled in the function and should be done outside of the function for now @@ -4008,8 +4055,8 @@ trace_bezier_curves(const Face_location ¢er, */ //TODO: make sure it is consistent with the rest to not duplicate the last point if closed template -std::vector> -trace_polyline_of_bezier_curves(const Face_location ¢er, +std::vector> +trace_polyline_of_bezier_curves(const CGAL::Polygon_mesh_processing::Face_location ¢er, const std::vector& directions, const std::vector& lengths, bool is_closed, // use [directions/lengths].front as last control point? @@ -4020,12 +4067,14 @@ trace_polyline_of_bezier_curves(const Face_location> result; + std::vector> result; // n is the number of quadruple of control points // After num_subdiv steps, we have 2^num_subdiv * n quadruples of control points @@ -4052,8 +4101,8 @@ trace_polyline_of_bezier_curves(const Face_location prev_loc = straightest_geodesic(center,directions[0],lengths[0],tmesh).back(), - first_loc = prev_loc; + PMP::Face_location prev_loc = straightest_geodesic(center,directions[0],lengths[0],tmesh).back(), + first_loc = prev_loc; for (std::size_t i=0; i> bezier = + std::vector> bezier = recursive_de_Casteljau(tmesh, control_loc, num_subdiv #ifndef CGAL_BSURF_USE_DIJKSTRA_SP , *solver_ptr @@ -4086,16 +4135,16 @@ trace_polyline_of_bezier_curves(const Face_location& loc = bezier[b]; - const Face_location& loc1 = bezier[b+1]; + const PMP::Face_location& loc = bezier[b]; + const PMP::Face_location& loc1 = bezier[b+1]; // connect the two face locations with shortest path is they are in different faces if (loc.first!=loc1.first) { - std::vector> edge_locations; + std::vector> edge_locations; locally_shortest_path(loc, loc1, tmesh, edge_locations, solver); - for (const Edge_location& e : edge_locations) - result.push_back(to_face_location(e, tmesh)); + for (const PMP::Edge_location& e : edge_locations) + result.push_back(PMP::to_face_location(e, tmesh)); } result.push_back(loc1); } @@ -4130,20 +4179,22 @@ trace_polyline_of_bezier_curves(const Face_location // std::vector::vertex_descriptor> OutputIterator -refine_mesh_along_paths(const std::vector>>& paths, +refine_mesh_along_paths(const std::vector>>& paths, TriangleMesh& tmesh, VNM vnm, FNM fnm, OutputIterator out) { + namespace PMP = CGAL::Polygon_mesh_processing; + // TODO: nothing is done here to identify identical points using vertex_descriptor = typename boost::graph_traits::vertex_descriptor; using edge_descriptor = typename boost::graph_traits::edge_descriptor; using halfedge_descriptor = typename boost::graph_traits::halfedge_descriptor; using face_descriptor = typename boost::graph_traits::face_descriptor; - using Face_loc = Face_location; - using dscrptr_vrnt = descriptor_variant; + using Face_loc = PMP::Face_location; + using dscrptr_vrnt = PMP::descriptor_variant; using Point_3 = typename K::Point_3; using EK = CGAL::Exact_predicates_exact_constructions_kernel; @@ -4221,7 +4272,7 @@ refine_mesh_along_paths(const std::vector& path : paths) { - bool closed = are_locations_identical(path.front(), path.back(), tmesh); + bool closed = PMP::are_locations_identical(path.front(), path.back(), tmesh); std::size_t nbp = path.size(); if (closed) nbp-=1; std::size_t prev_id=polyline_locations.size(), first_id=prev_id; - dscrptr_vrnt prev_var = get_descriptor_from_location(path.front(), tmesh), + dscrptr_vrnt prev_var = PMP::get_descriptor_from_location(path.front(), tmesh), first_var = prev_var; polyline_locations.push_back( path.front() ); @@ -4251,7 +4302,7 @@ refine_mesh_along_paths(const std::vector(tmesh.points()))); } @@ -4349,11 +4400,11 @@ refine_mesh_along_paths(const std::vector::null_vertex() ); polyline_vertices[vid] = vd; - exact_points[vid] = construct_point(polyline_locations[vid], tmesh, + exact_points[vid] = PMP::construct_point(polyline_locations[vid], tmesh, parameters::vertex_point_map(make_cartesian_converter_property_map(tmesh.points()))); } } @@ -4590,22 +4641,24 @@ refine_mesh_along_paths(const std::vector OutputIterator -convert_path_to_polyline(const std::vector>& path, +convert_path_to_polyline(const std::vector>& path, const TriangleMesh& tmesh, OutputIterator poly_out) { + namespace PMP = CGAL::Polygon_mesh_processing; + using vertex_descriptor = typename boost::graph_traits::vertex_descriptor; vertex_descriptor vd = boost::graph_traits::null_vertex(); - for (const Face_location& loc : path) + for (const PMP::Face_location& loc : path) { - std::optional ov = vertex_descriptor_from_location(loc, tmesh); + std::optional ov = PMP::vertex_descriptor_from_location(loc, tmesh); if (ov.has_value()) { if (vd==*ov) continue; vd=*ov; } - *poly_out++=construct_point(loc, tmesh); + *poly_out++=PMP::construct_point(loc, tmesh); } return poly_out; } @@ -4628,35 +4681,37 @@ convert_path_to_polyline(const std::vector>& pat */ template OutputIterator -convert_path_to_polyline(const Face_location& src, - const std::vector>& path, - const Face_location& tgt, +convert_path_to_polyline(const CGAL::Polygon_mesh_processing::Face_location& src, + const std::vector>& path, + const CGAL::Polygon_mesh_processing::Face_location& tgt, const TriangleMesh& tmesh, OutputIterator poly_out) { + namespace PMP = CGAL::Polygon_mesh_processing; + using vertex_descriptor = typename boost::graph_traits::vertex_descriptor; - *poly_out++=construct_point(src, tmesh); + *poly_out++=PMP::construct_point(src, tmesh); vertex_descriptor vd = boost::graph_traits::null_vertex(); - for (const Edge_location& loc : path) + for (const PMP::Edge_location& loc : path) { - std::optional ov = vertex_descriptor_from_location(loc, tmesh); + std::optional ov = PMP::vertex_descriptor_from_location(loc, tmesh); if (ov.has_value()) { if (vd==*ov) continue; vd=*ov; } - *poly_out++=construct_point(loc, tmesh); + *poly_out++=PMP::construct_point(loc, tmesh); } - *poly_out++=construct_point(tgt, tmesh); + *poly_out++=PMP::construct_point(tgt, tmesh); return poly_out; } // template // std::vector -// tangent_path_direction(const std::vector>& path, -// const Face_location& src, -// const Face_location& tgt, +// tangent_path_direction(const std::vector>& path, +// const CGAL::Polygon_mesh_processing::Face_location& src, +// const CGAL::Polygon_mesh_processing::Face_location& tgt, // const TriangleMesh &tmesh,const bool initial=true) // { // auto find = [](const std::array &vec, int x) { @@ -4713,19 +4768,21 @@ convert_path_to_polyline(const Face_location& src, template std::vector -trace_agap_polygon(const Face_location ¢er, +trace_agap_polygon(const CGAL::Polygon_mesh_processing::Face_location ¢er, const std::vector& directions, const std::vector& lengths, const TriangleMesh &tmesh, const Dual_geodesic_solver& solver = {}) { + namespace PMP = CGAL::Polygon_mesh_processing; + size_t n=directions.size(); std::vector result; - std::vector> vertices(n); + std::vector> vertices(n); for(std::size_t i=0;i(center,directions[i],lengths[i],tmesh).back(); - std::vector> edge_locations; + std::vector> edge_locations; const Dual_geodesic_solver* solver_ptr=&solver; Dual_geodesic_solver local_solver; @@ -4739,19 +4796,19 @@ trace_agap_polygon(const Face_location ¢er, { edge_locations.clear(); locally_shortest_path(vertices[i],vertices[(i+1)%n],tmesh, edge_locations, *solver_ptr); - result.push_back(construct_point(vertices[i],tmesh)); + result.push_back(PMP::construct_point(vertices[i],tmesh)); for(auto& el : edge_locations) { - result.push_back(construct_point(el, tmesh)); + result.push_back(PMP::construct_point(el, tmesh)); } } - result.push_back(construct_point(vertices[0],tmesh)); + result.push_back(PMP::construct_point(vertices[0],tmesh)); return result; } -} // namespace Polygon_mesh_processing +} // namespace Vector_graphics_on_surfaces } // namespace CGAL #endif diff --git a/Vector_graphics_on_surfaces/test/Vector_graphics_on_surfaces/CMakeLists.txt b/Vector_graphics_on_surfaces/test/Vector_graphics_on_surfaces/CMakeLists.txt index a8baaba3f80f..055149a13d4a 100644 --- a/Vector_graphics_on_surfaces/test/Vector_graphics_on_surfaces/CMakeLists.txt +++ b/Vector_graphics_on_surfaces/test/Vector_graphics_on_surfaces/CMakeLists.txt @@ -2,7 +2,7 @@ # This is the CMake script for compiling a CGAL application. cmake_minimum_required(VERSION 3.12...3.29) -project( Vector_graphics_on_surfaces ) +project( Vector_Graphics_on_surfaces_Tests ) find_package(CGAL REQUIRED) find_package(Eigen3 REQUIRED) diff --git a/Vector_graphics_on_surfaces/test/Vector_graphics_on_surfaces/test_straightest_geodesic.cpp b/Vector_graphics_on_surfaces/test/Vector_graphics_on_surfaces/test_straightest_geodesic.cpp index 92a812d7ab3e..0030d96844da 100644 --- a/Vector_graphics_on_surfaces/test/Vector_graphics_on_surfaces/test_straightest_geodesic.cpp +++ b/Vector_graphics_on_surfaces/test/Vector_graphics_on_surfaces/test_straightest_geodesic.cpp @@ -7,6 +7,7 @@ #include namespace PMP = CGAL::Polygon_mesh_processing; +namespace VGoS = CGAL::Vector_graphics_on_surfaces; using K = CGAL::Exact_predicates_inexact_constructions_kernel; using Mesh = CGAL::Surface_mesh; @@ -43,14 +44,14 @@ int main() { double theta = 2*CGAL_PI/8*n; K::Vector_2 dir(std::cos(theta), std::sin(theta)); - std::vector path = PMP::straightest_geodesic(src, dir, target_distance, mesh); + std::vector path = VGoS::straightest_geodesic(src, dir, target_distance, mesh); //TODO: check the output is of correct length /* std::vector poly; poly.reserve(path.size()); - PMP::convert_path_to_polyline(path, mesh, std::back_inserter(poly)); + VGoS::convert_path_to_polyline(path, mesh, std::back_inserter(poly)); out << path.size() << " "; @@ -82,14 +83,14 @@ int main() { double theta = 2*CGAL_PI/16*n; K::Vector_2 dir(std::cos(theta), std::sin(theta)); - std::vector path = PMP::straightest_geodesic(src, dir, target_distance, mesh); + std::vector path = VGoS::straightest_geodesic(src, dir, target_distance, mesh); //TODO: check the output is of correct length /* std::vector poly; poly.reserve(path.size()); - PMP::convert_path_to_polyline(path, mesh, std::back_inserter(poly)); + VGoS::convert_path_to_polyline(path, mesh, std::back_inserter(poly)); out << path.size() << " "; From bf312502c1ce7f7d989d319d9b0182074d6d436d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Tue, 7 Jan 2025 15:15:15 +0100 Subject: [PATCH 108/120] classified reference manual --- .../PackageDescription.txt | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/Vector_graphics_on_surfaces/doc/Vector_graphics_on_surfaces/PackageDescription.txt b/Vector_graphics_on_surfaces/doc/Vector_graphics_on_surfaces/PackageDescription.txt index 4e3969d0edc3..825f46eba807 100644 --- a/Vector_graphics_on_surfaces/doc/Vector_graphics_on_surfaces/PackageDescription.txt +++ b/Vector_graphics_on_surfaces/doc/Vector_graphics_on_surfaces/PackageDescription.txt @@ -33,4 +33,36 @@ \cgalPkgShortInfoEnd \cgalPkgDescriptionEnd + +\cgalClassifedRefPages + +\cgalCRPSection{Base Functions} +- `CGAL::Vector_graphics_on_surfaces::locally_shortest_path()` +- `CGAL::Vector_graphics_on_surfaces::recursive_de_Casteljau()` +- `CGAL::Vector_graphics_on_surfaces::straightest_geodesic()` + +\cgalCRPSection{Higher Level Tracing Functions} +- `CGAL::Vector_graphics_on_surfaces::trace_geodesic_polygon()` +- `CGAL::Vector_graphics_on_surfaces::trace_geodesic_polygons()` +- `CGAL::Vector_graphics_on_surfaces::trace_geodesic_label()` +- `CGAL::Vector_graphics_on_surfaces::trace_geodesic_label_along_curve` +- `CGAL::Vector_graphics_on_surfaces::trace_bezier_curves()` +- `CGAL::Vector_graphics_on_surfaces::trace_polyline_of_bezier_curves()` + +\cgalCRPSection{Utilities} +- `CGAL::Vector_graphics_on_surfaces::path_length()` +- `CGAL::Vector_graphics_on_surfaces::convert_path_to_polyline()` +- `CGAL::Vector_graphics_on_surfaces::convert_to_polar_coordinates` + +\cgalCRPSection{Miscellaneous} +- `CGAL::Vector_graphics_on_surfaces::Bezier_segment` +- `CGAL::Vector_graphics_on_surfaces::Dual_geodesic_solver` +- `CGAL::Vector_graphics_on_surfaces::init_geodesic_dual_solver()` +- `CGAL::Vector_graphics_on_surfaces::approximate_geodesic_distance_field()` +- `CGAL::Vector_graphics_on_surfaces::refine_mesh_along_paths()` + + +\todo tracing functions should probably be merged in only one or two functions, with parameters for the placement of centers + parallel transport, as well as an option for joining with locally shortest paths + + */ From 9e0ddbaaf5df1f04ef1e4935959dbcfae1ee586a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Tue, 7 Jan 2025 15:37:56 +0100 Subject: [PATCH 109/120] move tests --- .../Polygon_mesh_processing/CMakeLists.txt | 4 ---- .../CMakeLists.txt | 4 ++++ .../test_Bsurf.cpp | 20 +++++++++---------- .../test_Bsurf_grid.cpp | 8 ++++---- 4 files changed, 18 insertions(+), 18 deletions(-) rename {Polygon_mesh_processing/test/Polygon_mesh_processing => Vector_graphics_on_surfaces/test/Vector_graphics_on_surfaces}/test_Bsurf.cpp (93%) rename {Polygon_mesh_processing/test/Polygon_mesh_processing => Vector_graphics_on_surfaces/test/Vector_graphics_on_surfaces}/test_Bsurf_grid.cpp (95%) diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt b/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt index ba38990cffed..f814501997cf 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt @@ -88,10 +88,6 @@ if(TARGET CGAL::Eigen3_support) target_link_libraries(test_decimation_of_planar_patches PUBLIC CGAL::Eigen3_support) create_single_source_cgal_program("remeshing_quality_test.cpp" ) target_link_libraries(remeshing_quality_test PUBLIC CGAL::Eigen3_support) - create_single_source_cgal_program("test_Bsurf.cpp") - target_link_libraries(test_Bsurf PUBLIC CGAL::Eigen3_support) - create_single_source_cgal_program("test_Bsurf_grid.cpp") - target_link_libraries(test_Bsurf_grid PUBLIC CGAL::Eigen3_support) else() message(STATUS "NOTICE: Tests that use the Eigen library will not be compiled.") endif() diff --git a/Vector_graphics_on_surfaces/test/Vector_graphics_on_surfaces/CMakeLists.txt b/Vector_graphics_on_surfaces/test/Vector_graphics_on_surfaces/CMakeLists.txt index 055149a13d4a..c2fe2ddf6672 100644 --- a/Vector_graphics_on_surfaces/test/Vector_graphics_on_surfaces/CMakeLists.txt +++ b/Vector_graphics_on_surfaces/test/Vector_graphics_on_surfaces/CMakeLists.txt @@ -11,3 +11,7 @@ include(CGAL_Eigen3_support) create_single_source_cgal_program( "test_straightest_geodesic.cpp" ) target_link_libraries(test_straightest_geodesic PRIVATE CGAL::Eigen3_support) +create_single_source_cgal_program("test_Bsurf.cpp") +target_link_libraries(test_Bsurf PUBLIC CGAL::Eigen3_support) +create_single_source_cgal_program("test_Bsurf_grid.cpp") +target_link_libraries(test_Bsurf_grid PUBLIC CGAL::Eigen3_support) diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_Bsurf.cpp b/Vector_graphics_on_surfaces/test/Vector_graphics_on_surfaces/test_Bsurf.cpp similarity index 93% rename from Polygon_mesh_processing/test/Polygon_mesh_processing/test_Bsurf.cpp rename to Vector_graphics_on_surfaces/test/Vector_graphics_on_surfaces/test_Bsurf.cpp index 970ca76c6df5..06085e44c92f 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_Bsurf.cpp +++ b/Vector_graphics_on_surfaces/test/Vector_graphics_on_surfaces/test_Bsurf.cpp @@ -1,10 +1,10 @@ #include #include -#include +#include #include - +namespace VGoS = CGAL::Vector_graphics_on_surfaces; namespace PMP = CGAL::Polygon_mesh_processing; typedef CGAL::Exact_predicates_inexact_constructions_kernel K; @@ -24,8 +24,8 @@ int main(int argc, char** argv) std::cerr << "Invalid input." << std::endl; return 1; } - PMP::Dual_geodesic_solver solver; - CGAL::Polygon_mesh_processing::init_geodesic_dual_solver(solver, mesh); + VGoS::Dual_geodesic_solver solver; + VGoS::init_geodesic_dual_solver(solver, mesh); std::size_t nb_hedges = halfedges(mesh).size(); @@ -82,7 +82,7 @@ int main(int argc, char** argv) std::vector edge_locations; auto src_bk=src, tgt_bk=tgt; - PMP::locally_shortest_path(src, tgt, mesh, edge_locations, solver); + VGoS::locally_shortest_path(src, tgt, mesh, edge_locations, solver); assert(get(PMP::get_descriptor_from_location(src,mesh))==v1); assert(get(PMP::get_descriptor_from_location(tgt,mesh))==v2); @@ -107,7 +107,7 @@ int main(int argc, char** argv) src=src_bk; tgt=tgt_bk; edge_locations.clear(); - PMP::locally_shortest_path(tgt, src, mesh, edge_locations, solver); + VGoS::locally_shortest_path(tgt, src, mesh, edge_locations, solver); assert(get(PMP::get_descriptor_from_location(src,mesh))==v1); assert(get(PMP::get_descriptor_from_location(tgt,mesh))==v2); @@ -168,7 +168,7 @@ int main(int argc, char** argv) // std::cout << " " << PMP::construct_point(src, mesh) << " | " << PMP::construct_point(tgt, mesh) << "\n"; std::vector edge_locations; auto src_bk=src, tgt_bk=tgt; - PMP::locally_shortest_path(src, tgt, mesh, edge_locations, solver); + VGoS::locally_shortest_path(src, tgt, mesh, edge_locations, solver); out << edge_locations.size()+2; out << " " << PMP::construct_point(src, mesh); @@ -190,7 +190,7 @@ int main(int argc, char** argv) src=src_bk; tgt=tgt_bk; edge_locations.clear(); - PMP::locally_shortest_path(tgt, src, mesh, edge_locations, solver); + VGoS::locally_shortest_path(tgt, src, mesh, edge_locations, solver); out << edge_locations.size()+2; out << " " << PMP::construct_point(tgt, mesh); @@ -249,7 +249,7 @@ int main(int argc, char** argv) std::vector edge_locations; auto src_bk=src, tgt_bk=tgt; - PMP::locally_shortest_path(src, tgt, mesh, edge_locations, solver); + VGoS::locally_shortest_path(src, tgt, mesh, edge_locations, solver); out << edge_locations.size()+2; out << " " << PMP::construct_point(src, mesh); @@ -266,7 +266,7 @@ int main(int argc, char** argv) src=src_bk; tgt=tgt_bk; edge_locations.clear(); - PMP::locally_shortest_path(tgt, src, mesh, edge_locations, solver); + VGoS::locally_shortest_path(tgt, src, mesh, edge_locations, solver); out << edge_locations.size()+2; out << " " << PMP::construct_point(tgt, mesh); diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_Bsurf_grid.cpp b/Vector_graphics_on_surfaces/test/Vector_graphics_on_surfaces/test_Bsurf_grid.cpp similarity index 95% rename from Polygon_mesh_processing/test/Polygon_mesh_processing/test_Bsurf_grid.cpp rename to Vector_graphics_on_surfaces/test/Vector_graphics_on_surfaces/test_Bsurf_grid.cpp index 6c96dd5ec71c..625643f3e722 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_Bsurf_grid.cpp +++ b/Vector_graphics_on_surfaces/test/Vector_graphics_on_surfaces/test_Bsurf_grid.cpp @@ -1,12 +1,12 @@ #include #include -#include +#include #include #include #include - +namespace VGoS = CGAL::Vector_graphics_on_surfaces; namespace PMP = CGAL::Polygon_mesh_processing; typedef CGAL::Exact_predicates_inexact_constructions_kernel K; @@ -116,7 +116,7 @@ void test_vertex_pair(unsigned int vid1, unsigned int vid2, const Mesh& mesh) std::vector edge_locations; auto src_bk=src, tgt_bk=tgt; - PMP::locally_shortest_path(src, tgt, mesh, edge_locations); + VGoS::locally_shortest_path(src, tgt, mesh, edge_locations); assert(get(PMP::get_descriptor_from_location(src,mesh))==v1); assert(get(PMP::get_descriptor_from_location(tgt,mesh))==v2); @@ -135,7 +135,7 @@ void test_vertex_pair(unsigned int vid1, unsigned int vid2, const Mesh& mesh) src=src_bk; tgt=tgt_bk; edge_locations.clear(); - PMP::locally_shortest_path(tgt, src, mesh, edge_locations); + VGoS::locally_shortest_path(tgt, src, mesh, edge_locations); assert(get(PMP::get_descriptor_from_location(src,mesh))==v1); assert(get(PMP::get_descriptor_from_location(tgt,mesh))==v2); From e5099996682c8ba39a40586ceb3838a4f921bce8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Tue, 7 Jan 2025 15:48:21 +0100 Subject: [PATCH 110/120] please CI --- .../examples/Vector_graphics_on_surfaces/CMakeLists.txt | 2 +- .../test/Vector_graphics_on_surfaces/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/CMakeLists.txt b/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/CMakeLists.txt index 3f3cc61f1586..84962cca0793 100644 --- a/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/CMakeLists.txt +++ b/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/CMakeLists.txt @@ -2,7 +2,7 @@ # This is the CMake script for compiling a set of CGAL applications. cmake_minimum_required(VERSION 3.12...3.29) -project(Vector_Graphics_on_surfaces_Examples) +project(Vector_graphics_on_surfaces_Examples) # CGAL and its components find_package(CGAL REQUIRED) diff --git a/Vector_graphics_on_surfaces/test/Vector_graphics_on_surfaces/CMakeLists.txt b/Vector_graphics_on_surfaces/test/Vector_graphics_on_surfaces/CMakeLists.txt index c2fe2ddf6672..27a53ec8ecaa 100644 --- a/Vector_graphics_on_surfaces/test/Vector_graphics_on_surfaces/CMakeLists.txt +++ b/Vector_graphics_on_surfaces/test/Vector_graphics_on_surfaces/CMakeLists.txt @@ -2,7 +2,7 @@ # This is the CMake script for compiling a CGAL application. cmake_minimum_required(VERSION 3.12...3.29) -project( Vector_Graphics_on_surfaces_Tests ) +project(Vector_graphics_on_surfaces_Tests) find_package(CGAL REQUIRED) find_package(Eigen3 REQUIRED) From ae0b792edad0b9117ba2b628e9ce0d105c8fa79c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 8 Jan 2025 08:22:38 +0100 Subject: [PATCH 111/120] add missing dependencies --- .../PackageDescription.txt | 2 +- .../Vector_graphics_on_surfaces/dependencies | 30 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 Vector_graphics_on_surfaces/package_info/Vector_graphics_on_surfaces/dependencies diff --git a/Vector_graphics_on_surfaces/doc/Vector_graphics_on_surfaces/PackageDescription.txt b/Vector_graphics_on_surfaces/doc/Vector_graphics_on_surfaces/PackageDescription.txt index 825f46eba807..c4ce5e4b2882 100644 --- a/Vector_graphics_on_surfaces/doc/Vector_graphics_on_surfaces/PackageDescription.txt +++ b/Vector_graphics_on_surfaces/doc/Vector_graphics_on_surfaces/PackageDescription.txt @@ -20,7 +20,7 @@ \cgalPkgPicture{vgos-small.png} \cgalPkgSummaryBegin \cgalPkgAuthors{Claudio Mancinelli and Sébastien Loriot} -\cgalPkgDesc{The package provides functions to draw and manipulate basic geometric primitives such as geodesics, Bézier and B-Spline curves +\cgalPkgDesc{The package provides functions to draw and manipulate basic geometric primitives such as geodesics curves and Bézier splines on triangulated surface meshes.} \cgalPkgManuals{Chapter_VGoS,PkgVGoS_Ref} \cgalPkgSummaryEnd diff --git a/Vector_graphics_on_surfaces/package_info/Vector_graphics_on_surfaces/dependencies b/Vector_graphics_on_surfaces/package_info/Vector_graphics_on_surfaces/dependencies new file mode 100644 index 000000000000..2bb950b1db4b --- /dev/null +++ b/Vector_graphics_on_surfaces/package_info/Vector_graphics_on_surfaces/dependencies @@ -0,0 +1,30 @@ +AABB_tree +Algebraic_foundations +Arithmetic_kernel +BGL +CGAL_Core +Cartesian_kernel +Circulator +Distance_2 +Distance_3 +Filtered_kernel +Hash_map +Homogeneous_kernel +Installation +Intersections_2 +Intersections_3 +Interval_support +Kernel_23 +Kernel_d +Modular_arithmetic +Number_types +Polygon_mesh_processing +Profiling_tools +Property_map +Random_numbers +STL_Extension +Spatial_searching +Spatial_sorting +Stream_support +TDS_2 +Triangulation_2 From d1eea2b2b212e081d3d0fb07bd345b7a866a001d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 8 Jan 2025 10:08:09 +0100 Subject: [PATCH 112/120] missing link with Eigen --- Lab/demo/Lab/Plugins/Bsurf/CMakeLists.txt | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Lab/demo/Lab/Plugins/Bsurf/CMakeLists.txt b/Lab/demo/Lab/Plugins/Bsurf/CMakeLists.txt index b20407e5028d..9c3c4f1dbabd 100644 --- a/Lab/demo/Lab/Plugins/Bsurf/CMakeLists.txt +++ b/Lab/demo/Lab/Plugins/Bsurf/CMakeLists.txt @@ -1,8 +1,9 @@ include(CGALlab_macros) -cgal_lab_plugin(locally_shortest_path_plugin Locally_shortest_path_plugin) -target_link_libraries(locally_shortest_path_plugin PUBLIC locally_shortest_path_item - scene_surface_mesh_item - scene_polylines_item) - - +if(TARGET CGAL::Eigen3_support) + cgal_lab_plugin(locally_shortest_path_plugin Locally_shortest_path_plugin) + target_link_libraries(locally_shortest_path_plugin PUBLIC locally_shortest_path_item + scene_surface_mesh_item + scene_polylines_item + CGAL::Eigen3_support) +endif() From adec4e5bd7034dd57b49810b6045372f398aba19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 8 Jan 2025 10:17:32 +0100 Subject: [PATCH 113/120] fix compilation issue --- Lab/demo/Lab/Plugins/Bsurf/Locally_shortest_path_item.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lab/demo/Lab/Plugins/Bsurf/Locally_shortest_path_item.cpp b/Lab/demo/Lab/Plugins/Bsurf/Locally_shortest_path_item.cpp index 0945534b0dd3..224d4aaf838b 100644 --- a/Lab/demo/Lab/Plugins/Bsurf/Locally_shortest_path_item.cpp +++ b/Lab/demo/Lab/Plugins/Bsurf/Locally_shortest_path_item.cpp @@ -168,7 +168,7 @@ struct Locally_shortest_path_item_priv{ rotate_cursor = QCursor(pix); #ifndef CGAL_BSURF_USE_DIJKSTRA_SP - CGAL::Polygon_mesh_processing::init_geodesic_dual_solver(geodesic_solver, mesh); + VGoS::init_geodesic_dual_solver(geodesic_solver, mesh); #endif } From f6eb9e6ab19a823f37067c9a204a570da66c1a1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 8 Jan 2025 10:49:51 +0100 Subject: [PATCH 114/120] one more missing eigen link --- Lab/demo/Lab/CMakeLists.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Lab/demo/Lab/CMakeLists.txt b/Lab/demo/Lab/CMakeLists.txt index 2c419a98c926..d94c08e37bb1 100644 --- a/Lab/demo/Lab/CMakeLists.txt +++ b/Lab/demo/Lab/CMakeLists.txt @@ -274,8 +274,10 @@ if(CGAL_Qt6_FOUND AND Qt6_FOUND) add_item(scene_edit_box_item Plugins/PCA/Scene_edit_box_item.cpp) - add_item(locally_shortest_path_item Plugins/Bsurf/Locally_shortest_path_item.cpp) - target_link_libraries(locally_shortest_path_item PUBLIC scene_surface_mesh_item scene_polylines_item) + if (CGAL_Eigen3_support) + add_item(locally_shortest_path_item Plugins/Bsurf/Locally_shortest_path_item.cpp) + target_link_libraries(locally_shortest_path_item PUBLIC scene_surface_mesh_item scene_polylines_item CGAL::Eigen3_support) + endif() add_item(scene_image_item Scene_image_item.cpp) add_item(scene_surface_mesh_item Scene_surface_mesh_item.cpp) From 114fd88d4a8b926e7284744d3e6424f9c45d03b0 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Mon, 13 Jan 2025 08:09:13 +0000 Subject: [PATCH 115/120] doc fixes --- .../Vector_graphics_on_surfaces/dependencies | 1 + .../locally_shortest_path.h | 64 +++++++++---------- 2 files changed, 33 insertions(+), 32 deletions(-) diff --git a/Vector_graphics_on_surfaces/doc/Vector_graphics_on_surfaces/dependencies b/Vector_graphics_on_surfaces/doc/Vector_graphics_on_surfaces/dependencies index 1972566b0b5b..26c05c82d78d 100644 --- a/Vector_graphics_on_surfaces/doc/Vector_graphics_on_surfaces/dependencies +++ b/Vector_graphics_on_surfaces/doc/Vector_graphics_on_surfaces/dependencies @@ -5,3 +5,4 @@ Algebraic_foundations Circulator Stream_support Polygon_mesh_processing +BGL diff --git a/Vector_graphics_on_surfaces/include/CGAL/Vector_graphics_on_surfaces/locally_shortest_path.h b/Vector_graphics_on_surfaces/include/CGAL/Vector_graphics_on_surfaces/locally_shortest_path.h index 4bc21608a740..6d1901526d49 100644 --- a/Vector_graphics_on_surfaces/include/CGAL/Vector_graphics_on_surfaces/locally_shortest_path.h +++ b/Vector_graphics_on_surfaces/include/CGAL/Vector_graphics_on_surfaces/locally_shortest_path.h @@ -836,22 +836,22 @@ struct Locally_shortest_path_imp Vector_2 w = c1 - c0; FT phi_ij = angle(e0, w); if (e0.x()*w.y()-e0.y()*w.x() < 0) - phi_ij = 2 * M_PI - phi_ij; + phi_ij = 2 * CGAL_PI - phi_ij; w *= -1; FT phi_ji = angle(e1, w); if (e1.x()*w.y()-e1.y()*w.x() < 0) - phi_ji = 2 * M_PI - phi_ji; + phi_ji = 2 * CGAL_PI - phi_ji; Vector_3 n_from= face_normal(vpm, mesh, from); std::vector e_from = polar_basis(vpm, mesh, from); double teta = angle(e_from, v); if (cross_product(e_from, v)*n_from < 0) - teta = 2 * M_PI - teta; + teta = 2 * CGAL_PI - teta; std::vector e_to = polar_basis(vpm, mesh, from ,to); Vector_3 n_to = face_normal(vpm, mesh, to); - double rot = teta + phi_ji + M_PI - phi_ij; + double rot = teta + phi_ji + CGAL_PI - phi_ij; e_to =e_to*sqrt(v.squared_length()); v = rotate_vector(e_to, n_to, rot); @@ -1206,7 +1206,7 @@ struct Locally_shortest_path_imp FT l = sqrt(squared_distance(prev_vert_adj,vert)); FT phi = approximate_angle(vert - prev_vert_adj, vert_adj - prev_vert_adj); phi*=CGAL_PI/180.; - FT x = l * std::sin(offset) / std::sin(M_PI - phi - offset); + FT x = l * std::sin(offset) / std::sin(CGAL_PI - phi - offset); FT alpha = x / sqrt(squared_distance(vert_adj, prev_vert_adj)); halfedge_descriptor prev_h=prev(h,mesh); @@ -2813,7 +2813,7 @@ struct Geodesic_circle_impl #ifndef CGAL_BSURF_USE_DIJKSTRA_SP /*! * \ingroup VGSMiscellaneous - * Geodesic solver class used to store pre-computed information of a given mesh for + * Geodesic solver class used to store precomputed information of a given mesh for * approximate geodesic compution. Those information are computed by the function * `init_geodesic_dual_solver()`. * \tparam FT floating point number type (float or double) @@ -2835,8 +2835,8 @@ struct Dual_geodesic_solver * If `solver` was used in a previous call to this function, information will be ovewritten. * \tparam TriangleMesh a model of `FaceListGraph` and `EdgeListGraph` * \tparam FT floating point number type (float or double) - * \param `solver` the container for the pre-computed information - * \param `tmesh` triangle mesh to be considered for the precomputations + * \param solver the container for the precomputed information + * \param tmesh triangle mesh to be considered for the precomputations * \todo add named parameters * \todo make sure solver.graph is cleared before filling it */ @@ -2875,7 +2875,7 @@ void init_geodesic_dual_solver(Dual_geodesic_solver& solver, const TriangleM * of `edge_locations` is such that `f == face(opposite(halfedge(e_0, tmesh), tmesh), tmesh))`. * Similarly, if `tgt` is in the interior of a face `f`, then the last edge location `e_n` * of `edge_locations` is such that `f == face(halfedge(e_n, tmesh), tmesh)`. - * \param `solver` container for the pre-computed information. If not initialized, it will initialized internally. + * \param solver container for the precomputed information. If not initialized, it will be initialized internally. * \todo add named parameters * \todo should we have halfedge location instead? */ @@ -3121,10 +3121,10 @@ void locally_shortest_path(CGAL::Polygon_mesh_processing::Face_location std::vector> @@ -3232,7 +3232,7 @@ void approximate_geodesic_distance_field(const CGAL::Polygon_mesh_processing::Fa /*! * \ingroup VGSFunctions * computes a path on a triangle mesh that is computed by starting a walk on `tmesh` - * given a direction and a maximum distance. The distance will not be achieved is a border edge + * given a direction and a maximum distance. The distance will not be achieved if a border edge * is reached before. * \tparam TriangleMesh a model of `FaceListGraph` and `EdgeListGraph` * \tparam FT floating point number type (float or double) @@ -3242,7 +3242,7 @@ void approximate_geodesic_distance_field(const CGAL::Polygon_mesh_processing::Fa * \param dir the initial direction of the walk, given as a 2D vector in the face of src, the halfedge of the face being the y-axis. * \return the straightest path (not containing `src`) * \todo add named parameters - * \todo do we want to also have a way to return Bezier segments? The output is actually bezier segments subdivided. + * \todo do we want to also have a way to return Bézier segments? The output is actually Bézier segments subdivided. * \todo offer something better than a 2D vector for the direction */ template @@ -3264,7 +3264,7 @@ straightest_geodesic(const CGAL::Polygon_mesh_processing::Face_location::%vertex_descriptor` as key type and `Kernel::Vector_3` as value type. @@ -4166,7 +4166,7 @@ trace_polyline_of_bezier_curves(const CGAL::Polygon_mesh_processing::Face_locati * \tparam OutputIterator an output iterator accepting `boost::graph_traits::%halfedge_descriptor` to be put * \param tmesh the triangle mesh to be refined * \param paths a path described as a range of edge locations, with the property that - * for two consecutive edge locations, there exist a face in `tmesh` containing the two corresponding points. + * for two consecutive edge locations, there exists a face in `tmesh` containing the two corresponding points. * \param vnm property map associating a normal to each vertex of `tmesh` that is updated by this function * \param fnm property map associating a normal to each face of `tmesh` that is updated by this function * \param out output iterator where created halfedges are put @@ -4317,7 +4317,7 @@ refine_mesh_along_paths(const std::vector face_normals(faces_to_refine.size()); std::vector> face_input_vertices(faces_to_refine.size()); for (std::size_t fid=0; fid Date: Mon, 13 Jan 2025 11:05:44 +0000 Subject: [PATCH 116/120] uint -> unsigned int --- .../examples/Vector_graphics_on_surfaces/CMakeLists.txt | 1 + .../CGAL/Vector_graphics_on_surfaces/locally_shortest_path.h | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/CMakeLists.txt b/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/CMakeLists.txt index 84962cca0793..a7bb19178b5f 100644 --- a/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/CMakeLists.txt +++ b/Vector_graphics_on_surfaces/examples/Vector_graphics_on_surfaces/CMakeLists.txt @@ -38,3 +38,4 @@ if(TARGET CGAL::Eigen3_support) else() message(STATUS "NOTICE: Examples that use Eigen will not be compiled.") endif() + diff --git a/Vector_graphics_on_surfaces/include/CGAL/Vector_graphics_on_surfaces/locally_shortest_path.h b/Vector_graphics_on_surfaces/include/CGAL/Vector_graphics_on_surfaces/locally_shortest_path.h index 6d1901526d49..10187573d850 100644 --- a/Vector_graphics_on_surfaces/include/CGAL/Vector_graphics_on_surfaces/locally_shortest_path.h +++ b/Vector_graphics_on_surfaces/include/CGAL/Vector_graphics_on_surfaces/locally_shortest_path.h @@ -2218,8 +2218,8 @@ struct Geodesic_circle_impl const FT& len) { // TODO: avoid cast - uint vida=get(vidmap,a); - uint vidb=get(vidmap,b); + unsigned int vida=get(vidmap,a); + unsigned int vidb=get(vidmap,b); solver.graph[vida].push_back({static_cast(vidb), len}); solver.graph[vidb].push_back({static_cast(vida), len}); From 473d1d1754b48b1fa91278a3374aa3bb1403fd44 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Mon, 13 Jan 2025 14:14:22 +0000 Subject: [PATCH 117/120] std::max --- .../CGAL/Vector_graphics_on_surfaces/locally_shortest_path.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Vector_graphics_on_surfaces/include/CGAL/Vector_graphics_on_surfaces/locally_shortest_path.h b/Vector_graphics_on_surfaces/include/CGAL/Vector_graphics_on_surfaces/locally_shortest_path.h index 10187573d850..faf5183b2cb7 100644 --- a/Vector_graphics_on_surfaces/include/CGAL/Vector_graphics_on_surfaces/locally_shortest_path.h +++ b/Vector_graphics_on_surfaces/include/CGAL/Vector_graphics_on_surfaces/locally_shortest_path.h @@ -2245,8 +2245,8 @@ struct Geodesic_circle_impl double cos_alpha = ba_norm * bd_norm; double cos_beta = bc_norm * bd_norm; - double sin_alpha = sqrt(std::max(0.0, 1 - cos_alpha * cos_alpha)); - double sin_beta = sqrt(std::max(0.0, 1 - cos_beta * cos_beta)); + double sin_alpha = sqrt((std::max)(0.0, 1 - cos_alpha * cos_alpha)); + double sin_beta = sqrt((std::max)(0.0, 1 - cos_beta * cos_beta)); // cos(alpha + beta) From 27aaaa6a6b2b9a23137a9887d53fe7931854996c Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Mon, 13 Jan 2025 14:41:00 +0000 Subject: [PATCH 118/120] typo --- .../Vector_graphics_on_surfaces/locally_shortest_path.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Vector_graphics_on_surfaces/include/CGAL/Vector_graphics_on_surfaces/locally_shortest_path.h b/Vector_graphics_on_surfaces/include/CGAL/Vector_graphics_on_surfaces/locally_shortest_path.h index faf5183b2cb7..d9722d4af5f3 100644 --- a/Vector_graphics_on_surfaces/include/CGAL/Vector_graphics_on_surfaces/locally_shortest_path.h +++ b/Vector_graphics_on_surfaces/include/CGAL/Vector_graphics_on_surfaces/locally_shortest_path.h @@ -3371,7 +3371,7 @@ trace_geodesic_polygon(const CGAL::Polygon_mesh_processing::Face_location Date: Tue, 14 Jan 2025 12:41:48 +0000 Subject: [PATCH 119/120] typos --- .../locally_shortest_path.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Vector_graphics_on_surfaces/include/CGAL/Vector_graphics_on_surfaces/locally_shortest_path.h b/Vector_graphics_on_surfaces/include/CGAL/Vector_graphics_on_surfaces/locally_shortest_path.h index d9722d4af5f3..ce36fceccbb0 100644 --- a/Vector_graphics_on_surfaces/include/CGAL/Vector_graphics_on_surfaces/locally_shortest_path.h +++ b/Vector_graphics_on_surfaces/include/CGAL/Vector_graphics_on_surfaces/locally_shortest_path.h @@ -2832,7 +2832,7 @@ struct Dual_geodesic_solver * \ingroup VGSMiscellaneous * fills `solver` for a given mesh `tmesh`. It is the user responsability to * call again this function if `tmesh` or the points of its vertices are modified. - * If `solver` was used in a previous call to this function, information will be ovewritten. + * If `solver` was used in a previous call to this function, information will be overwritten. * \tparam TriangleMesh a model of `FaceListGraph` and `EdgeListGraph` * \tparam FT floating point number type (float or double) * \param solver the container for the precomputed information @@ -2859,7 +2859,7 @@ void init_geodesic_dual_solver(Dual_geodesic_solver& solver, const TriangleM /*! * \ingroup VGSFunctions * computes an approximated geodesic shortest path between two locations on a - * triangle mesh. `src` and `tgt` must be on the same connected component. + * `tmesh`. The points`src` and `tgt` must be on the same connected component. * \tparam TriangleMesh a model of `FaceListGraph` and `EdgeListGraph` * \tparam FT floating point number type (float or double) * \tparam EdgeLocationRange a model of `BackInsertionSequence` whose value type `CGAL::Polygon_mesh_processing::Edge_location`. @@ -3115,7 +3115,7 @@ void locally_shortest_path(CGAL::Polygon_mesh_processing::Face_location Date: Tue, 14 Jan 2025 12:47:57 +0000 Subject: [PATCH 120/120] Change inclusion order in tests --- .../test/Vector_graphics_on_surfaces/test_Bsurf.cpp | 3 +-- .../test/Vector_graphics_on_surfaces/test_Bsurf_grid.cpp | 2 +- .../Vector_graphics_on_surfaces/test_straightest_geodesic.cpp | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Vector_graphics_on_surfaces/test/Vector_graphics_on_surfaces/test_Bsurf.cpp b/Vector_graphics_on_surfaces/test/Vector_graphics_on_surfaces/test_Bsurf.cpp index 06085e44c92f..e028de3bd239 100644 --- a/Vector_graphics_on_surfaces/test/Vector_graphics_on_surfaces/test_Bsurf.cpp +++ b/Vector_graphics_on_surfaces/test/Vector_graphics_on_surfaces/test_Bsurf.cpp @@ -1,7 +1,6 @@ +#include #include #include -#include - #include namespace VGoS = CGAL::Vector_graphics_on_surfaces; diff --git a/Vector_graphics_on_surfaces/test/Vector_graphics_on_surfaces/test_Bsurf_grid.cpp b/Vector_graphics_on_surfaces/test/Vector_graphics_on_surfaces/test_Bsurf_grid.cpp index 625643f3e722..a7a2be5aca59 100644 --- a/Vector_graphics_on_surfaces/test/Vector_graphics_on_surfaces/test_Bsurf_grid.cpp +++ b/Vector_graphics_on_surfaces/test/Vector_graphics_on_surfaces/test_Bsurf_grid.cpp @@ -1,6 +1,6 @@ +#include #include #include -#include #include #include diff --git a/Vector_graphics_on_surfaces/test/Vector_graphics_on_surfaces/test_straightest_geodesic.cpp b/Vector_graphics_on_surfaces/test/Vector_graphics_on_surfaces/test_straightest_geodesic.cpp index 0030d96844da..92297b74e69d 100644 --- a/Vector_graphics_on_surfaces/test/Vector_graphics_on_surfaces/test_straightest_geodesic.cpp +++ b/Vector_graphics_on_surfaces/test/Vector_graphics_on_surfaces/test_straightest_geodesic.cpp @@ -1,6 +1,6 @@ +#include #include #include -#include #include #include