11#include <SimpleBVH/BVH.hpp>
13#include <igl/barycentric_coordinates.h>
22 using RowMajorMatrixX = Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
28 Eigen::MatrixXd uv_to_uvw(
const Eigen::MatrixXd &uv,
const int local_fid)
30 assert(uv.cols() == 2);
32 Eigen::MatrixXd uvw = Eigen::MatrixXd::Zero(uv.rows(), 3);
37 uvw.col(0) = uv.col(1);
38 uvw.col(1) = uv.col(0);
41 uvw.col(0) = uv.col(0);
42 uvw.col(2) = uv.col(1);
46 uvw.col(2) = 1 - uv.col(0).array() - uv.col(1).array();
49 uvw.col(1) = uv.col(1);
50 uvw.col(2) = uv.col(0);
58 Eigen::MatrixXd extract_face_vertices(
59 const basis::ElementBases &element,
const int local_fid)
61 Eigen::MatrixXd UV(3, 2);
66 const Eigen::MatrixXd UVW = uv_to_uvw(UV, local_fid);
69 element.eval_geom_mapping(UVW,
V);
76 const std::vector<basis::ElementBases> &bases,
77 const std::vector<basis::ElementBases> &geom_bases,
78 const std::vector<LocalBoundary> &total_local_boundary,
82 Eigen::MatrixXd &proxy_vertices,
83 Eigen::MatrixXi &proxy_faces,
84 std::vector<Eigen::Triplet<double>> &displacement_map_entries,
99 std::vector<double> proxy_vertices_list;
100 std::vector<int> proxy_faces_list;
101 std::vector<Eigen::Triplet<double>> displacement_map_entries_tmp;
104 Eigen::MatrixXi F_local;
111 for (
const LocalBoundary &local_boundary : total_local_boundary)
118 for (
int fi = 0; fi < local_boundary.size(); fi++)
120 const int local_fid = local_boundary.local_primitive_id(fi);
125 const Eigen::MatrixXd node_positions = extract_face_vertices(g, local_fid);
127 node_positions.row(0), node_positions.row(1), node_positions.row(2),
132 Eigen::MatrixXd UVW = uv_to_uvw(UV, local_fid);
134 Eigen::MatrixXd V_local;
135 g.eval_geom_mapping(UVW, V_local);
136 assert(V_local.rows() == UV.rows());
138 const int offset = proxy_vertices_list.size() / dim;
139 for (
const double x : V_local.reshaped<Eigen::RowMajor>())
140 proxy_vertices_list.push_back(
x);
141 for (
const int i : F_local.reshaped<Eigen::RowMajor>())
142 proxy_faces_list.push_back(i + offset);
146 assert(basis.global().size() == 1);
147 const int basis_id = basis.global()[0].index;
149 const Eigen::MatrixXd basis_values = basis(UVW);
151 for (
int i = 0; i < basis_values.size(); i++)
153 displacement_map_entries_tmp.emplace_back(
154 offset + i, basis_id, basis_values(i));
162 Eigen::Map<RowMajorMatrixX<double>>(proxy_vertices_list.data(), proxy_vertices_list.size() / dim, dim),
163 Eigen::Map<RowMajorMatrixX<int>>(proxy_faces_list.data(), proxy_faces_list.size() / dim, dim),
164 displacement_map_entries_tmp,
165 proxy_vertices, proxy_faces, displacement_map_entries);
171 const std::vector<basis::ElementBases> &bases,
172 const std::vector<basis::ElementBases> &geom_bases,
173 const std::vector<mesh::LocalBoundary> &total_local_boundary,
176 const Eigen::MatrixXd &proxy_vertices,
177 std::vector<Eigen::Triplet<double>> &displacement_map_entries)
190 if (basis.order() != 1)
191 log_and_throw_error(
"build_collision_proxy_displacement_map() is only implemented for P1 geometry!");
197 std::vector<std::array<Eigen::Vector3d, 2>> boxes(
198 geom_bases.size(), {{Eigen::Vector3d::Zero(), Eigen::Vector3d::Zero()}});
199 for (
int i = 0; i < geom_bases.size(); i++)
201 const Eigen::MatrixXd nodes = geom_bases[i].nodes();
202 boxes[i][0].head(dim) = nodes.colwise().minCoeff();
203 boxes[i][1].head(dim) = nodes.colwise().maxCoeff();
250 for (
int i = 0; i < proxy_vertices.rows(); i++)
252 Eigen::Vector3d v = Eigen::Vector3d::Zero();
253 v.head(dim) = proxy_vertices.row(i);
255 std::vector<unsigned int> candidates;
256 bvh.intersect_box(v, v, candidates);
259 int closest_element_id = -1;
261 for (
const unsigned int element_id : candidates)
263 const Eigen::MatrixXd nodes = geom_bases[element_id].nodes();
266 assert(nodes.rows() == 3);
267 Eigen::RowVector3d bc;
268 igl::barycentric_coordinates(
269 proxy_vertices.row(i), nodes.row(0), nodes.row(1), nodes.row(2), bc);
274 assert(dim == 3 &&
nodes.rows() == 4);
275 Eigen::RowVector4d bc;
276 igl::barycentric_coordinates(
280 if (vhat.minCoeff() >= 0 && vhat.maxCoeff() <= 1 && vhat.sum() <= 1)
282 closest_element_id = element_id;
287 if (closest_element_id < 0)
290 log_and_throw_error(
"build_collision_proxy_displacement_map(): closest point query not implemented!");
306 for (
const basis::Basis &basis : bases[closest_element_id].bases)
308 assert(
basis.global().size() == 1);
309 const int j =
basis.global()[0].index;
310 displacement_map_entries.emplace_back(i, j,
basis(vhat.transpose())(0));
319 const std::string &mesh_filename,
320 const std::string &weights_filename,
321 const Eigen::VectorXi &in_node_to_node,
322 const json &transformation,
323 Eigen::MatrixXd &vertices,
324 Eigen::VectorXi &codim_vertices,
325 Eigen::MatrixXi &edges,
326 Eigen::MatrixXi &
faces,
327 std::vector<Eigen::Triplet<double>> &displacement_map_entries)
335 const std::string &mesh_filename,
336 const json &transformation,
337 Eigen::MatrixXd &vertices,
338 Eigen::VectorXi &codim_vertices,
339 Eigen::MatrixXi &edges,
340 Eigen::MatrixXi &
faces)
343 vertices = std::move(surface.
vertices);
344 codim_vertices = std::move(surface.
points);
345 Eigen::MatrixXi codim_edges = std::move(surface.
edges);
349 igl::edges(
faces, edges);
351 utils::append_rows(edges, codim_edges);
354 std::array<RowVectorNd, 2> bbox;
355 bbox[0] = vertices.colwise().minCoeff();
356 bbox[1] = vertices.colwise().maxCoeff();
358 if (vertices.cols() > 2 && bbox[0][2] == bbox[1][2] && bbox[0][2] == 0)
360 vertices = vertices.leftCols(2).eval();
361 bbox[0] = vertices.colwise().minCoeff();
362 bbox[1] = vertices.colwise().maxCoeff();
368 GeometryLoader::construct_affine_transformation(
370 (bbox[1] - bbox[0]).cwiseAbs().transpose(),
372 vertices = (vertices * A.transpose()).rowwise() + b.transpose();
377 const std::string &weights_filename,
378 const Eigen::VectorXi &in_node_to_node,
379 const size_t num_proxy_vertices,
380 std::vector<Eigen::Triplet<double>> &displacement_map_entries)
383 const size_t num_fe_nodes = in_node_to_node.size();
385 auto read_group = [&](
const io::ResourceIO &reader,
const std::string &root) {
386 const auto child = [&](
const std::string &path) {
387 return (std::filesystem::path(root) / path).lexically_normal().generic_string();
389 const std::array<long, 2> shape = reader.
read_shape_attribute(child(
"weight_triplets"),
"shape");
390 assert(shape[0] == num_proxy_vertices && shape[1] == num_fe_nodes);
391 const std::vector<double> value_data = reader.
read_double_vector(child(
"weight_triplets/values"));
392 const std::vector<int> row_data = reader.
read_int_vector(child(
"weight_triplets/rows"));
393 const std::vector<int> col_data = reader.
read_int_vector(child(
"weight_triplets/cols"));
394 Eigen::Map<const Eigen::VectorXd> values(value_data.data(), value_data.size());
395 Eigen::Map<const Eigen::VectorXi> rows(row_data.data(), row_data.size());
396 Eigen::Map<const Eigen::VectorXi> cols(col_data.data(), col_data.size());
397 assert(rows.maxCoeff() < num_proxy_vertices);
398 assert(cols.maxCoeff() < num_fe_nodes);
402 displacement_map_entries.clear();
403 displacement_map_entries.reserve(values.size());
405 assert(in_node_to_node.size() == num_fe_nodes);
406 for (
int i = 0; i < values.size(); i++)
409 displacement_map_entries.emplace_back(rows[i], in_node_to_node[cols[i]], values[i]);
413 if (resources.
is_group(weights_filename))
414 read_group(resources, weights_filename);
std::vector< Eigen::VectorXi > faces
Represents one basis function and its gradient.
Stores the basis functions for a given element in a mesh (facet in 2d, cell in 3d).
std::vector< Basis > bases
one basis function per node in the element
HDF5 logical paths always use POSIX separators and roots.
Read-only interface to a hierarchical collection of simulation resources.
virtual std::array< long, 2 > read_shape_attribute(const std::string &path, const std::string &name) const =0
virtual std::vector< int > read_int_vector(const std::string &path) const =0
virtual std::filesystem::path materialize(const std::string &path) const =0
virtual std::vector< double > read_double_vector(const std::string &path) const =0
virtual bool is_group(const std::string &path) const =0
Boundary primitive IDs for a single element.
The only non-legacy decoder from a logical resource path to mesh data.
SurfaceMesh load_surface(const std::string &path) const
void build_collision_proxy(const std::vector< basis::ElementBases > &bases, const std::vector< basis::ElementBases > &geom_bases, const std::vector< LocalBoundary > &total_local_boundary, const int n_bases, const int dim, const double max_edge_length, Eigen::MatrixXd &proxy_vertices, Eigen::MatrixXi &proxy_faces, std::vector< Eigen::Triplet< double > > &displacement_map_entries, const CollisionProxyTessellation tessellation)
void load_collision_proxy_displacement_map(const io::ResourceIO &resources, const std::string &weights_filename, const Eigen::VectorXi &in_node_to_node, const size_t num_proxy_vertices, std::vector< Eigen::Triplet< double > > &displacement_map_entries)
Load a collision proxy displacement map from a resource.
void load_collision_proxy_mesh(const io::ResourceIO &resources, const std::string &mesh_filename, const json &transformation, Eigen::MatrixXd &vertices, Eigen::VectorXi &codim_vertices, Eigen::MatrixXi &edges, Eigen::MatrixXi &faces)
Load a collision proxy mesh from a resource.
void load_collision_proxy(const io::ResourceIO &resources, const std::string &mesh_filename, const std::string &weights_filename, const Eigen::VectorXi &in_node_to_node, const json &transformation, Eigen::MatrixXd &vertices, Eigen::VectorXi &codim_vertices, Eigen::MatrixXi &edges, Eigen::MatrixXi &faces, std::vector< Eigen::Triplet< double > > &displacement_map_entries)
Load a collision proxy mesh and displacement map from resources.
void build_collision_proxy_displacement_map(const std::vector< basis::ElementBases > &bases, const std::vector< basis::ElementBases > &geom_bases, const std::vector< mesh::LocalBoundary > &total_local_boundary, const int n_bases, const int dim, const Eigen::MatrixXd &proxy_vertices, std::vector< Eigen::Triplet< double > > &displacement_map_entries)
Build a collision proxy displacement map for a given mesh and proxy mesh.
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.
CollisionProxyTessellation
@ IRREGULAR
Irregular tessellation of the mesh (requires POLYFEM_WITH_TRIANGLE)
@ REGULAR
Regular tessellation of the mesh.
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< double, Eigen::Dynamic, 1, 0, 3, 1 > VectorNd
Eigen::Matrix< double, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor, 3, 3 > MatrixNd
void log_and_throw_error(const std::string &msg)