SemiDiscreteOT 1.0
Semi-Discrete Optimal Transport Library
Loading...
Searching...
No Matches
MeshHierarchy.cc
Go to the documentation of this file.
2#include <geogram/basic/file_system.h>
3#include <geogram/mesh/mesh_io.h>
4#include <geogram/mesh/mesh_tetrahedralize.h>
5#include <geogram/mesh/mesh_remesh.h>
6#include <geogram/basic/command_line.h>
7#include <geogram/basic/command_line_args.h>
8#include <iostream>
9#include <cmath>
10#include <stdexcept>
11#include <vector>
12#include <algorithm>
13
14namespace MeshHierarchy {
15
16MeshHierarchyManager::MeshHierarchyManager(int min_vertices, int max_vertices)
17 : min_vertices_(min_vertices)
18 , max_vertices_(max_vertices)
19 , num_levels_(0)
20 , is_initialized_(false)
21{}
22
24 if (!is_initialized_) {
25 GEO::initialize();
26 GEO::CmdLine::import_arg_group("algo");
27 GEO::CmdLine::set_arg("algo:delaunay", "BPOW");
28 is_initialized_ = true;
29 }
30}
31
32bool MeshHierarchyManager::loadVolumeMesh(const std::string& filename, GEO::Mesh& M, bool fill_volume) const {
33 std::cout << "Loading mesh..." << std::endl;
34 std::cout << "Fill volume mode: " << (fill_volume ? "enabled (3D)" : "disabled (2D surface)") << std::endl;
35
36 GEO::MeshIOFlags flags;
37 flags.set_element(GEO::MESH_FACETS);
38 if (fill_volume) {
39 flags.set_element(GEO::MESH_CELLS);
40 flags.set_attribute(GEO::MESH_CELL_REGION);
41 }
42
43 if(!mesh_load(filename, M, flags)) {
44 return false;
45 }
46
47 if(!M.facets.are_simplices()) {
48 std::cout << "Triangulating facets..." << std::endl;
49 M.facets.triangulate();
50 }
51
52 if (fill_volume) {
53 // For 3D: Fill volume with tetrahedra
54 if(!M.cells.are_simplices()) {
55 std::cout << "Tetrahedralizing cells..." << std::endl;
56 if(!mesh_tetrahedralize(M, true, true)) {
57 return false;
58 }
59 }
60
61 if(M.cells.nb() == 0) {
62 std::cout << "File " << filename << " does not contain a volume" << std::endl;
63 std::cout << "Trying to tetrahedralize..." << std::endl;
64 if(!mesh_tetrahedralize(M, true, true)) {
65 return false;
66 }
67 }
68 } else {
69 // For 2D: Keep as surface mesh, no volume filling
70 std::cout << "Surface mesh mode: skipping volume tetrahedralization" << std::endl;
71 if(M.cells.nb() > 0) {
72 std::cout << "Warning: Input mesh contains volume cells, but fill_volume=false. Cells will be ignored." << std::endl;
73 }
74 }
75
76 return true;
77}
78
80 max_vertices_ = max_vertices;
81}
82
84 min_vertices_ = min_vertices;
85}
86
90
91void MeshHierarchyManager::ensureDirectoryExists(const std::string& path) const {
92 if (!GEO::FileSystem::is_directory(path)) {
93 GEO::FileSystem::create_directory(path);
94 }
95}
96
97int MeshHierarchyManager::getPointsForLevel(int base_points, int level) const {
98 // Level 0 always uses original mesh size
99 if (level == 0) {
100 return base_points;
101 }
102 // Level 1 starts with max_points (if smaller than base_points)
103 if (level == 1) {
104 return std::min(base_points, max_vertices_);
105 }
106 // For subsequent levels, use a reduction factor of 4 based on level 1's size
107 int level1_points = std::min(base_points, max_vertices_);
108 int points = static_cast<int>(level1_points / std::pow(4.0, level - 1));
109 return std::max(points, min_vertices_);
110}
111
112int MeshHierarchyManager::generateHierarchyFromFile(const std::string& input_mesh_file, const std::string& output_dir, bool fill_volume) {
113 // Initialize Geogram if needed
115
116 // Load the input mesh
117 GEO::Mesh input_mesh;
118 if (!loadVolumeMesh(input_mesh_file, input_mesh, fill_volume)) {
119 throw std::runtime_error("Failed to load input mesh: " + input_mesh_file);
120 }
121
122 // Create output directories
123 std::string::size_type pos = 0;
124 while ((pos = output_dir.find('/', pos + 1)) != std::string::npos) {
125 ensureDirectoryExists(output_dir.substr(0, pos));
126 }
127 ensureDirectoryExists(output_dir);
128
129 // Calculate number of levels
130 const int total_vertices = input_mesh.vertices.nb();
131
132 int surface_vertices = 0;
133 std::vector<bool> visited(input_mesh.vertices.nb(), false);
134 for(GEO::index_t c: input_mesh.facet_corners) {
135 visited[input_mesh.facet_corners.vertex(c)] = true;
136 }
137 for(GEO::index_t v: input_mesh.vertices) {
138 if(visited[v]) {
139 ++surface_vertices;
140 }
141 }
142
143 const int level1_vertices = std::min(surface_vertices, max_vertices_);
144 num_levels_ = std::min(5, static_cast<int>(std::log(level1_vertices/min_vertices_) / std::log(4.0)) + 2);
145
146 std::cout << "Initial mesh has " << total_vertices << " total vertices (" << surface_vertices << " surface vertices)" << std::endl;
147 std::cout << "Level 1 will have maximum of " << max_vertices_ << " vertices" << std::endl;
148 std::cout << "Creating " << num_levels_ << " levels of meshes" << std::endl;
149
150 // Generate and save meshes for each level
151 for(int level = 0; level < num_levels_; ++level) {
152 GEO::Mesh level_mesh;
153
154 if(level == 0) {
155 // Always use original mesh for level 0
156 level_mesh.copy(input_mesh);
157 std::cout << "Level 0: using original mesh with " << surface_vertices << " surface vertices" << std::endl;
158 } else {
159 // For other levels, generate coarser versions
160 int points_for_level = getPointsForLevel(surface_vertices, level);
161 std::cout << "Level " << level << ": targeting " << points_for_level << " surface points" << std::endl;
162
163 GEO::remesh_smooth(input_mesh, level_mesh, points_for_level);
164
165 if (fill_volume) {
166 // Apply high-quality tetrahedralization for 3D
167 std::cout << "Applying volume tetrahedralization for level " << level << std::endl;
168 GEO::MeshTetrahedralizeParameters params;
169 params.refine = true;
170 params.refine_quality = 1.0;
171 mesh_tetrahedralize(level_mesh, params);
172 } else {
173 // For 2D, keep as surface mesh
174 std::cout << "Keeping level " << level << " as surface mesh (2D mode)" << std::endl;
175 }
176 }
177
178 // Save the mesh for this level
179 std::string output_file = output_dir + "/level_" + std::to_string(level) + ".msh";
180 mesh_save(level_mesh, output_file);
181 std::cout << "Saved level " << level << " mesh to " << output_file << std::endl;
182 }
183
184 return num_levels_;
185}
186
187} // namespace MeshHierarchy
int getPointsForLevel(int base_points, int level) const
Calculate number of points for a given level.
MeshHierarchyManager(int min_vertices=1000, int max_vertices=10000)
Constructor.
int getNumLevels() const
Get the number of levels in the last generated hierarchy.
void setMaxVertices(int max_vertices)
Set the maximum number of vertices for level 1.
bool loadVolumeMesh(const std::string &filename, GEO::Mesh &M, bool fill_volume) const
Load a volume mesh from file.
void ensureDirectoryExists(const std::string &path) const
Ensure directory exists, create if it doesn't.
void setMinVertices(int min_vertices)
Set the minimum number of vertices for coarsest level.
int generateHierarchyFromFile(const std::string &input_mesh_file, const std::string &output_dir, bool fill_volume=true)
Generate hierarchy of meshes from input mesh file.
void initializeGeogram()
Initialize Geogram if not already initialized.