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 "swaptriangles.h"
24 #include "guimainwindow.h"
26 #include <vtkCellArray.h>
28 #include "checksurfaceintegrity.h"
29 #include "statistics.h"
31 using namespace GeometryTools
;
33 SwapTriangles::SwapTriangles() : SurfaceOperation()
36 m_FeatureSwap
= false;
37 m_FeatureAngle
= GeometryTools::deg2rad(30);
39 m_SmallAreaSwap
= false;
40 m_SmallAreaRatio
= 1e-3;
42 getSet("surface meshing", "small area ratio for edge-swapping", 1e-3, m_SmallAreaRatio
);
43 getSet("surface meshing", "surface error threshold", 0.05, m_SurfErrorThreshold
);
44 m_SurfErrorThreshold
= deg2rad(m_SurfErrorThreshold
);
47 bool SwapTriangles::testOrientation(stencil_t S
)
50 vec3_t n1_old
= triNormal(m_Grid
, S
.id_node
[0], S
.p1
, S
.p2
);
51 vec3_t n2_old
= triNormal(m_Grid
, S
.id_node
[1], S
.p2
, S
.p1
);
54 vec3_t n1_new
= triNormal(m_Grid
, S
.p1
, S
.id_node
[1], S
.id_node
[0]);
55 vec3_t n2_new
= triNormal(m_Grid
, S
.p2
, S
.id_node
[0], S
.id_node
[1]);
58 vec3_t
x_summit(0,0,0);
61 m_Grid
->GetPoints()->GetPoint(S
.id_node
[0], x
[0].data());
62 m_Grid
->GetPoints()->GetPoint(S
.p1
, x
[1].data());
63 m_Grid
->GetPoints()->GetPoint(S
.id_node
[1], x
[2].data());
64 m_Grid
->GetPoints()->GetPoint(S
.p2
, x
[3].data());
65 for (int k
= 0; k
< 4; ++k
) {
68 for (int k
= 0; k
< 4; ++k
) {
74 l_max
= max(l_max
, (x
[i
]-x
[j
]).abs());
77 vec3_t n
= n1_old
+ n2_old
;
79 x_summit
+= 3*l_max
*n
;
82 double V1_old
= tetraVol(x
[0], x
[1], x
[3], x_summit
, true);
83 double V2_old
= tetraVol(x
[2], x
[3], x
[1], x_summit
, true);
85 double V1_new
= tetraVol(x
[1], x
[2], x
[0], x_summit
, true);
86 double V2_new
= tetraVol(x
[3], x
[0], x
[2], x_summit
, true);
89 if (m_SmallAreaSwap
) {
90 swap_ok
= V1_new
>0 && V2_new
>0;
92 swap_ok
= V1_old
>0 && V2_old
>0 && V1_new
>0 && V2_new
>0;
97 bool SwapTriangles::isEdge(vtkIdType id_node1
, vtkIdType id_node2
)
99 l2g_t nodes
= getPartNodes();
100 g2l_t _nodes
= getPartLocalNodes();
101 l2l_t n2n
= getPartN2N();
104 foreach(int i_node
, n2n
[_nodes
[id_node1
]]) {
105 vtkIdType id_node
= nodes
[i_node
];
106 if( id_node
== id_node2
) ret
= true;
111 double SwapTriangles::computeSurfaceDistance(vec3_t x1
, vec3_t x2
, vec3_t x3
, SurfaceProjection
* proj
)
113 static const double f13
= 1.0/3.0;
114 vec3_t x
= f13
*(x1
+ x2
+ x3
);
115 vec3_t n
= triNormal(x1
, x2
, x3
);
117 if (!checkVector(n
)) {
120 vec3_t xp
= proj
->project(x
, -1, true, n
);
121 n
= proj
->lastProjNormal();
122 xp
= proj
->project(x
, -1, true, n
);
123 return (x
- xp
).abs();
126 double SwapTriangles::edgeAngle(vec3_t x1
, vec3_t x2
, vec3_t x3
, vec3_t x4
)
128 static const double f13
= 1.0/3.0;
129 vec3_t xf1
= f13
*(x1
+ x2
+ x4
);
130 vec3_t xf2
= f13
*(x2
+ x3
+ x4
);
131 vec3_t xe
= 0.5*(x2
+ x4
);
132 vec3_t xfe
= 0.5*(xf1
+ xf2
);
133 vec3_t n1
= triNormal(x1
, x2
, x4
);
134 vec3_t n2
= triNormal(x2
, x3
, x4
);
135 vec3_t ve
= xe
- xfe
;
136 double alpha
= angle(n1
, n2
);
137 if ((n1
+ n2
)*ve
< 0) {
143 vtkIdType
SwapTriangles::neighbourNode(vtkIdType id_node0
, vtkIdType id_node1
, vtkIdType id_node2
)
145 for (int i
= 0; i
< m_Part
.n2nGSize(id_node1
); ++i
) {
146 vtkIdType id_neigh
= m_Part
.n2nGG(id_node1
, i
);
147 if (id_neigh
!= id_node0
) {
148 if (m_Part
.hasNeighNode(id_node2
, id_neigh
)) {
157 bool SwapTriangles::swapDueToSurfaceNoise(stencil_t S
)
159 vtkIdType p1
= S
.id_node
[0];
161 vtkIdType p3
= S
.id_node
[1];
163 vtkIdType p12
= neighbourNode(p4
, p1
, p2
);
164 vtkIdType p23
= neighbourNode(p4
, p2
, p3
);
165 vtkIdType p34
= neighbourNode(p2
, p3
, p4
);
166 vtkIdType p41
= neighbourNode(p2
, p4
, p1
);
167 vec3_t x1
, x2
, x3
, x4
, x12
, x23
, x34
, x41
;
168 m_Grid
->GetPoint(p1
, x1
.data());
169 m_Grid
->GetPoint(p2
, x2
.data());
170 m_Grid
->GetPoint(p3
, x3
.data());
171 m_Grid
->GetPoint(p4
, x4
.data());
172 m_Grid
->GetPoint(p12
, x12
.data());
173 m_Grid
->GetPoint(p23
, x23
.data());
174 m_Grid
->GetPoint(p34
, x34
.data());
175 m_Grid
->GetPoint(p41
, x41
.data());
177 double noise1
, noise2
;
179 double alpha_min
= M_PI
;
180 double alpha_max
= -M_PI
;
183 alpha
= edgeAngle(x4
, x1
, x12
, x2
);
184 alpha_max
= max(alpha
, alpha_max
);
185 alpha_min
= min(alpha
, alpha_min
);
187 alpha
= edgeAngle(x4
, x2
, x23
, x3
);
188 alpha_max
= max(alpha
, alpha_max
);
189 alpha_min
= min(alpha
, alpha_min
);
191 alpha
= edgeAngle(x2
, x3
, x34
, x4
);
192 alpha_max
= max(alpha
, alpha_max
);
193 alpha_min
= min(alpha
, alpha_min
);
195 alpha
= edgeAngle(x2
, x4
, x41
, x1
);
196 alpha_max
= max(alpha
, alpha_max
);
197 alpha_min
= min(alpha
, alpha_min
);
199 alpha
= edgeAngle(x1
, x2
, x3
, x4
);
200 alpha_max
= max(alpha
, alpha_max
);
201 alpha_min
= min(alpha
, alpha_min
);
203 noise1
= alpha_max
- alpha_min
;
206 double alpha_min
= M_PI
;
207 double alpha_max
= -M_PI
;
210 alpha
= edgeAngle(x3
, x1
, x12
, x2
);
211 alpha_max
= max(alpha
, alpha_max
);
212 alpha_min
= min(alpha
, alpha_min
);
214 alpha
= edgeAngle(x1
, x2
, x23
, x3
);
215 alpha_max
= max(alpha
, alpha_max
);
216 alpha_min
= min(alpha
, alpha_min
);
218 alpha
= edgeAngle(x1
, x3
, x34
, x4
);
219 alpha_max
= max(alpha
, alpha_max
);
220 alpha_min
= min(alpha
, alpha_min
);
222 alpha
= edgeAngle(x3
, x4
, x41
, x1
);
223 alpha_max
= max(alpha
, alpha_max
);
224 alpha_min
= min(alpha
, alpha_min
);
226 alpha
= edgeAngle(x2
, x3
, x4
, x1
);
227 alpha_max
= max(alpha
, alpha_max
);
228 alpha_min
= min(alpha
, alpha_min
);
230 noise2
= alpha_max
- alpha_min
;
233 if (noise1
> m_SurfErrorThreshold
&& noise1
> noise2
) {
240 void SwapTriangles::computeSurfaceErrors(const QVector
<vec3_t
> &x
, int bc
, double &err1
, double &err2
)
242 using namespace GeometryTools
;
245 SurfaceProjection
* proj
= GuiMainWindow::pointer()->getSurfProj(bc
);
250 double d013
= computeSurfaceDistance(x
[0], x
[1], x
[3], proj
);
251 double d123
= computeSurfaceDistance(x
[1], x
[2], x
[3], proj
);
252 double d012
= computeSurfaceDistance(x
[0], x
[1], x
[2], proj
);
253 double d023
= computeSurfaceDistance(x
[0], x
[2], x
[3], proj
);
255 double L
= max((x
[1] - x
[3]).abs(), (x
[0] - x
[2]).abs());
256 err1
= max(d013
, d123
)/L
;
257 err2
= max(d012
, d023
)/L
;
258 if (err1
< m_SurfErrorThreshold
) {
261 if (err2
< m_SurfErrorThreshold
) {
266 // new version based on surface normals
267 vec3_t n013 = triNormal(x[0], x[1], x[3]);
268 vec3_t n123 = triNormal(x[1], x[2], x[3]);
269 vec3_t n012 = triNormal(x[0], x[1], x[2]);
270 vec3_t n023 = triNormal(x[0], x[2], x[3]);
275 if (!checkVector(n013)) {
278 if (!checkVector(n123)) {
281 if (!checkVector(n012)) {
284 if (!checkVector(n023)) {
288 vec3_t xe13 = 0.5*(x[1] + x[3]);
289 vec3_t xe02 = 0.5*(x[0] + x[2]);
293 vec3_t xe13_p013 = proj->project(xe13, -1, true, n013);
294 n = proj->lastProjNormal();
295 double d13_013 = fabs((xe13_p013 - xe13)*n);
297 vec3_t xe13_p123 = proj->project(xe13, -1, true, n123);
298 n = proj->lastProjNormal();
299 double d13_123 = fabs((xe13_p123 - xe13)*n);
301 vec3_t xe02_p012 = proj->project(xe02, -1, true, n012);
302 n = proj->lastProjNormal();
303 double d02_012 = fabs((xe02_p012 - xe02)*n);
305 vec3_t xe02_p023 = proj->project(xe02, -1, true, n023);
306 n = proj->lastProjNormal();
307 double d02_023 = fabs((xe02_p023 - xe02)*n);
309 double L = max((x[1] - x[3]).abs(), (x[0] - x[2]).abs());
311 err1 = min(d13_013, d13_123)/L;
312 err2 = min(d02_012, d02_023)/L;
314 if (err1 < m_SurfErrorThreshold) {
317 if (err2 < m_SurfErrorThreshold) {
323 int SwapTriangles::swap()
326 setAllSurfaceCells();
327 //computeAverageSurfaceError();
328 EG_VTKDCC(vtkIntArray
, cell_code
, m_Grid
, "cell_code");
329 EG_VTKDCN(vtkCharArray
, node_type
, m_Grid
, "node_type");
330 QVector
<bool> marked(m_Grid
->GetNumberOfCells(), false);
331 for (int i
= 0; i
< m_Part
.getNumberOfCells(); ++i
) {
332 vtkIdType id_cell
= m_Part
.globalCell(i
);
333 if (!m_BoundaryCodes
.contains(cell_code
->GetValue(id_cell
)) && m_Grid
->GetCellType(id_cell
) == VTK_TRIANGLE
) { //if it is a selected triangle
334 if (!marked
[id_cell
] && !m_Swapped
[id_cell
]) {
335 for (int j
= 0; j
< 3; ++j
) {
337 bool feature_improvement_swap
= false;
338 stencil_t S
= getStencil(id_cell
, j
);
339 if(S
.id_cell
.size() == 2 && S
.sameBC
) {
340 if (S
.type_cell
[1] == VTK_TRIANGLE
) {
341 if(!isEdge(S
.id_node
[0], S
.id_node
[1]) ) {
342 if (!marked
[S
.id_cell
[1]] && !m_Swapped
[S
.id_cell
[1]]) {
343 QVector
<vec3_t
> x3(4);
346 m_Grid
->GetPoint(S
.id_node
[0], x3
[0].data());
347 m_Grid
->GetPoint(S
.p1
, x3
[1].data());
348 m_Grid
->GetPoint(S
.id_node
[1], x3
[2].data());
349 m_Grid
->GetPoint(S
.p2
, x3
[3].data());
351 vec3_t n1
= triNormal(x3
[0], x3
[1], x3
[3]);
352 vec3_t n2
= triNormal(x3
[1], x3
[2], x3
[3]);
354 bool force_swap
= false;
355 if (m_SmallAreaSwap
) {
356 double A1
= n1
.abs();
357 double A2
= n2
.abs();
358 if (isnan(A1
) || isnan(A2
)) {
361 force_swap
= A1
< m_SmallAreaRatio
*A2
|| A2
< m_SmallAreaRatio
*A1
;
364 if (node_type
->GetValue(S
.p1
) != EG_FEATURE_EDGE_VERTEX
&& node_type
->GetValue(S
.p2
) != EG_FEATURE_EDGE_VERTEX
) {
366 if (node_type
->GetValue(S
.id_node
[0]) == EG_FEATURE_EDGE_VERTEX
&& node_type
->GetValue(S
.id_node
[1]) == EG_FEATURE_EDGE_VERTEX
) {
374 vec3_t ex
= orthogonalVector(n
);
375 vec3_t ey
= ex
.cross(n
);
376 for (int k
= 0; k
< 4; ++k
) {
377 x
[k
] = vec2_t(x3
[k
]*ex
, x3
[k
]*ey
);
379 vec2_t r1
, r2
, r3
, u1
, u2
, u3
;
380 r1
= 0.5*(x
[0] + x
[1]); u1
= turnLeft(x
[1] - x
[0]);
381 r2
= 0.5*(x
[1] + x
[2]); u2
= turnLeft(x
[2] - x
[1]);
382 r3
= 0.5*(x
[1] + x
[3]); u3
= turnLeft(x
[3] - x
[1]);
386 if (intersection(k
, l
, r1
, u1
, r3
, u3
)) {
388 if (intersection(k
, l
, r2
, u2
, r3
, u3
)) {
398 if ((xm1
- x
[2]).abs() < (xm1
- x
[0]).abs()) {
401 if ((xm2
- x
[0]).abs() < (xm2
- x
[2]).abs()) {
406 } //end of if feature angle
407 } //end of if l_marked
408 } //end of if TestSwap
413 if (testOrientation(S
)) {
414 marked
[S
.id_cell
[0]] = true;
415 marked
[S
.id_cell
[1]] = true;
416 for (int k
= 0; k
< m_Part
.n2cGSize(S
.id_node
[0]); ++k
) {
417 vtkIdType id_neigh
= m_Part
.n2cGG(S
.id_node
[0], k
);
418 marked
[id_neigh
] = true;
420 for (int k
= 0; k
< m_Part
.n2cGSize(S
.id_node
[1]); ++k
) {
421 vtkIdType id_neigh
= m_Part
.n2cGG(S
.id_node
[1], k
);
422 marked
[id_neigh
] = true;
424 for (int k
= 0; k
< m_Part
.n2cGSize(S
.p1
); ++k
) {
425 vtkIdType id_neigh
= m_Part
.n2cGG(S
.p1
, k
);
426 marked
[id_neigh
] = true;
428 for (int k
= 0; k
< m_Part
.n2cGSize(S
.p2
); ++k
) {
429 vtkIdType id_neigh
= m_Part
.n2cGG(S
.p2
, k
);
430 marked
[id_neigh
] = true;
432 vtkIdType new_pts1
[3], new_pts2
[3];
434 new_pts1
[1] = S
.id_node
[1];
435 new_pts1
[2] = S
.id_node
[0];
436 new_pts2
[0] = S
.id_node
[1];
438 new_pts2
[2] = S
.id_node
[0];
439 vtkIdType old_pts1
[3], old_pts2
[3];
440 old_pts1
[0] = S
.id_node
[0];
443 old_pts2
[0] = S
.id_node
[1];
446 m_Grid
->ReplaceCell(S
.id_cell
[0], 3, new_pts1
);
447 m_Grid
->ReplaceCell(S
.id_cell
[1], 3, new_pts2
);
448 m_Swapped
[S
.id_cell
[0]] = true;
449 m_Swapped
[S
.id_cell
[1]] = true;
454 } //end of loop through sides
456 } //end of if selected triangle
457 } //end of loop through cells
461 void SwapTriangles::computeAverageSurfaceError()
463 EG_VTKDCC(vtkIntArray
, cell_code
, m_Grid
, "cell_code");
464 QList
<double> errors
;
465 EG_FORALL_NODES(id_node1
, m_Grid
) {
466 vec3_t n1
= m_Part
.globalNormal(id_node1
);
468 m_Grid
->GetPoint(id_node1
, x1
.data());
469 for (int i
= 0; i
< m_Part
.n2nGSize(id_node1
); ++i
) {
470 vtkIdType id_node2
= m_Part
.n2nGG(id_node1
, i
);
471 if (id_node1
< id_node2
) {
472 QVector
<vtkIdType
> faces
;
473 getEdgeCells(id_node1
, id_node2
, faces
);
474 if (faces
.size() == 2) {
475 int bc1
= cell_code
->GetValue(faces
[0]);
476 int bc2
= cell_code
->GetValue(faces
[1]);
478 SurfaceProjection
* proj
= GuiMainWindow::pointer()->getSurfProj(bc1
);
480 vec3_t n2
= m_Part
.globalNormal(id_node2
);
481 vec3_t n
= 0.5*(n1
+ n2
);
483 m_Grid
->GetPoint(id_node2
, x2
.data());
484 vec3_t x
= 0.5*(x1
+ x2
);
485 vec3_t xp
= proj
->project(x
, -1, true, n
);
486 double err
= (x
- xp
).abs()/(x1
- x2
).abs();
495 m_AverageSurfaceError
= Statistics::meanValue(errors
);
496 m_SurfaceErrorDeviation
= Statistics::standardDeviation(errors
, m_AverageSurfaceError
);
497 cout
<< "mean: " << m_AverageSurfaceError
<< " sigma: " << m_SurfaceErrorDeviation
<< endl
;
500 void SwapTriangles::operate()
502 if (m_Verbose
) cout
<< "swapping edges for surface triangles ..." << endl
;
503 long int N_swaps
= 100000000;
504 long int N_last_swaps
= 100000001;
506 while ((N_swaps
> 0) && (loop
<= m_MaxNumLoops
) && (N_swaps
< N_last_swaps
)) {
507 N_last_swaps
= N_swaps
;
508 if (m_Verbose
) cout
<< " loop " << loop
<< "/" << m_MaxNumLoops
<< endl
;
509 m_Swapped
.fill(false, m_Grid
->GetNumberOfCells());
516 if (m_Verbose
) cout
<< " sub-loop " << sub_loop
<< ": " << N
<< " swaps" << endl
;