Lagrange
compute_corner_normal.h
1/*
2 * Copyright 2019 Adobe. All rights reserved.
3 * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License. You may obtain a copy
5 * of the License at http://www.apache.org/licenses/LICENSE-2.0
6 *
7 * Unless required by applicable law or agreed to in writing, software distributed under
8 * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9 * OF ANY KIND, either express or implied. See the License for the specific language
10 * governing permissions and limitations under the License.
11 */
12#pragma once
13#include <cassert>
14#include <exception>
15#include <iostream>
16#include <numeric>
17
18#include <lagrange/Mesh.h>
19#include <lagrange/MeshTrait.h>
20#include <lagrange/common.h>
21#include <lagrange/compute_dihedral_angles.h>
22#include <lagrange/compute_facet_area.h>
23#include <lagrange/compute_triangle_normal.h>
24#include <lagrange/internal/internal_angles.h>
25#include <lagrange/legacy/inline.h>
26
27namespace lagrange {
28LAGRANGE_LEGACY_INLINE
29namespace legacy {
30
31template <typename MeshType>
32void compute_corner_normal(
33 MeshType& mesh,
34 std::function<bool(typename MeshType::Index, typename MeshType::Index)> is_sharp,
35 std::function<bool(typename MeshType::Index)> is_cone_vertex)
36{
37 static_assert(MeshTrait<MeshType>::is_mesh(), "Input type is not Mesh");
38
39 using AttributeArray = typename MeshType::AttributeArray;
40 using Index = typename MeshType::Index;
41 using Scalar = typename MeshType::Scalar;
42
43 if (!mesh.has_facet_attribute("normal")) {
44 compute_triangle_normal(mesh);
45 }
46 if (!mesh.is_connectivity_initialized()) {
47 mesh.initialize_connectivity();
48 }
49 mesh.initialize_edge_data();
50
51 const Index dim = mesh.get_dim();
52 const Index num_vertices = mesh.get_num_vertices();
53 const Index num_facets = mesh.get_num_facets();
54 const Index vertex_per_facet = mesh.get_vertex_per_facet();
55 const auto& facets = mesh.get_facets();
56 const AttributeArray& facet_normals = mesh.get_facet_attribute("normal");
57 // Compute corner angles
58 AttributeArray corner_angles;
59 {
60 lagrange::internal::internal_angles(mesh.get_vertices(), mesh.get_facets(), corner_angles);
61 assert(corner_angles.rows() == facets.rows());
62 assert(corner_angles.cols() == facets.cols());
63 }
64
65
66 AttributeArray corner_normals = AttributeArray::Zero(num_facets * vertex_per_facet, dim);
67
68 auto index_of = [](const auto& data, const auto item) -> Index {
69 auto itr = std::find(data.begin(), data.end(), item);
70 assert(itr != data.end());
71 return safe_cast<Index>(itr - data.begin());
72 };
73
74 auto get_root = [](const auto& data, Index i) -> Index {
75 while (data[i] != i) {
76 assert(data[i] < i);
77 i = data[i];
78 }
79 return i;
80 };
81
82 auto get_corner_index = [vertex_per_facet, &facets](const Index fid, const Index vid) -> Index {
83 for (auto i : range(vertex_per_facet)) {
84 if (facets(fid, i) == vid) {
85 return fid * vertex_per_facet + i;
86 }
87 }
88 la_runtime_assert(false, "Facet does not contain this vertex");
89 return 0; // To avoid compiler warning.
90 };
91
92 auto compute_corner_normal_around_cone_vertex =
93 [&mesh, &get_corner_index, &corner_normals, &facet_normals](Index vi) {
94 const auto& adj_facets = mesh.get_facets_adjacent_to_vertex(vi);
95 for (const auto fi : adj_facets) {
96 const auto ci = get_corner_index(fi, vi);
97 corner_normals.row(ci) = facet_normals.row(fi);
98 }
99 };
100
101 std::vector<Index> facet_ids; // 1-ring union-find array.
102 std::vector<Index> e_fids; // per-edge facet id buffer.
103 auto compute_corner_normal_around_regular_vertex = [&](Index vi) {
104 const auto& adj_facets = mesh.get_facets_adjacent_to_vertex(vi);
105 const auto num_adj_facets = adj_facets.size();
106 const auto& adj_vertices = mesh.get_vertices_adjacent_to_vertex(vi);
107
108 // Initialize union find array.
109 facet_ids.resize(num_adj_facets);
110 std::iota(facet_ids.begin(), facet_ids.end(), 0);
111 auto get_1_ring_root = [&get_root, &facet_ids](Index idx) {
112 return get_root(facet_ids, idx);
113 };
114
115 for (auto vj : adj_vertices) {
116 if (!is_sharp(vi, vj)) {
117 auto ei = mesh.find_edge_from_vertices(vi, vj);
118 e_fids.clear();
119 e_fids.reserve(mesh.get_num_facets_around_edge(ei));
120 mesh.foreach_facets_around_edge(ei, [&](Index fid) {
121 e_fids.push_back(index_of(adj_facets, fid));
122 });
123
124 std::transform(e_fids.begin(), e_fids.end(), e_fids.begin(), get_1_ring_root);
125 const auto root_id = *std::min_element(e_fids.begin(), e_fids.end());
126 std::for_each(e_fids.begin(), e_fids.end(), [&facet_ids, root_id](const Index fid) {
127 facet_ids[fid] = root_id;
128 });
129 }
130 }
131
132 std::transform(facet_ids.begin(), facet_ids.end(), facet_ids.begin(), get_1_ring_root);
133
134 // Accumulate normal to root corner.
135 for (auto i : range(num_adj_facets)) {
136 const Index fid = adj_facets[i];
137 const Index corner_id = get_corner_index(fid, vi);
138 const Index root_fid = adj_facets[facet_ids[i]];
139 const Index root_corner_id = get_corner_index(root_fid, vi);
140 const Scalar corner_angle =
141 corner_angles(corner_id / vertex_per_facet, corner_id % vertex_per_facet);
142 if (corner_angle >= 0.f &&
143 corner_angle <= 3.14f) { // filter numerical issues due to tiny or huge angels
144 corner_normals.row(root_corner_id) += facet_normals.row(fid) * corner_angle;
145 }
146 }
147 // Assign corner normal to each smooth group of the 1-ring.
148 for (auto i : range(num_adj_facets)) {
149 const Index fid = adj_facets[i];
150 const Index root_fid = adj_facets[facet_ids[i]];
151 const Index curr_corner_id = get_corner_index(fid, vi);
152 const Index root_corner_id = get_corner_index(root_fid, vi);
153
154 corner_normals.row(curr_corner_id) = corner_normals.row(root_corner_id);
155
156 if (corner_normals.row(curr_corner_id).template lpNorm<Eigen::Infinity>() > 1.e-4) {
157 corner_normals.row(curr_corner_id).normalize();
158 } else {
159 corner_normals.row(curr_corner_id).setZero();
160 }
161 }
162 };
163
164 for (auto vi : range(num_vertices)) {
165 if (is_cone_vertex(vi)) {
166 compute_corner_normal_around_cone_vertex(vi);
167 } else {
168 compute_corner_normal_around_regular_vertex(vi);
169 }
170 }
171
172 mesh.add_corner_attribute("normal");
173 mesh.import_corner_attribute("normal", corner_normals);
174}
175
180template <typename MeshType>
181void compute_corner_normal(
182 MeshType& mesh,
183 const typename MeshType::Scalar feature_angle_threshold,
184 const std::vector<typename MeshType::Index>& cone_vertices = {})
185{
186 static_assert(MeshTrait<MeshType>::is_mesh(), "Input type is not Mesh");
187 la_runtime_assert(feature_angle_threshold < 4, "This angle is in degrees, must be in radians");
188 using Index = typename MeshType::Index;
189
190 mesh.initialize_edge_data();
191 if (!mesh.has_edge_attribute("dihedral_angle")) {
193 }
194
195 auto is_sharp = [&mesh, feature_angle_threshold](Index vi, Index vj) {
196 assert(mesh.has_edge_attribute("dihedral_angle"));
197 const auto& dihedral_angles = mesh.get_edge_attribute("dihedral_angle");
198 Index eid = mesh.find_edge_from_vertices(vi, vj);
199 return std::abs(dihedral_angles(eid, 0)) > feature_angle_threshold;
200 };
201
202 if (cone_vertices.empty()) {
203 compute_corner_normal(mesh, is_sharp, [](Index) { return false; });
204 } else {
205 std::vector<bool> is_cone(mesh.get_num_vertices(), false);
206 std::for_each(cone_vertices.begin(), cone_vertices.end(), [&is_cone](Index vi) {
207 is_cone[vi] = true;
208 });
209 compute_corner_normal(mesh, is_sharp, [&is_cone](Index vi) { return is_cone[vi]; });
210 }
211}
212
213} // namespace legacy
214} // namespace lagrange
Definition: Mesh.h:48
AttributeId compute_dihedral_angles(SurfaceMesh< Scalar, Index > &mesh, const DihedralAngleOptions &options={})
Computes dihedral angles for each edge in the mesh.
Definition: compute_dihedral_angles.cpp:32
#define la_runtime_assert(...)
Runtime assertion check.
Definition: assert.h:169
internal::Range< Index > range(Index end)
Returns an iterable object representing the range [0, end).
Definition: range.h:176
void internal_angles(const Eigen::MatrixBase< DerivedV > &vertices, const Eigen::MatrixBase< DerivedF > &facets, Eigen::PlainObjectBase< DerivedK > &angles)
Compute internal angles for a triangle mesh.
Definition: internal_angles.h:39
Main namespace for Lagrange.
Definition: AABBIGL.h:30