17#include <lagrange/Mesh.h>
18#include <lagrange/chain_corners_around_edges.h>
19#include <lagrange/chain_corners_around_vertices.h>
20#include <lagrange/common.h>
21#include <lagrange/compute_triangle_normal.h>
22#include <lagrange/corner_to_edge_mapping.h>
23#include <lagrange/legacy/inline.h>
24#include <lagrange/utils/DisjointSets.h>
25#include <lagrange/utils/geometry3d.h>
41template <
typename MeshType>
44 std::function<
bool(
typename MeshType::Index,
typename MeshType::Index)> is_edge_smooth,
45 const std::vector<typename MeshType::Index>& cone_vertices = {})
47 static_assert(MeshTrait<MeshType>::is_mesh(),
"Input type is not Mesh");
48 using Scalar =
typename MeshType::Scalar;
49 using Index =
typename MeshType::Index;
50 using FacetArray =
typename MeshType::FacetArray;
51 using AttributeArray =
typename MeshType::AttributeArray;
52 using IndexArray = Eigen::Matrix<Index, Eigen::Dynamic, 1>;
53 using RowVector3r = Eigen::Matrix<Scalar, 1, 3>;
56 mesh.get_vertex_per_facet() == 3,
57 "Only triangle meshes are supported for this.");
58 if (!mesh.has_facet_attribute(
"normal")) {
59 compute_triangle_normal(mesh);
63 logger().trace(
"Corner to edge mapping");
66 logger().trace(
"Chain corners around edges");
68 IndexArray next_corner_around_edge;
70 logger().trace(
"Chain corners around vertices");
72 IndexArray next_corner_around_vertex;
74 mesh.get_num_vertices(),
77 next_corner_around_vertex);
79 const auto& vertices = mesh.get_vertices();
80 const auto& facets = mesh.get_facets();
81 const auto& facet_normals = mesh.get_facet_attribute(
"normal");
82 const auto nvpf = mesh.get_vertex_per_facet();
83 const auto num_vertices = mesh.get_num_vertices();
84 const auto num_corners = mesh.get_num_facets() * nvpf;
86 std::vector<bool> is_cone_vertex(num_vertices,
false);
87 for (
auto vi : cone_vertices) {
88 is_cone_vertex[vi] =
true;
92 auto is_face_degenerate = [&](Index f) {
93 for (Index lv = 0; lv < nvpf; ++lv) {
94 if (facets(f, lv) == facets(f, (lv + 1) % nvpf)) {
103 logger().trace(
"Loop to unify corner indices");
104 DisjointSets<Index> unified_indices(num_corners);
105 tbb::parallel_for(Index(0), num_vertices, [&](Index v) {
106 for (Index ci = v2c[v]; ci != invalid<Index>(); ci = next_corner_around_vertex[ci]) {
108 Index fi = ci / nvpf;
109 Index lvi = ci % nvpf;
110 Index vi = facets(fi, lvi);
112 if (is_cone_vertex[vi])
continue;
113 if (is_face_degenerate(fi))
continue;
115 for (Index cj = e2c[eij]; cj != invalid<Index>(); cj = next_corner_around_edge[cj]) {
116 Index fj = cj / nvpf;
117 Index lvj = cj % nvpf;
118 if (fi == fj)
continue;
119 Index vj = facets(fj, lvj);
121 lvj = (lvj + 1) % nvpf;
122 vj = facets(fj, lvj);
126 if (is_edge_smooth(fi, fj)) {
127 unified_indices.merge(ci, fj * nvpf + lvj);
134 logger().trace(
"Compute new indices");
135 std::vector<Index> repr(num_corners, invalid<Index>());
136 Index num_indices = 0;
137 for (Index n = 0; n < num_corners; ++n) {
138 Index r = unified_indices.find(n);
139 if (repr[r] == invalid<Index>()) {
140 repr[r] = num_indices++;
144 logger().trace(
"Compute offsets");
145 std::vector<Index> indices(num_corners);
146 std::vector<Index> offsets(num_indices + 1, 0);
147 for (Index c = 0; c < num_corners; ++c) {
148 offsets[repr[c] + 1]++;
150 for (Index r = 1; r <= num_indices; ++r) {
151 offsets[r] += offsets[r - 1];
155 std::vector<Index> counter = offsets;
156 for (Index c = 0; c < num_corners; ++c) {
157 indices[counter[repr[c]]++] = c;
161 logger().trace(
"Project and average normals");
162 FacetArray normal_indices(mesh.get_num_facets(), 3);
163 AttributeArray normal_values(num_indices, mesh.get_dim());
164 normal_values.setZero();
165 tbb::parallel_for(Index(0), Index(offsets.size() - 1), [&](Index r) {
166 for (Index i = offsets[r]; i < offsets[r + 1]; ++i) {
167 Index c = indices[i];
170 assert(repr[c] == r);
172 RowVector3r n = facet_normals.row(f);
175 RowVector3r v0 = vertices.row(facets(f, lv));
176 RowVector3r v1 = vertices.row(facets(f, (lv + 1) % nvpf));
177 RowVector3r v2 = vertices.row(facets(f, (lv + 2) % nvpf));
178 RowVector3r e01 = v1 - v0;
179 RowVector3r e02 = v2 - v0;
180 Scalar w = angle_between(e01, e02);
182 normal_values.row(r) += n * w;
183 normal_indices(f, lv) = r;
186 logger().trace(
"Normalize vectors");
187 tbb::parallel_for(Index(0), Index(normal_values.rows()), [&](Index c) {
188 normal_values.row(c).template head<3>().stableNormalize();
191 mesh.add_indexed_attribute(
"normal");
192 mesh.set_indexed_attribute(
"normal", normal_values, normal_indices);
195template <
typename MeshType>
198 const typename MeshType::Scalar feature_angle_threshold,
199 const std::vector<typename MeshType::Index>& cone_vertices = {})
201 static_assert(MeshTrait<MeshType>::is_mesh(),
"Input type is not Mesh");
202 using Index =
typename MeshType::Index;
203 using Scalar =
typename MeshType::Scalar;
204 using RowVector3r = Eigen::Matrix<Scalar, 1, 3>;
207 mesh.get_vertex_per_facet() == 3,
208 "Only triangle meshes are supported for this.");
209 if (!mesh.has_facet_attribute(
"normal")) {
210 compute_triangle_normal(mesh);
212 const auto& facet_normals = mesh.get_facet_attribute(
"normal");
215 auto is_edge_smooth = [&](Index fi, Index fj) {
216 const RowVector3r ni = facet_normals.row(fi);
217 const RowVector3r nj = facet_normals.row(fj);
219 return theta < feature_angle_threshold;
LA_CORE_API spdlog::logger & logger()
Retrieves the current logger.
Definition: Logger.cpp:40
AttributeId compute_normal(SurfaceMesh< Scalar, Index > &mesh, function_ref< bool(Index)> is_edge_smooth, span< const Index > cone_vertices={}, NormalOptions options={})
Compute smooth normals based on specified sharp edges and cone vertices.
Definition: compute_normal.cpp:195
AttributeId compute_normal(SurfaceMesh< Scalar, Index > &mesh, Scalar feature_angle_threshold, span< const Index > cone_vertices={}, NormalOptions options={})
Compute smooth normal based on specified dihedral angle threshold and cone vertices.
Definition: compute_normal.cpp:219
#define la_runtime_assert(...)
Runtime assertion check.
Definition: assert.h:169
Main namespace for Lagrange.
Definition: AABBIGL.h:30
Scalar angle_between(const Eigen::Matrix< Scalar, _Rows, _Cols > &v1, const Eigen::Matrix< Scalar, _Rows, _Cols > &v2)
Returns the angle between two 3d vectors.
Definition: geometry3d.h:36
Eigen::Index corner_to_edge_mapping(const Eigen::MatrixBase< DerivedF > &F, Eigen::PlainObjectBase< DerivedC > &C2E)
Computes a mapping from mesh corners (k*f+i) to unique edge ids.
Definition: corner_to_edge_mapping.impl.h:36
void chain_corners_around_edges(const Eigen::MatrixBase< DerivedF > &facets, const Eigen::MatrixBase< DerivedC > &corner_to_edge, Eigen::PlainObjectBase< DerivedE > &edge_to_corner, Eigen::PlainObjectBase< DerivedN > &next_corner_around_edge)
Chains facet corners around edges of a mesh.
Definition: chain_corners_around_edges.h:39
void chain_corners_around_vertices(typename DerivedF::Scalar num_vertices, const Eigen::MatrixBase< DerivedF > &facets, Eigen::PlainObjectBase< DerivedE > &vertex_to_corner, Eigen::PlainObjectBase< DerivedN > &next_corner_around_vertex)
Chains facet corners around vertices of a mesh.
Definition: chain_corners_around_vertices.h:41