1 /*========================================================================= 2 3 Program: Visualization Toolkit 4 Module: vtkWindowedSincPolyDataFilter.h 5 6 Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen 7 All rights reserved. 8 See Copyright.txt or http://www.kitware.com/Copyright.htm for details. 9 10 This software is distributed WITHOUT ANY WARRANTY; without even 11 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 12 PURPOSE. See the above copyright notice for more information. 13 14 =========================================================================*/ 15 /** 16 * @class vtkWindowedSincPolyDataFilter 17 * @brief adjust point positions using a windowed sinc function interpolation kernel 18 * 19 * vtkWindowedSincPolyDataFiler adjust point coordinate using a windowed 20 * sinc function interpolation kernel. The effect is to "relax" the mesh, 21 * making the cells better shaped and the vertices more evenly distributed. 22 * Note that this filter operates the lines, polygons, and triangle strips 23 * composing an instance of vtkPolyData. Vertex or poly-vertex cells are 24 * never modified. 25 * 26 * The algorithm proceeds as follows. For each vertex v, a topological and 27 * geometric analysis is performed to determine which vertices are connected 28 * to v, and which cells are connected to v. Then, a connectivity array is 29 * constructed for each vertex. (The connectivity array is a list of lists 30 * of vertices that directly attach to each vertex.) Next, an iteration 31 * phase begins over all vertices. For each vertex v, the coordinates of v 32 * are modified using a windowed sinc function interpolation kernel. 33 * Taubin describes this methodology is the IBM tech report RC-20404 34 * (#90237, dated 3/12/96) "Optimal Surface Smoothing as Filter Design" 35 * G. Taubin, T. Zhang and G. Golub. (Zhang and Golub are at Stanford 36 * University.) 37 * 38 * This report discusses using standard signal processing low-pass filters 39 * (in particular windowed sinc functions) to smooth polyhedra. The 40 * transfer functions of the low-pass filters are approximated by 41 * Chebyshev polynomials. This facilitates applying the filters in an 42 * iterative diffusion process (as opposed to a kernel convolution). The 43 * more smoothing iterations applied, the higher the degree of polynomial 44 * approximating the low-pass filter transfer function. Each smoothing 45 * iteration, therefore, applies the next higher term of the Chebyshev 46 * filter approximation to the polyhedron. This decoupling of the filter 47 * into an iteratively applied polynomial is possible since the Chebyshev 48 * polynomials are orthogonal, i.e. increasing the order of the 49 * approximation to the filter transfer function does not alter the 50 * previously calculated coefficients for the low order terms. 51 * 52 * Note: Care must be taken to avoid smoothing with too few iterations. 53 * A Chebyshev approximation with too few terms is an poor approximation. 54 * The first few smoothing iterations represent a severe scaling and 55 * translation of the data. Subsequent iterations cause the smoothed 56 * polyhedron to converge to the true location and scale of the object. 57 * We have attempted to protect against this by automatically adjusting 58 * the filter, effectively widening the pass band. This adjustment is only 59 * possible if the number of iterations is greater than 1. Note that this 60 * sacrifices some degree of smoothing for model integrity. For those 61 * interested, the filter is adjusted by searching for a value sigma 62 * such that the actual pass band is k_pb + sigma and such that the 63 * filter transfer function evaluates to unity at k_pb, i.e. f(k_pb) = 1 64 * 65 * To improve the numerical stability of the solution and minimize the 66 * scaling the translation effects, the algorithm can translate and 67 * scale the position coordinates to within the unit cube [-1, 1], 68 * perform the smoothing, and translate and scale the position 69 * coordinates back to the original coordinate frame. This mode is 70 * controlled with the NormalizeCoordinatesOn() / 71 * NormalizeCoordinatesOff() methods. For legacy reasons, the default 72 * is NormalizeCoordinatesOff. 73 * 74 * This implementation is currently limited to using an interpolation 75 * kernel based on Hamming windows. Other windows (such as Hann, Blackman, 76 * Kaiser, Lanczos, Gaussian, and exponential windows) could be used 77 * instead. 78 * 79 * There are some special instance variables used to control the execution 80 * of this filter. (These ivars basically control what vertices can be 81 * smoothed, and the creation of the connectivity array.) The 82 * BoundarySmoothing ivar enables/disables the smoothing operation on 83 * vertices that are on the "boundary" of the mesh. A boundary vertex is one 84 * that is surrounded by a semi-cycle of polygons (or used by a single 85 * line). 86 * 87 * Another important ivar is FeatureEdgeSmoothing. If this ivar is 88 * enabled, then interior vertices are classified as either "simple", 89 * "interior edge", or "fixed", and smoothed differently. (Interior 90 * vertices are manifold vertices surrounded by a cycle of polygons; or used 91 * by two line cells.) The classification is based on the number of feature 92 * edges attached to v. A feature edge occurs when the angle between the two 93 * surface normals of a polygon sharing an edge is greater than the 94 * FeatureAngle ivar. Then, vertices used by no feature edges are classified 95 * "simple", vertices used by exactly two feature edges are classified 96 * "interior edge", and all others are "fixed" vertices. 97 * 98 * Once the classification is known, the vertices are smoothed 99 * differently. Corner (i.e., fixed) vertices are not smoothed at all. 100 * Simple vertices are smoothed as before . Interior edge vertices are 101 * smoothed only along their two connected edges, and only if the angle 102 * between the edges is less than the EdgeAngle ivar. 103 * 104 * The total smoothing can be controlled by using two ivars. The 105 * NumberOfIterations determines the maximum number of smoothing passes. 106 * The NumberOfIterations corresponds to the degree of the polynomial that 107 * is used to approximate the windowed sinc function. Ten or twenty 108 * iterations is all the is usually necessary. Contrast this with 109 * vtkSmoothPolyDataFilter which usually requires 100 to 200 smoothing 110 * iterations. vtkSmoothPolyDataFilter is also not an approximation to 111 * an ideal low-pass filter, which can cause the geometry to shrink as the 112 * amount of smoothing increases. 113 * 114 * The second ivar is the specification of the PassBand for the windowed 115 * sinc filter. By design, the PassBand is specified as a doubling point 116 * number between 0 and 2. Lower PassBand values produce more smoothing. 117 * A good default value for the PassBand is 0.1 (for those interested, the 118 * PassBand (and frequencies) for PolyData are based on the valence of the 119 * vertices, this limits all the frequency modes in a polyhedral mesh to 120 * between 0 and 2.) 121 * 122 * There are two instance variables that control the generation of error 123 * data. If the ivar GenerateErrorScalars is on, then a scalar value 124 * indicating the distance of each vertex from its original position is 125 * computed. If the ivar GenerateErrorVectors is on, then a vector 126 * representing change in position is computed. 127 * 128 * @warning 129 * The smoothing operation reduces high frequency information in the 130 * geometry of the mesh. With excessive smoothing important details may be 131 * lost. Enabling FeatureEdgeSmoothing helps reduce this effect, but cannot 132 * entirely eliminate it. 133 * 134 * @warning 135 * For maximum performance, do not enable BoundarySmoothing, 136 * NonManifoldSmoothing, or FeatureEdgeSmoothing. FeatureEdgeSmoothing is 137 * particularly expensive as it requires building topological links and 138 * computing local polygon normals which are relatively expensive 139 * operations. BoundarySmoothing and NonManifoldSmoothing have a modest 140 * impact on performance. 141 * 142 * @warning 143 * This class has been threaded with vtkSMPTools. Using TBB or other 144 * non-sequential execution type (set in the CMake variable 145 * VTK_SMP_IMPLEMENTATION_TYPE) may improve performance significantly. 146 * 147 * @sa 148 * Another useful documentation resource is this SIGGRAPH publication: 149 * Gabriel Taubin. "A Signal Processing Approach To Fair Surface Design". 150 * 151 * @sa 152 * vtkSmoothPolyDataFilter vtkPointSmoothingFilter vtkDecimate vtkDecimatePro 153 * vtkQuadricDecimation 154 */ 155 156 #ifndef vtkWindowedSincPolyDataFilter_h 157 #define vtkWindowedSincPolyDataFilter_h 158 159 #include "vtkFiltersCoreModule.h" // For export macro 160 #include "vtkPolyDataAlgorithm.h" 161 162 class VTKFILTERSCORE_EXPORT vtkWindowedSincPolyDataFilter : public vtkPolyDataAlgorithm 163 { 164 public: 165 vtkTypeMacro(vtkWindowedSincPolyDataFilter, vtkPolyDataAlgorithm); 166 void PrintSelf(ostream& os, vtkIndent indent) override; 167 168 /** 169 * Construct object with number of iterations 20; passband .1; feature edge 170 * smoothing turned off; feature angle 45 degrees; edge angle 15 degrees; 171 * and boundary smoothing turned on. Error scalars and vectors are not 172 * generated (by default). 173 */ 174 static vtkWindowedSincPolyDataFilter* New(); 175 176 ///@{ 177 /** 178 * Specify the number of iterations (i.e., the degree of the polynomial 179 * approximating the windowed sinc function). Typically values around 20 180 * are used. 181 */ 182 vtkSetClampMacro(NumberOfIterations, int, 0, VTK_INT_MAX); 183 vtkGetMacro(NumberOfIterations, int); 184 ///@} 185 186 ///@{ 187 /** 188 * Set the passband value for the windowed sinc filter. 189 */ 190 vtkSetClampMacro(PassBand, double, 0.0, 2.0); 191 vtkGetMacro(PassBand, double); 192 ///@} 193 194 ///@{ 195 /** 196 * Turn on/off coordinate normalization. The positions can be translated 197 * and scaled such that they fit within a [-1, 1] prior to the smoothing 198 * computation. The default is off. The numerical stability of the 199 * solution can be improved by turning normalization on. If normalization 200 * is on, the coordinates will be rescaled to the original coordinate 201 * system after smoothing has completed. 202 */ 203 vtkSetMacro(NormalizeCoordinates, vtkTypeBool); 204 vtkGetMacro(NormalizeCoordinates, vtkTypeBool); 205 vtkBooleanMacro(NormalizeCoordinates, vtkTypeBool); 206 ///@} 207 208 ///@{ 209 /** 210 * Turn on/off smoothing points along sharp interior edges. Enabling this 211 * option has a performance impact on the algorithm since neihborhood 212 * information (cell links) and polygon normals must be computed. 213 */ 214 vtkSetMacro(FeatureEdgeSmoothing, vtkTypeBool); 215 vtkGetMacro(FeatureEdgeSmoothing, vtkTypeBool); 216 vtkBooleanMacro(FeatureEdgeSmoothing, vtkTypeBool); 217 ///@} 218 219 ///@{ 220 /** 221 * Specify the feature angle for sharp edge identification. It only affects 222 * the filter when FeatureEdgeSmoothing is enabled. 223 */ 224 vtkSetClampMacro(FeatureAngle, double, 0.0, 180.0); 225 vtkGetMacro(FeatureAngle, double); 226 ///@} 227 228 ///@{ 229 /** 230 * Specify the edge angle to control smoothing along edges (either interior 231 * or boundary). 232 */ 233 vtkSetClampMacro(EdgeAngle, double, 0.0, 180.0); 234 vtkGetMacro(EdgeAngle, double); 235 ///@} 236 237 ///@{ 238 /** 239 * Turn on/off the smoothing of points on the boundary of the mesh. 240 * Enabled this option has a modest impact on performance. 241 */ 242 vtkSetMacro(BoundarySmoothing, vtkTypeBool); 243 vtkGetMacro(BoundarySmoothing, vtkTypeBool); 244 vtkBooleanMacro(BoundarySmoothing, vtkTypeBool); 245 ///@} 246 247 ///@{ 248 /** 249 * Smooth non-manifold points. Enabling this option has a modest 250 * impact on performance. 251 */ 252 vtkSetMacro(NonManifoldSmoothing, vtkTypeBool); 253 vtkGetMacro(NonManifoldSmoothing, vtkTypeBool); 254 vtkBooleanMacro(NonManifoldSmoothing, vtkTypeBool); 255 ///@} 256 257 ///@{ 258 /** 259 * Turn on/off the generation of scalar distance values. 260 */ 261 vtkSetMacro(GenerateErrorScalars, vtkTypeBool); 262 vtkGetMacro(GenerateErrorScalars, vtkTypeBool); 263 vtkBooleanMacro(GenerateErrorScalars, vtkTypeBool); 264 ///@} 265 266 ///@{ 267 /** 268 * Turn on/off the generation of error vectors. 269 */ 270 vtkSetMacro(GenerateErrorVectors, vtkTypeBool); 271 vtkGetMacro(GenerateErrorVectors, vtkTypeBool); 272 vtkBooleanMacro(GenerateErrorVectors, vtkTypeBool); 273 ///@} 274 275 protected: 276 vtkWindowedSincPolyDataFilter(); 277 ~vtkWindowedSincPolyDataFilter() override = default; 278 279 int RequestData(vtkInformation*, vtkInformationVector**, vtkInformationVector*) override; 280 281 int NumberOfIterations; 282 double PassBand; 283 284 vtkTypeBool NormalizeCoordinates; 285 286 vtkTypeBool FeatureEdgeSmoothing; 287 double FeatureAngle; 288 double EdgeAngle; 289 vtkTypeBool BoundarySmoothing; 290 vtkTypeBool NonManifoldSmoothing; 291 292 vtkTypeBool GenerateErrorScalars; 293 vtkTypeBool GenerateErrorVectors; 294 295 private: 296 vtkWindowedSincPolyDataFilter(const vtkWindowedSincPolyDataFilter&) = delete; 297 void operator=(const vtkWindowedSincPolyDataFilter&) = delete; 298 }; 299 300 #endif 301