#include "voronoi.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sha-base-framework/declarations.h" #include "sha-base-framework/frame.h" #include "sha-base-framework/logger.h" #include "sha-surface-mesh/matmesh.h" #include "sha-surface-mesh/mesh3.h" #include "utility.h" namespace da { namespace sha { VoronoiDiagram CreateVoronoiDiagramInDomain(const Eigen::AlignedBox3d &domain, const Eigen::MatrixXd &mat_seeds, size_t num_lloyd_iterations) { const size_t num_seeds = mat_seeds.rows(); VoronoiDiagram voronoi_diagram; Eigen::Vector3d transform_factor = domain.min(); double scale_factor = domain.sizes().maxCoeff(); std::vector regulated_domain_box_planes = { {1, 0, 0, 0}, {-1, 0, 0, domain.sizes().x() / scale_factor}, {0, 1, 0, 0}, {0, -1, 0, domain.sizes().y() / scale_factor}, {0, 0, 1, 0}, {0, 0, -1, domain.sizes().z() / scale_factor}}; Eigen::MatrixXd mat_regulated_seeds; mat_regulated_seeds = (mat_seeds.rowwise() - transform_factor.transpose()).array() / scale_factor; auto MapUnitCoordinateToOriginSpace = [&](const Eigen::Vector3d &unit_coord) -> Eigen::Vector3d { return unit_coord * scale_factor + transform_factor; }; std::vector flatten_seeds(num_seeds * 3); boost::for_each(boost::counting_range(0, num_seeds), [&](index_t seed_idx) { boost::for_each(boost::counting_range(0, 3), [&](index_t idx) { flatten_seeds[seed_idx * 3 + idx] = mat_regulated_seeds(seed_idx, idx); }); }); GEO::initialize(); auto delaunay = std::make_shared(false); // GEO::PeriodicDelaunay3d delaunay(false); delaunay->set_keeps_infinite(true); delaunay->set_vertices(num_seeds, flatten_seeds.data()); delaunay->set_stores_neighbors(true); delaunay->compute(); // if using CVT if (num_lloyd_iterations > 0) { for (index_t iter_idx = 0; iter_idx < num_lloyd_iterations; ++iter_idx) { log::info("lloyd iteration {} / {}", iter_idx + 1, num_lloyd_iterations); std::vector new_points(mat_seeds.rows() * 3); for (index_t v = 0; v < mat_seeds.rows(); ++v) { GEO::ConvexCell convex_cell; delaunay->copy_Laguerre_cell_from_Delaunay(v, convex_cell); for (auto &plane : regulated_domain_box_planes) { convex_cell.clip_by_plane(GEO::vec4(plane.x(), plane.y(), plane.z(), plane.w())); } convex_cell.compute_geometry(); GEO::vec3 g = convex_cell.barycenter(); new_points[3 * v] = g.x; new_points[3 * v + 1] = g.y; new_points[3 * v + 2] = g.z; } // In periodic mode, points may escape out of // the domain. Relocate them in [0,1]^3 for (index_t i = 0; i < new_points.size(); ++i) { if (new_points[i] < 0.0) { new_points[i] += 1.0; } if (new_points[i] > 1.0) { new_points[i] -= 1.0; } } std::copy(new_points.begin(), new_points.end(), flatten_seeds.begin()); delaunay->set_vertices(flatten_seeds.size() / 3, flatten_seeds.data()); delaunay->compute(); } } auto CreateVoronoiCellFromConvexCell = [&](GEO::ConvexCell &convex_cell) -> VoronoiCell { VoronoiCell voronoi_cell; std::vector map_vertex_to_triangle_idx(convex_cell.nb_v(), GEO::index_t(-1)); std::vector triangle_indices(convex_cell.nb_t(), GEO::index_t(-1)); GEO::index_t curr_num_triangles = 0; GEO::index_t triangle_idx = convex_cell.first_triangle(); std::vector cell_vertex_vector; std::vector cell_triangle_vector; while (triangle_idx != VBW::END_OF_LIST) { auto triangle_with_flag = convex_cell.get_triangle_and_flags(triangle_idx); auto point = convex_cell.stored_triangle_point(triangle_idx); cell_vertex_vector.emplace_back(point.x, point.y, point.z); triangle_indices[triangle_idx] = curr_num_triangles; ++curr_num_triangles; map_vertex_to_triangle_idx[triangle_with_flag.i] = triangle_idx; map_vertex_to_triangle_idx[triangle_with_flag.j] = triangle_idx; map_vertex_to_triangle_idx[triangle_with_flag.k] = triangle_idx; triangle_idx = GEO::index_t(triangle_with_flag.flags); } for (GEO::index_t vertex_idx = 1; vertex_idx < convex_cell.nb_v(); ++vertex_idx) { if (map_vertex_to_triangle_idx[vertex_idx] != GEO::index_t(-1)) { GEO::index_t t = map_vertex_to_triangle_idx[vertex_idx]; PolygonFace face; face.boundary_vtx_loops.resize(1); auto &boundary_vtx_loop = face.boundary_vtx_loops.front(); do { boundary_vtx_loop.push_back(triangle_indices[t]); index_t lv = convex_cell.triangle_find_vertex(t, vertex_idx); t = convex_cell.triangle_adjacent(t, (lv + 1) % 3); } while (t != map_vertex_to_triangle_idx[vertex_idx]); for (int i = 2; i < boundary_vtx_loop.size(); ++i) { cell_triangle_vector.push_back( {boundary_vtx_loop[0], boundary_vtx_loop[i - 1], boundary_vtx_loop[i]}); face.triangle_face_indices_in_cell.push_back(cell_triangle_vector.size() - 1); voronoi_cell.map_triangle_to_polygon_idx.push_back( voronoi_cell.polyhedron.polygons.size()); } face.mat_triangle_faces = voronoi_cell.cell_triangle_mesh.mat_faces( face.triangle_face_indices_in_cell, Eigen::all); voronoi_cell.polyhedron.polygons.push_back(face); } } auto &mat_coordinates_of_cell = voronoi_cell.cell_triangle_mesh.mat_coordinates; auto &mat_faces_of_cell = voronoi_cell.cell_triangle_mesh.mat_faces; const size_t num_vertices_of_cell = cell_vertex_vector.size(); const size_t num_faces_of_cell = cell_triangle_vector.size(); mat_coordinates_of_cell.resize(num_vertices_of_cell, 3); mat_faces_of_cell.resize(num_faces_of_cell, 3); boost::for_each(boost::counting_range(0, num_vertices_of_cell), [&](index_t vertex_idx) { mat_coordinates_of_cell.row(vertex_idx) = MapUnitCoordinateToOriginSpace(cell_vertex_vector[vertex_idx]); }); boost::for_each(boost::counting_range(0, num_faces_of_cell), [&](index_t face_idx) { mat_faces_of_cell.row(face_idx) << cell_triangle_vector[face_idx].x(), cell_triangle_vector[face_idx].y(), cell_triangle_vector[face_idx].z(); }); return voronoi_cell; }; auto BuildVoronoiCell = [&](index_t seed_idx) -> VoronoiCell { GEO::ConvexCell convex_cell; delaunay->copy_Laguerre_cell_from_Delaunay(seed_idx, convex_cell); for (auto &plane : regulated_domain_box_planes) { convex_cell.clip_by_plane(GEO::vec4(plane.x(), plane.y(), plane.z(), plane.w())); } convex_cell.compute_geometry(); return CreateVoronoiCellFromConvexCell(convex_cell); }; voronoi_diagram.map_cell_to_neighbor_indices.resize(num_seeds); boost::for_each(boost::counting_range(0, num_seeds), [&](index_t seed_idx) { const index_t &cell_idx = seed_idx; VoronoiCell voronoi_cell = BuildVoronoiCell(seed_idx); voronoi_cell.seed = mat_seeds.row(seed_idx); // voronoi_cell.map_triangle_to_origin_face_idx.resize(voronoi_cell.cell_triangle_mesh.NumFaces(), // -1); voronoi_diagram.cells.push_back(voronoi_cell); VBW::vector neighbor_cell_indices_of_current_cell; delaunay->get_neighbors(seed_idx, neighbor_cell_indices_of_current_cell); for (auto neighbor_cell_idx : neighbor_cell_indices_of_current_cell) { if (neighbor_cell_idx == -1) continue; voronoi_diagram.map_cell_to_neighbor_indices[cell_idx].insert(neighbor_cell_idx); } }); return voronoi_diagram; } VoronoiDiagram CreateRestrictedVoronoiDiagramFromMesh(const MatMesh3 &shell_mesh, const Eigen::MatrixXd &mat_seeds, size_t num_lloyd_iterations, double sharp_angle, bool keep_number_flag) { auto shell_domain = shell_mesh.AlignedBox(); auto unrestricted_voronoi_diagram = CreateVoronoiDiagramInDomain(shell_domain, mat_seeds, num_lloyd_iterations); log::info("Created Voronoi Diagram In Domain"); std::map> map_unrestricted_cell_idx_to_restricted_cell_idx; const int num_cells = unrestricted_voronoi_diagram.cells.size(); VoronoiDiagram restricted_voronoi_diagram; std::map map_restricted_cell_idx_to_is_totally_in_shell; // oneapi::tbb::parallel_for(0, num_cells, 1, [&](index_t unrestricted_cell_idx) { // boost::range::for_each(boost::irange(0, num_cells), [&](index_t unrestricted_cell_idx) { // log::info("Building octree"); // DynamicBoundingBoxOctree shell_boundingbox_octree(shell_domain, 6); // for (index_t face_idx = 0; face_idx < shell_mesh.NumFaces(); ++face_idx) { // Eigen::MatrixXd triangle_coords(3, 3); // triangle_coords.row(0) = shell_mesh.mat_coordinates.row(shell_mesh.mat_faces(face_idx, 0)); // triangle_coords.row(1) = shell_mesh.mat_coordinates.row(shell_mesh.mat_faces(face_idx, 1)); // triangle_coords.row(2) = shell_mesh.mat_coordinates.row(shell_mesh.mat_faces(face_idx, 2)); // auto triangle_box = Eigen::AlignedBox3d(triangle_coords.colwise().minCoeff(), // triangle_coords.colwise().maxCoeff()); // shell_boundingbox_octree.InsertData(triangle_box, 1); // } double total_mmsecs = 0; log::info("Start Boolean"); #pragma omp parallel for for (index_t unrestricted_cell_idx = 0; unrestricted_cell_idx < num_cells; ++unrestricted_cell_idx) { auto &unrestricted_cell = unrestricted_voronoi_diagram.cells.at(unrestricted_cell_idx); VoronoiCell new_voronoi_cell; new_voronoi_cell.seed = unrestricted_cell.seed; MatMesh3 &new_voronoi_mesh = new_voronoi_cell.cell_triangle_mesh; Eigen::VectorXi birth_face_indices; auto t1 = std::chrono::steady_clock::now(); new_voronoi_mesh = sha::BooleanIntersectTwoMatMesh3( shell_mesh, unrestricted_cell.cell_triangle_mesh, birth_face_indices); auto t2 = std::chrono::steady_clock::now(); total_mmsecs += std::chrono::duration_cast(t2 - t1).count(); if (new_voronoi_mesh.NumVertices() == 0) { if (keep_number_flag) { index_t restricted_cell_idx = restricted_voronoi_diagram.cells.size(); restricted_voronoi_diagram.cells.push_back(new_voronoi_cell); restricted_voronoi_diagram.map_cell_to_neighbor_indices.push_back( unrestricted_voronoi_diagram.map_cell_to_neighbor_indices[unrestricted_cell_idx]); map_unrestricted_cell_idx_to_restricted_cell_idx[unrestricted_cell_idx].insert( restricted_cell_idx); } } else { bool cell_totally_in_shell = (birth_face_indices.minCoeff() >= shell_mesh.NumFaces()); if (keep_number_flag) { map_restricted_cell_idx_to_is_totally_in_shell[unrestricted_cell_idx] = cell_totally_in_shell; if (cell_totally_in_shell) { new_voronoi_cell = unrestricted_cell; } else { new_voronoi_cell.map_triangle_to_polygon_idx.resize( new_voronoi_cell.cell_triangle_mesh.NumFaces()); auto &new_polygons = new_voronoi_cell.polyhedron.polygons; new_polygons.resize(unrestricted_cell.polyhedron.polygons.size() + (cell_totally_in_shell ? 0 : 1)); // auto t1 = std::chrono::steady_clock::now(); for (index_t face_idx = 0; face_idx < new_voronoi_cell.cell_triangle_mesh.NumFaces(); ++face_idx) { if (birth_face_indices(face_idx) < shell_mesh.NumFaces()) { // from shell mesh new_polygons[0].triangle_face_indices_in_cell.push_back(face_idx); new_voronoi_cell.map_triangle_to_polygon_idx[face_idx] = 0; } else { index_t new_polygon_idx = unrestricted_cell.map_triangle_to_polygon_idx[birth_face_indices(face_idx) - shell_mesh.NumFaces()]; new_polygon_idx += cell_totally_in_shell ? 0 : 1; new_polygons[new_polygon_idx].triangle_face_indices_in_cell.push_back(face_idx); new_voronoi_cell.map_triangle_to_polygon_idx[face_idx] = new_polygon_idx; } } } // end if totally in shell for (auto &polygon : new_voronoi_cell.polyhedron.polygons) { if (polygon.triangle_face_indices_in_cell.empty()) continue; polygon.mat_triangle_faces = new_voronoi_mesh.mat_faces(polygon.triangle_face_indices_in_cell, Eigen::all); } index_t restricted_cell_idx = restricted_voronoi_diagram.cells.size(); restricted_voronoi_diagram.cells.push_back(new_voronoi_cell); restricted_voronoi_diagram.map_cell_to_neighbor_indices.push_back( unrestricted_voronoi_diagram.map_cell_to_neighbor_indices[unrestricted_cell_idx]); map_unrestricted_cell_idx_to_restricted_cell_idx[unrestricted_cell_idx].insert( restricted_cell_idx); // auto t2 = std::chrono::steady_clock::now(); // total_seconds["bool"] += std::chrono::duration(t2 - t1).count(); } else { // Don't keep cell number Eigen::VectorXi face_component_indices; igl::facet_components(new_voronoi_mesh.mat_faces, face_component_indices); const size_t num_components = face_component_indices.maxCoeff() + 1; for (index_t component_idx = 0; component_idx < num_components; ++component_idx) { size_t num_faces_in_component = (face_component_indices.array() == component_idx).count(); VoronoiCell component_voronoi_cell; MatMesh3 component_mesh; auto &component_polygons = component_voronoi_cell.polyhedron.polygons; component_mesh.mat_coordinates = new_voronoi_mesh.mat_coordinates; component_mesh.mat_faces.resize(num_faces_in_component, 3); component_voronoi_cell.map_triangle_to_polygon_idx.resize(num_faces_in_component); component_polygons.resize(unrestricted_cell.polyhedron.polygons.size() + (cell_totally_in_shell ? 0 : 1)); for (index_t face_idx = 0, component_face_idx = 0; face_idx < new_voronoi_mesh.NumFaces(); ++face_idx) { if (face_component_indices(face_idx) != component_idx) continue; component_mesh.mat_faces.row(component_face_idx) = new_voronoi_mesh.mat_faces.row(face_idx); if (birth_face_indices(face_idx) < shell_mesh.NumFaces()) { // from shell mesh component_polygons[0].triangle_face_indices_in_cell.push_back(component_face_idx); component_voronoi_cell.map_triangle_to_polygon_idx[component_face_idx] = 0; } else { index_t new_polygon_idx = unrestricted_cell.map_triangle_to_polygon_idx[birth_face_indices(face_idx) - shell_mesh.NumFaces()]; new_polygon_idx += cell_totally_in_shell ? 0 : 1; component_polygons[new_polygon_idx].triangle_face_indices_in_cell.push_back( component_face_idx); component_voronoi_cell.map_triangle_to_polygon_idx[component_face_idx] = new_polygon_idx; } component_face_idx++; } component_voronoi_cell.seed = unrestricted_cell.seed; component_voronoi_cell.cell_triangle_mesh = sha::RemoveUnreferencedVertices(component_mesh); for (auto &polygon : component_polygons) { if (polygon.triangle_face_indices_in_cell.empty()) continue; polygon.mat_triangle_faces = component_voronoi_cell.cell_triangle_mesh.mat_faces( polygon.triangle_face_indices_in_cell, Eigen::all); } index_t restricted_cell_idx = restricted_voronoi_diagram.cells.size(); restricted_voronoi_diagram.cells.push_back(component_voronoi_cell); map_unrestricted_cell_idx_to_restricted_cell_idx[unrestricted_cell_idx].insert( restricted_cell_idx); restricted_voronoi_diagram.map_cell_to_neighbor_indices.push_back( unrestricted_voronoi_diagram.map_cell_to_neighbor_indices[unrestricted_cell_idx]); map_restricted_cell_idx_to_is_totally_in_shell[restricted_cell_idx] = cell_totally_in_shell; } // for component_idx } // keep_number_flag } // new_voronoi_mesh.NumVertices() == 0 } // ); log::info("Boolean done."); log::info("{}mm", total_mmsecs); auto &map_cell_to_neighbor_indices = restricted_voronoi_diagram.map_cell_to_neighbor_indices; const size_t num_restricted_cells = restricted_voronoi_diagram.cells.size(); for (index_t unrestricted_cell_idx = 0; unrestricted_cell_idx < num_restricted_cells; ++unrestricted_cell_idx) { const auto &old_neighbor_indices = map_cell_to_neighbor_indices.at(unrestricted_cell_idx); map_cell_to_neighbor_indices[unrestricted_cell_idx].clear(); for (auto old_neighbor_cell_idx : old_neighbor_indices) { for (auto new_neighbor_cell_idx : map_unrestricted_cell_idx_to_restricted_cell_idx[old_neighbor_cell_idx]) { map_cell_to_neighbor_indices[unrestricted_cell_idx].insert(new_neighbor_cell_idx); } } } auto RebuildIndexingOfMapCellTriangleToPolygon = [&](VoronoiCell &voronoi_cell) { auto &polygons = voronoi_cell.polyhedron.polygons; for (size_t polygon_idx = 0; polygon_idx < polygons.size(); polygon_idx++) { auto &polygon = polygons[polygon_idx]; for (index_t face_idx : polygon.triangle_face_indices_in_cell) { voronoi_cell.map_triangle_to_polygon_idx[face_idx] = polygon_idx; } } }; log::info("Removing Empty Polygons From VoronoiCell"); boost::for_each(restricted_voronoi_diagram.cells, RemoveEmptyPolygonsFromVoronoiCell); log::info("Removed"); log::info("Separating Polygon If Not Connected"); boost::for_each(restricted_voronoi_diagram.cells, [&](VoronoiCell &voronoi_cell) { auto old_polygons = voronoi_cell.polyhedron.polygons; // copy auto &new_polygons = voronoi_cell.polyhedron.polygons; // ref new_polygons.clear(); for (auto &old_polygon : old_polygons) { auto &&separate_polygons = SeparatePolygonIfNotConnected(old_polygon, voronoi_cell); boost::copy(separate_polygons, std::back_inserter(new_polygons)); } }); log::info("Separated"); log::info("Separating Polygon If Edge Is Sharp"); oneapi::tbb::parallel_for_each( restricted_voronoi_diagram.cells | boost::adaptors::indexed(0), [&](auto &cell_iterator) { VoronoiCell &voronoi_cell = cell_iterator.value(); index_t cell_idx = cell_iterator.index(); if (map_restricted_cell_idx_to_is_totally_in_shell[cell_idx]) { return; } SurfaceMesh3 cell_mesh3 = sha::CreateSurfaceMesh3FromMatMesh3(voronoi_cell.cell_triangle_mesh); auto old_polygons = voronoi_cell.polyhedron.polygons; // copy auto &new_polygons = voronoi_cell.polyhedron.polygons; // ref new_polygons.clear(); for (auto &old_polygon : old_polygons) { auto separate_polygons = SeparatePolygonIfEdgeIsSharp(old_polygon, voronoi_cell, cell_mesh3, sharp_angle); boost::copy(separate_polygons, std::back_inserter(new_polygons)); } }); log::info("Separated"); log::info("Rebuilding Indexing Of Map Cell Triangle To Polygon"); boost::for_each(restricted_voronoi_diagram.cells, RebuildIndexingOfMapCellTriangleToPolygon); log::info("Rebuilt"); log::info("Computing Loop Vertex By Its Triangles"); oneapi::tbb::parallel_for_each( restricted_voronoi_diagram.cells | boost::adaptors::indexed(0), [&](auto &cell_iterator) { VoronoiCell &voronoi_cell = cell_iterator.value(); index_t cell_idx = cell_iterator.index(); if (!map_restricted_cell_idx_to_is_totally_in_shell[cell_idx]) { boost::for_each(voronoi_cell.polyhedron.polygons, ComputeLoopVertexByItsTriangles); } }); log::info("Computed"); return restricted_voronoi_diagram; } auto SeparatePolygonIfNotConnected(const PolygonFace &polygon, const VoronoiCell &voronoi_cell) -> std::vector { const auto &mat_faces_in_polygon = polygon.mat_triangle_faces; Eigen::VectorXi polygon_face_component_indices; igl::facet_components(mat_faces_in_polygon, polygon_face_component_indices); const size_t num_components = polygon_face_component_indices.maxCoeff() + 1; if (num_components == 1) return {polygon}; std::vector component_polygons(num_components); for (index_t idx = 0; idx < polygon_face_component_indices.size(); ++idx) { index_t face_idx = polygon.triangle_face_indices_in_cell[idx]; index_t component_idx = polygon_face_component_indices[idx]; component_polygons[component_idx].triangle_face_indices_in_cell.push_back(face_idx); } for (auto &polygon : component_polygons) { if (polygon.triangle_face_indices_in_cell.empty()) continue; polygon.mat_triangle_faces = voronoi_cell.cell_triangle_mesh.mat_faces( polygon.triangle_face_indices_in_cell, Eigen::all); } return component_polygons; } auto SeparatePolygonIfEdgeIsSharp(const PolygonFace &polygon, const VoronoiCell &voronoi_cell, const SurfaceMesh3 &voronoi_mesh3, double angle) -> std::vector { std::vector component_polygons; const size_t num_polygon_faces = polygon.triangle_face_indices_in_cell.size(); using Edge = std::pair; std::set edges; Eigen::MatrixXi mat_sharp_edges; igl::sharp_edges(voronoi_cell.cell_triangle_mesh.mat_coordinates, polygon.mat_triangle_faces, angle / 180.0 * igl::PI, mat_sharp_edges); for (index_t idx = 0; idx < mat_sharp_edges.rows(); ++idx) { edges.emplace(std::min(mat_sharp_edges(idx, 0), mat_sharp_edges(idx, 1)), std::max(mat_sharp_edges(idx, 0), mat_sharp_edges(idx, 1))); } std::vector face_visited_flags(num_polygon_faces, false); for (index_t idx = 0; idx < num_polygon_faces; ++idx) { if (face_visited_flags[idx]) continue; PolygonFace component_polygon; std::queue breadth_first_search_queue; breadth_first_search_queue.push(idx); while (!breadth_first_search_queue.empty()) { index_t curr_idx = breadth_first_search_queue.front(); index_t face_idx = polygon.triangle_face_indices_in_cell[curr_idx]; breadth_first_search_queue.pop(); if (face_visited_flags[curr_idx]) continue; component_polygon.triangle_face_indices_in_cell.push_back(face_idx); face_visited_flags[curr_idx] = true; auto face_halfedge = voronoi_mesh3.halfedge(SurfaceMesh3::Face_index(face_idx)); for (auto halfedge : voronoi_mesh3.halfedges_around_face(face_halfedge)) { auto edge_handle = voronoi_mesh3.edge(halfedge); index_t neighbor_face_idx = voronoi_mesh3.face(voronoi_mesh3.opposite(halfedge)); if (neighbor_face_idx != -1 && neighbor_face_idx != face_idx) { auto find_iterator = boost::find(polygon.triangle_face_indices_in_cell, neighbor_face_idx); if (find_iterator != polygon.triangle_face_indices_in_cell.end()) { Edge edge = {voronoi_mesh3.vertex(edge_handle, 0).idx(), voronoi_mesh3.vertex(edge_handle, 1).idx()}; if (edge.first > edge.second) { std::swap(edge.first, edge.second); } if (edges.find(edge) == edges.end()) { breadth_first_search_queue.push(find_iterator - polygon.triangle_face_indices_in_cell.begin()); } } // neighbor_face exists in polygon } // neighbor_face is valid } // for neighbors } // bfs component_polygon.mat_triangle_faces = voronoi_cell.cell_triangle_mesh.mat_faces( component_polygon.triangle_face_indices_in_cell, Eigen::all); component_polygons.push_back(component_polygon); } // for every polygon return component_polygons; } void RemoveEmptyPolygonsFromVoronoiCell(VoronoiCell &voronoi_cell) { /** const size_t num_old_polygons = voronoi_cell.polyhedron.polygons.size(); if (num_old_polygons == 0) return; auto old_map_triangle_to_polygon_idx = voronoi_cell.map_triangle_to_polygon_idx; auto old_polygons = voronoi_cell.polyhedron.polygons; voronoi_cell.polyhedron.polygons.clear(); std::map map_old_idx_to_new_idx_for_polygon; for (index_t old_polygon_idx = 0, new_polygon_idx = 0; old_polygon_idx < num_old_polygons; ++old_polygon_idx) { if (old_polygons[old_polygon_idx].triangle_face_indices_in_cell.empty()) continue; map_old_idx_to_new_idx_for_polygon[old_polygon_idx] = new_polygon_idx; voronoi_cell.polyhedron.polygons.push_back(old_polygons[old_polygon_idx]); new_polygon_idx++; } for (index_t idx = 0; idx < voronoi_cell.map_triangle_to_polygon_idx.size(); ++idx) { voronoi_cell.map_triangle_to_polygon_idx[idx] = map_old_idx_to_new_idx_for_polygon[old_map_triangle_to_polygon_idx[idx]]; } **/ const size_t num_old_polygons = voronoi_cell.polyhedron.polygons.size(); if (num_old_polygons == 0) return; auto old_polygons = voronoi_cell.polyhedron.polygons; voronoi_cell.polyhedron.polygons.clear(); for (index_t old_polygon_idx = 0; old_polygon_idx < num_old_polygons; ++old_polygon_idx) { if (old_polygons[old_polygon_idx].triangle_face_indices_in_cell.empty()) continue; voronoi_cell.polyhedron.polygons.push_back(old_polygons[old_polygon_idx]); } } void ComputeLoopVertexByItsTriangles(PolygonFace &polygon) { if (polygon.mat_triangle_faces.rows() == 0) return; polygon.boundary_vtx_loops.clear(); igl::boundary_loop(polygon.mat_triangle_faces, polygon.boundary_vtx_loops); } double distance_between_two_edges(const MatMesh2 edge_mesh_1, const MatMesh2 edge_mesh_2) { double dis1, dis2; { Eigen::MatrixXd sqrD; Eigen::MatrixXi I; Eigen::MatrixXd C; igl::point_mesh_squared_distance(edge_mesh_2.mat_coordinates, edge_mesh_1.mat_coordinates, edge_mesh_1.mat_beams, sqrD, I, C); dis1 = sqrD.sum() / sqrD.size(); } { Eigen::MatrixXd sqrD; Eigen::MatrixXi I; Eigen::MatrixXd C; igl::point_mesh_squared_distance(edge_mesh_1.mat_coordinates, edge_mesh_2.mat_coordinates, edge_mesh_2.mat_beams, sqrD, I, C); dis2 = sqrD.sum() / sqrD.size(); } return std::max(dis1, dis2); } struct VoronoiCellChain { std::vector path; MatMesh2 edge_mesh; std::vector cell_indices; public: bool equals(const std::vector &other, const MatMesh2 &other_mesh) { constexpr double kEpsilon = 1e-9; if (((path.front() - other.front()).norm() < kEpsilon) && ((path.back() - other.back()).norm() < kEpsilon) || ((path.front() - other.back()).norm() < kEpsilon) && ((path.back() - other.front()).norm() < kEpsilon)) { // iff two points coincides return distance_between_two_edges(edge_mesh, other_mesh) < kEpsilon; } else { return false; } } public: Eigen::AlignedBox3d bbox; }; using cell_edge_ptr = std::shared_ptr; struct SharedBoundaryChain { std::vector boundary_vertices; index_t polygon_idx_0 = -1; index_t polygon_idx_1 = -1; }; using SharedBoundaryPtr = std::shared_ptr; auto ComputeBoundaryChainsOfCell(const VoronoiCell &cell) -> std::vector { using Edge = std::pair; using PatchEdges = std::vector; const size_t num_polygons = cell.polyhedron.polygons.size(); std::vector> shared_boundaries_of_polygons(num_polygons); std::vector> boundary_edges_of_polygons(num_polygons); std::vector cell_boundaries; std::set corner_vertex_indices; std::vector patches_edges(num_polygons); // find corner visit more than twice std::map visited; for (int polygon_idx = 0; polygon_idx < num_polygons; ++polygon_idx) { auto &patch = cell.polyhedron.polygons[polygon_idx]; PatchEdges polygon_edges; for (auto &boundary_loop : patch.boundary_vtx_loops) { for (int i = 0; i < boundary_loop.size(); ++i) { index_t a = boundary_loop[i]; index_t b = boundary_loop[(i + 1) % boundary_loop.size()]; visited[a] += 1; if (visited[a] > 2) { corner_vertex_indices.insert(a); } polygon_edges.emplace_back(a, b); } } patches_edges[polygon_idx] = polygon_edges; } // divide edges via corner auto find_or_create_an_edge = [&](std::vector vs) -> SharedBoundaryPtr { auto fd = boost::find_if(cell_boundaries, [&](const SharedBoundaryPtr &boundary) { return boost::equal(boundary->boundary_vertices, vs) || boost::equal(boundary->boundary_vertices, boost::reversed_range(vs)); }); if (fd == cell_boundaries.end()) { // create SharedBoundaryPtr new_e = std::make_shared(); new_e->boundary_vertices = vs; new_e->polygon_idx_0 = -1; new_e->polygon_idx_1 = -1; cell_boundaries.push_back(new_e); return new_e; } else { return *fd; } }; for (int polygon_idx = 0; polygon_idx < num_polygons; ++polygon_idx) { auto &polygon = cell.polyhedron.polygons[polygon_idx]; const size_t num_loops = polygon.boundary_vtx_loops.size(); auto &shared_boundaries_of_polygon = shared_boundaries_of_polygons[polygon_idx]; auto &boundary_edges_of_polygon = boundary_edges_of_polygons[polygon_idx]; std::vector> corners_of_loops(num_loops); for (auto &&[loop_idx, patch_loop] : polygon.boundary_vtx_loops | boost::adaptors::indexed(0)) { for (auto v : patch_loop) { if (corner_vertex_indices.find(v) != corner_vertex_indices.end()) { corners_of_loops[loop_idx].push_back(v); } } } for (auto &&[loop_idx, corners_of_loop] : corners_of_loops | boost::adaptors::indexed(0)) { auto &boundary_vtx_loop = polygon.boundary_vtx_loops[loop_idx]; for (index_t idx = 0; idx < corners_of_loop.size(); ++idx) { index_t corner_vtx_0 = corners_of_loop[idx]; index_t corner_vtx_1 = corners_of_loop[(idx + 1) % corners_of_loop.size()]; std::vector vs; // truncate int ai = std::find(boundary_vtx_loop.begin(), boundary_vtx_loop.end(), corner_vtx_0) - boundary_vtx_loop.begin(); int bi = std::find(boundary_vtx_loop.begin(), boundary_vtx_loop.end(), corner_vtx_1) - boundary_vtx_loop.begin(); if (ai < bi) { for (int j = ai; j <= bi; ++j) { vs.push_back(boundary_vtx_loop[j]); } } else { for (int j = ai; j < boundary_vtx_loop.size(); ++j) { vs.push_back(boundary_vtx_loop[j]); } for (int j = 0; j <= bi; ++j) { vs.push_back(boundary_vtx_loop[j]); } } auto e = find_or_create_an_edge(vs); if (e->polygon_idx_0 == -1) { e->polygon_idx_0 = polygon_idx; shared_boundaries_of_polygon.push_back(e); boundary_edges_of_polygon.emplace_back(corner_vtx_0, corner_vtx_1); } else if (e->polygon_idx_1 == -1) { e->polygon_idx_1 = polygon_idx; shared_boundaries_of_polygon.push_back(e); boundary_edges_of_polygon.emplace_back(corner_vtx_0, corner_vtx_1); } else { log::info("Non manifold"); continue; // Terminate("Non manifold"); } } } } return cell_boundaries; } auto ComputeRelatedEdgesFromVoronoiDiagram(const VoronoiDiagram &voronoi, const Eigen::AlignedBox3d &domain) -> MatMesh2 { std::vector> map_beam_idx_to_cell_indices; return ComputeRelatedEdgesFromVoronoiDiagram(voronoi, domain, map_beam_idx_to_cell_indices); } auto ComputeRelatedEdgesFromVoronoiDiagram( const VoronoiDiagram &voronoi, const Eigen::AlignedBox3d &domain, std::vector> &map_beam_idx_to_cell_indices) -> MatMesh2 { map_beam_idx_to_cell_indices.clear(); const size_t num_cells = voronoi.cells.size(); std::vector> boundary_chains_of_cells; // log::info("Computing BoundaryChainsOfCell"); boost::transform(voronoi.cells, std::back_inserter(boundary_chains_of_cells), ComputeBoundaryChainsOfCell); // log::info("Computed"); // log::info("Building tree"); DynamicBoundingBoxOctree octree(domain, 6); std::vector &cells_edges = octree.data_vector; // log::info("Built"); double total_seconds = 0; auto FindOrCreateCellEdge = [&](const std::vector &path) -> cell_edge_ptr { MatMesh2 edge_mesh; edge_mesh.mat_coordinates.resize(path.size(), 3); for (int i = 0; i < path.size(); ++i) { edge_mesh.mat_coordinates.row(i) << path[i].x(), path[i].y(), path[i].z(); } edge_mesh.mat_beams.resize(edge_mesh.NumVertices() - 1, 2); for (int i = 0; i < edge_mesh.NumVertices() - 1; ++i) { edge_mesh.mat_beams.row(i) << i, i + 1; } Eigen::AlignedBox3d edge_boundingbox(edge_mesh.mat_coordinates.colwise().minCoeff(), edge_mesh.mat_coordinates.colwise().maxCoeff()); // auto t1 = std::chrono::steady_clock::now(); auto data_indices = octree.FindDataIndicesByBoundingBox(edge_boundingbox); cell_edge_ptr find_edge_ptr = nullptr; for (index_t data_idx : data_indices) { if (cells_edges.at(data_idx)->equals(path, edge_mesh)) { find_edge_ptr = cells_edges.at(data_idx); break; } } // auto t2 = std::chrono::steady_clock::now(); // total_seconds += std::chrono::duration(t2 - t1).count(); if (find_edge_ptr == nullptr) { // create auto new_e = std::make_shared(); new_e->path = path; new_e->edge_mesh = edge_mesh; octree.InsertData(edge_boundingbox, new_e); return new_e; } else { return find_edge_ptr; } // auto t1 = std::chrono::steady_clock::now(); // auto fd = boost::find_if(cells_edges, // [&](const cell_edge_ptr &e) { return e->equals(path, edge_mesh); }); // auto t2 = std::chrono::steady_clock::now(); // total_seconds += std::chrono::duration(t2 - t1).count(); // if (fd == cells_edges.end()) { // create // auto new_e = std::make_shared(); // new_e->path = path; // new_e->edge_mesh = edge_mesh; // cells_edges.push_back(new_e); // return new_e; // } else { // return *fd; // } }; // log::info("FindOrCreateCellEdge"); for (index_t cell_idx = 0; cell_idx < num_cells; ++cell_idx) { auto &cell = voronoi.cells.at(cell_idx); auto &shared_chains = boundary_chains_of_cells.at(cell_idx); for (const auto &chain : shared_chains) { std::vector edge_path(chain->boundary_vertices.size()); for (index_t idx = 0; idx < chain->boundary_vertices.size(); ++idx) { index_t vertex_idx = chain->boundary_vertices[idx]; edge_path[idx] = cell.cell_triangle_mesh.mat_coordinates.row(vertex_idx); } auto cell_edge = FindOrCreateCellEdge(edge_path); cell_edge->cell_indices.push_back(cell_idx); } } // log::info("FindOrCreateCellEdge Over"); const size_t num_beams = boost::accumulate( cells_edges, 0, [](size_t accumulated_value, cell_edge_ptr chain) -> size_t { return accumulated_value + chain->path.size() - 1; }); MatMesh2 beam_mesh; beam_mesh.mat_coordinates.resize(num_beams * 2, 3); beam_mesh.mat_beams.resize(num_beams, 2); map_beam_idx_to_cell_indices.resize(num_beams); index_t beam_idx = 0; for (index_t chain_idx = 0; chain_idx < cells_edges.size(); chain_idx++) { auto &chain = cells_edges[chain_idx]; for (index_t idx = 0; idx < chain->path.size() - 1; ++idx, ++beam_idx) { beam_mesh.mat_coordinates.row(beam_idx * 2 + 0) = chain->path[idx]; beam_mesh.mat_coordinates.row(beam_idx * 2 + 1) = chain->path[idx + 1]; beam_mesh.mat_beams.row(beam_idx) << beam_idx * 2, beam_idx * 2 + 1; boost::copy(chain->cell_indices, std::inserter(map_beam_idx_to_cell_indices.at(beam_idx), map_beam_idx_to_cell_indices.at(beam_idx).begin())); } } // log::info("total_fd_seconds: {}", total_seconds); return beam_mesh; } } // namespace sha } // namespace da