15#include <lagrange/common.h>
16#include <lagrange/compute_triangle_normal.h>
17#include <lagrange/utils/assert.h>
18#include <lagrange/utils/range.h>
34template <
typename MeshType>
37 using Scalar =
typename MeshType::Scalar;
38 using Index =
typename MeshType::Index;
44 Scalar flood_error_limit = std::numeric_limits<Scalar>::max();
46 bool should_smooth_boundary =
true;
53 std::vector<bool> is_facet_selectable = std::vector<bool>();
55 Scalar flood_second_to_first_order_limit_ratio = 1.0 / 6.0;
57 Index num_smooth_iterations = 3;
59 enum class SearchType { BFS = 0, DFS = 1 };
60 SearchType search_type = SearchType::DFS;
63 void set_selectable_facets(
MeshType& mesh_ref,
const std::vector<Index>& selectable_facets)
65 if (selectable_facets.empty()) {
66 is_facet_selectable.clear();
68 is_facet_selectable.resize(mesh_ref.get_num_facets(),
false);
69 for (
auto facet_id :
range_sparse(mesh_ref.get_num_facets(), selectable_facets)) {
70 is_facet_selectable[facet_id] =
true;
84template <
typename MeshType>
87 const typename MeshType::Index seed_facet_id,
94 using VertexType =
typename MeshType::VertexType;
95 using Index =
typename MeshType::Index;
96 using Scalar =
typename MeshType::Scalar;
97 using IndexList =
typename MeshType::IndexList;
98 using AttributeArray =
typename MeshType::AttributeArray;
102 auto is_facet_selectable = [&mesh, ¶meters](
const Index facet_id) ->
bool {
103 if (parameters.is_facet_selectable.empty()) {
107 return parameters.is_facet_selectable[facet_id];
112 auto normal_direction_error = [](
const VertexType& n1,
const VertexType& n2) -> Scalar {
113 return static_cast<Scalar
>(1.0) - std::abs(n1.dot(n2));
122 if (mesh.get_vertex_per_facet() != 3) {
123 throw std::runtime_error(
"Input mesh must be a triangle mesh.");
127 if (!mesh.is_connectivity_initialized()) {
128 mesh.initialize_connectivity();
130 if (!mesh.has_facet_attribute(
"normal")) {
131 compute_triangle_normal(mesh);
135 std::vector<bool> is_facet_selected(mesh.get_num_facets(),
false);
138 const AttributeArray& facet_normals = mesh.get_facet_attribute(
"normal");
139 const VertexType seed_n = facet_normals.row(seed_facet_id);
142 std::vector<bool> is_facet_processed(mesh.get_num_facets(),
false);
143 std::deque<Index> search_queue;
148 is_facet_processed[seed_facet_id] =
true;
149 is_facet_selected[seed_facet_id] =
true;
151 const IndexList& facets_adjacent_to_seed = mesh.get_facets_adjacent_to_facet(seed_facet_id);
152 for (Index j = 0; j < static_cast<Index>(facets_adjacent_to_seed.size()); j++) {
153 const Index ne_fid = facets_adjacent_to_seed[j];
154 const VertexType ne_n = facet_normals.row(ne_fid);
156 if ((!is_facet_processed[ne_fid]) && is_facet_selectable(ne_fid)) {
157 Scalar error = normal_direction_error(seed_n, ne_n);
158 if (error < parameters.flood_error_limit) {
159 is_facet_selected[ne_fid] =
true;
160 search_queue.push_back(ne_fid);
170 while (!search_queue.empty()) {
171 Index fid = invalid<Index>();
172 if (parameters.search_type == Parameters::SearchType::DFS) {
173 fid = search_queue.front();
174 search_queue.pop_front();
176 fid = search_queue.back();
177 search_queue.pop_back();
180 const VertexType center_n = facet_normals.row(fid);
183 const IndexList& facets_adjacent_to_candidate = mesh.get_facets_adjacent_to_facet(fid);
184 for (Index j = 0; j < static_cast<Index>(facets_adjacent_to_candidate.size()); j++) {
185 const Index ne_fid = facets_adjacent_to_candidate[j];
186 const VertexType ne_n = facet_normals.row(ne_fid);
188 if ((!is_facet_processed[ne_fid]) && is_facet_selectable(ne_fid)) {
189 const Scalar error_1 = normal_direction_error(seed_n, ne_n);
190 const Scalar error_2 = normal_direction_error(center_n, ne_n);
191 is_facet_processed[ne_fid] =
true;
193 if ((error_1 < parameters.flood_error_limit) &&
194 (error_2 < parameters.flood_error_limit)) {
195 is_facet_selected[ne_fid] =
true;
196 search_queue.push_back(ne_fid);
198 error_2 < parameters.flood_error_limit *
199 parameters.flood_second_to_first_order_limit_ratio) {
201 is_facet_selected[ne_fid] =
true;
202 search_queue.push_back(ne_fid);
211 if (parameters.should_smooth_boundary) {
212 for (Index st_iter_2 = 0; st_iter_2 < parameters.num_smooth_iterations; st_iter_2++) {
214 for (Index i = 0; i < mesh.get_num_facets(); i++) {
215 const IndexList& facets_adjacent_to_candidate =
216 mesh.get_facets_adjacent_to_facet(i);
218 if (is_facet_selectable(i) && (facets_adjacent_to_candidate.size() > 2)) {
219 const bool select_flag = is_facet_selected[i];
220 Index neighbor_diff_flag = 0;
222 const Index ne_fid_0 = facets_adjacent_to_candidate[0];
223 const bool ne_select_0 = is_facet_selected[ne_fid_0];
224 if (select_flag != ne_select_0) neighbor_diff_flag++;
226 const Index ne_fid_1 = facets_adjacent_to_candidate[1];
227 const bool ne_select_1 = is_facet_selected[ne_fid_1];
228 if (select_flag != ne_select_1) neighbor_diff_flag++;
230 const Index ne_fid_2 = facets_adjacent_to_candidate[2];
231 const bool ne_select_2 = is_facet_selected[ne_fid_2];
232 if (select_flag != ne_select_2) neighbor_diff_flag++;
234 if (neighbor_diff_flag > 1) {
235 const VertexType self_n = facet_normals.row(i);
236 const VertexType ne_n_0 = facet_normals.row(ne_fid_0);
237 const VertexType ne_n_1 = facet_normals.row(ne_fid_1);
238 const VertexType ne_n_2 = facet_normals.row(ne_fid_2);
240 const Scalar e_0 = normal_direction_error(self_n, ne_n_0);
241 const Scalar e_1 = normal_direction_error(self_n, ne_n_1);
242 const Scalar e_2 = normal_direction_error(self_n, ne_n_2);
244 const bool e_0_ok = e_0 < parameters.flood_error_limit;
245 const bool e_1_ok = e_1 < parameters.flood_error_limit;
246 const bool e_2_ok = e_2 < parameters.flood_error_limit;
248 if (ne_select_0 && ne_select_1 && (e_0_ok || e_1_ok)) {
249 is_facet_selected[i] =
true;
250 }
else if (ne_select_1 && ne_select_2 && (e_1_ok || e_2_ok)) {
251 is_facet_selected[i] =
true;
252 }
else if (ne_select_2 && ne_select_0 && (e_2_ok || e_0_ok)) {
253 is_facet_selected[i] =
true;
262 return is_facet_selected;
AttributeId select_facets_by_normal_similarity(SurfaceMesh< Scalar, Index > &mesh, const Index seed_facet_id, const SelectFacetsByNormalSimilarityOptions &options={})
Given a seed facet, selects facets around it based on the change in triangle normals.
Definition: select_facets_by_normal_similarity.cpp:27
#define la_runtime_assert(...)
Runtime assertion check.
Definition: assert.h:169
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
Main namespace for Lagrange.
Definition: AABBIGL.h:30
Given a seed vertex, selects faces around it based on the change in triangle normals.
Definition: select_facets_by_normal_similarity.h:36