2 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 // + This file is part of enGrid. +
6 // + Copyright 2008-2012 enGits GmbH +
8 // + enGrid is free software: you can redistribute it and/or modify +
9 // + it under the terms of the GNU General Public License as published by +
10 // + the Free Software Foundation, either version 3 of the License, or +
11 // + (at your option) any later version. +
13 // + enGrid is distributed in the hope that it will be useful, +
14 // + but WITHOUT ANY WARRANTY; without even the implied warranty of +
15 // + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +
16 // + GNU General Public License for more details. +
18 // + You should have received a copy of the GNU General Public License +
19 // + along with enGrid. If not, see <http://www.gnu.org/licenses/>. +
21 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
23 #include "laplacesmoother.h"
24 #include <vtkCellLocator.h>
25 #include <vtkCharArray.h>
26 #include <vtkGenericCell.h>
28 #include "guimainwindow.h"
29 #include "globalnodegraphinterface.h"
31 using namespace GeometryTools
;
33 LaplaceSmoother::LaplaceSmoother() : SurfaceOperation()
37 m_UseProjection
= true;
38 // m_UseNormalCorrection = false;
39 getSet("surface meshing", "under relaxation for smoothing", 0.5, m_UnderRelaxation
);
40 getSet("surface meshing", "feature magic", 0.0, m_FeatureMagic
);
41 getSet("surface meshing", "smoothing limiter", 1.0, m_Limit
);
42 getSet("surface meshing", "use uniform smoothing", false, m_UniformSnapPoints
);
43 m_Limit
= min(1.0, max(0.0, m_Limit
));
45 m_ProjectionIterations
= 50;
46 m_AllowedCellTypes
.clear();
47 m_AllowedCellTypes
.insert(VTK_TRIANGLE
);
50 bool LaplaceSmoother::setNewPosition(vtkIdType id_node
, vec3_t x_new
)
52 using namespace GeometryTools
;
55 m_Grid
->GetPoint(id_node
, x_old
.data());
56 m_Grid
->GetPoints()->SetPoint(id_node
, x_new
.data());
59 if(m_NoCheck
) return move
;
61 // compute the extrusion vector to compute the tetrahedrons for volume checking
62 // start with an average of all adjacent cell normals and count the number of
63 // adjacent boundary codes (for one boundary code an alternative vector can be
64 // computed with the help of a SurfaceProjection
67 QVector
<vec3_t
> cell_normals(m_Part
.n2cGSize(id_node
));
69 EG_VTKDCC(vtkIntArray
, cell_code
, m_Grid
, "cell_code");
70 double A_max
= 0; //area of the biggest neighbour cell of id_node
71 for (int i
= 0; i
< m_Part
.n2cGSize(id_node
); ++i
) {
72 double A
= fabs(GeometryTools::cellVA(m_Grid
, m_Part
.n2cGG(id_node
, i
)));
73 A_max
= max(A
, A_max
);
74 cell_normals
[i
] = GeometryTools::cellNormal(m_Grid
, m_Part
.n2cGG(id_node
, i
));
75 cell_normals
[i
].normalise();
76 bcs
.insert(cell_code
->GetValue(m_Part
.n2cGG(id_node
, i
)));
79 for (int i
= 0; i
< m_Part
.n2cGSize(id_node
); ++i
) {
80 double A
= fabs(GeometryTools::cellVA(m_Grid
, m_Part
.n2cGG(id_node
, i
)));
87 SurfaceProjection* proj = NULL;
88 if (bcs.size() == 1) {
89 proj = GuiMainWindow::pointer()->getSurfProj(*bcs.begin());
91 proj->project(x_new, id_node, false, n);
92 n = proj->lastProjNormal();
101 double L_max
= 0;// distance to the furthest neighbour node of id_node
102 for (int i
= 0; i
< m_Part
.n2nGSize(id_node
); ++i
) {
104 m_Grid
->GetPoint(m_Part
.n2nGG(id_node
, i
), xn
.data());
105 double L
= (xn
- x_old
).abs();
106 L_max
= max(L
, L_max
);
110 if(m_CorrectCurvature
) {
111 // better for mesher with interpolation
112 x_summit
= x_new
+ L_max
*n
;
115 // better for mesher without interpolation
116 x_summit
= x_old
+ L_max
*n
;
119 for (int i
= 0; i
< m_Part
.n2cGSize(id_node
); ++i
) {
121 vtkIdType N_pts
, *pts
;
122 m_Grid
->GetCellPoints(m_Part
.n2cGG(id_node
, i
), N_pts
, pts
);
128 for (int j
= 0; j
< N_pts
; ++j
) {
129 m_Grid
->GetPoint(pts
[j
], x
[j
].data());
131 if (GeometryTools::tetraVol(x
[0], x
[1], x
[2], x_summit
, false) <= 0) {
139 for (int i = 0; i < cell_normals.size(); ++i) {
140 for (int j = 0; j < cell_normals.size(); ++j) {
141 if (cell_normals[i]*cell_normals[j] < -100.0) {//Why -100.0?
142 saveGrid(m_Grid, "after_move2");
143 qWarning()<<"Cannot move point "<<id_node<<" because opposite cell_normals.";
145 m_Grid->GetPoints()->SetPoint(id_node, x_old.data());
146 saveGrid(m_Grid, "before_move2");
155 m_Grid
->GetPoints()->SetPoint(id_node
, x_old
.data());
160 void LaplaceSmoother::featureCorrection(vtkIdType id_node
, SurfaceProjection
* proj
, vec3_t
&x_new
)
162 if (m_FeatureMagic
> 0) {
163 EG_VTKDCN(vtkDoubleArray
, cl
, m_Grid
, "node_meshdensity_desired");
164 EG_VTKDCN(vtkCharArray
, node_type
, m_Grid
, "node_type");
165 //if (node_type->GetValue(id_node) == VTK_FEATURE_EDGE_VERTEX) {
167 // "magic" vector to displace node for re-projection
168 vec3_t magic_vector
= m_NodeNormal
[id_node
];
171 vec3_t x1
= proj
->project(x_new
, id_node
, m_CorrectCurvature
);
172 vec3_t n
= proj
->lastProjNormal();
174 // check if mesh normal and projection normal are aligned
175 // .. try to displace node slightly in order to get a proper projection normal
177 if (fabs(n
*magic_vector
) > 0.99) {
178 x_new
+= 0.1*cl
->GetValue(id_node
)*magic_vector
;
179 x1
= proj
->project(x_new
, id_node
, m_CorrectCurvature
);
180 n
= proj
->lastProjNormal();
183 if (fabs(n
*magic_vector
) <= 0.99) {
185 // start the procedure if the vectors are not aligned
188 double L2
= m_FeatureMagic
*cl
->GetValue(id_node
);
189 for (int i
= 0; i
< 30; ++i
) {
190 x_new
= x1
- 0.5*(L1
+ L2
)*magic_vector
;
191 x_new
= proj
->project(x_new
, id_node
, m_CorrectCurvature
, n
);
192 double displacement
= fabs((x_new
- x1
)*n
);
193 if (displacement
> 0.01*cl
->GetValue(id_node
)) {
197 // if there is no significant displacement after the first iteration
198 // the node is probably in a smooth region of the surface
209 x_new
= x1
- L1
*magic_vector
;
210 x_new
= proj
->project(x_new
, id_node
, m_CorrectCurvature
, n
);
213 // If they are still aligned it is an awkward situation.
214 // .. The node might be in a corner already
215 // .. skip iteration in this case
224 bool LaplaceSmoother::moveNode(vtkIdType id_node
, vec3_t
&Dx
)
226 if (!checkVector(Dx
)) {
229 EG_VTKDCN(vtkDoubleArray
, cl
, m_Grid
, "node_meshdensity_desired");
231 m_Grid
->GetPoint(id_node
, x_old
.data());
233 for (int i_relaxation
= 0; i_relaxation
< 1; ++i_relaxation
) {
234 vec3_t x_new
= x_old
+ Dx
;
235 if (m_UseProjection
) {
236 int i_nodes
= m_Part
.localNode(id_node
);
237 if (m_NodeToBc
[i_nodes
].size() == 1 || m_UniformSnapPoints
) {
238 int bc
= m_NodeToBc
[i_nodes
][0];
239 x_new
= GuiMainWindow::pointer()->getSurfProj(bc
)->project(x_new
, id_node
, m_CorrectCurvature
);
240 featureCorrection(id_node
, GuiMainWindow::pointer()->getSurfProj(bc
), x_new
);
242 for (int i_proj_iter
= 0; i_proj_iter
< m_ProjectionIterations
; ++i_proj_iter
) {
243 foreach (int bc
, m_NodeToBc
[i_nodes
]) {
244 x_new
= GuiMainWindow::pointer()->getSurfProj(bc
)->project(x_new
, id_node
, m_CorrectCurvature
);
248 for (int i_proj_iter
= 0; i_proj_iter
< m_ProjectionIterations
; ++i_proj_iter
) {
249 if (m_CorrectCurvature
) {
250 foreach (int bc
, m_NodeToBc
[i_nodes
]) {
251 x_new
= GuiMainWindow::pointer()->getSurfProj(bc
)->correctCurvature(GuiMainWindow::pointer()->getSurfProj(bc
)->lastProjTriangle(), x_new
);
259 // compute the minimal length of any edge adjacent to this node
260 // .. This will be used to limit the node movement.
261 // .. Hopefully jammed topologies can be avoided this way.
263 EG_VTKDCN(vtkDoubleArray
, cl
, m_Grid
, "node_meshdensity_desired");
265 m_Grid
->GetPoint(id_node
, x_old
.data());
266 double L_min
= cl
->GetValue(id_node
);
267 for (int i
= 0; i
< m_Part
.n2nGSize(id_node
); ++i
) {
268 vtkIdType id_neigh
= m_Part
.n2nGG(id_node
, i
);
270 m_Grid
->GetPoint(id_neigh
, x_neigh
.data());
271 L_min
= min(L_min
, (x_old
- x_neigh
).abs());
274 // limit node displacement
275 vec3_t dx
= x_new
- x_old
;
276 if (dx
.abs() > m_Limit
*L_min
) {
279 x_new
+= m_Limit
*L_min
*dx
;
282 if (setNewPosition(id_node
, x_new
)) {
283 //if (m_NodeMovementCheck.moveNode(id_node, x_new)) {
293 void LaplaceSmoother::fixNodes(const QVector
<bool> &fixnodes
)
295 if (fixnodes
.size() != m_Grid
->GetNumberOfPoints()) {
301 void LaplaceSmoother::operate()
303 if (m_Fixed
.size() != m_Grid
->GetNumberOfPoints()) {
304 m_Fixed
.fill(false, m_Grid
->GetNumberOfPoints());
307 GuiMainWindow::pointer()->getAllBoundaryCodes(bcs
);
308 if (m_UseProjection
) {
309 foreach (int bc
, bcs
) {
310 GuiMainWindow::pointer()->getSurfProj(bc
)->setForegroundGrid(m_Grid
);
313 UpdatePotentialSnapPoints(false, false);
314 EG_VTKDCC(vtkIntArray
, cell_code
, m_Grid
, "cell_code");
315 EG_VTKDCN(vtkCharArray
, node_type
, m_Grid
, "node_type" );
316 EG_VTKDCN(vtkDoubleArray
, cl
, m_Grid
, "node_meshdensity_desired");
317 QVector
<vtkIdType
> smooth_node(m_Grid
->GetNumberOfPoints(), false);
319 l2g_t nodes
= m_Part
.getNodes();
320 foreach (vtkIdType id_node
, nodes
) {
321 smooth_node
[id_node
] = true;
324 setAllSurfaceCells();
325 l2g_t nodes
= m_Part
.getNodes();
326 m_NodeToBc
.resize(nodes
.size());
327 for (int i_nodes
= 0; i_nodes
< nodes
.size(); ++i_nodes
) {
329 for (int j
= 0; j
< m_Part
.n2cLSize(i_nodes
); ++j
) {
330 bcs
.insert(cell_code
->GetValue(m_Part
.n2cLG(i_nodes
, j
)));
332 m_NodeToBc
[i_nodes
].resize(bcs
.size());
333 qCopy(bcs
.begin(), bcs
.end(), m_NodeToBc
[i_nodes
].begin());
336 QVector
<vec3_t
> x_new(nodes
.size());
338 QVector
<bool> blocked(nodes
.size(), false);
339 for (int i_nodes
= 0; i_nodes
< nodes
.size(); ++i_nodes
) {
340 for (int i
= 0; i
< m_Part
.n2cLSize(i_nodes
); ++i
) {
341 vtkIdType type
= m_Grid
->GetCellType(m_Part
.n2cLG(i_nodes
, i
));
342 if (!m_AllowedCellTypes
.contains(type
)) {
343 blocked
[i_nodes
] = true;
349 for (int i_iter
= 0; i_iter
< m_NumberOfIterations
; ++i_iter
) {
352 for (int i_nodes
= 0; i_nodes
< nodes
.size(); ++i_nodes
) {
353 vtkIdType id_node
= nodes
[i_nodes
];
354 if (!m_Fixed
[id_node
] && !blocked
[i_nodes
]) {
355 if (smooth_node
[id_node
] && node_type
->GetValue(id_node
) != VTK_FIXED_VERTEX
) {
356 if (node_type
->GetValue(id_node
) != VTK_FIXED_VERTEX
) {
357 QVector
<vtkIdType
> snap_points
= getPotentialSnapPoints(id_node
);
359 if (snap_points
.size() > 0) {
362 x_new
[i_nodes
] = vec3_t(0,0,0);
363 m_Grid
->GetPoint(id_node
, x_old
.data());
366 foreach (vtkIdType id_snap_node
, snap_points
) {
367 m_Grid
->GetPoint(id_snap_node
, x
.data());
370 x_new
[i_nodes
] += w
*x
;
371 n
+= m_NodeNormal
[id_snap_node
];
372 double L
= (x
- x_old
).abs();
373 L_min
= min(L
, L_min
);
376 x_new
[i_nodes
] *= 1.0/w_tot
;
378 if (m_UseNormalCorrection
) {
379 vec3_t dx
= x_new
[i_nodes
] - x_old
;
380 double scal
= dx
*m_NodeNormal
[id_node
];
381 x_new
[i_nodes
] += scal
*m_NodeNormal
[id_node
];
383 vec3_t Dx
= x_new
[i_nodes
] - x_old
;
384 Dx
*= m_UnderRelaxation
;
385 if (moveNode(id_node
, Dx
)) {
386 x_new
[i_nodes
] = x_old
+ Dx
;
388 x_new
[i_nodes
] = x_old
;