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 v->normal().setZero();
226 }
227 }
228 }
229 };
230
231 log.log(0, "Clearing per-Vertex normals...");
232
233 // apply the function f to all the containers of the mesh
234 ForEachType<typename MeshType::Containers>::apply(f);
235
236 log.log(100, "Per-Vertex normals cleared.");
237}
238
244template<LoggerConcept LogType = NullLogger>
245void clearPerFaceNormals(FaceMeshConcept auto& mesh, LogType& log = nullLogger)
246{
247 clearPerElementNormals<ElemId::FACE>(mesh, log);
248}
249
255template<LoggerConcept LogType = NullLogger>
256void normalizePerVertexNormals(
257 MeshConcept auto& mesh,
258 LogType& log = nullLogger)
259{
260 normalizePerElementNormals<ElemId::VERTEX>(mesh, log);
261}
262
279template<MeshConcept MeshType, LoggerConcept LogType = NullLogger>
280void normalizePerReferencedVertexNormals(
281 MeshType& mesh,
282 LogType& log = nullLogger)
283{
284 requirePerVertexNormal(mesh);
285
286 // function that is called for each container Cont
287 auto f = [&]<mesh::ElementContainerConcept Cont>() {
288 using Elem = typename Cont::ElementType; // the Element type of Cont
289 if constexpr (comp::HasVertexReferences<Elem>) { // if Elem has vertices
290 for (auto& e : mesh.template elements<Elem::ELEMENT_ID>()) {
291 for (auto* v : e.vertices()) {
292 detail::normalizeNoThrow<ElemId::VERTEX>(*v, log);
293 }
294 }
295 }
296 };
297
298 log.log(0, "Normalizing per-Vertex normals...");
299
300 // apply the function f to all the containers of the mesh
301 ForEachType<typename MeshType::Containers>::apply(f);
302
303 log.log(100, "Per-Vertex normals normalized.");
304}
305
311template<LoggerConcept LogType = NullLogger>
312void normalizePerFaceNormals(
313 FaceMeshConcept auto& mesh,
314 LogType& log = nullLogger)
315{
316 normalizePerElementNormals<ElemId::FACE>(mesh, log);
317}
318
327template<typename Matrix, LoggerConcept LogType = NullLogger>
328void multiplyPerVertexNormalsByMatrix(
329 MeshConcept auto& mesh,
330 const Matrix& mat,
331 bool removeScalingFromMatrix = true,
332 LogType& log = nullLogger)
333{
334 multiplyPerElementNormalsByMatrix<ElemId::VERTEX>(
335 mesh, mat, removeScalingFromMatrix, log);
336}
337
345template<typename Matrix, LoggerConcept LogType = NullLogger>
346void multiplyPerFaceNormalsByMatrix(
347 FaceMeshConcept auto& mesh,
348 const Matrix& mat,
349 bool removeScalingFromMatrix = true,
350 LogType& log = nullLogger)
351{
352 multiplyPerElementNormalsByMatrix<ElemId::FACE>(
353 mesh, mat, removeScalingFromMatrix, log);
354}
355
371template<LoggerConcept LogType = NullLogger>
372void updatePerFaceNormals(
373 FaceMeshConcept auto& mesh,
374 bool normalize = true,
375 LogType& log = nullLogger)
376{
378
379 using FaceType = RemoveRef<decltype(mesh)>::FaceType;
380 using ScalarType = FaceType::NormalType::ScalarType;
381
382 log.log(0, "Updating per-Face normals...");
383
384 parallelFor(mesh.faces(), [](auto& f) {
385 f.normal() = faceNormal(f).template cast<ScalarType>();
386 });
387
388 if (normalize) {
389 log.startNewTask(50, 100, "Normalizing per-Face normals...");
390 normalizePerFaceNormals(mesh, log);
391 log.endTask("Normalizing per-Face normals...");
392 }
393
394 log.log(100, "Per-Face normals updated.");
395}
396
414template<LoggerConcept LogType = NullLogger>
415void updatePerVertexNormals(
416 FaceMeshConcept auto& mesh,
417 bool normalize = true,
418 LogType& log = nullLogger)
419{
420 using VertexType = RemoveRef<decltype(mesh)>::VertexType;
421 using NScalar = VertexType::NormalType::ScalarType;
422
423 log.log(0, "Updating per-Vertex normals...");
424
425 log.startNewTask(0, 20, "Clearing per-Vertex normals...");
426 clearPerReferencedVertexNormals(mesh, log);
427 log.endTask("Clearing per-Vertex normals...");
428
429 log.log(20, "Updating per-Vertex normals...");
430
431 for (auto& f : mesh.faces()) {
432 for (auto* v : f.vertices()) {
433 v->normal() += faceNormal(f).template cast<NScalar>();
434 }
435 }
436
437 if (normalize) {
438 log.startNewTask(80, 100, "Normalizing per-Vertex normals...");
439 normalizePerReferencedVertexNormals(mesh, log);
440 log.endTask("Normalizing per-Vertex normals...");
441 }
442
443 log.log(100, "Per-Vertex normals updated.");
444}
445
463template<LoggerConcept LogType = NullLogger>
464void updatePerVertexNormalsFromFaceNormals(
465 FaceMeshConcept auto& mesh,
466 bool normalize = true,
467 LogType& log = nullLogger)
468{
470
471 using VertexType = RemoveRef<decltype(mesh)>::VertexType;
472 using ScalarType = VertexType::NormalType::ScalarType;
473
474 log.log(0, "Updating per-Vertex normals...");
475
476 log.startNewTask(0, 20, "Clearing per-Vertex normals...");
477 clearPerReferencedVertexNormals(mesh, log);
478 log.endTask("Clearing per-Vertex normals...");
479
480 log.log(20, "Updating per-Vertex normals...");
481
482 for (auto& f : mesh.faces()) {
483 for (auto* v : f.vertices()) {
484 v->normal() += f.normal().template cast<ScalarType>();
485 }
486 }
487
488 if (normalize) {
489 log.startNewTask(80, 100, "Normalizing per-Vertex normals...");
490 normalizePerReferencedVertexNormals(mesh, log);
491 log.endTask("Normalizing per-Vertex normals...");
492 }
493
494 log.log(100, "Per-Vertex normals updated.");
495}
496
517template<LoggerConcept LogType = NullLogger>
518void updatePerVertexAndFaceNormals(
519 FaceMeshConcept auto& mesh,
520 bool normalize = true,
521 LogType& log = nullLogger)
522{
523 log.log(0, "Updating per-Vertex and per-Face normals...");
524
525 log.startNewTask(0, 40, "Updating per-Face normals...");
526 updatePerFaceNormals(mesh, false, log); // normals are not normalized here
527 log.endTask("");
528
529 log.startNewTask(40, 80, "Updating per-Vertex normals...");
530 updatePerVertexNormalsFromFaceNormals(mesh, normalize, log);
531 log.endTask("");
532
533 if (normalize) {
534 log.startNewTask(80, 100, "Normalizing per-Face normals...");
535 normalizePerFaceNormals(mesh, log);
536 log.endTask("");
537 }
538
539 log.log(100, "Per-Vertex normals updated.");
540}
541
569template<LoggerConcept LogType = NullLogger>
570void updatePerVertexNormalsAngleWeighted(
571 FaceMeshConcept auto& mesh,
572 bool normalize = true,
573 LogType& log = nullLogger)
574{
575 using VertexType = RemoveRef<decltype(mesh)>::VertexType;
576 using NormalType = VertexType::NormalType;
577 using NScalarType = NormalType::ScalarType;
578
579 log.log(0, "Updating per-Vertex normals...");
580
581 log.startNewTask(0, 5, "Clearing per-Vertex normals...");
582 clearPerReferencedVertexNormals(mesh, log);
583 log.endTask("Clearing per-Vertex normals...");
584
585 log.log(5, "Updating per-Vertex normals...");
586
587 for (auto& f : mesh.faces()) {
588 auto n = faceNormal(f).template cast<NScalarType>();
589
590 for (uint i = 0; i < f.vertexNumber(); ++i) {
591 NormalType vec1 =
592 (f.vertexMod(i - 1)->position() - f.vertexMod(i)->position())
593 .normalized()
594 .template cast<NScalarType>();
595 NormalType vec2 =
596 (f.vertexMod(i + 1)->position() - f.vertexMod(i)->position())
597 .normalized()
598 .template cast<NScalarType>();
599
600 f.vertex(i)->normal() += n * vec1.angle(vec2);
601 }
602 }
603
604 if (normalize) {
605 log.startNewTask(95, 100, "Normalizing per-Vertex normals...");
606 normalizePerReferencedVertexNormals(mesh, log);
607 log.endTask("Normalizing per-Vertex normals...");
608 }
609
610 log.log(100, "Per-Vertex normals updated.");
611}
612
643template<LoggerConcept LogType = NullLogger>
644void updatePerVertexNormalsNelsonMaxWeighted(
645 FaceMeshConcept auto& mesh,
646 bool normalize = true,
647 LogType& log = nullLogger)
648{
649 using VertexType = RemoveRef<decltype(mesh)>::VertexType;
650 using NScalarType = VertexType::NormalType::ScalarType;
651
652 log.log(0, "Updating per-Vertex normals...");
653
654 log.startNewTask(0, 5, "Clearing per-Vertex normals...");
655 clearPerReferencedVertexNormals(mesh, log);
656 log.endTask("Clearing per-Vertex normals...");
657
658 log.log(5, "Updating per-Vertex normals...");
659
660 for (auto& f : mesh.faces()) {
661 auto n = faceNormal(f).template cast<NScalarType>();
662
663 for (uint i = 0; i < f.vertexNumber(); ++i) {
664 NScalarType e1 =
665 (f.vertexMod(i - 1)->position() - f.vertexMod(i)->position())
666 .squaredNorm();
667 NScalarType e2 =
668 (f.vertexMod(i + 1)->position() - f.vertexMod(i)->position())
669 .squaredNorm();
670
671 f.vertex(i)->normal() += n / (e1 * e2);
672 }
673 }
674
675 if (normalize) {
676 log.startNewTask(95, 100, "Normalizing per-Vertex normals...");
677 normalizePerReferencedVertexNormals(mesh, log);
678 log.endTask("Normalizing per-Vertex normals...");
679 }
680
681 log.log(100, "Per-Vertex normals updated.");
682}
683
684} // namespace vcl
685
686#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:1066
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