Visual Computing Library
Loading...
Searching...
No Matches
smooth.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_SMOOTH_H
24#define VCL_ALGORITHMS_MESH_SMOOTH_H
25
26#include <vclib/mesh/requirements.h>
27#include <vclib/space/complex/kd_tree.h>
28
29#include <cmath>
30#include <vector>
31
32namespace vcl {
33
34namespace detail {
35
36template<typename CoordType>
37struct LaplacianInfo
38{
39 using ScalarType = CoordType::ScalarType;
40 CoordType sum;
41 ScalarType cnt;
42};
43
44template<typename MeshType, typename CoordType>
45void accumulateLaplacianInfo(
46 const MeshType& m,
47 std::vector<LaplacianInfo<CoordType>>& data,
48 bool cotangentFlag = false)
49{
50 using ScalarType = CoordType::ScalarType;
51 using VertexType = MeshType::VertexType;
52 using FaceType = MeshType::FaceType;
53
54 ScalarType weight = 1.0f;
55
56 for (const FaceType& f : m.faces()) {
57 for (uint j = 0; j < f.vertexNumber(); ++j) {
58 if (!f.edgeOnBorder(j)) {
59 const VertexType& v0 = *f.vertex(j);
60 const VertexType& v1 = *f.vertexMod(j + 1);
61 const VertexType& v2 = *f.vertexMod(j + 2);
62 const CoordType& p0 = v0.coord();
63 const CoordType& p1 = v1.coord();
64 const CoordType& p2 = v2.coord();
65 if (cotangentFlag) {
66 ScalarType angle = CoordType(p1 - p2).angle(p0 - p2);
67 weight = std::tan((M_PI * 0.5) - angle);
68 }
69
70 data[m.index(v0)].sum += f.vertexMod(j + 1)->coord() * weight;
71 data[m.index(v1)].sum += f.vertex(j)->coord() * weight;
72 data[m.index(v0)].cnt += weight;
73 data[m.index(v1)].cnt += weight;
74 }
75 }
76 }
77 // si azzaera i dati per i vertici di bordo
78 for (const FaceType& f : m.faces()) {
79 for (uint j = 0; j < f.vertexNumber(); ++j) {
80 if (f.edgeOnBorder(j)) {
81 const VertexType& v0 = *f.vertex(j);
82 const VertexType& v1 = *f.vertexMod(j + 1);
83 const CoordType& p0 = v0.coord();
84 const CoordType& p1 = v1.coord();
85 data[m.index(v0)].sum = p0;
86 data[m.index(v1)].sum = p1;
87 data[m.index(v0)].cnt = 1;
88 data[m.index(v1)].cnt = 1;
89 }
90 }
91 }
92
93 // se l'edge j e' di bordo si deve mediare solo con gli adiacenti
94 for (const FaceType& f : m.faces()) {
95 for (uint j = 0; j < f.vertexNumber(); ++j) {
96 if (f.edgeOnBorder(j)) {
97 const VertexType& v0 = *f.vertex(j);
98 const VertexType& v1 = *f.vertexMod(j + 1);
99 const CoordType& p0 = v0.coord();
100 const CoordType& p1 = v1.coord();
101 data[m.index(v0)].sum += p1;
102 data[m.index(v1)].sum += p0;
103 ++data[m.index(v0)].cnt;
104 ++data[m.index(v1)].cnt;
105 }
106 }
107 }
108}
109
110} // namespace detail
111
126template<FaceMeshConcept MeshType>
127void laplacianSmoothing(
128 MeshType& m,
129 uint step,
130 bool smoothSelected = false,
131 bool cotangentWeight = false /*, CallBackPos *cb*/)
132{
133 using VertexType = MeshType::VertexType;
134 using CoordType = VertexType::CoordType;
135
136 const detail::LaplacianInfo<CoordType> lpz = {CoordType(0, 0, 0), 0};
137
138 for (uint i = 0; i < step; ++i) {
139 std::vector<detail::LaplacianInfo<CoordType>> laplData(
140 m.vertexContainerSize(), lpz);
141 detail::accumulateLaplacianInfo(m, laplData, cotangentWeight);
142 for (VertexType& v : m.vertices()) {
143 if (laplData[m.index(v)].cnt > 0) {
144 if (!smoothSelected || v.selected()) {
145 v.coord() = (v.coord() + laplData[m.index(v)].sum) /
146 (laplData[m.index(v)].cnt + 1);
147 }
148 }
149 }
150 }
151}
152
153template<FaceMeshConcept MeshType>
154void taubinSmoothing(
155 MeshType& m,
156 uint step,
157 float lambda,
158 float mu,
159 bool smoothSelected = false /*, CallBackPos *cb*/)
160{
161 using VertexType = MeshType::VertexType;
162 using CoordType = VertexType::CoordType;
163
164 const detail::LaplacianInfo<CoordType> lpz = {CoordType(0, 0, 0), 0};
165
166 for (uint i = 0; i < step; ++i) {
167 std::vector<detail::LaplacianInfo<CoordType>> laplData(
168 m.vertexContainerSize(), lpz);
169 detail::accumulateLaplacianInfo(m, laplData);
170 for (VertexType& v : m.vertices()) {
171 if (laplData[m.index(v)].cnt > 0) {
172 if (!smoothSelected || v.selected()) {
173 CoordType delta =
174 laplData[m.index(v)].sum / laplData[m.index(v)].cnt -
175 v.coord();
176 v.coord() = v.coord() + delta * lambda;
177 }
178 }
179 }
180 std::fill(laplData.begin(), laplData.end(), lpz);
181 detail::accumulateLaplacianInfo(m, laplData);
182 for (VertexType& v : m.vertices()) {
183 if (laplData[m.index(v)].cnt > 0) {
184 if (!smoothSelected || v.selected()) {
185 CoordType delta =
186 laplData[m.index(v)].sum / laplData[m.index(v)].cnt -
187 v.coord();
188 v.coord() = v.coord() + delta * mu;
189 }
190 }
191 }
192 }
193}
194
208template<MeshConcept MeshType, PointConcept PointType>
209void smoothPerVertexNormalsPointCloud(
210 MeshType& m,
211 const KDTree<PointType>& tree,
212 uint neighborNum,
213 uint iterNum)
214{
215 requirePerVertexNormal(m);
216
217 using Scalar = PointType::ScalarType;
218 using VertexType = MeshType::VertexType;
219 using NormalType = VertexType::NormalType;
220
221 std::vector<NormalType> TD(m.vertexContainerSize(), NormalType(0, 0, 0));
222
223 for (uint ii = 0; ii < iterNum; ++ii) {
224 for (const VertexType& v : m.vertices()) {
225 std::vector<Scalar> distances;
226
227 std::vector<uint> neighbors = tree.kNearestNeighborsIndices(
228 v.coord(), neighborNum, distances);
229
230 for (uint nid : neighbors) {
231 if (m.vertex(nid).normal() * v.normal() > 0) {
232 TD[m.index(v)] += m.vertex(nid).normal();
233 }
234 else {
235 TD[m.index(v)] -= m.vertex(nid).normal();
236 }
237 }
238 }
239 for (VertexType& v : m.vertices()) {
240 v.normal() = TD[m.index(v)];
241 }
242 }
243}
244
257template<MeshConcept MeshType>
258void smoothPerVertexNormalsPointCloud(
259 MeshType& m,
260 uint neighborNum,
261 uint iterNum)
262{
263 using Scalar = MeshType::VertexType::CoordType::ScalarType;
264 KDTree<Scalar> tree(m);
265 smoothPerVertexNormalsPointCloud(m, tree, neighborNum, iterNum);
266}
267
268} // namespace vcl
269
270#endif // VCL_ALGORITHMS_MESH_SMOOTH_H
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