Lagrange
All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Modules Pages
save_mesh.h
1/*
2 * Copyright 2017 Adobe. All rights reserved.
3 * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License. You may obtain a copy
5 * of the License at http://www.apache.org/licenses/LICENSE-2.0
6 *
7 * Unless required by applicable law or agreed to in writing, software distributed under
8 * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9 * OF ANY KIND, either express or implied. See the License for the specific language
10 * governing permissions and limitations under the License.
11 */
12#pragma once
13
14#include <fstream>
15#include <numeric>
16#include <string>
17
18// clang-format off
19#include <lagrange/utils/warnoff.h>
20#include <igl/writeOBJ.h>
21#include <lagrange/utils/warnon.h>
22// clang-format on
23
24#include <lagrange/Mesh.h>
25#include <lagrange/MeshTrait.h>
26#include <lagrange/common.h>
27#include <lagrange/utils/assert.h>
28#include <lagrange/utils/range.h>
29#include <lagrange/utils/safe_cast.h>
30#include <lagrange/legacy/inline.h>
31
32#include <lagrange/io/legacy/save_mesh_ply.h>
33
34#include <lagrange/fs/filesystem.h>
35
36namespace lagrange::io {
37LAGRANGE_LEGACY_INLINE
38namespace legacy {
39
40namespace internal {
41template <typename MeshType>
42void save_mesh_2D(const fs::path& filename, const MeshType& mesh)
43{
44 fs::ofstream fout(filename);
45 const auto& vertices = mesh.get_vertices();
46 for (auto i : range(mesh.get_num_vertices())) {
47 fout << "v " << vertices(i, 0) << " " << vertices(i, 1) << std::endl;
48 }
49
50 const auto& facets = mesh.get_facets();
51 const auto vertex_per_facet = mesh.get_vertex_per_facet();
52 for (auto i : range(mesh.get_num_facets())) {
53 fout << "f";
54 for (auto j : range(vertex_per_facet)) {
55 fout << " " << facets(i, j) + 1;
56 }
57 fout << std::endl;
58 }
59 fout.close();
60}
61
62template <typename MeshType>
63void save_mesh_basic(const fs::path& filename, const MeshType& mesh)
64{
65 if (mesh.get_dim() == 2) {
66 save_mesh_2D(filename, mesh);
67 } else {
68 const auto& vertices = mesh.get_vertices();
69 const auto& facets = mesh.get_facets();
70 igl::writeOBJ(filename.string(), vertices, facets);
71 }
72}
73
74template <typename MeshType, typename DerivedV, typename DerivedF>
75void extract_attribute(
76 const MeshType& mesh,
77 const std::string& attr_name,
78 Eigen::MatrixBase<DerivedV>& values,
79 Eigen::MatrixBase<DerivedF>& indices)
80{
81 const auto& facets = mesh.get_facets();
82 if (mesh.has_indexed_attribute(attr_name)) {
83 auto attr = mesh.get_indexed_attribute(attr_name);
84 values = std::get<0>(attr);
85 indices = std::get<1>(attr);
86 } else if (mesh.has_corner_attribute(attr_name)) {
87 values = mesh.get_corner_attribute(attr_name);
88 indices.derived().resize(facets.rows(), facets.cols());
89 typename MeshType::Index count = 0;
90 for (auto i : range(facets.rows())) {
91 for (auto j : range(facets.cols())) {
92 indices(i, j) = count;
93 count++;
94 }
95 }
96 } else if (mesh.has_vertex_attribute(attr_name)) {
97 values = mesh.get_vertex_attribute(attr_name);
98 indices = facets;
99 }
100}
101
102template <typename MeshType>
103void save_mesh_obj(const fs::path& filename, const MeshType& mesh)
104{
105 using Scalar = typename MeshType::Scalar;
106 using Index = typename MeshType::Index;
107
108 const auto& vertices = mesh.get_vertices();
109 const auto& facets = mesh.get_facets();
110
111 Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> CN;
112 Eigen::Matrix<Index, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> FN;
113 Eigen::Matrix<Scalar, Eigen::Dynamic, 2, Eigen::RowMajor> TC;
114 Eigen::Matrix<Index, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> FTC;
115
116 extract_attribute(mesh, "uv", TC, FTC);
117 extract_attribute(mesh, "normal", CN, FN);
118
119 igl::writeOBJ(filename.string(), vertices, facets, CN, FN, TC, FTC);
120}
121
122template <typename MeshType>
123void save_mesh_vtk(
124 const fs::path& filename,
125 const MeshType& mesh,
126 const std::vector<std::string>& face_attrib_names,
127 const std::vector<std::string>& vertex_attrib_names)
128{
129 using AttributeArray = typename MeshType::AttributeArray;
130
131 auto write_connectivity = [&](std::ostream& fl) {
133 // Not a hard requirement. But since this is just a debugging tool
134 // let's just enforce it for now.
136
137 /*
138 * Write the vtk file.
139 */
140
141 // write the header
142 fl << "# vtk DataFile Version 2.0\n";
143 fl << "Lagrange output mesh\n";
144 fl << "ASCII\n";
145 fl << "DATASET UNSTRUCTURED_GRID\n";
146 fl << "\n";
147
148 // write the vertices
149 fl << "POINTS " << mesh.get_num_vertices() << " float\n";
150 for (auto vnidx : range(mesh.get_num_vertices())) {
151 auto xyz = mesh.get_vertices().row(vnidx).eval();
152 if (xyz.cols() == 3) {
153 fl << xyz(0) << " " << xyz(1) << " " << xyz(2) << "\n";
154 } else if (xyz.cols() == 2) {
155 fl << xyz(0) << " " << xyz(1) << " " << 0 << "\n";
156 } else {
157 throw std::runtime_error("This dimension is not supported");
158 }
159 }
160 fl << "\n";
161
162 //
163 // write the faces
164 //
165
166 // count their total number of vertices.
167 fl << "CELLS " << mesh.get_num_facets() << " "
168 << mesh.get_num_facets() * (mesh.get_vertex_per_facet() + 1) << "\n";
169 for (auto fn : range(mesh.get_num_facets())) {
170 fl << mesh.get_vertex_per_facet() << " ";
171 for (auto voffset : range(mesh.get_vertex_per_facet())) { //
172 fl << mesh.get_facets()(fn, voffset) << " ";
173 }
174 fl << "\n";
175 }
176 fl << "\n";
177
178 // write the face types
179 fl << "CELL_TYPES " << mesh.get_num_facets() << "\n";
180 for (auto f : range(mesh.get_num_facets())) {
181 (void)f;
182 // fl << "7 \n"; // VTK POLYGON
183 fl << "5 \n"; // VTK TRIANGLE
184 }
185 fl << "\n";
186 }; // end of write connectivity
187
188 auto write_vertex_data_header = [&](std::ostream& fl) {
189 fl << "POINT_DATA " << mesh.get_num_vertices() << " \n";
190 }; // end of write_vert_data_header
191
192 auto write_cell_data_header = [&](std::ostream& fl) {
193 fl << "CELL_DATA " << mesh.get_num_facets() << " \n";
194 }; // end of write_cell_header
195
196 auto write_data =
197 [](std::ostream& fl, const std::string attrib_name, const AttributeArray& attrib) {
198 fl << "SCALARS " << attrib_name << " float " << attrib.cols() << "\n";
199 fl << "LOOKUP_TABLE default \n";
200 for (auto row : range(attrib.rows())) {
201 for (auto col : range(attrib.cols())) {
202 fl << attrib(row, col) << " ";
203 } // end of col
204 fl << "\n";
205 } // end of row
206 fl << "\n";
207 }; // end of write_data()
208
209 // Open the file
210 fs::ofstream fl(filename, fs::fstream::out);
211 fl.precision(12);
212 fl.flags(fs::fstream::scientific);
213 la_runtime_assert(fl.is_open());
214
215 // write the connectivity
216 write_connectivity(fl);
217
218 // Writing the face attribs
219 {
220 // Check if the mesh has any of the requested attribs
221 bool has_any_facet_attrib = false;
222 for (const auto& attrib_name : face_attrib_names) {
223 if (mesh.has_facet_attribute(attrib_name)) {
224 has_any_facet_attrib = true;
225 break;
226 }
227 }
228 // Write the header if it does
229 if (has_any_facet_attrib) {
230 write_cell_data_header(fl);
231 }
232 // Now write the data
233 for (const auto& attrib_name : face_attrib_names) {
234 if (mesh.has_facet_attribute(attrib_name)) {
235 write_data(fl, attrib_name, mesh.get_facet_attribute(attrib_name));
236 }
237 }
238
239 } // end of writing face attribs
240
241 // Writing the vertex attribs
242 {
243 // Check if the mesh has any of the requested attribs
244 bool has_any_vertex_attrib = false;
245 for (const auto& attrib_name : vertex_attrib_names) {
246 if (mesh.has_vertex_attribute(attrib_name)) {
247 has_any_vertex_attrib = true;
248 break;
249 }
250 }
251
252 // Write the header if it does
253 if (has_any_vertex_attrib) {
254 write_vertex_data_header(fl);
255 }
256
257 // Now write the data
258 for (const auto& attrib_name : vertex_attrib_names) {
259 if (mesh.has_vertex_attribute(attrib_name)) {
260 write_data(fl, attrib_name, mesh.get_vertex_attribute(attrib_name));
261 }
262 }
263
264 } // end of writing vertex attribs
265
266} // end of write vtk
267
268} // namespace internal
269
270
271template <typename MeshType>
272void save_mesh(const fs::path& filename, const MeshType& mesh)
273{
274 static_assert(MeshTrait<MeshType>::is_mesh(), "Input type is not Mesh");
275
276 if (filename.extension() == ".obj") {
277 internal::save_mesh_obj(filename, mesh);
278 } else if (filename.extension() == ".vtk") {
279 internal::save_mesh_vtk(
280 filename,
281 mesh,
282 mesh.get_facet_attribute_names(),
283 mesh.get_vertex_attribute_names());
284 } else if (filename.extension() == ".ply") {
285 save_mesh_ply(filename, mesh);
286 } else {
287 internal::save_mesh_basic(filename, mesh);
288 }
289}
290
291} // namespace legacy
292} // namespace lagrange::io
Definition: Mesh.h:48
Index get_num_vertices() const
Retrieves the number of vertices.
Definition: SurfaceMesh.h:671
auto get_indexed_attribute(std::string_view name) const -> const IndexedAttribute< ValueType, Index > &
Gets a read-only reference to an indexed attribute given its name.
Definition: SurfaceMesh.cpp:1310
Index get_num_facets() const
Retrieves the number of facets.
Definition: SurfaceMesh.h:678
Index get_vertex_per_facet() const
Retrieves the number of vertex per facet in a regular mesh.
Definition: SurfaceMesh.cpp:2118
#define la_runtime_assert(...)
Runtime assertion check.
Definition: assert.h:169
internal::Range< Index > range(Index end)
Returns an iterable object representing the range [0, end).
Definition: range.h:176
Mesh input/output.
Definition: detect_file_format.h:18
void save_mesh(std::ostream &output_stream, const SurfaceMesh< Scalar, Index > &mesh, FileFormat format, const SaveOptions &options={})
Save a mesh to a stream.
Definition: save_mesh.cpp:30
void save_mesh_obj(std::ostream &output_stream, const SurfaceMesh< Scalar, Index > &mesh, const SaveOptions &options={})
Saves a mesh to a stream in OBJ format.
Definition: save_mesh_obj.cpp:31
void save_mesh_ply(std::ostream &output_stream, const SurfaceMesh< Scalar, Index > &mesh, const SaveOptions &options={})
Saves a mesh to a stream in PLY format.
Definition: save_mesh_ply.cpp:214