Visual Computing Library  devel
Loading...
Searching...
No Matches
normal.h
1/*****************************************************************************
2 * VCLib *
3 * Visual Computing Library *
4 * *
5 * Copyright(C) 2021-2025 *
6 * Visual Computing Lab *
7 * ISTI - Italian National Research Council *
8 * *
9 * All rights reserved. *
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the Mozilla Public License Version 2.0 as published *
13 * by the Mozilla Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 * This program is distributed in the hope that it will be useful, *
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
19 * Mozilla Public License Version 2.0 *
20 * (https://www.mozilla.org/en-US/MPL/2.0/) for more details. *
21 ****************************************************************************/
22
23#ifndef VCL_ALGORITHMS_MESH_UPDATE_NORMAL_H
24#define VCL_ALGORITHMS_MESH_UPDATE_NORMAL_H
25
26#include <vclib/algorithms/core.h>
27#include <vclib/mesh.h>
28#include <vclib/space/core.h>
29
30namespace vcl {
31
32namespace detail {
33
34template<uint ELEM_ID, LoggerConcept LogType = NullLogger>
35void normalizeNoThrow(auto& elem, LogType& log = nullLogger)
36{
37 try {
38 elem.normal().normalize();
39 }
40 catch (const std::exception& e) {
41 log.log(
42 log.WARNING_LOG,
43 elementEnumString<ELEM_ID>() + " " + std::to_string(elem.index()) +
44 ": " + e.what());
45 }
46}
47
48} // namespace detail
49
64template<uint ELEM_ID, LoggerConcept LogType = NullLogger>
65void clearPerElementNormals(MeshConcept auto& mesh, LogType& log = nullLogger)
66{
67 requirePerElementComponent<ELEM_ID, CompId::NORMAL>(mesh);
68
69 log.log(0, "Clearing per-" + elementEnumString<ELEM_ID>() + " normals...");
70
71 parallelFor(mesh.template elements<ELEM_ID>(), [](auto& e) {
72 e.normal().setZero();
73 });
74
75 log.log(100, "Per-" + elementEnumString<ELEM_ID>() + " normals cleared.");
76}
77
91template<uint ELEM_ID, LoggerConcept LogType = NullLogger>
92void normalizePerElementNormals(
93 MeshConcept auto& mesh,
94 LogType& log = nullLogger)
95{
96 requirePerElementComponent<ELEM_ID, CompId::NORMAL>(mesh);
97
98 log.log(
99 0, "Normalizing per-" + elementEnumString<ELEM_ID>() + " normals...");
100
101 // define a lambda that, for each element, normalizes the normal
102 auto normalize = [&](auto& elem) {
103 detail::normalizeNoThrow<ELEM_ID>(elem, log);
104 };
105
106 parallelFor(mesh.template elements<ELEM_ID>(), normalize);
107
108 log.log(
109 100, "Per-" + elementEnumString<ELEM_ID>() + " normals normalized.");
110}
111
130template<uint ELEM_ID, typename MScalar, LoggerConcept LogType = NullLogger>
131void multiplyPerElementNormalsByMatrix(
132 MeshConcept auto& mesh,
133 Matrix33<MScalar> mat,
134 bool removeScalingFromMatrix = true,
135 LogType& log = nullLogger)
136{
137 requirePerElementComponent<ELEM_ID, CompId::NORMAL>(mesh);
138
139 log.log(
140 0,
141 "Multiplying per-" + elementEnumString<ELEM_ID>() +
142 " normals by matrix...");
143
144 multiplyNormalsByMatrix(
145 mesh.template elements<ELEM_ID>() | vcl::views::normals,
146 mat,
148
149 log.log(
150 100, "Per-" + elementEnumString<ELEM_ID>() + " normals multiplied.");
151}
152
172template<uint ELEM_ID, typename MScalar, LoggerConcept LogType = NullLogger>
173void multiplyPerElementNormalsByMatrix(
174 MeshConcept auto& mesh,
175 const Matrix44<MScalar>& mat,
176 bool removeScalingFromMatrix = true,
177 LogType& log = nullLogger)
178{
179 requirePerElementComponent<ELEM_ID, CompId::NORMAL>(mesh);
180
181 Matrix33<MScalar> m33 = mat.block(0, 0, 3, 3);
182 multiplyPerElementNormalsByMatrix<ELEM_ID>(
183 mesh, m33, removeScalingFromMatrix, log);
184}
185
191template<LoggerConcept LogType = NullLogger>
192void clearPerVertexNormals(MeshConcept auto& mesh, LogType& log = nullLogger)
193{
194 clearPerElementNormals<ElemId::VERTEX>(mesh, log);
195}
196
214template<MeshConcept MeshType, LoggerConcept LogType = NullLogger>
215void clearPerReferencedVertexNormals(MeshType& mesh, LogType& log = nullLogger)
216{
217 requirePerVertexNormal(mesh);
218
219 // function that is called for each container Cont of the mesh
220 auto f = [&]<mesh::ElementContainerConcept Cont>() {
221 using Elem = typename Cont::ElementType; // the Element type of Cont
222 if constexpr (comp::HasVertexReferences<Elem>) { // if Elem has vertices
223 for (auto& e : mesh.template elements<Elem::ELEMENT_ID>()) {
224 for (auto* v : e.vertices()) {
225 // normal().setZero() does not compile on gcc (Release) with
226 // Eigen 5.0.1
227 v->normal() = {0., 0., 0.};
228 }
229 }
230 }
231 };
232
233 log.log(0, "Clearing per-Vertex normals...");
234
235 // apply the function f to all the containers of the mesh
236 ForEachType<typename MeshType::Containers>::apply(f);
237
238 log.log(100, "Per-Vertex normals cleared.");
239}
240
246template<LoggerConcept LogType = NullLogger>
247void clearPerFaceNormals(FaceMeshConcept auto& mesh, LogType& log = nullLogger)
248{
249 clearPerElementNormals<ElemId::FACE>(mesh, log);
250}
251
257template<LoggerConcept LogType = NullLogger>
258void normalizePerVertexNormals(
259 MeshConcept auto& mesh,
260 LogType& log = nullLogger)
261{
262 normalizePerElementNormals<ElemId::VERTEX>(mesh, log);
263}
264
281template<MeshConcept MeshType, LoggerConcept LogType = NullLogger>
282void normalizePerReferencedVertexNormals(
283 MeshType& mesh,
284 LogType& log = nullLogger)
285{
286 requirePerVertexNormal(mesh);
287
288 // function that is called for each container Cont
289 auto f = [&]<mesh::ElementContainerConcept Cont>() {
290 using Elem = typename Cont::ElementType; // the Element type of Cont
291 if constexpr (comp::HasVertexReferences<Elem>) { // if Elem has vertices
292 for (auto& e : mesh.template elements<Elem::ELEMENT_ID>()) {
293 for (auto* v : e.vertices()) {
294 detail::normalizeNoThrow<ElemId::VERTEX>(*v, log);
295 }
296 }
297 }
298 };
299
300 log.log(0, "Normalizing per-Vertex normals...");
301
302 // apply the function f to all the containers of the mesh
303 ForEachType<typename MeshType::Containers>::apply(f);
304
305 log.log(100, "Per-Vertex normals normalized.");
306}
307
313template<LoggerConcept LogType = NullLogger>
314void normalizePerFaceNormals(
315 FaceMeshConcept auto& mesh,
316 LogType& log = nullLogger)
317{
318 normalizePerElementNormals<ElemId::FACE>(mesh, log);
319}
320
329template<typename Matrix, LoggerConcept LogType = NullLogger>
330void multiplyPerVertexNormalsByMatrix(
331 MeshConcept auto& mesh,
332 const Matrix& mat,
333 bool removeScalingFromMatrix = true,
334 LogType& log = nullLogger)
335{
336 multiplyPerElementNormalsByMatrix<ElemId::VERTEX>(
337 mesh, mat, removeScalingFromMatrix, log);
338}
339
347template<typename Matrix, LoggerConcept LogType = NullLogger>
348void multiplyPerFaceNormalsByMatrix(
349 FaceMeshConcept auto& mesh,
350 const Matrix& mat,
351 bool removeScalingFromMatrix = true,
352 LogType& log = nullLogger)
353{
354 multiplyPerElementNormalsByMatrix<ElemId::FACE>(
355 mesh, mat, removeScalingFromMatrix, log);
356}
357
373template<LoggerConcept LogType = NullLogger>
374void updatePerFaceNormals(
375 FaceMeshConcept auto& mesh,
376 bool normalize = true,
377 LogType& log = nullLogger)
378{
380
381 using FaceType = RemoveRef<decltype(mesh)>::FaceType;
382 using ScalarType = FaceType::NormalType::ScalarType;
383
384 log.log(0, "Updating per-Face normals...");
385
386 parallelFor(mesh.faces(), [](auto& f) {
387 f.normal() = faceNormal(f).template cast<ScalarType>();
388 });
389
390 if (normalize) {
391 log.startNewTask(50, 100, "Normalizing per-Face normals...");
392 normalizePerFaceNormals(mesh, log);
393 log.endTask("Normalizing per-Face normals...");
394 }
395
396 log.log(100, "Per-Face normals updated.");
397}
398
416template<LoggerConcept LogType = NullLogger>
417void updatePerVertexNormals(
418 FaceMeshConcept auto& mesh,
419 bool normalize = true,
420 LogType& log = nullLogger)
421{
422 using VertexType = RemoveRef<decltype(mesh)>::VertexType;
423 using NScalar = VertexType::NormalType::ScalarType;
424
425 log.log(0, "Updating per-Vertex normals...");
426
427 log.startNewTask(0, 20, "Clearing per-Vertex normals...");
428 clearPerReferencedVertexNormals(mesh, log);
429 log.endTask("Clearing per-Vertex normals...");
430
431 log.log(20, "Updating per-Vertex normals...");
432
433 for (auto& f : mesh.faces()) {
434 for (auto* v : f.vertices()) {
435 v->normal() += faceNormal(f).template cast<NScalar>();
436 }
437 }
438
439 if (normalize) {
440 log.startNewTask(80, 100, "Normalizing per-Vertex normals...");
441 normalizePerReferencedVertexNormals(mesh, log);
442 log.endTask("Normalizing per-Vertex normals...");
443 }
444
445 log.log(100, "Per-Vertex normals updated.");
446}
447
465template<LoggerConcept LogType = NullLogger>
466void updatePerVertexNormalsFromFaceNormals(
467 FaceMeshConcept auto& mesh,
468 bool normalize = true,
469 LogType& log = nullLogger)
470{
472
473 using VertexType = RemoveRef<decltype(mesh)>::VertexType;
474 using ScalarType = VertexType::NormalType::ScalarType;
475
476 log.log(0, "Updating per-Vertex normals...");
477
478 log.startNewTask(0, 20, "Clearing per-Vertex normals...");
479 clearPerReferencedVertexNormals(mesh, log);
480 log.endTask("Clearing per-Vertex normals...");
481
482 log.log(20, "Updating per-Vertex normals...");
483
484 for (auto& f : mesh.faces()) {
485 for (auto* v : f.vertices()) {
486 v->normal() += f.normal().template cast<ScalarType>();
487 }
488 }
489
490 if (normalize) {
491 log.startNewTask(80, 100, "Normalizing per-Vertex normals...");
492 normalizePerReferencedVertexNormals(mesh, log);
493 log.endTask("Normalizing per-Vertex normals...");
494 }
495
496 log.log(100, "Per-Vertex normals updated.");
497}
498
519template<LoggerConcept LogType = NullLogger>
520void updatePerVertexAndFaceNormals(
521 FaceMeshConcept auto& mesh,
522 bool normalize = true,
523 LogType& log = nullLogger)
524{
525 log.log(0, "Updating per-Vertex and per-Face normals...");
526
527 log.startNewTask(0, 40, "Updating per-Face normals...");
528 updatePerFaceNormals(mesh, false, log); // normals are not normalized here
529 log.endTask("");
530
531 log.startNewTask(40, 80, "Updating per-Vertex normals...");
532 updatePerVertexNormalsFromFaceNormals(mesh, normalize, log);
533 log.endTask("");
534
535 if (normalize) {
536 log.startNewTask(80, 100, "Normalizing per-Face normals...");
537 normalizePerFaceNormals(mesh, log);
538 log.endTask("");
539 }
540
541 log.log(100, "Per-Vertex normals updated.");
542}
543
571template<LoggerConcept LogType = NullLogger>
572void updatePerVertexNormalsAngleWeighted(
573 FaceMeshConcept auto& mesh,
574 bool normalize = true,
575 LogType& log = nullLogger)
576{
577 using VertexType = RemoveRef<decltype(mesh)>::VertexType;
578 using NormalType = VertexType::NormalType;
579 using NScalarType = NormalType::ScalarType;
580
581 log.log(0, "Updating per-Vertex normals...");
582
583 log.startNewTask(0, 5, "Clearing per-Vertex normals...");
584 clearPerReferencedVertexNormals(mesh, log);
585 log.endTask("Clearing per-Vertex normals...");
586
587 log.log(5, "Updating per-Vertex normals...");
588
589 for (auto& f : mesh.faces()) {
590 auto n = faceNormal(f).template cast<NScalarType>();
591
592 for (uint i = 0; i < f.vertexNumber(); ++i) {
593 NormalType vec1 =
594 (f.vertexMod(i - 1)->position() - f.vertexMod(i)->position())
595 .normalized()
596 .template cast<NScalarType>();
597 NormalType vec2 =
598 (f.vertexMod(i + 1)->position() - f.vertexMod(i)->position())
599 .normalized()
600 .template cast<NScalarType>();
601
602 f.vertex(i)->normal() += n * vec1.angle(vec2);
603 }
604 }
605
606 if (normalize) {
607 log.startNewTask(95, 100, "Normalizing per-Vertex normals...");
608 normalizePerReferencedVertexNormals(mesh, log);
609 log.endTask("Normalizing per-Vertex normals...");
610 }
611
612 log.log(100, "Per-Vertex normals updated.");
613}
614
645template<LoggerConcept LogType = NullLogger>
646void updatePerVertexNormalsNelsonMaxWeighted(
647 FaceMeshConcept auto& mesh,
648 bool normalize = true,
649 LogType& log = nullLogger)
650{
651 using VertexType = RemoveRef<decltype(mesh)>::VertexType;
652 using NScalarType = VertexType::NormalType::ScalarType;
653
654 log.log(0, "Updating per-Vertex normals...");
655
656 log.startNewTask(0, 5, "Clearing per-Vertex normals...");
657 clearPerReferencedVertexNormals(mesh, log);
658 log.endTask("Clearing per-Vertex normals...");
659
660 log.log(5, "Updating per-Vertex normals...");
661
662 for (auto& f : mesh.faces()) {
663 auto n = faceNormal(f).template cast<NScalarType>();
664
665 for (uint i = 0; i < f.vertexNumber(); ++i) {
666 NScalarType e1 =
667 (f.vertexMod(i - 1)->position() - f.vertexMod(i)->position())
668 .squaredNorm();
669 NScalarType e2 =
670 (f.vertexMod(i + 1)->position() - f.vertexMod(i)->position())
671 .squaredNorm();
672
673 f.vertex(i)->normal() += n / (e1 * e2);
674 }
675 }
676
677 if (normalize) {
678 log.startNewTask(95, 100, "Normalizing per-Vertex normals...");
679 normalizePerReferencedVertexNormals(mesh, log);
680 log.endTask("Normalizing per-Vertex normals...");
681 }
682
683 log.log(100, "Per-Vertex normals updated.");
684}
685
686} // namespace vcl
687
688#endif // VCL_ALGORITHMS_MESH_UPDATE_NORMAL_H
MatrixType removeScalingFromMatrix(const MatrixType &matrix)
Returns a matrix with the scaling factors removed.
Definition transform.h:274
NullLogger nullLogger
The nullLogger object is an object of type NullLogger that is used as default argument in the functio...
Definition null_logger.h:123
std::remove_reference_t< T > RemoveRef
Utility alias to get a type type without the reference. e.g. If T is int&, the resulting type is int.
Definition pointers.h:55
FaceType::VertexType::PositionType faceNormal(const FaceType &f)
Computes the normal of a face, without modifying the face. Works both for triangle and polygonal face...
Definition geometry.h:46
void requirePerFaceNormal(const MeshType &m)
This function asserts that a Mesh has a FaceContainer, the Face has a Normal Component,...
Definition face_requirements.h:1179
constexpr detail::FacesView faces
A view that allows to iterate overt the Face elements of an object.
Definition face.h:84
constexpr detail::VerticesView vertices
A view that allows to iterate over the Vertex elements of an object.
Definition vertex.h:92