Visual Computing Library
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/polygon.h>
27#include <vclib/mesh/requirements.h>
28#include <vclib/misc/logger.h>
29#include <vclib/misc/parallel.h>
30#include <vclib/space/core/matrix.h>
31
32namespace vcl {
33
34namespace detail {
35
36template<uint ELEM_ID, LoggerConcept LogType = NullLogger>
37void normalizeNoThrow(auto& elem, LogType& log = nullLogger)
38{
39 try {
40 elem.normal().normalize();
41 }
42 catch (const std::exception& e) {
43 log.log(
44 log.WARNING_LOG,
45 elementEnumString<ELEM_ID>() + " " + std::to_string(elem.index()) +
46 ": " + e.what());
47 }
48}
49
50} // namespace detail
51
66template<uint ELEM_ID, LoggerConcept LogType = NullLogger>
67void clearPerElementNormals(MeshConcept auto& mesh, LogType& log = nullLogger)
68{
69 requirePerElementComponent<ELEM_ID, CompId::NORMAL>(mesh);
70
71 log.log(0, "Clearing per-" + elementEnumString<ELEM_ID>() + " normals...");
72
73 parallelFor(mesh.template elements<ELEM_ID>(), [](auto& e) {
74 e.normal().setZero();
75 });
76
77 log.log(100, "Per-" + elementEnumString<ELEM_ID>() + " normals cleared.");
78}
79
93template<uint ELEM_ID, LoggerConcept LogType = NullLogger>
94void normalizePerElementNormals(
95 MeshConcept auto& mesh,
96 LogType& log = nullLogger)
97{
98 requirePerElementComponent<ELEM_ID, CompId::NORMAL>(mesh);
99
100 log.log(
101 0, "Normalizing per-" + elementEnumString<ELEM_ID>() + " normals...");
102
103 // define a lambda that, for each element, normalizes the normal
104 auto normalize = [&](auto& elem) {
105 detail::normalizeNoThrow<ELEM_ID>(elem, log);
106 };
107
108 parallelFor(mesh.template elements<ELEM_ID>(), normalize);
109
110 log.log(
111 100, "Per-" + elementEnumString<ELEM_ID>() + " normals normalized.");
112}
113
132template<uint ELEM_ID, typename MScalar, LoggerConcept LogType = NullLogger>
133void multiplyPerElementNormalsByMatrix(
134 MeshConcept auto& mesh,
135 Matrix33<MScalar> mat,
136 bool removeScalingFromMatrix = true,
137 LogType& log = nullLogger)
138{
139 requirePerElementComponent<ELEM_ID, CompId::NORMAL>(mesh);
140
141 if (removeScalingFromMatrix) {
142 MScalar scaleX = std::sqrt(
143 mat(0, 0) * mat(0, 0) + mat(0, 1) * mat(0, 1) +
144 mat(0, 2) * mat(0, 2));
145 MScalar scaleY = std::sqrt(
146 mat(1, 0) * mat(1, 0) + mat(1, 1) * mat(1, 1) +
147 mat(1, 2) * mat(1, 2));
148 MScalar scaleZ = std::sqrt(
149 mat(2, 0) * mat(2, 0) + mat(2, 1) * mat(2, 1) +
150 mat(2, 2) * mat(2, 2));
151 for (int i = 0; i < 3; ++i) {
152 mat(0, i) /= scaleX;
153 mat(1, i) /= scaleY;
154 mat(2, i) /= scaleZ;
155 }
156 }
157
158 log.log(
159 0,
160 "Multiplying per-" + elementEnumString<ELEM_ID>() +
161 " normals by matrix...");
162
163 parallelFor(mesh.template elements<ELEM_ID>(), [&](auto& e) {
164 e.normal() = mat * e.normal();
165 });
166
167 log.log(
168 100, "Per-" + elementEnumString<ELEM_ID>() + " normals multiplied.");
169}
170
190template<uint ELEM_ID, typename MScalar, LoggerConcept LogType = NullLogger>
191void multiplyPerElementNormalsByMatrix(
192 MeshConcept auto& mesh,
193 const Matrix44<MScalar>& mat,
194 bool removeScalingFromMatrix = true,
195 LogType& log = nullLogger)
196{
197 requirePerElementComponent<ELEM_ID, CompId::NORMAL>(mesh);
198
199 Matrix33<MScalar> m33 = mat.block(0, 0, 3, 3);
200 multiplyPerElementNormalsByMatrix<ELEM_ID>(
201 mesh, m33, removeScalingFromMatrix, log);
202}
203
209template<LoggerConcept LogType = NullLogger>
210void clearPerVertexNormals(MeshConcept auto& mesh, LogType& log = nullLogger)
211{
212 clearPerElementNormals<ElemId::VERTEX>(mesh, log);
213}
214
232template<MeshConcept MeshType, LoggerConcept LogType = NullLogger>
233void clearPerReferencedVertexNormals(MeshType& mesh, LogType& log = nullLogger)
234{
235 requirePerVertexNormal(mesh);
236
237 // function that is called for each container Cont of the mesh
238 auto f = [&]<mesh::ElementContainerConcept Cont>() {
239 using Elem = typename Cont::ElementType; // the Element type of Cont
240 if constexpr (comp::HasVertexReferences<Elem>) { // if Elem has vertices
241 for (auto& e : mesh.template elements<Elem::ELEMENT_ID>()) {
242 for (auto* v : e.vertices()) {
243 v->normal().setZero();
244 }
245 }
246 }
247 };
248
249 log.log(0, "Clearing per-Vertex normals...");
250
251 // apply the function f to all the containers of the mesh
252 ForEachType<typename MeshType::Containers>::apply(f);
253
254 log.log(100, "Per-Vertex normals cleared.");
255}
256
262template<LoggerConcept LogType = NullLogger>
263void clearPerFaceNormals(FaceMeshConcept auto& mesh, LogType& log = nullLogger)
264{
265 clearPerElementNormals<ElemId::FACE>(mesh, log);
266}
267
273template<LoggerConcept LogType = NullLogger>
274void normalizePerVertexNormals(
275 MeshConcept auto& mesh,
276 LogType& log = nullLogger)
277{
278 normalizePerElementNormals<ElemId::VERTEX>(mesh, log);
279}
280
297template<MeshConcept MeshType, LoggerConcept LogType = NullLogger>
298void normalizePerReferencedVertexNormals(
299 MeshType& mesh,
300 LogType& log = nullLogger)
301{
302 requirePerVertexNormal(mesh);
303
304 // function that is called for each container Cont
305 auto f = [&]<mesh::ElementContainerConcept Cont>() {
306 using Elem = typename Cont::ElementType; // the Element type of Cont
307 if constexpr (comp::HasVertexReferences<Elem>) { // if Elem has vertices
308 for (auto& e : mesh.template elements<Elem::ELEMENT_ID>()) {
309 for (auto* v : e.vertices()) {
310 detail::normalizeNoThrow<ElemId::VERTEX>(*v, log);
311 }
312 }
313 }
314 };
315
316 log.log(0, "Normalizing per-Vertex normals...");
317
318 // apply the function f to all the containers of the mesh
319 ForEachType<typename MeshType::Containers>::apply(f);
320
321 log.log(100, "Per-Vertex normals normalized.");
322}
323
329template<LoggerConcept LogType = NullLogger>
330void normalizePerFaceNormals(
331 FaceMeshConcept auto& mesh,
332 LogType& log = nullLogger)
333{
334 normalizePerElementNormals<ElemId::FACE>(mesh, log);
335}
336
345template<typename Matrix, LoggerConcept LogType = NullLogger>
346void multiplyPerVertexNormalsByMatrix(
347 MeshConcept auto& mesh,
348 const Matrix& mat,
349 bool removeScalingFromMatrix = true,
350 LogType& log = nullLogger)
351{
352 multiplyPerElementNormalsByMatrix<ElemId::VERTEX>(
353 mesh, mat, removeScalingFromMatrix, log);
354}
355
363template<typename Matrix, LoggerConcept LogType = NullLogger>
364void multiplyPerFaceNormalsByMatrix(
365 FaceMeshConcept auto& mesh,
366 const Matrix& mat,
367 bool removeScalingFromMatrix = true,
368 LogType& log = nullLogger)
369{
370 multiplyPerElementNormalsByMatrix<ElemId::FACE>(
371 mesh, mat, removeScalingFromMatrix, log);
372}
373
389template<LoggerConcept LogType = NullLogger>
390void updatePerFaceNormals(
391 FaceMeshConcept auto& mesh,
392 bool normalize = true,
393 LogType& log = nullLogger)
394{
396
397 using FaceType = RemoveRef<decltype(mesh)>::FaceType;
398 using ScalarType = FaceType::NormalType::ScalarType;
399
400 log.log(0, "Updating per-Face normals...");
401
402 parallelFor(mesh.faces(), [](auto& f) {
403 f.normal() = faceNormal(f).template cast<ScalarType>();
404 });
405
406 if (normalize) {
407 log.startNewTask(50, 100, "Normalizing per-Face normals...");
408 normalizePerFaceNormals(mesh, log);
409 log.endTask("Normalizing per-Face normals...");
410 }
411
412 log.log(100, "Per-Face normals updated.");
413}
414
432template<LoggerConcept LogType = NullLogger>
433void updatePerVertexNormals(
434 FaceMeshConcept auto& mesh,
435 bool normalize = true,
436 LogType& log = nullLogger)
437{
438 using VertexType = RemoveRef<decltype(mesh)>::VertexType;
439 using NScalar = VertexType::NormalType::ScalarType;
440
441 log.log(0, "Updating per-Vertex normals...");
442
443 log.startNewTask(0, 20, "Clearing per-Vertex normals...");
444 clearPerReferencedVertexNormals(mesh, log);
445 log.endTask("Clearing per-Vertex normals...");
446
447 log.log(20, "Updating per-Vertex normals...");
448
449 for (auto& f : mesh.faces()) {
450 for (auto* v : f.vertices()) {
451 v->normal() += faceNormal(f).template cast<NScalar>();
452 }
453 }
454
455 if (normalize) {
456 log.startNewTask(80, 100, "Normalizing per-Vertex normals...");
457 normalizePerReferencedVertexNormals(mesh, log);
458 log.endTask("Normalizing per-Vertex normals...");
459 }
460
461 log.log(100, "Per-Vertex normals updated.");
462}
463
481template<LoggerConcept LogType = NullLogger>
482void updatePerVertexNormalsFromFaceNormals(
483 FaceMeshConcept auto& mesh,
484 bool normalize = true,
485 LogType& log = nullLogger)
486{
488
489 using VertexType = RemoveRef<decltype(mesh)>::VertexType;
490 using ScalarType = VertexType::NormalType::ScalarType;
491
492 log.log(0, "Updating per-Vertex normals...");
493
494 log.startNewTask(0, 20, "Clearing per-Vertex normals...");
495 clearPerReferencedVertexNormals(mesh, log);
496 log.endTask("Clearing per-Vertex normals...");
497
498 log.log(20, "Updating per-Vertex normals...");
499
500 for (auto& f : mesh.faces()) {
501 for (auto* v : f.vertices()) {
502 v->normal() += f.normal().template cast<ScalarType>();
503 }
504 }
505
506 if (normalize) {
507 log.startNewTask(80, 100, "Normalizing per-Vertex normals...");
508 normalizePerReferencedVertexNormals(mesh, log);
509 log.endTask("Normalizing per-Vertex normals...");
510 }
511
512 log.log(100, "Per-Vertex normals updated.");
513}
514
535template<LoggerConcept LogType = NullLogger>
536void updatePerVertexAndFaceNormals(
537 FaceMeshConcept auto& mesh,
538 bool normalize = true,
539 LogType& log = nullLogger)
540{
541 log.log(0, "Updating per-Vertex and per-Face normals...");
542
543 log.startNewTask(0, 40, "Updating per-Face normals...");
544 updatePerFaceNormals(mesh, false, log); // normals are not normalized here
545 log.endTask("");
546
547 log.startNewTask(40, 80, "Updating per-Vertex normals...");
548 updatePerVertexNormalsFromFaceNormals(mesh, normalize, log);
549 log.endTask("");
550
551 if (normalize) {
552 log.startNewTask(80, 100, "Normalizing per-Face normals...");
553 normalizePerFaceNormals(mesh, log);
554 log.endTask("");
555 }
556
557 log.log(100, "Per-Vertex normals updated.");
558}
559
587template<LoggerConcept LogType = NullLogger>
588void updatePerVertexNormalsAngleWeighted(
589 FaceMeshConcept auto& mesh,
590 bool normalize = true,
591 LogType& log = nullLogger)
592{
593 using VertexType = RemoveRef<decltype(mesh)>::VertexType;
594 using NormalType = VertexType::NormalType;
595 using NScalarType = NormalType::ScalarType;
596
597 log.log(0, "Updating per-Vertex normals...");
598
599 log.startNewTask(0, 5, "Clearing per-Vertex normals...");
600 clearPerReferencedVertexNormals(mesh, log);
601 log.endTask("Clearing per-Vertex normals...");
602
603 log.log(5, "Updating per-Vertex normals...");
604
605 for (auto& f : mesh.faces()) {
606 auto n = faceNormal(f).template cast<NScalarType>();
607
608 for (uint i = 0; i < f.vertexNumber(); ++i) {
609 NormalType vec1 =
610 (f.vertexMod(i - 1)->coord() - f.vertexMod(i)->coord())
611 .normalized()
612 .template cast<NScalarType>();
613 NormalType vec2 =
614 (f.vertexMod(i + 1)->coord() - f.vertexMod(i)->coord())
615 .normalized()
616 .template cast<NScalarType>();
617
618 f.vertex(i)->normal() += n * vec1.angle(vec2);
619 }
620 }
621
622 if (normalize) {
623 log.startNewTask(95, 100, "Normalizing per-Vertex normals...");
624 normalizePerReferencedVertexNormals(mesh, log);
625 log.endTask("Normalizing per-Vertex normals...");
626 }
627
628 log.log(100, "Per-Vertex normals updated.");
629}
630
661template<LoggerConcept LogType = NullLogger>
662void updatePerVertexNormalsNelsonMaxWeighted(
663 FaceMeshConcept auto& mesh,
664 bool normalize = true,
665 LogType& log = nullLogger)
666{
667 using VertexType = RemoveRef<decltype(mesh)>::VertexType;
668 using NScalarType = VertexType::NormalType::ScalarType;
669
670 log.log(0, "Updating per-Vertex normals...");
671
672 log.startNewTask(0, 5, "Clearing per-Vertex normals...");
673 clearPerReferencedVertexNormals(mesh, log);
674 log.endTask("Clearing per-Vertex normals...");
675
676 log.log(5, "Updating per-Vertex normals...");
677
678 for (auto& f : mesh.faces()) {
679 auto n = faceNormal(f).template cast<NScalarType>();
680
681 for (uint i = 0; i < f.vertexNumber(); ++i) {
682 NScalarType e1 =
683 (f.vertexMod(i - 1)->coord() - f.vertexMod(i)->coord())
684 .squaredNorm();
685 NScalarType e2 =
686 (f.vertexMod(i + 1)->coord() - f.vertexMod(i)->coord())
687 .squaredNorm();
688
689 f.vertex(i)->normal() += n / (e1 * e2);
690 }
691 }
692
693 if (normalize) {
694 log.startNewTask(95, 100, "Normalizing per-Vertex normals...");
695 normalizePerReferencedVertexNormals(mesh, log);
696 log.endTask("Normalizing per-Vertex normals...");
697 }
698
699 log.log(100, "Per-Vertex normals updated.");
700}
701
702} // namespace vcl
703
704#endif // VCL_ALGORITHMS_MESH_UPDATE_NORMAL_H
FaceType::VertexType::CoordType faceNormal(const FaceType &f)
Computes the normal of a face, without modifying the face. Works both for triangle and polygonal face...
Definition geometry.h:45
void requirePerFaceNormal(const MeshType &m)
This function asserts that a Mesh has a FaceContainer, the Face has a Normal Component,...
Definition face_requirements.h:804
NullLogger nullLogger
The nullLogger object is an object of type NullLogger that is used as default argument in the functio...
Definition null_logger.h:125
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
constexpr detail::FacesView faces
A view that allows to iterate overt the Face elements of an object.
Definition face.h:52
constexpr detail::VerticesView vertices
A view that allows to iterate over the Vertex elements of an object.
Definition vertex.h:60