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
->snapNode(x
, -1, false);
121 return (x
- xp
).abs();
124 double SwapTriangles::edgeAngle(vec3_t x1
, vec3_t x2
, vec3_t x3
, vec3_t x4
)
126 static const double f13
= 1.0/3.0;
127 vec3_t xf1
= f13
*(x1
+ x2
+ x4
);
128 vec3_t xf2
= f13
*(x2
+ x3
+ x4
);
129 vec3_t xe
= 0.5*(x2
+ x4
);
130 vec3_t xfe
= 0.5*(xf1
+ xf2
);
131 vec3_t n1
= triNormal(x1
, x2
, x4
);
132 vec3_t n2
= triNormal(x2
, x3
, x4
);
133 vec3_t ve
= xe
- xfe
;
134 double alpha
= angle(n1
, n2
);
135 if ((n1
+ n2
)*ve
< 0) {
141 vtkIdType
SwapTriangles::neighbourNode(vtkIdType id_node0
, vtkIdType id_node1
, vtkIdType id_node2
)
143 for (int i
= 0; i
< m_Part
.n2nGSize(id_node1
); ++i
) {
144 vtkIdType id_neigh
= m_Part
.n2nGG(id_node1
, i
);
145 if (id_neigh
!= id_node0
) {
146 if (m_Part
.hasNeighNode(id_node2
, id_neigh
)) {
155 bool SwapTriangles::swapDueToSurfaceNoise(stencil_t S
)
157 vtkIdType p1
= S
.id_node
[0];
159 vtkIdType p3
= S
.id_node
[1];
161 vtkIdType p12
= neighbourNode(p4
, p1
, p2
);
162 vtkIdType p23
= neighbourNode(p4
, p2
, p3
);
163 vtkIdType p34
= neighbourNode(p2
, p3
, p4
);
164 vtkIdType p41
= neighbourNode(p2
, p4
, p1
);
165 vec3_t x1
, x2
, x3
, x4
, x12
, x23
, x34
, x41
;
166 m_Grid
->GetPoint(p1
, x1
.data());
167 m_Grid
->GetPoint(p2
, x2
.data());
168 m_Grid
->GetPoint(p3
, x3
.data());
169 m_Grid
->GetPoint(p4
, x4
.data());
170 m_Grid
->GetPoint(p12
, x12
.data());
171 m_Grid
->GetPoint(p23
, x23
.data());
172 m_Grid
->GetPoint(p34
, x34
.data());
173 m_Grid
->GetPoint(p41
, x41
.data());
175 double noise1
, noise2
;
177 double alpha_min
= M_PI
;
178 double alpha_max
= -M_PI
;
181 alpha
= edgeAngle(x4
, x1
, x12
, x2
);
182 alpha_max
= max(alpha
, alpha_max
);
183 alpha_min
= min(alpha
, alpha_min
);
185 alpha
= edgeAngle(x4
, x2
, x23
, x3
);
186 alpha_max
= max(alpha
, alpha_max
);
187 alpha_min
= min(alpha
, alpha_min
);
189 alpha
= edgeAngle(x2
, x3
, x34
, x4
);
190 alpha_max
= max(alpha
, alpha_max
);
191 alpha_min
= min(alpha
, alpha_min
);
193 alpha
= edgeAngle(x2
, x4
, x41
, x1
);
194 alpha_max
= max(alpha
, alpha_max
);
195 alpha_min
= min(alpha
, alpha_min
);
197 alpha
= edgeAngle(x1
, x2
, x3
, x4
);
198 alpha_max
= max(alpha
, alpha_max
);
199 alpha_min
= min(alpha
, alpha_min
);
201 noise1
= alpha_max
- alpha_min
;
204 double alpha_min
= M_PI
;
205 double alpha_max
= -M_PI
;
208 alpha
= edgeAngle(x3
, x1
, x12
, x2
);
209 alpha_max
= max(alpha
, alpha_max
);
210 alpha_min
= min(alpha
, alpha_min
);
212 alpha
= edgeAngle(x1
, x2
, x23
, x3
);
213 alpha_max
= max(alpha
, alpha_max
);
214 alpha_min
= min(alpha
, alpha_min
);
216 alpha
= edgeAngle(x1
, x3
, x34
, x4
);
217 alpha_max
= max(alpha
, alpha_max
);
218 alpha_min
= min(alpha
, alpha_min
);
220 alpha
= edgeAngle(x3
, x4
, x41
, x1
);
221 alpha_max
= max(alpha
, alpha_max
);
222 alpha_min
= min(alpha
, alpha_min
);
224 alpha
= edgeAngle(x2
, x3
, x4
, x1
);
225 alpha_max
= max(alpha
, alpha_max
);
226 alpha_min
= min(alpha
, alpha_min
);
228 noise2
= alpha_max
- alpha_min
;
231 if (noise1
> m_SurfErrorThreshold
&& noise1
> noise2
) {
238 void SwapTriangles::computeSurfaceErrors(const QVector
<vec3_t
> &x
, int bc
, double &err1
, double &err2
)
240 using namespace GeometryTools
;
243 SurfaceProjection
* proj
= GuiMainWindow::pointer()->getSurfProj(bc
);
248 double d013
= computeSurfaceDistance(x
[0], x
[1], x
[3], proj
);
249 double d123
= computeSurfaceDistance(x
[1], x
[2], x
[3], proj
);
250 double d012
= computeSurfaceDistance(x
[0], x
[1], x
[2], proj
);
251 double d023
= computeSurfaceDistance(x
[0], x
[2], x
[3], proj
);
253 double L
= max((x
[1] - x
[3]).abs(), (x
[0] - x
[2]).abs());
254 err1
= max(d013
, d123
)/L
;
255 err2
= max(d012
, d023
)/L
;
256 if (err1
< m_SurfErrorThreshold
) {
259 if (err2
< m_SurfErrorThreshold
) {
264 // new version based on surface normals
265 vec3_t n013 = triNormal(x[0], x[1], x[3]);
266 vec3_t n123 = triNormal(x[1], x[2], x[3]);
267 vec3_t n012 = triNormal(x[0], x[1], x[2]);
268 vec3_t n023 = triNormal(x[0], x[2], x[3]);
273 if (!checkVector(n013)) {
276 if (!checkVector(n123)) {
279 if (!checkVector(n012)) {
282 if (!checkVector(n023)) {
286 vec3_t xe13 = 0.5*(x[1] + x[3]);
287 vec3_t xe02 = 0.5*(x[0] + x[2]);
291 vec3_t xe13_p013 = proj->project(xe13, -1, true, n013);
292 n = proj->lastProjNormal();
293 double d13_013 = fabs((xe13_p013 - xe13)*n);
295 vec3_t xe13_p123 = proj->project(xe13, -1, true, n123);
296 n = proj->lastProjNormal();
297 double d13_123 = fabs((xe13_p123 - xe13)*n);
299 vec3_t xe02_p012 = proj->project(xe02, -1, true, n012);
300 n = proj->lastProjNormal();
301 double d02_012 = fabs((xe02_p012 - xe02)*n);
303 vec3_t xe02_p023 = proj->project(xe02, -1, true, n023);
304 n = proj->lastProjNormal();
305 double d02_023 = fabs((xe02_p023 - xe02)*n);
307 double L = max((x[1] - x[3]).abs(), (x[0] - x[2]).abs());
309 err1 = min(d13_013, d13_123)/L;
310 err2 = min(d02_012, d02_023)/L;
312 if (err1 < m_SurfErrorThreshold) {
315 if (err2 < m_SurfErrorThreshold) {
321 int SwapTriangles::swap()
324 setAllSurfaceCells();
325 //computeAverageSurfaceError();
326 EG_VTKDCC(vtkIntArray
, cell_code
, m_Grid
, "cell_code");
327 EG_VTKDCN(vtkCharArray
, node_type
, m_Grid
, "node_type");
328 QVector
<bool> marked(m_Grid
->GetNumberOfCells(), false);
329 for (int i
= 0; i
< m_Part
.getNumberOfCells(); ++i
) {
330 vtkIdType id_cell
= m_Part
.globalCell(i
);
331 if (!m_BoundaryCodes
.contains(cell_code
->GetValue(id_cell
)) && m_Grid
->GetCellType(id_cell
) == VTK_TRIANGLE
) { //if it is a selected triangle
332 if (!marked
[id_cell
] && !m_Swapped
[id_cell
]) {
333 for (int j
= 0; j
< 3; ++j
) {
335 bool feature_improvement_swap
= false;
336 stencil_t S
= getStencil(id_cell
, j
);
337 if(S
.id_cell
.size() == 2 && S
.sameBC
) {
338 if (S
.type_cell
[1] == VTK_TRIANGLE
) {
339 if(!isEdge(S
.id_node
[0], S
.id_node
[1]) ) {
340 if (!marked
[S
.id_cell
[1]] && !m_Swapped
[S
.id_cell
[1]]) {
341 QVector
<vec3_t
> x3(4);
344 m_Grid
->GetPoint(S
.id_node
[0], x3
[0].data());
345 m_Grid
->GetPoint(S
.p1
, x3
[1].data());
346 m_Grid
->GetPoint(S
.id_node
[1], x3
[2].data());
347 m_Grid
->GetPoint(S
.p2
, x3
[3].data());
349 vec3_t n1
= triNormal(x3
[0], x3
[1], x3
[3]);
350 vec3_t n2
= triNormal(x3
[1], x3
[2], x3
[3]);
352 bool force_swap
= false;
353 if (m_SmallAreaSwap
) {
354 double A1
= n1
.abs();
355 double A2
= n2
.abs();
356 if (isnan(A1
) || isnan(A2
)) {
359 force_swap
= A1
< m_SmallAreaRatio
*A2
|| A2
< m_SmallAreaRatio
*A1
;
362 if (!isFeatureNode(S
.p1
) && !isFeatureNode(S
.p2
)) {
364 if (isFeatureNode(S
.id_node
[0]) && isFeatureNode(S
.id_node
[1])) {
372 vec3_t ex
= orthogonalVector(n
);
373 vec3_t ey
= ex
.cross(n
);
374 for (int k
= 0; k
< 4; ++k
) {
375 x
[k
] = vec2_t(x3
[k
]*ex
, x3
[k
]*ey
);
377 vec2_t r1
, r2
, r3
, u1
, u2
, u3
;
378 r1
= 0.5*(x
[0] + x
[1]); u1
= turnLeft(x
[1] - x
[0]);
379 r2
= 0.5*(x
[1] + x
[2]); u2
= turnLeft(x
[2] - x
[1]);
380 r3
= 0.5*(x
[1] + x
[3]); u3
= turnLeft(x
[3] - x
[1]);
384 if (intersection(k
, l
, r1
, u1
, r3
, u3
)) {
386 if (intersection(k
, l
, r2
, u2
, r3
, u3
)) {
396 if ((xm1
- x
[2]).abs() < (xm1
- x
[0]).abs()) {
399 if ((xm2
- x
[0]).abs() < (xm2
- x
[2]).abs()) {
404 } //end of if feature angle
405 } //end of if l_marked
406 } //end of if TestSwap
411 if (testOrientation(S
)) {
412 marked
[S
.id_cell
[0]] = true;
413 marked
[S
.id_cell
[1]] = true;
414 for (int k
= 0; k
< m_Part
.n2cGSize(S
.id_node
[0]); ++k
) {
415 vtkIdType id_neigh
= m_Part
.n2cGG(S
.id_node
[0], k
);
416 marked
[id_neigh
] = true;
418 for (int k
= 0; k
< m_Part
.n2cGSize(S
.id_node
[1]); ++k
) {
419 vtkIdType id_neigh
= m_Part
.n2cGG(S
.id_node
[1], k
);
420 marked
[id_neigh
] = true;
422 for (int k
= 0; k
< m_Part
.n2cGSize(S
.p1
); ++k
) {
423 vtkIdType id_neigh
= m_Part
.n2cGG(S
.p1
, k
);
424 marked
[id_neigh
] = true;
426 for (int k
= 0; k
< m_Part
.n2cGSize(S
.p2
); ++k
) {
427 vtkIdType id_neigh
= m_Part
.n2cGG(S
.p2
, k
);
428 marked
[id_neigh
] = true;
430 vtkIdType new_pts1
[3], new_pts2
[3];
432 new_pts1
[1] = S
.id_node
[1];
433 new_pts1
[2] = S
.id_node
[0];
434 new_pts2
[0] = S
.id_node
[1];
436 new_pts2
[2] = S
.id_node
[0];
437 vtkIdType old_pts1
[3], old_pts2
[3];
438 old_pts1
[0] = S
.id_node
[0];
441 old_pts2
[0] = S
.id_node
[1];
444 m_Grid
->ReplaceCell(S
.id_cell
[0], 3, new_pts1
);
445 m_Grid
->ReplaceCell(S
.id_cell
[1], 3, new_pts2
);
446 m_Swapped
[S
.id_cell
[0]] = true;
447 m_Swapped
[S
.id_cell
[1]] = true;
452 } //end of loop through sides
454 } //end of if selected triangle
455 } //end of loop through cells
459 void SwapTriangles::computeAverageSurfaceError()
461 EG_VTKDCC(vtkIntArray
, cell_code
, m_Grid
, "cell_code");
462 QList
<double> errors
;
463 EG_FORALL_NODES(id_node1
, m_Grid
) {
464 vec3_t n1
= m_Part
.globalNormal(id_node1
);
466 m_Grid
->GetPoint(id_node1
, x1
.data());
467 for (int i
= 0; i
< m_Part
.n2nGSize(id_node1
); ++i
) {
468 vtkIdType id_node2
= m_Part
.n2nGG(id_node1
, i
);
469 if (id_node1
< id_node2
) {
470 QVector
<vtkIdType
> faces
;
471 getEdgeCells(id_node1
, id_node2
, faces
);
472 if (faces
.size() == 2) {
473 int bc1
= cell_code
->GetValue(faces
[0]);
474 int bc2
= cell_code
->GetValue(faces
[1]);
476 SurfaceProjection
* proj
= GuiMainWindow::pointer()->getSurfProj(bc1
);
478 vec3_t n2
= m_Part
.globalNormal(id_node2
);
479 vec3_t n
= 0.5*(n1
+ n2
);
481 m_Grid
->GetPoint(id_node2
, x2
.data());
482 vec3_t x
= 0.5*(x1
+ x2
);
483 vec3_t xp
= proj
->snapNode(x
, -1, false);
484 double err
= (x
- xp
).abs()/(x1
- x2
).abs();
493 m_AverageSurfaceError
= Statistics::meanValue(errors
);
494 m_SurfaceErrorDeviation
= Statistics::standardDeviation(errors
, m_AverageSurfaceError
);
495 cout
<< "mean: " << m_AverageSurfaceError
<< " sigma: " << m_SurfaceErrorDeviation
<< endl
;
498 void SwapTriangles::operate()
500 if (m_Verbose
) cout
<< "swapping edges for surface triangles ..." << endl
;
501 long int N_swaps
= 100000000;
502 long int N_last_swaps
= 100000001;
504 while ((N_swaps
> 0) && (loop
<= m_MaxNumLoops
) && (N_swaps
< N_last_swaps
)) {
505 N_last_swaps
= N_swaps
;
506 if (m_Verbose
) cout
<< " loop " << loop
<< "/" << m_MaxNumLoops
<< endl
;
507 m_Swapped
.fill(false, m_Grid
->GetNumberOfCells());
514 if (m_Verbose
) cout
<< " sub-loop " << sub_loop
<< ": " << N
<< " swaps" << endl
;