2 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 // + This file is part of enGrid. +
6 // + Copyright 2008-2013 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 "localnodegraphinterface.h"
30 #include "checkerboardgraphiterator.h"
32 using namespace GeometryTools
;
34 LaplaceSmoother::LaplaceSmoother() : SurfaceOperation()
38 m_UseProjection
= true;
39 // m_UseNormalCorrection = false;
40 getSet("surface meshing", "under relaxation for smoothing", 0.5, m_UnderRelaxation
);
41 getSet("surface meshing", "feature magic", 1.0, m_FeatureMagic
);
42 getSet("surface meshing", "smoothing limiter", 1.0, m_Limit
);
43 getSet("surface meshing", "use uniform smoothing", false, m_UniformSnapPoints
);
44 m_Limit
= min(1.0, max(0.0, m_Limit
));
46 m_ProjectionIterations
= 50;
47 m_AllowedCellTypes
.clear();
48 m_AllowedCellTypes
.insert(VTK_TRIANGLE
);
49 //m_StrictFeatureSnap = false;
52 bool LaplaceSmoother::setNewPosition(vtkIdType id_node
, vec3_t x_new
)
54 using namespace GeometryTools
;
57 m_Grid
->GetPoint(id_node
, x_old
.data());
58 EG_VTKDCN(vtkCharArray
, node_type
, m_Grid
, "node_type");
63 QVector
<vec3_t
> old_cell_normals(m_Part
.n2cGSize(id_node
));
64 EG_VTKDCC(vtkIntArray
, cell_code
, m_Grid
, "cell_code");
65 for (int i
= 0; i
< m_Part
.n2cGSize(id_node
); ++i
) {
66 old_cell_normals
[i
] = GeometryTools::cellNormal(m_Grid
, m_Part
.n2cGG(id_node
, i
));
68 m_Grid
->GetPoints()->SetPoint(id_node
, x_new
.data());
70 for (int i
= 0; i
< m_Part
.n2cGSize(id_node
); ++i
) {
71 vec3_t n
= GeometryTools::cellNormal(m_Grid
, m_Part
.n2cGG(id_node
, i
));
72 if (n
*old_cell_normals
[i
] < 0.2*old_cell_normals
[i
].abs2()) {
78 m_Grid
->GetPoints()->SetPoint(id_node
, x_old
.data());
83 void LaplaceSmoother::featureCorrection(vtkIdType id_node
, CadInterface
*cad_interface
, vec3_t
&x_new
)
85 EG_VTKDCN(vtkDoubleArray
, node_mesh_quality
, m_Grid
, "node_mesh_quality");
86 if (m_FeatureMagic
> 0 && node_mesh_quality
->GetValue(id_node
) > m_FaceOrientationThreshold
) {
88 EG_VTKDCN(vtkDoubleArray
, cl
, m_Grid
, "node_meshdensity_desired");
89 EG_VTKDCN(vtkCharArray
, node_type
, m_Grid
, "node_type");
90 bool convex
= isConvexNode(id_node
);
91 if (node_type
->GetValue(id_node
) == EG_FEATURE_CORNER_VERTEX
) {
94 double L
= 0.1*cl
->GetValue(id_node
);
96 // do not use curvature correction here
97 // .. a proper CAD model does not need it
98 // .. a discrete model (e.g. triangulation) will create bulges on features
99 vec3_t x0
= cad_interface
->projectNode(id_node
, x_new
, false);
102 x
= x0
- L
*m_NodeNormal
[id_node
];
104 x
= x0
+ L
*m_NodeNormal
[id_node
];
106 vec3_t n
= cad_interface
->getLastNormal();
107 if (!cad_interface
->failed()) {
108 double d
= 2*L
/tan(0.5*m_FeatureAngle
);
109 static const int num_steps
= 36;
110 double D_alpha
= 2*M_PI
/num_steps
;
113 v
= GeometryTools::orthogonalVector(m_NodeNormal
[id_node
]);
116 vec3_t
x_corner(0,0,0);
117 for (int i
= 0; i
< num_steps
; ++i
) {
118 v
= GeometryTools::rotate(v
, m_NodeNormal
[id_node
], D_alpha
);
119 vec3_t xp
= cad_interface
->projectNode(id_node
, x
, v
, true, true);
120 if (cad_interface
->failed()) {
123 double l
= (x
- xp
).abs();
132 if (num_miss
== 0 && num_hit
> 0) {
133 x_corner
*= 1.0/num_hit
;
134 x_new
= cad_interface
->projectNode(id_node
, x_corner
, m_NodeNormal
[id_node
]);
140 // "magic" vector to displace node for re-projection
143 // do not use curvature correction here
144 // .. a proper CAD model does not need it
145 // .. a discrete model (e.g. triangulation) will create bulges on features
146 vec3_t x1
= cad_interface
->projectNode(id_node
, x_new
);
147 vec3_t n
= cad_interface
->getLastNormal();
148 double l
= cl
->GetValue(id_node
);
150 vec3_t mv
= l
*m_NodeNormal
[id_node
];
153 if (checkVector(mv
)) {
155 double L2
= 1;//m_FeatureMagic*cl->GetValue(id_node);
156 bool flipped
= false;
160 while (i
< 30 && amp
< 10) {
161 x_new
= x1
+ 0.5*amp
*(L1
+ L2
)*mv
;// + 2*eps*n;
162 //x_new = proj->findClosest(x_new, id_node, n);
163 x_new
= cad_interface
->projectNode(id_node
, x_new
, n
, false, m_CorrectCurvature
);
164 double displacement
= fabs((x_new
- x1
)*n
);
165 if (displacement
> eps
|| cad_interface
->failed()) {
170 // if there is no significant displacement after the first iteration
171 // the node is probably in a smooth region of the surface
191 x_new
= x1
+ L1
*m_FeatureMagic
*amp
*mv
;
192 x_new
= cad_interface
->projectNode(id_node
, x_new
, n
, false, m_CorrectCurvature
);
193 if (cad_interface
->failed()) {
194 cout
<< "bad!" << endl
;
204 bool LaplaceSmoother::moveNode(vtkIdType id_node
, vec3_t
&Dx
)
206 if (!checkVector(Dx
)) {
209 EG_VTKDCN(vtkDoubleArray
, cl
, m_Grid
, "node_meshdensity_desired");
211 m_Grid
->GetPoint(id_node
, x_old
.data());
214 for (int i_relaxation
= 0; i_relaxation
< 10; ++i_relaxation
) {
215 vec3_t x_new
= x_old
+ Dx
;
216 if (m_UseProjection
) {
217 int i_nodes
= m_Part
.localNode(id_node
);
218 if (m_NodeToBc
[i_nodes
].size() == 1 || m_UniformSnapPoints
) {
219 int bc
= m_NodeToBc
[i_nodes
][0];
220 x_new
= GuiMainWindow::pointer()->getCadInterface(bc
)->snapNode(id_node
, x_new
, m_CorrectCurvature
);
221 featureCorrection(id_node
, GuiMainWindow::pointer()->getCadInterface(bc
), x_new
);
223 for (int i_proj_iter
= 0; i_proj_iter
< m_ProjectionIterations
; ++i_proj_iter
) {
224 foreach (int bc
, m_NodeToBc
[i_nodes
]) {
225 x_new
= GuiMainWindow::pointer()->getCadInterface(bc
)->snapNode(id_node
, x_new
, m_CorrectCurvature
);
229 for (int i_proj_iter
= 0; i_proj_iter
< m_ProjectionIterations
; ++i_proj_iter
) {
230 if (m_CorrectCurvature
) {
231 foreach (int bc
, m_NodeToBc
[i_nodes
]) {
232 //x_new = GuiMainWindow::pointer()->getCadInterface(bc)->correctCurvature(GuiMainWindow::pointer()->getCadInterface(bc)->lastProjTriangle(), x_new);
233 x_new
= GuiMainWindow::pointer()->getCadInterface(bc
)->correctCurvature(x_new
);
241 // compute the minimal length of any edge adjacent to this node
242 // .. This will be used to limit the node movement.
243 // .. Hopefully jammed topologies can be avoided this way.
245 EG_VTKDCN(vtkDoubleArray
, cl
, m_Grid
, "node_meshdensity_desired");
247 m_Grid
->GetPoint(id_node
, x_old
.data());
248 double L_min
= cl
->GetValue(id_node
);
249 for (int i
= 0; i
< m_Part
.n2nGSize(id_node
); ++i
) {
250 vtkIdType id_neigh
= m_Part
.n2nGG(id_node
, i
);
252 m_Grid
->GetPoint(id_neigh
, x_neigh
.data());
253 L_min
= min(L_min
, (x_old
- x_neigh
).abs());
256 // limit node displacement
257 vec3_t dx
= x_new
- x_old
;
258 if (dx
.abs() > m_Limit
*L_min
) {
261 x_new
+= m_Limit
*L_min
*dx
;
264 if (setNewPosition(id_node
, x_new
)) {
265 m_Grid
->GetPoints()->SetPoint(id_node
, x_new
.data());
275 void LaplaceSmoother::fixNodes(const QVector
<bool> &fixnodes
)
277 if (fixnodes
.size() != m_Grid
->GetNumberOfPoints()) {
283 void LaplaceSmoother::operate()
285 if (m_BCodeFeatureDefinition
) {
286 m_FeatureMagic
= 0.0;
292 if (m_Fixed
.size() != m_Grid
->GetNumberOfPoints()) {
293 m_Fixed
.fill(false, m_Grid
->GetNumberOfPoints());
296 GuiMainWindow::pointer()->getAllBoundaryCodes(bcs
);
297 if (m_UseProjection
) {
298 foreach (int bc
, bcs
) {
299 GuiMainWindow::pointer()->getCadInterface(bc
)->setForegroundGrid(m_Grid
);
303 EG_VTKDCC(vtkIntArray
, cell_code
, m_Grid
, "cell_code");
304 EG_VTKDCN(vtkCharArray
, node_type
, m_Grid
, "node_type" );
305 EG_VTKDCN(vtkDoubleArray
, cl
, m_Grid
, "node_meshdensity_desired");
306 QVector
<vtkIdType
> smooth_node(m_Grid
->GetNumberOfPoints(), false);
308 l2g_t nodes
= m_Part
.getNodes();
309 foreach (vtkIdType id_node
, nodes
) {
310 smooth_node
[id_node
] = true;
313 setAllSurfaceCells();
314 l2g_t nodes
= m_Part
.getNodes();
315 m_NodeToBc
.resize(nodes
.size());
316 for (int i_nodes
= 0; i_nodes
< nodes
.size(); ++i_nodes
) {
318 for (int j
= 0; j
< m_Part
.n2cLSize(i_nodes
); ++j
) {
319 bcs
.insert(cell_code
->GetValue(m_Part
.n2cLG(i_nodes
, j
)));
321 m_NodeToBc
[i_nodes
].resize(bcs
.size());
322 qCopy(bcs
.begin(), bcs
.end(), m_NodeToBc
[i_nodes
].begin());
325 QVector
<vec3_t
> x_new(nodes
.size());
327 QVector
<bool> blocked(nodes
.size(), false);
328 for (int i_nodes
= 0; i_nodes
< nodes
.size(); ++i_nodes
) {
329 for (int i
= 0; i
< m_Part
.n2cLSize(i_nodes
); ++i
) {
330 vtkIdType type
= m_Grid
->GetCellType(m_Part
.n2cLG(i_nodes
, i
));
331 if (!m_AllowedCellTypes
.contains(type
)) {
332 blocked
[i_nodes
] = true;
338 for (int i_iter
= 0; i_iter
< m_NumberOfIterations
; ++i_iter
) {
342 for (int i_nodes
= 0; i_nodes
< nodes
.size(); ++i_nodes
) {
343 vtkIdType id_node
= nodes
[i_nodes
];
344 m_Grid
->GetPoint(id_node
, x_new
[i_nodes
].data());
345 if (!m_Fixed
[id_node
] && !blocked
[i_nodes
]) {
346 if (smooth_node
[id_node
] && node_type
->GetValue(id_node
) != EG_FIXED_VERTEX
) {
347 if (node_type
->GetValue(id_node
) != EG_FIXED_VERTEX
) {
348 QVector
<vtkIdType
> snap_points
= getPotentialSnapPoints(id_node
);
350 vec3_t x_old
= x_new
[i_nodes
];
352 if (snap_points
.size() > 0) {
354 x_new
[i_nodes
] = vec3_t(0,0,0);
357 foreach (vtkIdType id_snap_node
, snap_points
) {
358 m_Grid
->GetPoint(id_snap_node
, x
.data());
361 x_new
[i_nodes
] += w
*x
;
362 n
+= m_NodeNormal
[id_snap_node
];
363 double L
= (x
- x_old
).abs();
364 L_min
= min(L
, L_min
);
367 x_new
[i_nodes
] *= 1.0/w_tot
;
369 x_new
[i_nodes
] = x_old
;
372 if (m_UseNormalCorrection
) {
373 vec3_t dx
= x_new
[i_nodes
] - x_old
;
374 double scal
= dx
*m_NodeNormal
[id_node
];
375 x_new
[i_nodes
] += scal
*m_NodeNormal
[id_node
];
377 vec3_t Dx
= x_new
[i_nodes
] - x_old
;
378 //Dx *= m_UnderRelaxation;
379 if (moveNode(id_node
, Dx
)) {
380 x_new
[i_nodes
] = x_old
+ m_UnderRelaxation
*Dx
;
382 x_new
[i_nodes
] = x_old
;
385 m_Grid
->GetPoints()->SetPoint(id_node
, x_old
.data());
390 for (int i_nodes
= 0; i_nodes
< nodes
.size(); ++i_nodes
) {
391 vtkIdType id_node
= nodes
[i_nodes
];
392 m_Grid
->GetPoints()->SetPoint(id_node
, x_new
[i_nodes
].data());