You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
881 lines
38 KiB
881 lines
38 KiB
2 years ago
|
#include "voronoi.h"
|
||
|
|
||
|
#include <geogram/delaunay/delaunay_3d.h>
|
||
|
#include <geogram/delaunay/periodic_delaunay_3d.h>
|
||
|
#include <geogram/voronoi/convex_cell.h>
|
||
|
|
||
|
#include <oneapi/tbb.h>
|
||
|
|
||
|
#include <igl/adjacency_list.h>
|
||
|
#include <igl/boundary_loop.h>
|
||
|
#include <igl/copyleft/cgal/mesh_boolean.h>
|
||
|
#include <igl/facet_adjacency_matrix.h>
|
||
|
#include <igl/facet_components.h>
|
||
|
#include <igl/octree.h>
|
||
|
#include <igl/point_mesh_squared_distance.h>
|
||
|
#include <igl/remove_duplicate_vertices.h>
|
||
|
#include <igl/remove_unreferenced.h>
|
||
|
#include <igl/sharp_edges.h>
|
||
|
|
||
|
#include <algorithm>
|
||
|
#include <iostream>
|
||
|
#include <iterator>
|
||
|
#include <map>
|
||
|
#include <memory>
|
||
|
#include <queue>
|
||
|
#include <set>
|
||
|
#include <utility>
|
||
|
#include <vector>
|
||
|
|
||
|
#include <boost/range/adaptors.hpp>
|
||
|
#include <boost/range/algorithm.hpp>
|
||
|
#include <boost/range/combine.hpp>
|
||
|
#include <boost/range/numeric.hpp>
|
||
|
|
||
|
#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<Eigen::Vector4d> 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<double> flatten_seeds(num_seeds * 3);
|
||
|
boost::for_each(boost::counting_range<size_t>(0, num_seeds), [&](index_t seed_idx) {
|
||
|
boost::for_each(boost::counting_range<size_t>(0, 3), [&](index_t idx) {
|
||
|
flatten_seeds[seed_idx * 3 + idx] = mat_regulated_seeds(seed_idx, idx);
|
||
|
});
|
||
|
});
|
||
|
GEO::initialize();
|
||
|
auto delaunay = std::make_shared<GEO::PeriodicDelaunay3d>(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<double> 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<GEO::index_t> map_vertex_to_triangle_idx(convex_cell.nb_v(), GEO::index_t(-1));
|
||
|
std::vector<GEO::index_t> 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<Eigen::Vector3d> cell_vertex_vector;
|
||
|
std::vector<Eigen::Vector3i> 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<index_t>(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<index_t>(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<size_t>(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<GEO::index_t> 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<index_t, std::set<index_t>> map_unrestricted_cell_idx_to_restricted_cell_idx;
|
||
|
|
||
|
const int num_cells = unrestricted_voronoi_diagram.cells.size();
|
||
|
|
||
|
VoronoiDiagram restricted_voronoi_diagram;
|
||
|
|
||
|
std::map<index_t, bool> 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<int> 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<std::chrono::milliseconds>(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<double>(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<PolygonFace> {
|
||
|
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<PolygonFace> 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<PolygonFace> {
|
||
|
std::vector<PolygonFace> component_polygons;
|
||
|
const size_t num_polygon_faces = polygon.triangle_face_indices_in_cell.size();
|
||
|
using Edge = std::pair<index_t, index_t>;
|
||
|
std::set<Edge> 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<bool> 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<index_t> 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<index_t, index_t> 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<Eigen::Vector3d> path;
|
||
|
|
||
|
MatMesh2 edge_mesh;
|
||
|
|
||
|
std::vector<index_t> cell_indices;
|
||
|
|
||
|
public:
|
||
|
bool equals(const std::vector<Eigen::Vector3d> &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<VoronoiCellChain>;
|
||
|
|
||
|
struct SharedBoundaryChain {
|
||
|
std::vector<index_t> boundary_vertices;
|
||
|
index_t polygon_idx_0 = -1;
|
||
|
index_t polygon_idx_1 = -1;
|
||
|
};
|
||
|
|
||
|
using SharedBoundaryPtr = std::shared_ptr<SharedBoundaryChain>;
|
||
|
|
||
|
auto ComputeBoundaryChainsOfCell(const VoronoiCell &cell) -> std::vector<SharedBoundaryPtr> {
|
||
|
using Edge = std::pair<index_t, index_t>;
|
||
|
using PatchEdges = std::vector<Edge>;
|
||
|
|
||
|
const size_t num_polygons = cell.polyhedron.polygons.size();
|
||
|
|
||
|
std::vector<std::vector<SharedBoundaryPtr>> shared_boundaries_of_polygons(num_polygons);
|
||
|
std::vector<std::vector<Edge>> boundary_edges_of_polygons(num_polygons);
|
||
|
|
||
|
std::vector<SharedBoundaryPtr> cell_boundaries;
|
||
|
std::set<index_t> corner_vertex_indices;
|
||
|
std::vector<PatchEdges> patches_edges(num_polygons);
|
||
|
// find corner visit more than twice
|
||
|
std::map<int, int> 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<index_t> 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<SharedBoundaryChain>();
|
||
|
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<std::vector<int>> 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<index_t> 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<std::set<index_t>> 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<std::set<index_t>> &map_beam_idx_to_cell_indices) -> MatMesh2 {
|
||
|
map_beam_idx_to_cell_indices.clear();
|
||
|
const size_t num_cells = voronoi.cells.size();
|
||
|
std::vector<std::vector<SharedBoundaryPtr>> 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<cell_edge_ptr> octree(domain, 6);
|
||
|
|
||
|
std::vector<cell_edge_ptr> &cells_edges = octree.data_vector;
|
||
|
|
||
|
// log::info("Built");
|
||
|
|
||
|
double total_seconds = 0;
|
||
|
|
||
|
auto FindOrCreateCellEdge = [&](const std::vector<Eigen::Vector3d> &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<double>(t2 - t1).count();
|
||
|
|
||
|
if (find_edge_ptr == nullptr) { // create
|
||
|
auto new_e = std::make_shared<VoronoiCellChain>();
|
||
|
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<double>(t2 - t1).count();
|
||
|
// if (fd == cells_edges.end()) { // create
|
||
|
// auto new_e = std::make_shared<VoronoiCellChain>();
|
||
|
// 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<Eigen::Vector3d> 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
|