PolyFEM
Loading...
Searching...
No Matches
PolygonalBasis3d.cpp
Go to the documentation of this file.
1
3#include "LagrangeBasis3d.hpp"
4
15
17
18#include <igl/per_vertex_normals.h>
19#include <random>
20#include <memory>
22
23namespace polyfem
24{
25 using namespace assembler;
26 using namespace mesh;
27 using namespace quadrature;
28 using namespace utils;
29
30 namespace basis
31 {
32 namespace
33 {
34
35 const int max_num_kernels = 300;
36
37 // -----------------------------------------------------------------------------
38
39 std::vector<int> compute_nonzero_bases_ids(const Mesh3D &mesh, const int c,
40 const std::vector<ElementBases> &bases,
41 const std::map<int, InterfaceData> &poly_face_to_data)
42 {
43 std::vector<int> local_to_global;
44
45 for (int lf = 0; lf < mesh.n_cell_faces(c); ++lf)
46 {
47 auto index = mesh.get_index_from_element(c, lf, 0);
48 const int c2 = mesh.switch_element(index).element;
49 assert(c2 >= 0); // no boundary polytope
50 assert(poly_face_to_data.count(index.face) > 0);
51 const InterfaceData &bdata = poly_face_to_data.at(index.face);
52 const ElementBases &b = bases[c2];
53 for (int other_local_basis_id : bdata.local_indices)
54 {
55 for (const auto &x : b.bases[other_local_basis_id].global())
56 {
57 const int global_node_id = x.index;
58 local_to_global.push_back(global_node_id);
59 }
60 }
61 }
62
63 std::sort(local_to_global.begin(), local_to_global.end());
64 auto it = std::unique(local_to_global.begin(), local_to_global.end());
65 local_to_global.resize(std::distance(local_to_global.begin(), it));
66
67 return local_to_global;
68 }
69
70 // -----------------------------------------------------------------------------
71
72 // Canonical triangle mesh in parametric domain
73 void compute_canonical_pattern(int n_samples_per_edge, Eigen::MatrixXd &V, Eigen::MatrixXi &F)
74 {
75 regular_2d_grid(n_samples_per_edge, false, V, F);
76
77 // igl::opengl::glfw::Viewer viewer;
78 // viewer.data().set_mesh(V, F);
79 // viewer.launch();
80 }
81
82 // -----------------------------------------------------------------------------
83
84 // Needs to be consistent between `evalFunc` and `compute_quad_mesh_from_cell`
85 constexpr int lv0 = 3;
86
87 // Assemble the surface quad mesh (V, F) corresponding to the polyhedron c
88 GetAdjacentLocalEdge compute_quad_mesh_from_cell(
89 const Mesh3D &mesh, int c, Eigen::MatrixXd &V, Eigen::MatrixXi &F)
90 {
91 std::vector<std::array<int, 4>> quads(mesh.n_cell_faces(c));
92 typedef std::tuple<int, int, bool> QuadLocalEdge;
93 std::vector<std::array<QuadLocalEdge, 4>> adj(mesh.n_cell_faces(c));
94 int num_vertices = 0;
95 std::map<int, int> vertex_g2l;
96 std::map<int, int> face_g2l;
97 for (int lf = 0; lf < mesh.n_cell_faces(c); ++lf)
98 {
99 face_g2l.emplace(mesh.get_index_from_element(c, lf, lv0).face, lf);
100 }
101 for (int lf = 0; lf < mesh.n_cell_faces(c); ++lf)
102 {
103 auto index = mesh.get_index_from_element(c, lf, lv0);
104 assert(mesh.n_face_vertices(index.face) == 4);
105 for (int lv = 0; lv < 4; ++lv)
106 {
107 if (!vertex_g2l.count(index.vertex))
108 {
109 vertex_g2l.emplace(index.vertex, num_vertices++);
110 }
111 quads[lf][lv] = vertex_g2l.at(index.vertex);
112
113 // Set adjacency info
114 auto index2 = mesh.switch_face(index);
115 int lf2 = face_g2l.at(index2.face);
116 std::get<0>(adj[lf][lv]) = lf2;
117 auto index3 = mesh.get_index_from_element(c, lf2, lv0);
118 for (int lv2 = 0; lv2 < 4; ++lv2)
119 {
120 if (index3.edge == index2.edge)
121 {
122 std::get<1>(adj[lf][lv]) = lv2;
123 if (index2.vertex != index3.vertex)
124 {
125 assert(mesh.switch_vertex(index3).vertex == index2.vertex);
126 std::get<2>(adj[lf][lv]) = true;
127 }
128 else
129 {
130 std::get<2>(adj[lf][lv]) = false;
131 }
132 }
133 index3 = mesh.next_around_face(index3);
134 }
135
136 index = mesh.next_around_face(index);
137 }
138 }
139 V.resize(num_vertices, 3);
140 for (const auto &kv : vertex_g2l)
141 {
142 V.row(kv.second) = mesh.point(kv.first);
143 }
144 F.resize(quads.size(), 4);
145 int f = 0;
146 for (auto q : quads)
147 {
148 F.row(f++) << q[0], q[1], q[2], q[3];
149 }
150
151 return [adj](int q, int lv) {
152 return adj[q][lv];
153 };
154 }
155
156 // -----------------------------------------------------------------------------
157
158 void compute_offset_kernels(const Eigen::MatrixXd &QV, const Eigen::MatrixXi &QF,
159 int n_kernels_per_edge, double eps, Eigen::MatrixXd &kernel_centers,
160 Eigen::MatrixXd &KV, Eigen::MatrixXi &KF,
161 EvalParametersFunc evalFuncGeom, GetAdjacentLocalEdge getAdjLocalEdge)
162 {
163 Eigen::MatrixXd PV, KN;
164 Eigen::MatrixXi PF;
165 Eigen::VectorXd D;
166 compute_canonical_pattern(n_kernels_per_edge, PV, PF);
167 instantiate_pattern(QV, QF, PV, PF, KV, KF, nullptr, evalFuncGeom, getAdjLocalEdge);
168 orient_closed_surface(KV, KF);
169 double volume = std::pow(signed_volume(KV, KF), 1.0 / 3.0);
170
171 if (true || KV.rows() < max_num_kernels)
172 {
173 igl::per_vertex_normals(KV, KF, KN);
174 kernel_centers = KV;
175 }
176 else
177 {
178 // std::cout << "fancy sampling" << std::endl;
179 polyfem::sample_surface(KV, KF, max_num_kernels, kernel_centers, &KN, 10, 10);
180 // std::cout << "size: "<< kernel_centers.size() << std::endl;
181 }
182 // std::cout << "eps: " << eps << std::endl;
183 // std::cout << eps * volume << std::endl;
184 kernel_centers += eps * volume * KN;
185
186 // std::default_random_engine gen;
187 // std::uniform_real_distribution<double> dist(-1.0, 1.0);
188 // for (int v = 0; v < kernel_centers.rows(); ++v) {
189 // kernel_centers.row(v) = KV.row(v) + dist(gen) * KN.row(v);
190 // }
191 assert(kernel_centers.cols() == 3);
192 signed_squared_distances(KV, KF, kernel_centers, D);
193 std::vector<Eigen::RowVector3d> remap;
194 std::vector<Eigen::RowVector3d> rejected;
195 for (int v = 0; v < kernel_centers.rows(); ++v)
196 {
197 if (std::sqrt(D(v)) > 0.8 * eps * volume)
198 {
199 remap.push_back(kernel_centers.row(v));
200 }
201 else
202 {
203 // rejected.push_back(kernel_centers.row(v));
204 }
205 }
206 kernel_centers.resize(remap.size(), 3);
207 for (int v = 0; v < kernel_centers.rows(); ++v)
208 {
209 kernel_centers.row(v) = remap[v];
210 }
211 // Eigen::MatrixXd rej(rejected.size(), 3);
212 // for (int v = 0; v < (int) rejected.size(); ++v) {
213 // rej.row(v) = rejected[v];
214 // }
215 // igl::write_triangle_mesh("foo_medium.obj", KV, KF);
216 // std::cout << "nkernels: " << KV.rows() << std::endl;
217 // igl::write_triangle_mesh("foo.obj", KV, KF);
218
219 // igl::opengl::glfw::Viewer viewer;
220 // viewer.data().set_mesh(KV, KF);
221 // viewer.data().add_points(kernel_centers, Eigen::RowVector3d(0,1,1));
222 // viewer.data().add_points(rej, Eigen::RowVector3d(1,0,0));
223 // viewer.launch();
224 }
225
226 // -----------------------------------------------------------------------------
227
229 void sample_polyhedra(
230 const int element_index,
231 const int n_quadrature_vertices_per_edge,
232 const int n_kernels_per_edge,
233 int n_samples_per_edge,
234 const int quadrature_order,
235 const int mass_quadrature_order,
236 const Mesh3D &mesh,
237 const std::map<int, InterfaceData> &poly_face_to_data,
238 const std::vector<ElementBases> &bases,
239 const std::vector<ElementBases> &gbases,
240 const double eps,
241 std::vector<int> &local_to_global,
242 Eigen::MatrixXd &collocation_points,
243 Eigen::MatrixXd &kernel_centers,
244 Eigen::MatrixXd &rhs,
245 Eigen::MatrixXd &triangulated_vertices,
246 Eigen::MatrixXi &triangulated_faces,
247 Quadrature &quadrature,
248 Quadrature &mass_quadrature,
249 double &scaling,
250 Eigen::RowVector3d &translation)
251 {
252 // Local ids of nonzero bases over the polygon
253 local_to_global = compute_nonzero_bases_ids(mesh, element_index, bases, poly_face_to_data);
254
255 // Compute the image of the canonical pattern vertices through the geometric mapping
256 // of the given local face
257 auto evalFunc = [&](const Eigen::MatrixXd &uv, Eigen::MatrixXd &mapped, int lf) {
258 const auto &u = uv.col(0).array();
259 const auto &v = uv.col(1).array();
260 auto index = mesh.get_index_from_element(element_index, lf, lv0);
261 index = mesh.switch_element(index);
262 // Eigen::MatrixXd abcd = LagrangeBasis3d::linear_hex_face_local_nodes_coordinates(mesh, index);
263 const auto indices = LagrangeBasis3d::hex_face_local_nodes(false, 1, mesh, index);
264 assert(indices.size() == 4);
265 Eigen::MatrixXd abcd;
267 Eigen::RowVector3d a = abcd.row(indices(0));
268 Eigen::RowVector3d b = abcd.row(indices(1));
269 Eigen::RowVector3d c = abcd.row(indices(2));
270 Eigen::RowVector3d d = abcd.row(indices(3));
271 mapped = ((1 - u) * (1 - v)).matrix() * a + (u * (1 - v)).matrix() * b + (u * v).matrix() * c + ((1 - u) * v).matrix() * d;
272 mapped = mapped.array().max(0.0).min(1.0);
273 assert(mapped.maxCoeff() >= 0.0);
274 assert(mapped.maxCoeff() <= 1.0);
275 };
276 auto evalFuncGeom = [&](const Eigen::MatrixXd &uv, Eigen::MatrixXd &mapped, int lf) {
277 Eigen::MatrixXd samples;
278 evalFunc(uv, samples, lf);
279 auto index = mesh.get_index_from_element(element_index, lf, lv0);
280 index = mesh.switch_element(index);
281 const ElementBases &gb = gbases[index.element];
282 gb.eval_geom_mapping(samples, mapped);
283 };
284
285 Eigen::MatrixXd QV, KV;
286 Eigen::MatrixXi QF, KF;
287 auto getAdjLocalEdge = compute_quad_mesh_from_cell(mesh, element_index, QV, QF);
288
289 // Compute kernel centers
290 compute_offset_kernels(QV, QF, n_kernels_per_edge, eps, kernel_centers, KV, KF,
291 evalFuncGeom, getAdjLocalEdge);
292 // if (KV.rows() >= max_num_kernels) { n_samples_per_edge = 5; }
293
294 // Compute collocation points
295 Eigen::MatrixXd PV, UV;
296 Eigen::MatrixXi PF, CF, UF;
297 Eigen::VectorXi uv_sources, uv_ranges;
298 compute_canonical_pattern(n_samples_per_edge, PV, PF);
299 instantiate_pattern(QV, QF, PV, PF, UV, UF, &uv_sources, evalFunc, getAdjLocalEdge);
300 orient_closed_surface(UV, UF);
301 instantiate_pattern(QV, QF, PV, PF, collocation_points, CF, nullptr, evalFuncGeom, getAdjLocalEdge);
302 orient_closed_surface(collocation_points, CF);
303 reorder_mesh(collocation_points, CF, uv_sources, uv_ranges);
304 reorder_mesh(UV, UF, uv_sources, uv_ranges);
305 assert(uv_ranges.size() == mesh.n_cell_faces(element_index) + 1);
306
307 // Compute coarse surface surface for visualization
308 compute_canonical_pattern(n_quadrature_vertices_per_edge, PV, PF);
309 instantiate_pattern(QV, QF, PV, PF, triangulated_vertices, triangulated_faces,
310 nullptr, evalFuncGeom, getAdjLocalEdge);
311 orient_closed_surface(triangulated_vertices, triangulated_faces);
312
313 // for (int f = 0; f < KF.rows(); ++f) {
314 // triangulated_faces.row(f) = KF.row(f).reverse();
315 // }
316
317 // {
318 // Eigen::MatrixXd V;
319 // evalFuncGeom(PV, V, 0);
320 // igl::write_triangle_mesh("foo_dense.obj", collocation_points, CF);
321 // igl::write_triangle_mesh("foo_small.obj", triangulated_vertices, triangulated_faces);
322 // igl::opengl::glfw::Viewer viewer;
323 // viewer.data().set_points(kernel_centers, Eigen::RowVector3d(1,0,1));
324 // viewer.data().set_mesh(KV, KF);
325 // viewer.launch();
326 // }
327
328 // igl::opengl::glfw::Viewer viewer;
329 // viewer.data().set_mesh(collocation_points, CF);
330 // viewer.data().add_points(kernel_centers, Eigen::RowVector3d(0,1,1));
331 // for (int lf = 0; lf < mesh.n_cell_faces(element_index); ++lf) {
332 // Eigen::MatrixXd samples;
333 // samples = UV.middleRows(uv_ranges(lf), uv_ranges(lf+1) - uv_ranges(lf));
334 // Eigen::RowVector3d c = Eigen::RowVector3d::Random();
335 // viewer.data().add_points(samples, c);
336 // }
337 // viewer.launch();
338
339 // Compute right-hand side constraints for setting the harmonic kernels
340 Eigen::MatrixXd samples;
341 std::vector<AssemblyValues> basis_val;
342 rhs.resize(UV.rows(), local_to_global.size());
343 rhs.setZero();
344 for (int lf = 0; lf < mesh.n_cell_faces(element_index); ++lf)
345 {
346 auto index = mesh.get_index_from_element(element_index, lf, 0);
347 const int c2 = mesh.switch_element(index).element;
348 assert(c2 >= 0); // no boundary polytope
349
350 const InterfaceData &bdata = poly_face_to_data.at(index.face);
351 const ElementBases &b = bases[c2];
352
353 samples = UV.middleRows(uv_ranges(lf), uv_ranges(lf + 1) - uv_ranges(lf));
354 b.evaluate_bases(samples, basis_val);
355
356 // Evaluate field basis and set up the rhs
357 for (int other_local_basis_id : bdata.local_indices)
358 {
359 // b.bases[other_local_basis_id].basis(samples, basis_val);
360
361 for (const auto &x : b.bases[other_local_basis_id].global())
362 {
363 const int global_node_id = x.index;
364 const double weight = x.val;
365
366 const int poly_local_basis_id = std::distance(local_to_global.begin(),
367 std::find(local_to_global.begin(), local_to_global.end(), global_node_id));
368 rhs.block(uv_ranges(lf), poly_local_basis_id, basis_val[other_local_basis_id].val.size(), 1) += basis_val[other_local_basis_id].val * weight;
369 }
370 }
371 }
372
373 // Compute quadrature points + normalize kernels and collocation points
374 Eigen::MatrixXd NV = triangulated_vertices;
375 // scaling = (NV.colwise().maxCoeff() - NV.colwise().minCoeff()).maxCoeff();
376 // translation = NV.colwise().minCoeff();
377 scaling = 1.0;
378 translation.setZero();
379 // NV = (NV.rowwise() - translation) / scaling;
380 PolyhedronQuadrature::get_quadrature(NV, triangulated_faces, mesh.kernel(element_index),
381 quadrature_order, quadrature);
382 PolyhedronQuadrature::get_quadrature(NV, triangulated_faces, mesh.kernel(element_index),
383 mass_quadrature_order, mass_quadrature);
384
385 // Normalization
386 // collocation_points = (collocation_points.rowwise() - translation) / scaling;
387 // kernel_centers = (kernel_centers.rowwise() - translation) / scaling;
388 // KV = (KV.rowwise() - translation) / scaling;
389
390 triangulated_vertices = KV;
391 triangulated_faces = KF;
392
393 // std::cout << "volume: " << signed_volume(KV, KF) << std::endl;
394 }
395
396 } // anonymous namespace
397
399
400 // Compute the integral constraints for each basis of the mesh
402 const Assembler &assembler,
403 const Mesh3D &mesh,
404 const int n_bases,
405 const std::vector<ElementBases> &bases,
406 const std::vector<ElementBases> &gbases,
407 Eigen::MatrixXd &basis_integrals)
408 {
409 assert(mesh.is_volume());
410
411 basis_integrals.resize(n_bases, 9);
412 basis_integrals.setZero();
413 Eigen::MatrixXd rhs(n_bases, 9);
414 rhs.setZero();
415
416 const int n_elements = mesh.n_elements();
417 for (int e = 0; e < n_elements; ++e)
418 {
419 if (mesh.is_polytope(e))
420 {
421 continue;
422 }
423 // ElementAssemblyValues vals = values[e];
424 // const ElementAssemblyValues &gvals = gvalues[e];
426 vals.compute(e, mesh.is_volume(), bases[e], gbases[e]);
427
428 // Computes the discretized integral of the PDE over the element
429 const int n_local_bases = int(vals.basis_values.size());
430 for (int j = 0; j < n_local_bases; ++j)
431 {
432 const AssemblyValues &v = vals.basis_values[j];
433 const double integral_100 = (v.grad_t_m.col(0).array() * vals.det.array() * vals.quadrature.weights.array()).sum();
434 const double integral_010 = (v.grad_t_m.col(1).array() * vals.det.array() * vals.quadrature.weights.array()).sum();
435 const double integral_001 = (v.grad_t_m.col(2).array() * vals.det.array() * vals.quadrature.weights.array()).sum();
436
437 const double integral_110 = ((vals.val.col(1).array() * v.grad_t_m.col(0).array() + vals.val.col(0).array() * v.grad_t_m.col(1).array()) * vals.det.array() * vals.quadrature.weights.array()).sum();
438 const double integral_011 = ((vals.val.col(2).array() * v.grad_t_m.col(1).array() + vals.val.col(1).array() * v.grad_t_m.col(2).array()) * vals.det.array() * vals.quadrature.weights.array()).sum();
439 const double integral_101 = ((vals.val.col(0).array() * v.grad_t_m.col(2).array() + vals.val.col(2).array() * v.grad_t_m.col(0).array()) * vals.det.array() * vals.quadrature.weights.array()).sum();
440
441 const double integral_200 = 2 * (vals.val.col(0).array() * v.grad_t_m.col(0).array() * vals.det.array() * vals.quadrature.weights.array()).sum();
442 const double integral_020 = 2 * (vals.val.col(1).array() * v.grad_t_m.col(1).array() * vals.det.array() * vals.quadrature.weights.array()).sum();
443 const double integral_002 = 2 * (vals.val.col(2).array() * v.grad_t_m.col(2).array() * vals.det.array() * vals.quadrature.weights.array()).sum();
444
445 const double area = (v.val.array() * vals.det.array() * vals.quadrature.weights.array()).sum();
446
447 for (size_t ii = 0; ii < v.global.size(); ++ii)
448 {
449 basis_integrals(v.global[ii].index, 0) += integral_100 * v.global[ii].val;
450 basis_integrals(v.global[ii].index, 1) += integral_010 * v.global[ii].val;
451 basis_integrals(v.global[ii].index, 2) += integral_001 * v.global[ii].val;
452
453 basis_integrals(v.global[ii].index, 3) += integral_110 * v.global[ii].val;
454 basis_integrals(v.global[ii].index, 4) += integral_011 * v.global[ii].val;
455 basis_integrals(v.global[ii].index, 5) += integral_101 * v.global[ii].val;
456
457 basis_integrals(v.global[ii].index, 6) += integral_200 * v.global[ii].val;
458 basis_integrals(v.global[ii].index, 7) += integral_020 * v.global[ii].val;
459 basis_integrals(v.global[ii].index, 8) += integral_002 * v.global[ii].val;
460
461 rhs(v.global[ii].index, 6) += -2.0 * area * v.global[ii].val;
462 rhs(v.global[ii].index, 7) += -2.0 * area * v.global[ii].val;
463 rhs(v.global[ii].index, 8) += -2.0 * area * v.global[ii].val;
464 }
465 }
466 }
467
468 basis_integrals -= rhs;
469 }
470
471 // -----------------------------------------------------------------------------
472
473 // Distance from harmonic kernels to polygon boundary
474 double compute_epsilon(const Mesh3D &mesh, int e)
475 {
476 // double area = 0;
477 // const int n_edges = mesh.n_element_vertices(e);
478 // for (int i = 0; i < n_edges; ++i) {
479 // const int ip = (i + 1) % n_edges;
480
481 // Eigen::RowVector2d p0 = mesh.point(mesh.vertex_global_index(e, i));
482 // Eigen::RowVector2d p1 = mesh.point(mesh.vertex_global_index(e, ip));
483
484 // Eigen::Matrix2d det_mat;
485 // det_mat.row(0) = p0;
486 // det_mat.row(1) = p1;
487
488 // area += det_mat.determinant();
489 // }
490 // area = std::fabs(area);
491 // // const double eps = use_harmonic ? (0.08*area) : 0;
492 // const double eps = 0.08*area;
493
494 return 0.1; // will be relative to the volume of the poly
495 }
496
497 // -----------------------------------------------------------------------------
498
499 // namespace {
500
501 // void add_spheres(igl::opengl::glfw::Viewer &viewer0, const Eigen::MatrixXd &P, double radius) {
502 // Eigen::MatrixXd V = viewer0.data().V, VS, VN;
503 // Eigen::MatrixXi F = viewer0.data().F, FS;
504 // igl::read_triangle_mesh(POLYFEM_MESH_PATH "sphere.ply", VS, FS);
505
506 // Eigen::RowVector3d minV = VS.colwise().minCoeff();
507 // Eigen::RowVector3d maxV = VS.colwise().maxCoeff();
508 // VS.rowwise() -= minV + 0.5 * (maxV - minV);
509 // VS /= (maxV - minV).maxCoeff();
510 // VS *= 2.0 * radius;
511 // // std::cout << V.colwise().minCoeff() << std::endl;
512 // // std::cout << V.colwise().maxCoeff() << std::endl;
513
514 // Eigen::MatrixXd C = viewer0.data().F_material_ambient.leftCols(3);
515 // C *= 10;
516
517 // int nv = V.rows();
518 // int nf = F.rows();
519 // V.conservativeResize(V.rows() + P.rows() * VS.rows(), V.cols());
520 // F.conservativeResize(F.rows() + P.rows() * FS.rows(), F.cols());
521 // C.conservativeResize(C.rows() + P.rows() * FS.rows(), C.cols());
522 // for (int i = 0; i < P.rows(); ++i) {
523 // V.middleRows(nv, VS.rows()) = VS.rowwise() + P.row(i);
524 // F.middleRows(nf, FS.rows()) = FS.array() + nv;
525 // C.middleRows(nf, FS.rows()).rowwise() = Eigen::RowVector3d(142, 68, 173)/255.;
526 // nv += VS.rows();
527 // nf += FS.rows();
528 // }
529
530 // igl::per_corner_normals(V, F, 20.0, VN);
531
532 // std::cout << C.topRows(10) << std::endl;
533 // std::cout << C.bottomRows(10) << std::endl;
534
535 // igl::opengl::glfw::Viewer viewer;
536 // viewer.data().set_mesh(V, F);
537 // // viewer.data().add_points(P, Eigen::Vector3d(0,1,1).transpose());
538 // viewer.data().set_normals(VN);
539 // viewer.data().set_face_based(false);
540 // viewer.data().set_colors(C);
541 // viewer.data().lines = viewer0.data().lines;
542 // viewer.data().show_lines = false;
543 // viewer.data().line_width = 5;
544 // viewer.core.background_color.setOnes();
545 // viewer.core.set_rotation_type(igl::opengl::ViewerCore::RotationType::ROTATION_TYPE_TRACKBALL);
546
547 // // #ifdef IGL_VIEWER_WITH_NANOGUI
548 // // viewer.callback_init = [&](igl::opengl::glfw::Viewer& viewer_) {
549 // // viewer_.ngui->addButton("Save screenshot", [&] {
550 // // // Allocate temporary buffers
551 // // Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic> R(6400, 4000);
552 // // Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic> G(6400, 4000);
553 // // Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic> B(6400, 4000);
554 // // Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic> A(6400, 4000);
555
556 // // // Draw the scene in the buffers
557 // // viewer_.core.draw_buffer(viewer.data,viewer.opengl,false,R,G,B,A);
558 // // A.setConstant(255);
559
560 // // // Save it to a PNG
561 // // igl::png::writePNG(R,G,B,A,"foo.png");
562 // // });
563 // // viewer_.screen->performLayout();
564 // // return false;
565 // // };
566 // // #endif
567
568 // viewer.launch();
569 // }
570
571 // } // anonymous namespace
572
573 // -----------------------------------------------------------------------------
574
576 const LinearAssembler &assembler,
577 const int nn_samples_per_edge,
578 const Mesh3D &mesh,
579 const int n_bases,
580 const int quadrature_order,
581 const int mass_quadrature_order,
582 const int integral_constraints,
583 std::vector<ElementBases> &bases,
584 const std::vector<ElementBases> &gbases,
585 const std::map<int, InterfaceData> &poly_face_to_data,
586 std::map<int, std::pair<Eigen::MatrixXd, Eigen::MatrixXi>> &mapped_boundary)
587 {
588 assert(mesh.is_volume());
589 if (poly_face_to_data.empty())
590 {
591 return 0;
592 }
593 int n_kernels_per_edge = 4; //(int) std::round(n_samples_per_edge / 3.0);
594 int n_samples_per_edge = 3 * n_kernels_per_edge;
595
596 // Step 1: Compute integral constraints
597 Eigen::MatrixXd basis_integrals;
598 compute_integral_constraints(assembler, mesh, n_bases, bases, gbases, basis_integrals);
599
600 // Step 2: Compute the rest =)
601 for (int e = 0; e < mesh.n_elements(); ++e)
602 {
603 if (!mesh.is_polytope(e))
604 {
605 continue;
606 }
607 // No boundary polytope
608 // assert(element_type[e] != ElementType::BOUNDARY_POLYTOPE);
609
610 // Kernel distance to polygon boundary
611 const double eps = compute_epsilon(mesh, e);
612
613 std::vector<int> local_to_global; // map local basis id (the ones that are nonzero on the polygon boundary) to global basis id
614 Eigen::MatrixXd collocation_points, kernel_centers, triangulated_vertices;
615 Eigen::MatrixXi triangulated_faces;
616 Eigen::MatrixXd rhs; // 1 row per collocation point, 1 column per basis that is nonzero on the polygon boundary
617
618 ElementBases &b = bases[e];
619 b.has_parameterization = false;
620
621 Quadrature tmp_quadrature, tmp_mass_quadrature;
622 double scaling;
623 Eigen::RowVector3d translation;
624 sample_polyhedra(e, 2, n_kernels_per_edge, n_samples_per_edge,
625 quadrature_order > 0 ? quadrature_order : AssemblerUtils::quadrature_order(assembler.name(), 2, AssemblerUtils::BasisType::POLY, 3),
626 mass_quadrature_order > 0 ? mass_quadrature_order : AssemblerUtils::quadrature_order("Mass", 2, AssemblerUtils::BasisType::POLY, 3),
627 mesh, poly_face_to_data, bases, gbases, eps, local_to_global,
628 collocation_points, kernel_centers, rhs, triangulated_vertices,
629 triangulated_faces, tmp_quadrature, tmp_mass_quadrature, scaling, translation);
630
631 b.set_quadrature([tmp_quadrature](Quadrature &quad) { quad = tmp_quadrature; });
632 b.set_mass_quadrature([tmp_mass_quadrature](Quadrature &quad) { quad = tmp_mass_quadrature; });
633 // b.scaling_ = scaling;
634 // b.translation_ = translation;
635
636 // igl::opengl::glfw::Viewer & viewer = UIState::ui_state().viewer;
637 // viewer.data().clear();
638 // viewer.data().set_mesh(triangulated_vertices, triangulated_faces);
639 // viewer.data().add_points(kernel_centers, Eigen::Vector3d(0,1,1).transpose());
640 // add_spheres(viewer, kernel_centers, 0.005);
641
642 // Eigen::MatrixXd pts = triangulated_vertices, normals;
643 // Eigen::MatrixXi tris = triangulated_faces;
644 // igl::per_corner_normals(pts, tris, 20, normals);
645 // viewer.data().set_normals(normals);
646 // viewer.data().set_face_based(false);
647 // viewer.launch();
648
649 // for(int a = 0; rhs.cols();++a)
650 // {
651 // igl::opengl::glfw::Viewer viewer;
652 // Eigen::MatrixXd asd(collocation_points.rows(), 3);
653 // asd.col(0)=collocation_points.col(0);
654 // asd.col(1)=collocation_points.col(1);
655 // asd.col(2)=collocation_points.col(2);
656 // Eigen::VectorXd S = rhs.col(a);
657 // Eigen::MatrixXd C;
658 // igl::colormap(igl::COLOR_MAP_TYPE_VIRIDIS, S, true, C);
659 // viewer.data().add_points(asd, C);
660 // viewer.launch();
661 // }
662
663 // for(int asd = 0; asd < collocation_points.rows(); ++asd) {
664 // viewer.data().add_label(collocation_points.row(asd), std::to_string(asd));
665 // }
666
667 // Compute the weights of the RBF kernels
668 Eigen::MatrixXd local_basis_integrals(rhs.cols(), basis_integrals.cols());
669 for (long k = 0; k < rhs.cols(); ++k)
670 {
671 local_basis_integrals.row(k) = -basis_integrals.row(local_to_global[k]);
672 }
673 auto set_rbf = [&b](auto rbf) {
674 b.set_bases_func([rbf](const Eigen::MatrixXd &uv, std::vector<AssemblyValues> &val) {
675 Eigen::MatrixXd tmp;
676 rbf->bases_values(uv, tmp);
677 val.resize(tmp.cols());
678 assert(tmp.rows() == uv.rows());
679
680 for (size_t i = 0; i < tmp.cols(); ++i)
681 {
682 val[i].val = tmp.col(i);
683 }
684 });
685 b.set_grads_func([rbf](const Eigen::MatrixXd &uv, std::vector<AssemblyValues> &val) {
686 Eigen::MatrixXd tmpx, tmpy, tmpz;
687
688 rbf->bases_grads(0, uv, tmpx);
689 rbf->bases_grads(1, uv, tmpy);
690 rbf->bases_grads(2, uv, tmpz);
691
692 val.resize(tmpx.cols());
693 assert(tmpx.cols() == tmpy.cols());
694 assert(tmpx.cols() == tmpz.cols());
695 assert(tmpx.rows() == uv.rows());
696 for (size_t i = 0; i < tmpx.cols(); ++i)
697 {
698 val[i].grad.resize(uv.rows(), uv.cols());
699 val[i].grad.col(0) = tmpx.col(i);
700 val[i].grad.col(1) = tmpy.col(i);
701 val[i].grad.col(2) = tmpz.col(i);
702 }
703 });
704 };
705 if (integral_constraints == 0)
706 {
707 set_rbf(std::make_shared<RBFWithLinear>(
708 kernel_centers, collocation_points, local_basis_integrals, tmp_quadrature, rhs, false));
709 }
710 else if (integral_constraints == 1)
711 {
712 set_rbf(std::make_shared<RBFWithLinear>(
713 kernel_centers, collocation_points, local_basis_integrals, tmp_quadrature, rhs));
714 }
715 else if (integral_constraints == 2)
716 {
717 set_rbf(std::make_shared<RBFWithQuadratic>(
718 // set_rbf(std::make_shared<RBFWithQuadraticLagrange>(
719 assembler, kernel_centers, collocation_points, local_basis_integrals, tmp_quadrature, rhs));
720 }
721 else
722 {
723 throw std::runtime_error(fmt::format("Unsupported constraint order: {:d}", integral_constraints));
724 }
725
726 // Set the bases which are nonzero inside the polygon
727 const int n_poly_bases = int(local_to_global.size());
728 b.bases.resize(n_poly_bases);
729 for (int i = 0; i < n_poly_bases; ++i)
730 {
731 b.bases[i].init(-2, local_to_global[i], i, Eigen::MatrixXd::Constant(1, 3, std::nan("")));
732 }
733
734 // Polygon boundary after geometric mapping from neighboring elements
735 orient_closed_surface(triangulated_vertices, triangulated_faces, false); // stupid viewer is flipping all the faces
736 mapped_boundary[e].first = triangulated_vertices;
737 mapped_boundary[e].second = triangulated_faces;
738 }
739
740 return 0;
741 }
742 } // namespace basis
743} // namespace polyfem
vector< list< int > > adj
int V
double val
Definition Assembler.cpp:86
ElementAssemblyValues vals
Definition Assembler.cpp:22
Quadrature quadrature
int x
virtual std::string name() const =0
static int quadrature_order(const std::string &assembler, const int basis_degree, const BasisType &b_type, const int dim)
utility for retrieving the needed quadrature order to precisely integrate the given form on the given...
stores per local bases evaluations
std::vector< basis::Local2Global > global
stores per element basis values at given quadrature points and geometric mapping
void compute(const int el_index, const bool is_volume, const Eigen::MatrixXd &pts, const basis::ElementBases &basis, const basis::ElementBases &gbasis)
computes the per element values at the local (ref el) points (pts) sets basis_values,...
assemble matrix based on the local assembler local assembler is eg Laplace, LinearElasticity etc
Stores the basis functions for a given element in a mesh (facet in 2d, cell in 3d).
void set_bases_func(EvalBasesFunc fun)
void set_quadrature(const QuadratureFunction &fun)
void set_grads_func(EvalBasesFunc fun)
std::vector< Basis > bases
one basis function per node in the element
void set_mass_quadrature(const QuadratureFunction &fun)
static Eigen::VectorXi hex_face_local_nodes(const bool serendipity, const int q, const mesh::Mesh3D &mesh, mesh::Navigation3D::Index index)
static int build_bases(const assembler::LinearAssembler &assembler, const int n_samples_per_edge, const mesh::Mesh3D &mesh, const int n_bases, const int quadrature_order, const int mass_quadrature_order, const int integral_constraints, std::vector< ElementBases > &bases, const std::vector< ElementBases > &gbases, const std::map< int, InterfaceData > &poly_face_to_data, std::map< int, std::pair< Eigen::MatrixXd, Eigen::MatrixXi > > &mapped_boundary)
Build bases over the remaining polygons of a mesh.
static void compute_integral_constraints(const assembler::Assembler &assembler, const mesh::Mesh3D &mesh, const int n_bases, const std::vector< ElementBases > &bases, const std::vector< ElementBases > &gbases, Eigen::MatrixXd &basis_integrals)
bool is_volume() const override
checks if mesh is volume
Definition Mesh3D.hpp:28
int n_elements() const
utitlity to return the number of elements, cells or faces in 3d and 2d
Definition Mesh.hpp:158
bool is_polytope(const int el_id) const
checks if element is polygon compatible
Definition Mesh.cpp:361
static void get_quadrature(const Eigen::MatrixXd &V, const Eigen::MatrixXi &F, const Eigen::RowVector3d &kernel, const int order, Quadrature &quadr)
Gets the quadrature points & weights for a polyhedron.
list indices
Definition p_bases.py:232
void q_nodes_3d(const int q, Eigen::MatrixXd &val)
double compute_epsilon(const Mesh2D &mesh, int e)
void sample_surface(const Eigen::MatrixXd &V, const Eigen::MatrixXi &F, int num_samples, Eigen::MatrixXd &P, Eigen::MatrixXd *N=nullptr, int num_lloyd=10, int num_newton=10)
Samples points on a surface.
void orient_closed_surface(const Eigen::MatrixXd &V, Eigen::MatrixXi &F, bool positive=true)
Orient a triangulated surface to have positive volume.
double signed_volume(const Eigen::MatrixXd &V, const Eigen::MatrixXi &F)
Compute the signed volume of a surface mesh.
void reorder_mesh(Eigen::MatrixXd &V, Eigen::MatrixXi &F, const Eigen::VectorXi &C, Eigen::VectorXi &R)
Reorder vertices of a mesh using color tags, so that vertices are ordered by increasing colors.
void signed_squared_distances(const Eigen::MatrixXd &V, const Eigen::MatrixXi &F, const Eigen::MatrixXd &P, Eigen::VectorXd &D)
Computes the signed squared distance from a list of points to a triangle mesh.
bool instantiate_pattern(const Eigen::MatrixXd &IV, const Eigen::MatrixXi &IF, const Eigen::MatrixXd &PV, const Eigen::MatrixXi &PF, Eigen::MatrixXd &OV, Eigen::MatrixXi &OF, Eigen::VectorXi *SF=nullptr, EvalParametersFunc evalFunc=nullptr, GetAdjacentLocalEdge getAdjLocalEdge=nullptr)
std::function< void(const Eigen::MatrixXd &, Eigen::MatrixXd &, int)> EvalParametersFunc
std::function< std::tuple< int, int, bool >(int, int)> GetAdjacentLocalEdge
void regular_2d_grid(const int n, bool tri, Eigen::MatrixXd &V, Eigen::MatrixXi &F)
Generate a canonical triangle/quad subdivided from a regular grid.