PolyFEM
Loading...
Searching...
No Matches
MeshReader.cpp
Go to the documentation of this file.
1#include "MeshReader.hpp"
2
7
8#include <geogram/mesh/mesh.h>
9#include <geogram/mesh/mesh_io.h>
10
11#include <igl/read_triangle_mesh.h>
12
13#include <algorithm>
14#include <numeric>
15#include <set>
16
17namespace polyfem::mesh
18{
19 namespace
20 {
21 Eigen::MatrixXi padded_elements(const std::vector<std::vector<int>> &elements)
22 {
23 int width = 0;
24 for (const auto &element : elements)
25 width = std::max(width, int(element.size()));
26 Eigen::MatrixXi result;
27 result.setConstant(elements.size(), width, -1);
28 for (int i = 0; i < elements.size(); ++i)
29 for (int j = 0; j < elements[i].size(); ++j)
30 result(i, j) = elements[i][j];
31 return result;
32 }
33
34 Eigen::MatrixXd geogram_vertices(const GEO::Mesh &mesh, const int dimension)
35 {
36 Eigen::MatrixXd vertices(mesh.vertices.nb(), dimension);
37 for (int i = 0; i < vertices.rows(); ++i)
38 for (int d = 0; d < dimension; ++d)
39 vertices(i, d) = mesh.vertices.point(i)[d];
40 return vertices;
41 }
42
43 void finalize_surface(SurfaceMeshData &surface)
44 {
45 std::vector<bool> used(surface.vertices.rows(), false);
46 for (int i = 0; i < surface.edges.rows(); ++i)
47 for (int j = 0; j < surface.edges.cols(); ++j)
48 used[surface.edges(i, j)] = true;
49 for (int i = 0; i < surface.faces.rows(); ++i)
50 for (int j = 0; j < surface.faces.cols(); ++j)
51 used[surface.faces(i, j)] = true;
52 const int count = std::count(used.begin(), used.end(), false);
53 surface.points.resize(count);
54 for (int i = 0, point = 0; i < used.size(); ++i)
55 if (!used[i])
56 surface.points[point++] = i;
57 }
58 } // namespace
59
60 MeshData MeshReader::read_msh(const std::filesystem::path &path)
61 {
62 Eigen::MatrixXd vertices;
63 Eigen::MatrixXi cells;
64 std::vector<std::vector<int>> higher_order_connectivity;
65 std::vector<std::vector<double>> weights;
66 std::vector<int> body_ids;
67 std::vector<std::vector<int>> boundary_elements;
68 std::vector<int> boundary_ids;
70 path.string(), vertices, cells, higher_order_connectivity, weights,
71 body_ids, boundary_elements, boundary_ids))
72 log_and_throw_error("Unable to decode MSH mesh {}.", path.string());
73
74 MeshData data(std::move(vertices), std::move(cells));
75 data.body_ids = std::move(body_ids);
76 data.boundary_elements = std::move(boundary_elements);
77 data.boundary_ids = std::move(boundary_ids);
78 if ((data.dimension() == 2 && data.elements.cols() == 3)
79 || (data.dimension() == 3 && data.elements.cols() == 4))
80 {
81 data.higher_order_nodes = data.vertices;
82 data.higher_order_connectivity = std::move(higher_order_connectivity);
83 data.higher_order_weights = std::move(weights);
84 }
85 return data;
86 }
87
88 MeshData MeshReader::read_hybrid(std::istream &input, const std::string &description)
89 {
90 int vertex_count = 0;
91 int face_count = 0;
92 int encoded_cell_count = 0;
93 if (!(input >> vertex_count >> face_count >> encoded_cell_count)
94 || vertex_count <= 0 || face_count <= 0 || encoded_cell_count <= 0 || encoded_cell_count % 3 != 0)
95 log_and_throw_error("Invalid HYBRID header in {}.", description);
96 const int cell_count = encoded_cell_count / 3;
97
98 Eigen::MatrixXd vertices(vertex_count, 3);
99 for (int i = 0; i < vertex_count; ++i)
100 if (!(input >> vertices(i, 0) >> vertices(i, 1) >> vertices(i, 2)))
101 log_and_throw_error("HYBRID vertex data ended early in {}.", description);
102
103 std::vector<std::vector<int>> faces(face_count);
104 for (auto &face : faces)
105 {
106 int size = 0;
107 if (!(input >> size) || size < 3)
108 log_and_throw_error("Invalid HYBRID face in {}.", description);
109 face.resize(size);
110 for (int &vertex : face)
111 {
112 if (!(input >> vertex))
113 log_and_throw_error("HYBRID face data ended early in {}.", description);
114 if (vertex < 0 || vertex >= vertex_count)
115 log_and_throw_error("Invalid HYBRID face vertex {} in {}: expected an index in [0, {}).", vertex, description, vertex_count);
116 }
117 }
118
119 std::vector<std::vector<int>> cell_faces(cell_count);
120 std::vector<std::vector<int>> orientations(cell_count);
121 std::vector<std::vector<int>> elements(cell_count);
122 for (int i = 0; i < cell_count; ++i)
123 {
124 int face_size = 0;
125 if (!(input >> face_size) || face_size <= 0)
126 log_and_throw_error("Invalid HYBRID cell in {}.", description);
127 cell_faces[i].resize(face_size);
128 for (int &face : cell_faces[i])
129 if (!(input >> face) || face < 0 || face >= face_count)
130 log_and_throw_error("Invalid HYBRID cell face in {}.", description);
131
132 int orientation_size = 0;
133 if (!(input >> orientation_size) || orientation_size != face_size)
134 log_and_throw_error("Invalid HYBRID face orientations in {}.", description);
135 orientations[i].resize(orientation_size);
136 for (int &orientation : orientations[i])
137 if (!(input >> orientation))
138 log_and_throw_error("HYBRID orientation data ended early in {}.", description);
139
140 std::set<int> vertices_in_cell;
141 for (const int face : cell_faces[i])
142 vertices_in_cell.insert(faces[face].begin(), faces[face].end());
143 elements[i].assign(vertices_in_cell.begin(), vertices_in_cell.end());
144 }
145
146 std::vector<bool> cell_is_hex(cell_count);
147 for (int i = 0; i < cell_count; ++i)
148 {
149 int value = 0;
150 if (!(input >> value))
151 log_and_throw_error("HYBRID cell-type data ended early in {}.", description);
152 cell_is_hex[i] = value != 0;
153 }
154
155 Eigen::MatrixXd kernels(cell_count, 3);
156 kernels.setZero();
157 std::string marker;
158 if (input >> marker)
159 {
160 int count = 0;
161 if (marker != "KERNEL" || !(input >> count) || count != cell_count)
162 log_and_throw_error("Invalid HYBRID kernel block in {}.", description);
163 for (int i = 0; i < cell_count; ++i)
164 if (!(input >> kernels(i, 0) >> kernels(i, 1) >> kernels(i, 2)))
165 log_and_throw_error("HYBRID kernel data ended early in {}.", description);
166 }
167 input.clear();
168 // Preserve the established HYBRID decoder semantics: the stored kernel
169 // points are validated above, but historically they were replaced by cell
170 // barycenters before constructing the runtime mesh.
171 kernels.setZero();
172 for (int i = 0; i < cell_count; ++i)
173 {
174 for (const int vertex : elements[i])
175 kernels.row(i) += vertices.row(vertex);
176 kernels.row(i) /= elements[i].size();
177 }
178
179 MeshData data(std::move(vertices), padded_elements(elements));
180 data.elements_are_ordered = false;
181 data.faces = std::move(faces);
182 data.cell_faces = std::move(cell_faces);
183 data.cell_face_orientations = std::move(orientations);
184 data.cell_is_hex = std::move(cell_is_hex);
185 data.cell_kernel_points = std::move(kernels);
186 return data;
187 }
188
189 MeshData MeshReader::read_geogram(const std::filesystem::path &path)
190 {
191 GEO::Mesh mesh;
192 if (!GEO::mesh_load(path.string(), mesh))
193 log_and_throw_error("Unable to decode mesh {} with Geogram.", path.string());
194 return from_geogram(mesh);
195 }
196
198 {
199 if (is_planar(mesh))
200 {
201 std::vector<std::vector<int>> elements(mesh.facets.nb());
202 for (int i = 0; i < elements.size(); ++i)
203 {
204 elements[i].resize(mesh.facets.nb_vertices(i));
205 for (int j = 0; j < elements[i].size(); ++j)
206 elements[i][j] = mesh.facets.vertex(i, j);
207 }
208 return MeshData(geogram_vertices(mesh, 2), padded_elements(elements));
209 }
210
211 if (mesh.cells.nb() != 0)
212 {
213 std::vector<std::vector<int>> elements(mesh.cells.nb());
214 for (int i = 0; i < elements.size(); ++i)
215 {
216 elements[i].resize(mesh.cells.nb_vertices(i));
217 for (int j = 0; j < elements[i].size(); ++j)
218 elements[i][j] = mesh.cells.vertex(i, j);
219 if (mesh.cells.type(i) == GEO::MESH_HEX)
220 {
221 // MeshData uses Gmsh hex ordering; build_topology converts it
222 // back to Geogram ordering when constructing the runtime mesh.
223 std::swap(elements[i][0], elements[i][1]);
224 std::swap(elements[i][4], elements[i][5]);
225 }
226 }
227 return MeshData(geogram_vertices(mesh, 3), padded_elements(elements));
228 }
229
230 if (mesh.facets.nb() == 0)
231 log_and_throw_error("Geogram mesh contains neither cells nor facets.");
232 std::vector<std::vector<int>> faces(mesh.facets.nb());
233 std::set<int> used_vertices;
234 for (int i = 0; i < faces.size(); ++i)
235 {
236 faces[i].resize(mesh.facets.nb_vertices(i));
237 for (int j = 0; j < faces[i].size(); ++j)
238 {
239 faces[i][j] = mesh.facets.vertex(i, j);
240 used_vertices.insert(faces[i][j]);
241 }
242 }
243 std::vector<std::vector<int>> elements(1);
244 elements[0].assign(used_vertices.begin(), used_vertices.end());
245 MeshData data(geogram_vertices(mesh, 3), padded_elements(elements));
246 data.faces = std::move(faces);
247 data.cell_faces.resize(1);
248 data.cell_faces[0].resize(data.faces.size());
249 std::iota(data.cell_faces[0].begin(), data.cell_faces[0].end(), 0);
250 data.cell_face_orientations = {std::vector<int>(data.faces.size(), 1)};
251 data.cell_is_hex = {false};
252 data.cell_kernel_points.resize(1, 3);
253 const int last = mesh.vertices.nb() - 1;
254 if (used_vertices.find(last) == used_vertices.end())
255 data.cell_kernel_points.row(0) = data.vertices.row(last);
256 else
257 {
258 data.cell_kernel_points.setZero();
259 for (const int vertex : elements[0])
260 data.cell_kernel_points.row(0) += data.vertices.row(vertex);
261 data.cell_kernel_points.row(0) /= elements[0].size();
262 }
263 return data;
264 }
265
266 SurfaceMeshData MeshReader::read_msh_surface(const std::filesystem::path &path)
267 {
268 SurfaceMeshData result;
269 Eigen::MatrixXi cells;
270 std::vector<std::vector<int>> elements;
271 std::vector<std::vector<double>> weights;
272 std::vector<int> body_ids;
273 if (!io::MshReader::load(path.string(), result.vertices, cells, elements, weights, body_ids))
274 log_and_throw_error("Unable to decode MSH surface {}.", path.string());
275
276 if (cells.cols() == 1)
277 result.points = cells;
278 else if (cells.cols() == 2)
279 result.edges = cells;
280 else if (cells.cols() == 3)
281 result.faces = cells;
282 else if (cells.cols() == 4 && result.vertices.cols() == 3)
283 {
284 Eigen::MatrixXd surface_vertices;
286 result.vertices, cells, surface_vertices, result.faces);
287 result.vertices = std::move(surface_vertices);
288 }
289 else
290 log_and_throw_error("Unsupported MSH surface cell type in {}.", path.string());
291
292 finalize_surface(result);
293 return result;
294 }
295
296 SurfaceMeshData MeshReader::read_obj_surface(const std::filesystem::path &path)
297 {
298 SurfaceMeshData result;
299 if (!io::OBJReader::read(path.string(), result.vertices, result.edges, result.faces))
300 log_and_throw_error("Unable to decode OBJ surface {}.", path.string());
301 finalize_surface(result);
302 return result;
303 }
304
306 const std::filesystem::path &path,
307 SurfaceMeshData &result)
308 {
309 result = SurfaceMeshData();
310 if (!igl::read_triangle_mesh(path.string(), result.vertices, result.faces))
311 return false;
312 finalize_surface(result);
313 return true;
314 }
315
316 SurfaceMeshData MeshReader::read_geogram_surface(const std::filesystem::path &path)
317 {
318 GEO::Mesh mesh;
319 if (!GEO::mesh_load(path.string(), mesh))
320 log_and_throw_error("Unable to decode surface {} with Geogram.", path.string());
321 if (mesh.facets.nb() == 0)
322 log_and_throw_error("Surface mesh {} contains no facets.", path.string());
323
324 SurfaceMeshData result;
325 result.vertices = geogram_vertices(mesh, is_planar(mesh) ? 2 : 3);
326 const int width = mesh.facets.nb_vertices(0);
327 result.faces.resize(mesh.facets.nb(), width);
328 for (int i = 0; i < result.faces.rows(); ++i)
329 {
330 if (mesh.facets.nb_vertices(i) != width)
331 log_and_throw_error("Surface mesh {} has mixed facet sizes.", path.string());
332 for (int j = 0; j < width; ++j)
333 result.faces(i, j) = mesh.facets.vertex(i, j);
334 }
335 finalize_surface(result);
336 return result;
337 }
338} // namespace polyfem::mesh
Eigen::RowVectorXd point
std::vector< std::pair< int, double > > weights
std::vector< Eigen::VectorXi > faces
static bool load(const std::string &path, Eigen::MatrixXd &vertices, Eigen::MatrixXi &cells, std::vector< std::vector< int > > &elements, std::vector< std::vector< double > > &weights, std::vector< int > &body_ids)
Definition MshReader.cpp:43
static bool read(const std::string obj_file_name, std::vector< std::vector< double > > &V, std::vector< std::vector< double > > &TC, std::vector< std::vector< double > > &N, std::vector< std::vector< int > > &F, std::vector< std::vector< int > > &FTC, std::vector< std::vector< int > > &FN, std::vector< std::vector< int > > &L)
Read a mesh from an ascii obj file.
Definition OBJReader.cpp:32
Format-independent input used to construct a runtime Mesh.
Definition MeshData.hpp:12
Eigen::MatrixXd vertices
Definition MeshData.hpp:21
Eigen::MatrixXd higher_order_nodes
Definition MeshData.hpp:33
std::vector< std::vector< int > > higher_order_connectivity
Definition MeshData.hpp:34
std::vector< std::vector< int > > boundary_elements
Definition MeshData.hpp:30
std::vector< std::vector< int > > faces
Optional topology for arbitrary polyhedral cells.
Definition MeshData.hpp:38
std::vector< int > body_ids
Definition MeshData.hpp:27
bool elements_are_ordered
Whether each element row carries a format-defined local vertex order.
Definition MeshData.hpp:25
std::vector< int > boundary_ids
Definition MeshData.hpp:31
Eigen::MatrixXi elements
Definition MeshData.hpp:22
Eigen::MatrixXd cell_kernel_points
Definition MeshData.hpp:42
std::vector< std::vector< int > > cell_faces
Definition MeshData.hpp:39
std::vector< std::vector< int > > cell_face_orientations
Definition MeshData.hpp:40
std::vector< std::vector< double > > higher_order_weights
Definition MeshData.hpp:35
std::vector< bool > cell_is_hex
Definition MeshData.hpp:41
static MeshData read_hybrid(std::istream &input, const std::string &description)
static MeshData read_msh(const std::filesystem::path &path)
static bool read_triangle_surface(const std::filesystem::path &path, SurfaceMeshData &result)
static SurfaceMeshData read_geogram_surface(const std::filesystem::path &path)
static SurfaceMeshData read_obj_surface(const std::filesystem::path &path)
static SurfaceMeshData read_msh_surface(const std::filesystem::path &path)
static MeshData read_geogram(const std::filesystem::path &path)
static MeshData from_geogram(GEO::Mesh &mesh)
list vertices
Definition p_bases.py:238
bool is_planar(const GEO::Mesh &M, const double tol=1e-5)
Determine if the given mesh is planar (2D or tiny z-range).
Definition MeshUtils.cpp:28
void extract_triangle_surface_from_tets(const Eigen::MatrixXd &vertices, const Eigen::MatrixXi &tets, Eigen::MatrixXd &surface_vertices, Eigen::MatrixXi &tris)
Extract triangular surface from a tetmesh.
void log_and_throw_error(const std::string &msg)
Definition Logger.cpp:73