PolyFEM
Loading...
Searching...
No Matches
UpsampleMesh.cpp
Go to the documentation of this file.
1#include "UpsampleMesh.hpp"
2
6
7#include <igl/remove_duplicate_vertices.h>
8#include <igl/barycentric_coordinates.h>
9#ifdef POLYFEM_WITH_TRIANGLE
10#include <igl/triangle/triangulate.h>
11#endif
12
13namespace polyfem::mesh
14{
15 namespace
16 {
17 Eigen::MatrixXd sample_triangle(
18 const VectorNd &a,
19 const VectorNd &b,
20 const VectorNd &c,
21 const Eigen::MatrixXd &coords)
22 {
23 // c
24 // | \
25 // a--b
26 Eigen::MatrixXd V(coords.rows(), a.size());
27 for (int i = 0; i < coords.rows(); i++)
28 {
29 V.row(i) = coords(i, 0) * (b - a) + coords(i, 1) * (c - a) + a;
30 }
31 return V;
32 }
33 } // namespace
34
36 const Eigen::MatrixXd &V,
37 const Eigen::MatrixXi &F,
38 Eigen::MatrixXd &V_out,
39 Eigen::MatrixXi &F_out,
40 const double epsilon)
41 {
42 std::vector<Eigen::Triplet<double>> _, __;
43 stitch_mesh(V, F, _, V_out, F_out, __, epsilon);
44 }
45
47 const Eigen::MatrixXd &V,
48 const Eigen::MatrixXi &F,
49 const std::vector<Eigen::Triplet<double>> &W,
50 Eigen::MatrixXd &V_out,
51 Eigen::MatrixXi &F_out,
52 std::vector<Eigen::Triplet<double>> &W_out,
53 const double epsilon)
54 {
57 Eigen::VectorXi indices, inverse;
58 igl::remove_duplicate_vertices(
59 V, F, epsilon, V_out, indices, inverse, F_out);
60 assert(indices.size() == V_out.rows());
61 assert(inverse.size() == V.rows());
62
63 // Find indices of vertices that were removed
64 std::sort(indices.data(), indices.data() + indices.size());
65 std::vector<int> removed_indices;
66 removed_indices.reserve(V.rows() - indices.size());
67 for (int i = 0, j = 0; i < V.rows(); i++)
68 {
69 if (j < indices.size() && indices(j) == i)
70 j++;
71 else
72 removed_indices.push_back(i);
73 }
74 assert(removed_indices.size() == V.rows() - indices.size());
75 assert(std::is_sorted(removed_indices.begin(), removed_indices.end()));
76
77 // Filter out the weights that correspond to duplicate vertices
78 W_out.clear();
79 W_out.reserve(W.size());
80 for (const Eigen::Triplet<double> &w : W)
81 {
82 if (!std::binary_search(removed_indices.begin(), removed_indices.end(), w.row()))
83 W_out.emplace_back(inverse(w.row()), w.col(), w.value());
84 }
85 }
86
87 double max_edge_length(const Eigen::MatrixXd &V, const Eigen::MatrixXi &F)
88 {
89 double max_edge_length = 0;
90 for (int i = 0; i < F.cols(); i++)
91 {
92 max_edge_length = std::max(
94 (V(F.col(i), Eigen::all) - V(F.col((i + 1) % F.cols()), Eigen::all))
95 .rowwise()
96 .norm()
97 .maxCoeff());
98 }
99 return max_edge_length;
100 }
101
102 // Regular tessellation
103
105 const int n, Eigen::MatrixXd &V, Eigen::MatrixXi &F)
106 {
107 const double delta = 1.0 / (n - 1);
108 // map from(i, j) coordinates to vertex id
109 Eigen::MatrixXd ij2v = Eigen::MatrixXd::Constant(n, n, -1);
110 V.resize(n * (n + 1) / 2, 2);
111
112 int vi = 0;
113 for (int i = 0; i < n; i++)
114 {
115 for (int j = 0; j < n - i; j++)
116 {
117 ij2v(i, j) = vi;
118 V.row(vi++) << i * delta, j * delta;
119 }
120 }
121 assert(vi == V.rows());
122
123 // Create triangulated faces
124 F.resize((n - 1) * (n - 1), 3);
125 int fi = 0;
126 Eigen::Vector3i f;
127 for (int i = 0; i < n - 1; i++)
128 {
129 for (int j = 0; j < n - 1; j++)
130 {
131 f << ij2v(i, j), ij2v(i + 1, j), ij2v(i, j + 1);
132 if (f.x() >= 0 && f.y() >= 0 && f.z() >= 0)
133 {
134 F.row(fi++) = f;
135 }
136
137 f << ij2v(i + 1, j), ij2v(i + 1, j + 1), ij2v(i, j + 1);
138 if (f.x() >= 0 && f.y() >= 0 && f.z() >= 0)
139 {
140 F.row(fi++) = f;
141 }
142 }
143 }
144
145 F.conservativeResize(fi, Eigen::NoChange);
146 }
147
149 const Eigen::MatrixXd &V,
150 const Eigen::MatrixXi &F,
151 const double out_max_edge_length,
152 Eigen::MatrixXd &V_out,
153 Eigen::MatrixXi &F_out)
154 {
155 // Add one because n is the number of edge vertices not edges segments
156 const double in_max_edge_length = max_edge_length(V, F);
157 const int n =
158 std::max(1, int(std::ceil(in_max_edge_length / out_max_edge_length)))
159 + 1;
160
161 Eigen::MatrixXd coords;
162 Eigen::MatrixXi local_F;
164
165 Eigen::MatrixXd V_tmp(F.rows() * coords.rows(), V.cols());
166 Eigen::MatrixXi F_tmp(F.rows() * local_F.rows(), F.cols());
167 for (int i = 0; i < F.rows(); i++)
168 {
169 F_tmp.middleRows(i * local_F.rows(), local_F.rows()) =
170 local_F.array() + i * coords.rows();
171 V_tmp.middleRows(i * coords.rows(), coords.rows()) = sample_triangle(
172 V.row(F(i, 0)), V.row(F(i, 1)), V.row(F(i, 2)), coords);
173 }
174
175 stitch_mesh(V_tmp, F_tmp, V_out, F_out);
176 }
177
178 // ------------------------------------------------------------------------
179 // Irregular tessellation
180 // ------------------------------------------------------------------------
181
182 Eigen::MatrixXd
183 refine_edge(const VectorNd &a, const VectorNd &b, const double max_edge_length)
184 {
185 const int n(std::ceil((b - a).norm() / max_edge_length));
186 Eigen::MatrixXd V(n + 1, a.size());
187 for (int i = 0; i <= n; i++)
188 {
189 V.row(i) = (b - a) * (i / double(n)) + a;
190 }
191 return V;
192 }
193
195 const VectorNd &a,
196 const VectorNd &b,
197 const VectorNd &c,
198 const double max_edge_len,
199 Eigen::MatrixXd &V,
200 Eigen::MatrixXi &E)
201 {
202 const int Nab = std::ceil((b - a).norm() / max_edge_len);
203 const int Nbc = std::ceil((c - b).norm() / max_edge_len);
204 const int Nca = std::ceil((a - c).norm() / max_edge_len);
205 V.resize(Nab + Nbc + Nca, a.size());
206 V.topRows(Nab) = refine_edge(a, b, max_edge_len).topRows(Nab);
207 V.middleRows(Nab, Nbc) = refine_edge(b, c, max_edge_len).topRows(Nbc);
208 V.bottomRows(Nca) = refine_edge(c, a, max_edge_len).topRows(Nca);
209
210 E.resize(V.rows(), 2);
211 for (int i = 0; i < V.rows(); i++)
212 {
213 E.row(i) << i, (i + 1) % V.rows();
214 }
215 }
216
218 const Eigen::Vector3d &a,
219 const Eigen::Vector3d &b,
220 const Eigen::Vector3d &c,
221 const double max_edge_length,
222 Eigen::MatrixXd &V,
223 Eigen::MatrixXi &F)
224 {
225#ifdef POLYFEM_WITH_TRIANGLE
226 const double p = 3.0 * max_edge_length / 2.0;
227 const double max_area = sqrt(p * std::pow(p - max_edge_length, 3));
228
229 Eigen::MatrixXi E;
231
232 // Compute a rotation that aligns the z axis with the triangle normal
233 const Eigen::Matrix3d R =
234 Eigen::Quaterniond::FromTwoVectors(
235 (b - a).cross(c - a).normalized(), Eigen::Vector3d::UnitZ())
236 .toRotationMatrix();
237
238 // Align the triangle with the z axis
239 Eigen::MatrixXd V_2D = V * R.transpose();
240 const double z = V_2D(0, 2); // Save the z-offset
241 assert((abs(V_2D.col(2).array() - z) < 1e-10).all());
242 V_2D.conservativeResize(Eigen::NoChange, 2); // Drop the z coordinate
243
244 igl::triangle::triangulate(
245 V_2D, E, Eigen::MatrixXi(), fmt::format("Ya{:f}qQ", max_area), V, F);
246
247 V.conservativeResize(V.rows(), 3);
248 V.col(2).setConstant(z); // Restore the z-offset
249 V = V * R; // Rotate back to the original orientation
250
251 // TODO: IDK why there are zero-area faces
252 int fi = 0;
253 for (int i = 0; i < F.rows(); i++)
254 {
255 if (utils::triangle_area(V(F.row(i), Eigen::all)) > 1e-12)
256 {
257 F.row(fi++) = F.row(i);
258 }
259 }
260 F.conservativeResize(fi, Eigen::NoChange);
261#else
262 log_and_throw_error("irregular_triangle(): POLYFEM_WITH_TRIANGLE is not enabled!");
263#endif
264 }
265
267 const Eigen::Vector3d &a,
268 const Eigen::Vector3d &b,
269 const Eigen::Vector3d &c,
270 const double max_edge_length,
271 Eigen::MatrixXd &UV,
272 Eigen::MatrixXi &F)
273 {
274 Eigen::MatrixXd V;
276
277 // Convert the triangle vertices to barycentric coordinates
278 UV.resize(V.rows(), 2);
279 for (int i = 0; i < UV.rows(); i++)
280 {
281 Eigen::RowVector3d tmp;
282 igl::barycentric_coordinates(
283 V.row(i), a.transpose(), b.transpose(), c.transpose(), tmp);
284 UV.row(i) = tmp.head<2>();
285 }
286 }
287
289 const Eigen::MatrixXd &V,
290 const Eigen::MatrixXi &F,
291 const double max_edge_length,
292 Eigen::MatrixXd &V_out,
293 Eigen::MatrixXi &F_out)
294 {
295 Eigen::MatrixXd V_tmp(0, V.cols());
296 Eigen::MatrixXi F_tmp(0, F.cols());
297 for (int i = 0; i < F.rows(); i++)
298 {
299 Eigen::MatrixXd local_V;
300 Eigen::MatrixXi local_F;
302 V.row(F(i, 0)), V.row(F(i, 1)), V.row(F(i, 2)), max_edge_length,
303 local_V, local_F);
304
305 F_tmp.conservativeResize(F_tmp.rows() + local_F.rows(), Eigen::NoChange);
306 F_tmp.bottomRows(local_F.rows()) = local_F.array() + V_tmp.rows();
307
308 V_tmp.conservativeResize(V_tmp.rows() + local_V.rows(), Eigen::NoChange);
309 V_tmp.bottomRows(local_V.rows()) = local_V;
310 }
311
312 stitch_mesh(V_tmp, F_tmp, V_out, F_out);
313 }
314} // namespace polyfem::mesh
int V
int z
Eigen::Matrix< double, dim, 1 > cross(const Eigen::Matrix< double, dim, 1 > &x, const Eigen::Matrix< double, dim, 1 > &y)
Eigen::MatrixXd refine_edge(const VectorNd &a, const VectorNd &b, const double max_edge_length)
Refine an edge (a, b) so each refined edge has length at most max_edge_length.
void irregular_triangle(const Eigen::Vector3d &a, const Eigen::Vector3d &b, const Eigen::Vector3d &c, const double max_edge_length, Eigen::MatrixXd &V, Eigen::MatrixXi &F)
Refine a triangle (a, b, c) into a well shaped triangle mesh.
void irregular_tessellation(const Eigen::MatrixXd &V, const Eigen::MatrixXi &F, const double max_edge_length, Eigen::MatrixXd &V_out, Eigen::MatrixXi &F_out)
Tessilate a triangle mesh (V, F) with well shaped triangles.
double max_edge_length(const Eigen::MatrixXd &V, const Eigen::MatrixXi &F)
Compute the maximum edge length of a triangle mesh (V, F)
void regular_grid_triangle_barycentric_coordinates(const int n, Eigen::MatrixXd &V, Eigen::MatrixXi &F)
Compute the barycentric coordinates of a regular grid of triangles.
void irregular_triangle_barycentric_coordinates(const Eigen::Vector3d &a, const Eigen::Vector3d &b, const Eigen::Vector3d &c, const double max_edge_length, Eigen::MatrixXd &UV, Eigen::MatrixXi &F)
Refine a triangle (a, b, c) into a well shaped triangle mesh.
void regular_grid_tessellation(const Eigen::MatrixXd &V, const Eigen::MatrixXi &F, const double out_max_edge_length, Eigen::MatrixXd &V_out, Eigen::MatrixXi &F_out)
Tessilate a triangle mesh (V, F) with regular grids of triangles of maximum edge length.
void refine_triangle_edges(const VectorNd &a, const VectorNd &b, const VectorNd &c, const double max_edge_len, Eigen::MatrixXd &V, Eigen::MatrixXi &E)
Refine the edges of a triangle (a, b, c) so each refined edge has length at most max_edge_length.
void stitch_mesh(const Eigen::MatrixXd &V, const Eigen::MatrixXi &F, Eigen::MatrixXd &V_out, Eigen::MatrixXi &F_out, const double epsilon)
Stitch a triangle mesh (V, F) together by removing duplicate vertices.
Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic, 0, 3, 3 > inverse(const Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic, 0, 3, 3 > &mat)
double triangle_area(const Eigen::MatrixXd V)
Compute the signed area of a triangle defined by three points.
Eigen::Matrix< double, Eigen::Dynamic, 1, 0, 3, 1 > VectorNd
Definition Types.hpp:11
void log_and_throw_error(const std::string &msg)
Definition Logger.cpp:71