14#include <unordered_map>
16#include <lagrange/common.h>
17#include <lagrange/compute_facet_area.h>
18#include <lagrange/internal/constants.h>
19#include <lagrange/utils/assert.h>
20#include <lagrange/utils/range.h>
35template <
typename MeshType>
39 using Index =
typename MeshType::Index;
40 using IndexList =
typename MeshType::IndexList;
41 using VertexArray =
typename MeshType::VertexArray;
43 using BarycentricArray = Eigen::Matrix<
44 typename VertexArray::Scalar,
45 VertexArray::RowsAtCompileTime,
47 VertexArray::Options>;
50 Index num_samples = 0;
52 IndexList facet_ids = IndexList(0);
54 VertexArray positions = VertexArray();
56 BarycentricArray barycentrics = BarycentricArray();
69template <
typename MeshType>
72 const typename MeshType::Index approx_num_points,
73 const typename MeshType::IndexList& active_facets)
76 using Index =
typename MeshType::Index;
77 using Scalar =
typename MeshType::Scalar;
78 using VertexType =
typename MeshType::VertexType;
82 using RowVectorS = Eigen::Matrix<Scalar, 1, Eigen::Dynamic>;
83 using RowVector3S = Eigen::Matrix<Scalar, 1, 3>;
84 using RowVectorI = Eigen::Matrix<Index, 1, Eigen::Dynamic>;
85 using BoundingBox = Eigen::AlignedBox<Scalar, Eigen::Dynamic>;
88 la_runtime_assert(mesh.get_vertex_per_facet() == 3,
"only works for triangle meshes");
91 for (
const auto facet_id : active_facets) {
97 if (!mesh.has_facet_attribute(
"area")) {
100 const auto& facet_area = mesh.get_facet_attribute(
"area");
101 const auto& facets = mesh.get_facets();
102 const auto& vertices = mesh.get_vertices();
105 const Index n_dims = mesh.get_dim();
110 Scalar total_mesh_area = 0;
112 total_mesh_area += facet_area(facet_id);
122 const Scalar sampling_length =
123 1.5 * sqrt((total_mesh_area / (approx_num_points * lagrange::internal::pi)));
126 BoundingBox bounding_box(n_dims);
129 const Index vertex_id = facets(facet_id, vertex_offset);
131 bounding_box.extend(vertices.row(vertex_id).transpose());
136 const RowVectorS grid_lens = bounding_box.sizes();
137 const RowVectorI grid_dims =
138 (grid_lens / sampling_length).array().ceil().template
cast<Index>();
141 const Index num_grid_cells = grid_dims.prod();
144 auto get_flattend_index = [grid_dims, n_dims](
const RowVectorI& index_nd) -> Index {
146 assert(index_nd.minCoeff() >= 0);
147 assert((index_nd.array() < grid_dims.array()).all());
152 answer = index_nd(1) * grid_dims(0) + index_nd(0);
153 }
else if (n_dims == 3) {
155 answer = index_nd(2) * grid_dims(1) * grid_dims(0) + index_nd(1) * grid_dims(0) +
157 LA_IGNORE_ARRAY_BOUNDS_END
162 assert(answer >= 0 && answer < grid_dims.prod());
171 std::unordered_set<Index> is_grid_cell_marked_backend(num_grid_cells);
174 auto is_grid_cell_marked = [&is_grid_cell_marked_backend](
const Index cell_index) {
178 return is_grid_cell_marked_backend.count(cell_index) != 0;
182 auto mark_grid_cell = [&is_grid_cell_marked_backend](
const Index cell_index) {
186 is_grid_cell_marked_backend.insert(cell_index);
192 std::list<VertexType> sample_positions;
193 std::list<Index> sample_facet_ids;
194 std::list<RowVector3S> sample_barycentrics;
195 Index num_samples = 0;
205 const Index num_triangle_cols = 3 + n_dims;
206 using Triangle = Eigen::Matrix<Scalar, 3, Eigen::Dynamic>;
207 auto get_triangle_pos = [&n_dims](
const Triangle& tri,
const Index vert_offset) -> RowVectorS {
208 return tri.row(vert_offset).head(n_dims);
210 auto get_triangle_bary = [](
const Triangle& tri,
const Index vert_offset) -> RowVectorS {
211 return tri.row(vert_offset).tail(3);
213 auto set_triangle_pos =
214 [&n_dims](Triangle& tri,
const Index vert_offset,
const RowVectorS& v) ->
void {
215 tri.row(vert_offset).head(n_dims) = v;
217 auto set_triangle_bary = [](Triangle& tri,
218 const Index vert_offset,
219 const RowVectorS& v) ->
void { tri.row(vert_offset).tail(3) = v; };
220 std::queue<Triangle> sub_triangles;
236 sub_triangles = std::queue<Triangle>();
239 Triangle mother_facet = Triangle::Zero(3, num_triangle_cols);
240 set_triangle_pos(mother_facet, 0, vertices.row(facets(facet_id, 0)));
241 set_triangle_pos(mother_facet, 1, vertices.row(facets(facet_id, 1)));
242 set_triangle_pos(mother_facet, 2, vertices.row(facets(facet_id, 2)));
243 set_triangle_bary(mother_facet, 0, RowVector3S(1, 0, 0));
244 set_triangle_bary(mother_facet, 1, RowVector3S(0, 1, 0));
245 set_triangle_bary(mother_facet, 2, RowVector3S(0, 0, 1));
248 sub_triangles.push(mother_facet);
251 while (!sub_triangles.empty()) {
252 const Triangle current_facet = sub_triangles.front();
256 Scalar longest_edge_length = -std::numeric_limits<Scalar>::max();
258 for (
auto edge_offset :
range(3)) {
259 const auto edge_offset_p1 = (edge_offset + 1) % 3;
260 const Scalar edge_length = (get_triangle_pos(current_facet, edge_offset)
261 - get_triangle_pos(current_facet, edge_offset_p1))
263 if (edge_length > longest_edge_length) {
264 longest_edge_length = edge_length;
265 longest_edge_offset = edge_offset;
270 if (longest_edge_length > 2 * sampling_length) {
271 Triangle f1 = Triangle::Zero(3, num_triangle_cols);
272 Triangle f2 = Triangle::Zero(3, num_triangle_cols);
274 f1.row(0) = (current_facet.row(longest_edge_offset) +
275 current_facet.row((longest_edge_offset + 1) % 3)) /
277 f1.row(1) = current_facet.row(longest_edge_offset);
278 f1.row(2) = current_facet.row((longest_edge_offset + 2) % 3);
280 f2.row(0) = f1.row(0);
281 f2.row(1) = current_facet.row((longest_edge_offset + 1) % 3);
282 f2.row(2) = current_facet.row((longest_edge_offset + 2) % 3);
284 sub_triangles.push(f1);
285 sub_triangles.push(f2);
287 const RowVector3S point_barycentric =
288 (get_triangle_bary(current_facet, 0) + get_triangle_bary(current_facet, 1) +
289 get_triangle_bary(current_facet, 2)) /
291 const VertexType point_position =
292 (get_triangle_pos(current_facet, 0) + get_triangle_pos(current_facet, 1) +
293 get_triangle_pos(current_facet, 2)) /
299 const RowVectorI grid_cell =
300 ((point_position - bounding_box.min().transpose()) / sampling_length)
304 const Index grid_cell_id = get_flattend_index(grid_cell);
306 if (!is_grid_cell_marked(grid_cell_id)) {
307 mark_grid_cell(grid_cell_id);
309 sample_facet_ids.push_back(facet_id);
311 sample_barycentrics.push_back(point_barycentric);
313 sample_positions.push_back(point_position);
325 output.num_samples = num_samples;
327 output.barycentrics.resize(num_samples, 3);
328 output.facet_ids.resize(num_samples);
329 output.positions.resize(num_samples, n_dims);
331 auto it_barycentric = sample_barycentrics.begin();
332 auto it_facet_id = sample_facet_ids.begin();
333 auto it_position = sample_positions.begin();
335 for (Index i :
range(num_samples)) {
336 output.barycentrics.row(i) = *it_barycentric;
337 output.facet_ids[i] = *it_facet_id;
338 output.positions.row(i) = *it_position;
353template <
typename MeshType>
356 const typename MeshType::Index approx_num_points,
357 const std::vector<bool>& is_facet_active)
359 typename MeshType::IndexList active_facets;
361 if (is_facet_active[i]) {
362 active_facets.push_back(i);
365 return sample_points_on_surface(mesh, approx_num_points, active_facets);
372template <
typename MeshType>
375 const typename MeshType::Index approx_num_points)
377 return sample_points_on_surface(mesh, approx_num_points,
typename MeshType::IndexList());
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:2130
@ Scalar
Mesh attribute must have exactly 1 channel.
Definition AttributeFwd.h:56
AttributeId compute_facet_area(SurfaceMesh< Scalar, Index > &mesh, FacetAreaOptions options={})
Compute per-facet area.
Definition compute_area.cpp:304
SurfaceMesh< ToScalar, ToIndex > cast(const SurfaceMesh< FromScalar, FromIndex > &source_mesh, const AttributeFilter &convertible_attributes={}, std::vector< std::string > *converted_attributes_names=nullptr)
Cast a mesh to a mesh of different scalar and/or index type.
#define la_runtime_assert(...)
Runtime assertion check.
Definition assert.h:174
internal::SparseRange< Index > range_sparse(Index max, const std::vector< Index > &active)
Returns an iterable object representing a subset of the range [0, max).
Definition range.h:217
internal::Range< Index > range(Index end)
Returns an iterable object representing the range [0, end).
Definition range.h:176
constexpr T invalid()
You can use invalid<T>() to get a value that can represent "invalid" values, such as invalid indices ...
Definition invalid.h:40
constexpr auto safe_cast(SourceType value) -> std::enable_if_t<!std::is_same< SourceType, TargetType >::value, TargetType >
Perform safe cast from SourceType to TargetType, where "safe" means:
Definition safe_cast.h:50
#define LA_IGNORE_ARRAY_BOUNDS_BEGIN
Ignore warning "out of bounds subscripts or offsets into arrays" This is used to bypass the following...
Definition warning.h:157
Main namespace for Lagrange.
Definition sample_points_on_surface.h:37