PolyFEM
Loading...
Searching...
No Matches
PolygonalBasis2d.cpp
Go to the documentation of this file.
1
3#include "LagrangeBasis2d.hpp"
4
12
14
15#include <random>
16#include <memory>
18
19namespace polyfem
20{
21 using namespace assembler;
22 using namespace mesh;
23 using namespace quadrature;
24
25 namespace basis
26 {
27
28 namespace
29 {
30
31 // -----------------------------------------------------------------------------
32
33 std::vector<int> compute_nonzero_bases_ids(const Mesh2D &mesh, const int element_index, const std::vector<ElementBases> &bases, const std::map<int, InterfaceData> &poly_edge_to_data)
34 {
35 std::vector<int> local_to_global;
36
37 const int n_edges = mesh.n_face_vertices(element_index);
38 Navigation::Index index = mesh.get_index_from_face(element_index);
39 for (int i = 0; i < n_edges; ++i)
40 {
41 const int f2 = mesh.switch_face(index).face;
42 assert(f2 >= 0); // no boundary polygons
43 const InterfaceData &bdata = poly_edge_to_data.at(index.edge);
44 const ElementBases &b = bases[f2];
45 for (int other_local_basis_id : bdata.local_indices)
46 {
47 for (const auto &x : b.bases[other_local_basis_id].global())
48 {
49 const int global_node_id = x.index;
50 local_to_global.push_back(global_node_id);
51 }
52 }
53
54 index = mesh.next_around_face(index);
55 }
56
57 std::sort(local_to_global.begin(), local_to_global.end());
58 auto it = std::unique(local_to_global.begin(), local_to_global.end());
59 local_to_global.resize(std::distance(local_to_global.begin(), it));
60
61 // assert(int(local_to_global.size()) <= n_edges);
62 return local_to_global;
63 }
64
65 // -----------------------------------------------------------------------------
66
67 void sample_parametric_edge(const Mesh2D &mesh, Navigation::Index index, int n_samples, Eigen::MatrixXd &samples)
68 {
69 // Eigen::MatrixXd endpoints = LagrangeBasis2d::linear_quad_edge_local_nodes_coordinates(mesh, index);
70 const auto indices = LagrangeBasis2d::quad_edge_local_nodes(1, mesh, index);
71 assert(mesh.is_cube(index.face));
72 assert(indices.size() == 2);
73 Eigen::MatrixXd tmp;
74 autogen::q_nodes_2d(1, tmp);
75 Eigen::Matrix2d endpoints;
76 endpoints.row(0) = tmp.row(indices(0));
77 endpoints.row(1) = tmp.row(indices(1));
78 const Eigen::VectorXd t = Eigen::VectorXd::LinSpaced(n_samples, 0, 1);
79 samples.resize(n_samples, endpoints.cols());
80 for (int c = 0; c < 2; ++c)
81 {
82 samples.col(c) = (1.0 - t.array()).matrix() * endpoints(0, c) + t * endpoints(1, c);
83 }
84 }
85
86 // -----------------------------------------------------------------------------
87
88 void compute_offset_kernels(const Eigen::MatrixXd &polygon, int n_kernels, double eps, Eigen::MatrixXd &kernel_centers)
89 {
90 Eigen::MatrixXd offset, samples;
91 std::vector<bool> inside;
92 offset_polygon(polygon, offset, eps);
93 sample_polygon(offset, n_kernels, samples);
94 int n_inside = is_inside(polygon, samples, inside);
95 assert(n_inside == 0);
96 kernel_centers = samples;
97 // kernel_centers.resize(samples.rows() - n_inside, samples.cols());
98 // for (int i = 0, j = 0; i < samples.rows(); ++i) {
99 // if (!inside[i]) {
100 // kernel_centers.row(j++) = samples.row(i);
101 // }
102 // }
103
104 // igl::opengl::glfw::Viewer &viewer = UIState::ui_state().viewer;
105 // viewer.data().add_points(samples, Eigen::Vector3d(0,1,1).transpose());
106 }
107
108 // -----------------------------------------------------------------------------
109
111 void sample_polygon(const int element_index, const int n_samples_per_edge, const Mesh2D &mesh, const std::map<int, InterfaceData> &poly_edge_to_data,
112 const std::vector<ElementBases> &bases, const std::vector<ElementBases> &gbases, const double eps, std::vector<int> &local_to_global,
113 Eigen::MatrixXd &collocation_points, Eigen::MatrixXd &kernel_centers, Eigen::MatrixXd &rhs)
114 {
115 const int n_edges = mesh.n_face_vertices(element_index);
116
117 const int n_kernel_per_edges = (n_samples_per_edge - 1) / 3;
118 const int n_collocation_points = (n_samples_per_edge - 1) * n_edges;
119 const int n_kernels = n_kernel_per_edges * n_edges;
120
121 // Local ids of nonzero bases over the polygon
122 local_to_global = compute_nonzero_bases_ids(mesh, element_index, bases, poly_edge_to_data);
123
124 collocation_points.resize(n_collocation_points, 2);
125 collocation_points.setZero();
126
127 rhs.resize(n_collocation_points, local_to_global.size());
128 rhs.setZero();
129
130 Eigen::MatrixXd samples, mapped;
131 std::vector<AssemblyValues> basis_val;
132 auto index = mesh.get_index_from_face(element_index);
133 for (int i = 0; i < n_edges; ++i)
134 {
135 const int f2 = mesh.switch_face(index).face;
136 assert(f2 >= 0); // no boundary polygons
137
138 const InterfaceData &bdata = poly_edge_to_data.at(index.edge);
139 const ElementBases &b = bases[f2];
140 const ElementBases &gb = gbases[f2];
141
142 // Sample collocation points on the boundary edge
143 sample_parametric_edge(mesh, mesh.switch_face(index), n_samples_per_edge, samples);
144 samples.conservativeResize(samples.rows() - 1, samples.cols());
145 gb.eval_geom_mapping(samples, mapped);
146 assert(mapped.rows() == (n_samples_per_edge - 1));
147 collocation_points.block(i * (n_samples_per_edge - 1), 0, mapped.rows(), mapped.cols()) = mapped;
148
149 b.evaluate_bases(samples, basis_val);
150 // Evaluate field basis and set up the rhs
151 for (int other_local_basis_id : bdata.local_indices)
152 {
153 // b.bases[other_local_basis_id].basis(samples, basis_val);
154
155 for (const auto &x : b.bases[other_local_basis_id].global())
156 {
157 const int global_node_id = x.index;
158 const double weight = x.val;
159
160 const int poly_local_basis_id = std::distance(local_to_global.begin(), std::find(local_to_global.begin(), local_to_global.end(), global_node_id));
161 rhs.block(i * (n_samples_per_edge - 1), poly_local_basis_id, basis_val[other_local_basis_id].val.size(), 1) += basis_val[other_local_basis_id].val * weight;
162 }
163 }
164
165 index = mesh.next_around_face(index);
166 }
167
168 compute_offset_kernels(collocation_points, n_kernels, eps, kernel_centers);
169 }
170
171 } // anonymous namespace
172
174
175 // Compute the integral constraints for each basis of the mesh
176 void PolygonalBasis2d::compute_integral_constraints(const LinearAssembler &assembler, const Mesh2D &mesh, const int n_bases,
177 const std::vector<ElementBases> &bases, const std::vector<ElementBases> &gbases, Eigen::MatrixXd &basis_integrals)
178 {
179 // TODO
180 const double t = 0;
181 assert(!mesh.is_volume());
182
183 const int dim = assembler.is_tensor() ? 2 : 1;
184
185 basis_integrals.resize(n_bases, RBFWithQuadratic::index_mapping(dim - 1, dim - 1, 4, dim) + 1);
186 basis_integrals.setZero();
187
188 std::array<Eigen::MatrixXd, 5> strong;
189
190 const int n_elements = mesh.n_elements();
191 for (int e = 0; e < n_elements; ++e)
192 {
193 if (mesh.is_polytope(e))
194 {
195 continue;
196 }
197 // ElementAssemblyValues vals = values[e];
198 // const ElementAssemblyValues &gvals = gvalues[e];
200 vals.compute(e, false, bases[e], gbases[e]);
201
202 const auto &quadr = vals.quadrature;
203 const QuadratureVector da = vals.det.array() * quadr.weights.array();
204
205 // Computes the discretized integral of the PDE over the element
206 const int n_local_bases = int(vals.basis_values.size());
207
208 // add monomials
209 vals.basis_values.resize(n_local_bases + 5);
211 RBFWithQuadratic::setup_monomials_strong_2d(dim, assembler, vals.val, da, strong);
212
213 for (int j = 0; j < n_local_bases; ++j)
214 {
215 const AssemblyValues &v = vals.basis_values[j];
216
217 for (int d = 0; d < 5; ++d)
218 {
219 const auto tmp = assembler.assemble(LinearAssemblerData(vals, t, n_local_bases + d, j, da));
220
221 for (size_t ii = 0; ii < v.global.size(); ++ii)
222 {
223 for (int alpha = 0; alpha < dim; ++alpha)
224 {
225 for (int beta = 0; beta < dim; ++beta)
226 {
227 const int loc_index = alpha * dim + beta;
228 const int r = RBFWithQuadratic::index_mapping(alpha, beta, d, dim);
229
230 basis_integrals(v.global[ii].index, r) += tmp(loc_index) + (strong[d].row(loc_index).transpose().array() * v.val.array()).sum();
231 }
232 }
233 }
234 }
235 }
236 }
237 }
238
239 // -----------------------------------------------------------------------------
240
241 // Distance from harmonic kernels to polygon boundary
242 double compute_epsilon(const Mesh2D &mesh, int e)
243 {
244 double area = 0;
245 const int n_edges = mesh.n_face_vertices(e);
247
248 for (int i = 0; i < n_edges; ++i)
249 {
250 Eigen::Matrix2d det_mat;
251 det_mat.row(0) = mesh.point(index.vertex);
252 det_mat.row(1) = mesh.point(mesh.switch_vertex(index).vertex);
253
254 area += det_mat.determinant();
255
256 index = mesh.next_around_face(index);
257 }
258 area = std::fabs(area);
259 // const double eps = use_harmonic ? (0.08*area) : 0;
260 const double eps = 0.08 * area;
261
262 return eps;
263 }
264
265 // namespace {
266
267 // void add_spheres(igl::opengl::glfw::Viewer &viewer0, const Eigen::MatrixXd &PP, double radius) {
268 // Eigen::MatrixXd V = viewer0.data().V, VS, VN;
269 // Eigen::MatrixXi F = viewer0.data().F, FS;
270 // igl::read_triangle_mesh(POLYFEM_MESH_PATH "sphere.ply", VS, FS);
271
272 // Eigen::RowVector3d minV = VS.colwise().minCoeff();
273 // Eigen::RowVector3d maxV = VS.colwise().maxCoeff();
274 // VS.rowwise() -= minV + 0.5 * (maxV - minV);
275 // VS /= (maxV - minV).maxCoeff();
276 // VS *= 2.0 * radius;
277 // std::cout << V.colwise().minCoeff() << std::endl;
278 // std::cout << V.colwise().maxCoeff() << std::endl;
279
280 // Eigen::MatrixXd C = viewer0.data().F_material_ambient.leftCols(3);
281 // C *= 10 / 2.0;
282
283 // Eigen::MatrixXd P(PP.rows(), 3);
284 // P.leftCols(2) = PP;
285 // P.col(2).setZero();
286
287 // int nv = V.rows();
288 // int nf = F.rows();
289 // V.conservativeResize(V.rows() + P.rows() * VS.rows(), V.cols());
290 // F.conservativeResize(F.rows() + P.rows() * FS.rows(), F.cols());
291 // C.conservativeResize(C.rows() + P.rows() * FS.rows(), C.cols());
292 // for (int i = 0; i < P.rows(); ++i) {
293 // V.middleRows(nv, VS.rows()) = VS.rowwise() + P.row(i);
294 // F.middleRows(nf, FS.rows()) = FS.array() + nv;
295 // C.middleRows(nf, FS.rows()).rowwise() = Eigen::RowVector3d(142, 68, 173)/255.;
296 // nv += VS.rows();
297 // nf += FS.rows();
298 // }
299
300 // igl::per_corner_normals(V, F, 20.0, VN);
301
302 // igl::opengl::glfw::Viewer viewer;
303 // viewer.data().set_mesh(V, F);
304 // // viewer.data().add_points(P, Eigen::Vector3d(0,1,1).transpose());
305 // viewer.data().set_normals(VN);
306 // viewer.data().set_face_based(false);
307 // viewer.data().set_colors(C);
308 // viewer.data().lines = viewer0.data().lines;
309 // viewer.data().show_lines = false;
310 // viewer.data().line_width = 5;
311 // viewer.core.background_color.setOnes();
312 // viewer.core.set_rotation_type(igl::opengl::ViewerCore::RotationType::ROTATION_TYPE_TRACKBALL);
313
314 // // #ifdef IGL_VIEWER_WITH_NANOGUI
315 // // viewer.callback_init = [&](igl::opengl::glfw::Viewer& viewer_) {
316 // // viewer_.ngui->addButton("Save screenshot", [&] {
317 // // // Allocate temporary buffers
318 // // Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic> R(6400, 4000);
319 // // Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic> G(6400, 4000);
320 // // Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic> B(6400, 4000);
321 // // Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic> A(6400, 4000);
322
323 // // // Draw the scene in the buffers
324 // // viewer_.core.draw_buffer(viewer.data,viewer.opengl,false,R,G,B,A);
325 // // A.setConstant(255);
326
327 // // // Save it to a PNG
328 // // igl::png::writePNG(R,G,B,A,"foo.png");
329 // // });
330 // // viewer_.screen->performLayout();
331 // // return false;
332 // // };
333 // // #endif
334
335 // viewer.data().F_material_specular.setZero();
336 // viewer.data().V_material_specular.setZero();
337 // viewer.data().dirty |= igl::opengl::MeshGL::DIRTY_DIFFUSE;
338 // viewer.data().V_material_ambient *= 2;
339 // viewer.data().F_material_ambient *= 2;
340
341 // viewer.core.align_camera_center(V);
342 // viewer.launch();
343 // }
344
345 // } // anonymous namespace
346 // -----------------------------------------------------------------------------
347
348 int PolygonalBasis2d::build_bases(const LinearAssembler &assembler, const int n_samples_per_edge, const Mesh2D &mesh, const int n_bases,
349 const int quadrature_order, const int mass_quadrature_order, const int integral_constraints, std::vector<ElementBases> &bases, const std::vector<ElementBases> &gbases,
350 const std::map<int, InterfaceData> &poly_edge_to_data, std::map<int, Eigen::MatrixXd> &mapped_boundary)
351 {
352 assert(!mesh.is_volume());
353 if (poly_edge_to_data.empty())
354 {
355 return 0;
356 }
357
358 const int dim = assembler.is_tensor() ? 2 : 1;
359
360 // Step 1: Compute integral constraints
361 Eigen::MatrixXd basis_integrals;
362 compute_integral_constraints(assembler, mesh, n_bases, bases, gbases, basis_integrals);
363
364 // Step 2: Compute the rest =)
365 PolygonQuadrature poly_quadr;
366 for (int e = 0; e < mesh.n_elements(); ++e)
367 {
368 if (!mesh.is_polytope(e))
369 {
370 continue;
371 }
372 // No boundary polytope
373 // assert(element_type[e] != ElementType::BOUNDARY_POLYTOPE);
374
375 // Kernel distance to polygon boundary
376 const double eps = compute_epsilon(mesh, e);
377
378 std::vector<int> local_to_global; // map local basis id (the ones that are nonzero on the polygon boundary) to global basis id
379 Eigen::MatrixXd collocation_points, kernel_centers;
380 Eigen::MatrixXd rhs; // 1 row per collocation point, 1 column per basis that is nonzero on the polygon boundary
381
382 sample_polygon(e, n_samples_per_edge, mesh, poly_edge_to_data, bases, gbases, eps, local_to_global, collocation_points, kernel_centers, rhs);
383
384 // igl::opengl::glfw::Viewer viewer;
385 // viewer.data().add_points(kernel_centers, Eigen::Vector3d(0,1,1).transpose());
386
387 // Eigen::MatrixXd asd(collocation_points.rows(), 3);
388 // asd.col(0)=collocation_points.col(0);
389 // asd.col(1)=collocation_points.col(1);
390 // asd.col(2)=rhs.col(0);
391 // viewer.data().add_points(asd, Eigen::Vector3d(1,0,1).transpose());
392
393 // for(int asd = 0; asd < collocation_points.rows(); ++asd) {
394 // viewer.data().add_label(collocation_points.row(asd), std::to_string(asd));
395 // }
396
397 // viewer.launch();
398
399 // igl::opengl::glfw::Viewer & viewer = UIState::ui_state().viewer;
400 // viewer.data().clear();
401 // viewer.data().set_mesh(triangulated_vertices, triangulated_faces);
402 // viewer.data().add_points(kernel_centers, Eigen::Vector3d(0,1,1).transpose());
403 // add_spheres(viewer, kernel_centers, 0.01);
404
405 ElementBases &b = bases[e];
406 b.has_parameterization = false;
407
408 // Compute quadrature points for the polygon
409 Quadrature tmp_quadrature;
410 poly_quadr.get_quadrature(collocation_points, quadrature_order > 0 ? quadrature_order : AssemblerUtils::quadrature_order(assembler.name(), 2, AssemblerUtils::BasisType::POLY, 2), tmp_quadrature);
411
412 Quadrature tmp_mass_quadrature;
413 poly_quadr.get_quadrature(collocation_points, mass_quadrature_order > 0 ? mass_quadrature_order : AssemblerUtils::quadrature_order("Mass", 2, AssemblerUtils::BasisType::POLY, 2), tmp_mass_quadrature);
414
415 b.set_quadrature([tmp_quadrature](Quadrature &quad) { quad = tmp_quadrature; });
416 b.set_mass_quadrature([tmp_mass_quadrature](Quadrature &quad) { quad = tmp_mass_quadrature; });
417
418 // Compute the weights of the harmonic kernels
419 Eigen::MatrixXd local_basis_integrals(rhs.cols(), basis_integrals.cols());
420 for (long k = 0; k < rhs.cols(); ++k)
421 {
422 local_basis_integrals.row(k) = -basis_integrals.row(local_to_global[k]);
423 }
424 auto set_rbf = [&b](auto rbf) {
425 b.set_bases_func([rbf](const Eigen::MatrixXd &uv, std::vector<AssemblyValues> &val) {
426 Eigen::MatrixXd tmp;
427 rbf->bases_values(uv, tmp);
428 val.resize(tmp.cols());
429 assert(tmp.rows() == uv.rows());
430
431 for (size_t i = 0; i < tmp.cols(); ++i)
432 {
433 val[i].val = tmp.col(i);
434 }
435 });
436 b.set_grads_func([rbf](const Eigen::MatrixXd &uv, std::vector<AssemblyValues> &val) {
437 Eigen::MatrixXd tmpx, tmpy;
438
439 rbf->bases_grads(0, uv, tmpx);
440 rbf->bases_grads(1, uv, tmpy);
441
442 val.resize(tmpx.cols());
443 assert(tmpx.cols() == tmpy.cols());
444 assert(tmpx.rows() == uv.rows());
445 for (size_t i = 0; i < tmpx.cols(); ++i)
446 {
447 val[i].grad.resize(uv.rows(), uv.cols());
448 val[i].grad.col(0) = tmpx.col(i);
449 val[i].grad.col(1) = tmpy.col(i);
450 }
451 });
452 };
453 if (integral_constraints == 0)
454 {
455 set_rbf(std::make_shared<RBFWithLinear>(kernel_centers, collocation_points, local_basis_integrals, tmp_quadrature, rhs, false));
456 }
457 else if (integral_constraints == 1)
458 {
459 set_rbf(std::make_shared<RBFWithLinear>(kernel_centers, collocation_points, local_basis_integrals, tmp_quadrature, rhs));
460 }
461 else if (integral_constraints == 2)
462 {
463 set_rbf(std::make_shared<RBFWithQuadraticLagrange>(assembler, kernel_centers, collocation_points, local_basis_integrals, tmp_quadrature, rhs));
464 }
465 else
466 {
467 throw std::runtime_error(fmt::format("Unsupported constraint order: {:d}", integral_constraints));
468 }
469
470 // Set the bases which are nonzero inside the polygon
471 const int n_poly_bases = int(local_to_global.size());
472 b.bases.resize(n_poly_bases);
473 for (int i = 0; i < n_poly_bases; ++i)
474 {
475 b.bases[i].init(-2, local_to_global[i], i, Eigen::MatrixXd::Constant(1, 2, std::nan("")));
476 }
477
478 // Polygon boundary after geometric mapping from neighboring elements
479 mapped_boundary[e] = collocation_points;
480 }
481
482 return 0;
483 }
484 } // namespace basis
485} // namespace polyfem
double val
Definition Assembler.cpp:86
QuadratureVector da
Definition Assembler.cpp:23
ElementAssemblyValues vals
Definition Assembler.cpp:22
Quadrature quadrature
int x
virtual bool is_tensor() const
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
void assemble(const bool is_volume, const int n_basis, const std::vector< basis::ElementBases > &bases, const std::vector< basis::ElementBases > &gbases, const AssemblyValsCache &cache, const double t, StiffnessMatrix &stiffness, const bool is_mass=false) const override
assembles the stiffness matrix for the given basis the bilinear form (local assembler) is encoded by ...
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 quad_edge_local_nodes(const int q, const mesh::Mesh2D &mesh, mesh::Navigation::Index index)
static void compute_integral_constraints(const assembler::LinearAssembler &assembler, const mesh::Mesh2D &mesh, const int n_bases, const std::vector< ElementBases > &bases, const std::vector< ElementBases > &gbases, Eigen::MatrixXd &basis_integrals)
static int build_bases(const assembler::LinearAssembler &assembler, const int n_samples_per_edge, const mesh::Mesh2D &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_edge_to_data, std::map< int, Eigen::MatrixXd > &mapped_boundary)
Build bases over the remaining polygons of a mesh.
static void setup_monomials_strong_2d(const int dim, const assembler::LinearAssembler &assembler, const Eigen::MatrixXd &pts, const QuadratureVector &da, std::array< Eigen::MatrixXd, 5 > &strong)
static void setup_monomials_vals_2d(const int star_index, const Eigen::MatrixXd &pts, assembler::ElementAssemblyValues &vals)
static int index_mapping(const int alpha, const int beta, const int d, const int ass_dim)
Navigation::Index next_around_face(Navigation::Index idx) const
Definition Mesh2D.hpp:61
bool is_volume() const override
checks if mesh is volume
Definition Mesh2D.hpp:25
virtual Navigation::Index switch_vertex(Navigation::Index idx) const =0
virtual Navigation::Index get_index_from_face(int f, int lv=0) const =0
int n_elements() const
utitlity to return the number of elements, cells or faces in 3d and 2d
Definition Mesh.hpp:161
bool is_polytope(const int el_id) const
checks if element is polygon compatible
Definition Mesh.cpp:365
virtual RowVectorNd point(const int global_index) const =0
point coordinates
virtual int n_face_vertices(const int f_id) const =0
number of vertices of a face
void get_quadrature(const Eigen::MatrixXd &poly, const int order, Quadrature &quadr)
Gets the quadrature points & weights for a polygon.
list tmp
Definition p_bases.py:339
list indices
Definition p_bases.py:232
void q_nodes_2d(const int q, Eigen::MatrixXd &val)
double compute_epsilon(const Mesh2D &mesh, int e)
void sample_polygon(const Eigen::MatrixXd &IV, int num_samples, Eigen::MatrixXd &S)
Sample points on a polygon, evenly spaced from each other.
void offset_polygon(const Eigen::MatrixXd &IV, Eigen::MatrixXd &OV, double eps)
Compute offset polygon.
int is_inside(const Eigen::MatrixXd &IV, const Eigen::MatrixXd &Q, std::vector< bool > &inside)
Compute whether points are inside a polygon.
Eigen::Matrix< double, Eigen::Dynamic, 1, 0, MAX_QUAD_POINTS, 1 > QuadratureVector
Definition Types.hpp:17