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 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
25 #include "polymolecule.h"
26 #include "guimainwindow.h"
28 #include <vtkXMLPolyDataWriter.h>
29 #include <vtkTriangleFilter.h>
30 #include <vtkCurvatures.h>
36 PolyMesh::face_t::face_t(int N
, int o
, int n
, vec3_t rv
, int b
)
50 PolyMesh::node_t::node_t(const QVector
<vtkIdType
> &ids
)
56 PolyMesh::node_t::node_t(vtkIdType id1
)
62 PolyMesh::node_t::node_t(vtkIdType id1
, vtkIdType id2
)
70 PolyMesh::node_t::node_t(vtkIdType id1
, vtkIdType id2
, vtkIdType id3
)
79 PolyMesh::node_t::node_t(vtkIdType id1
, vtkIdType id2
, vtkIdType id3
, vtkIdType id4
)
89 bool PolyMesh::node_t::operator<(const PolyMesh::node_t
&N
) const
91 int num
= min(id
.size(), N
.id
.size());
92 for (int i
= 0; i
< num
; ++i
) {
93 if (id
[i
] < N
.id
[i
]) {
96 if (id
[i
] > N
.id
[i
]) {
100 if (id
.size() < N
.id
.size()) {
106 bool PolyMesh::node_t::operator>(const PolyMesh::node_t
&N
) const
108 int num
= min(id
.size(), N
.id
.size());
109 for (int i
= 0; i
< num
; ++i
) {
110 if (id
[i
] > N
.id
[i
]) {
113 if (id
[i
] < N
.id
[i
]) {
117 if (id
.size() > N
.id
.size()) {
123 bool PolyMesh::node_t::operator==(const PolyMesh::node_t
&N
) const
125 if (id
.size() != N
.id
.size()) {
128 for (int i
= 0; i
< id
.size(); ++i
) {
129 if (id
[i
] != N
.id
[i
]) {
144 PolyMesh::PolyMesh(vtkUnstructuredGrid
*grid
, bool dual_mesh
)
149 m_AttractorWeight
= 0.0;
150 m_PullInFactor
= 0.0;
152 m_Part
.setGrid(m_Grid
);
153 m_Part
.setAllCells();
155 createNodesAndFaces();
156 checkFaceOrientation();
159 m_PullInFactor
= 0.5;
161 for (int iter
= 0; iter
< 5; ++iter
) {
164 for (int i
= 0; i
< numCells(); ++i
) {
165 PolyMolecule
pm(this, i
);
166 if (!pm
.allPositive()) {
169 if (pm
.minPyramidVolume() < 0) {
174 cout
<< i_improve
<< " cells out of " << numCells() << " were smoothed." << endl
;
175 //cout << num_bad << " cells out of " << numCells() << " are still concave!" << endl;
183 void PolyMesh::triangulateBadFaces()
185 m_IsBadCell
.fill(false, numCells());
187 for (int i
= 0; i
< numCells(); ++i
) {
188 PolyMolecule
pm(this, i
);
189 if (!pm
.allPositive()) {
191 m_IsBadCell
[i
] = true;
194 QList
<int> bad_faces
;
195 for (int i
= 0; i
< numFaces(); ++i
) {
196 if (m_IsBadCell
[owner(i
)]) {
198 } else if (neighbour(i
) != -1) {
199 if (m_IsBadCell
[neighbour(i
)]) {
205 foreach (int i_face
, bad_faces
) {
206 face_t face
= m_Faces
[i_face
];
207 QVector
<vec3_t
> x(face
.node
.size());
208 for (int i
= 0; i
< x
.size(); ++i
) {
209 x
[i
] = nodeVector(face
.node
[i
]);
211 EG_VTKSP(vtkPolyData
, poly
);
212 createPolyData(x
, poly
);
213 EG_VTKSP(vtkTriangleFilter
, tri
);
216 if (tri
->GetOutput()->GetNumberOfPoints() > face
.node
.size()) {
219 QVector
<face_t
> new_faces(tri
->GetOutput()->GetNumberOfCells(), face
);
220 for (int i
= 0; i
< new_faces
.size(); ++i
) {
221 new_faces
[i
].node
.resize(3);
222 EG_GET_CELL(i
, tri
->GetOutput());
223 for (int j
= 0; j
< 3; ++j
) {
224 new_faces
[i
].node
[j
] = face
.node
[pts
[j
]];
227 m_Faces
[i_face
] = new_faces
[0];
228 for (int i
= 1; i
< new_faces
.size(); ++i
) {
229 m_Faces
.append(new_faces
[i
]);
237 cout
<< num_bad
<< " cells out of " << numCells() << " are concave" << endl
;
238 cout
<< bad_faces
.size() << " faces have been triangulated" << endl
;
242 void PolyMesh::splitConcaveCells()
244 for (int i_cell
= 0; i_cell
< numCells(); ++i_cell
) {
245 if (m_IsBadCell
[i_cell
]) {
246 PolyMolecule
pm(this, i_cell
);
247 EG_VTKSP(vtkPolyData
, poly
);
248 pm
.createPolyData(poly
);
249 EG_VTKSP(vtkCurvatures
, curv
);
250 curv
->SetCurvatureTypeToMinimum();
251 curv
->SetInput(poly
);
253 EG_VTKDCN(vtkDoubleArray
, curvature
, curv
->GetOutput(), "Minimum_Curvature");
254 vtkIdType id_ref
= 0;
256 double curv_min
= 1e99
;
257 EG_FORALL_NODES(id_node
, poly
) {
258 if (curvature
->GetValue(id_node
) < curv_min
) {
259 curv_min
= curvature
->GetValue(id_node
);
261 poly
->GetPoint(id_ref
, x_ref
.data());
265 EG_FORALL_CELLS(id_face
, poly
) {
266 EG_GET_CELL(id_face
, poly
);
268 if (type_cell
!= VTK_TRIANGLE
) {
271 for (int i
= 0; i
< num_pts
; ++i
) {
272 if (pts
[i
] == id_ref
) {
278 QVector
<vec3_t
> x(num_pts
);
279 for (int i
= 0; i
< num_pts
; ++i
) {
280 poly
->GetPoint(pts
[i
], x
[i
].data());
282 n_ref
= (x
[1] - x
[0]);
283 n_ref
= n_ref
.cross(x
[2] - x
[0]);
290 EG_VTKSP(vtkDoubleArray
, div
);
291 div
->SetNumberOfComponents(1);
292 div
->SetNumberOfTuples(poly
->GetNumberOfPoints());
296 vtkIdType id_node0
= id_ref
;
297 vtkIdType id_node1
= id_ref
;
298 QVector
<QSet
<vtkIdType
> > n2n
;
299 QVector
<QSet
<vtkIdType
> > n2c
;
300 QVector
<QVector
<vtkIdType
> > c2c
;
301 createPolyDataN2N(poly
, n2n
);
302 createPolyDataN2C(poly
, n2c
);
303 createPolyDataC2C(poly
, c2c
);
305 vec3_t
n_node(0,0,0);
306 foreach (vtkIdType id_face
, n2c
[id_ref
]) {
307 EG_GET_CELL(id_face
, poly
);
308 QVector
<vec3_t
> x(3);
309 for (int i
= 0; i
< 3; ++i
) {
310 poly
->GetPoint(pts
[i
], x
[i
].data());
312 n_node
+= triNormal(x
[0], x
[1], x
[2]);
316 QVector
<bool> div_node(poly
->GetNumberOfPoints(), false);
317 EG_FORALL_NODES(id_node
, poly
) {
318 div
->SetValue(id_node
, 0);
320 div_node
[id_ref
] = true;
322 div
->SetValue(id_ref
, dv
);
327 foreach (vtkIdType id
, n2n
[id_node0
]) {
329 poly
->GetPoint(id
, x
.data());
330 L
= min(L
, (x
- x_ref
).abs());
331 double c
= curvature
->GetValue(id
);
337 div_node
[id_node1
] = true;
338 div
->SetValue(id_node1
, dv
);
341 vtkIdType id_node2
= id_node1
;
343 foreach (vtkIdType id
, n2n
[id_node1
]) {
344 if (id
== id_ref
&& id_node0
!= id_ref
) {
350 foreach (vtkIdType id
, n2n
[id_node1
]) {
353 poly
->GetPoint(id
, x
.data());
354 if ((x
- x_ref
)*n_node
< 0.25*L
) {
355 double d
= (x
- x_ref
)*n_ref
;
356 if (fabs(d
) < d_min
&& d
< 0.1*L
) {
363 div_node
[id_node2
] = true;
364 div
->SetValue(id_node2
, dv
);
375 EG_FORALL_NODES(id_node, poly) {
376 div->SetValue(id_node, 0);
377 if (div_node[id_node]) {
378 div->SetValue(id_node, 1);
381 div->SetValue(id_ref, 2);
383 poly
->GetPointData()->AddArray(div
);
388 //EG_VTKSP(vtkDoubleArray, dist);
389 //dist->SetNumberOfComponents(1);
390 //dist->SetNumberOfTuples(poly->GetNumberOfCells());
391 //dist->SetName("dist");
393 EG_FORALL_CELLS(id_face, poly) {
395 EG_GET_CELL(id_face, poly);
396 for (int i = 0; i < num_pts; ++i) {
398 poly->GetPoint(pts[i], x.data());
402 dist->SetValue(id_face, (xf - x_ref)*n_ref);
404 poly->GetCellData()->AddArray(dist);
407 EG_VTKSP(vtkXMLPolyDataWriter
, vtp
);
408 vtp
->SetFileName(qPrintable(GuiMainWindow::pointer()->getCwd() + "/triangulated_cell.vtp"));
410 vtp
->SetDataModeToAscii();
417 void PolyMesh::getFacesOfEdgeInsideCell(vtkIdType id_cell
, vtkIdType id_node1
, vtkIdType id_node2
, int &face1
, int &face2
)
419 vtkIdType num_pts
, *pts
;
420 m_Grid
->GetCellPoints(id_cell
, num_pts
, pts
);
424 if (m_Grid
->GetCellType(id_cell
) == VTK_TETRA
) {
425 if (id_node1
== pts
[0]) {
426 if (id_node2
== pts
[1]) { face1
= 0; face2
= 1; }
427 else if (id_node2
== pts
[2]) { face1
= 2; face2
= 0; }
428 else if (id_node2
== pts
[3]) { face1
= 1; face2
= 2; }
429 } else if (id_node1
== pts
[1]) {
430 if (id_node2
== pts
[0]) { face1
= 1; face2
= 0; }
431 else if (id_node2
== pts
[2]) { face1
= 0; face2
= 3; }
432 else if (id_node2
== pts
[3]) { face1
= 3; face2
= 1; }
433 } else if (id_node1
== pts
[2]) {
434 if (id_node2
== pts
[0]) { face1
= 0; face2
= 2; }
435 else if (id_node2
== pts
[1]) { face1
= 3; face2
= 0; }
436 else if (id_node2
== pts
[3]) { face1
= 2; face2
= 3; }
437 } else if (id_node1
== pts
[3]) {
438 if (id_node2
== pts
[0]) { face1
= 2; face2
= 1; }
439 else if (id_node2
== pts
[1]) { face1
= 1; face2
= 3; }
440 else if (id_node2
== pts
[2]) { face1
= 3; face2
= 2; }
442 } else if (m_Grid
->GetCellType(id_cell
) == VTK_PYRAMID
) {
443 if (id_node1
== pts
[0]) {
444 if (id_node2
== pts
[1]) { face1
= 0; face2
= 1; }
445 else if (id_node2
== pts
[3]) { face1
= 4; face2
= 0; }
446 else if (id_node2
== pts
[4]) { face1
= 1; face2
= 4; }
447 } else if (id_node1
== pts
[1]) {
448 if (id_node2
== pts
[0]) { face1
= 1; face2
= 0; }
449 else if (id_node2
== pts
[2]) { face1
= 0; face2
= 2; }
450 else if (id_node2
== pts
[4]) { face1
= 2; face2
= 1; }
451 } else if (id_node1
== pts
[2]) {
452 if (id_node2
== pts
[1]) { face1
= 2; face2
= 0; }
453 else if (id_node2
== pts
[3]) { face1
= 0; face2
= 3; }
454 else if (id_node2
== pts
[4]) { face1
= 3; face2
= 2; }
455 } else if (id_node1
== pts
[3]) {
456 if (id_node2
== pts
[0]) { face1
= 0; face2
= 4; }
457 else if (id_node2
== pts
[2]) { face1
= 3; face2
= 0; }
458 else if (id_node2
== pts
[4]) { face1
= 4; face2
= 3; }
459 } else if (id_node1
== pts
[4]) {
460 if (id_node2
== pts
[0]) { face1
= 4; face2
= 1; }
461 else if (id_node2
== pts
[1]) { face1
= 1; face2
= 2; }
462 else if (id_node2
== pts
[2]) { face1
= 2; face2
= 3; }
463 else if (id_node2
== pts
[3]) { face1
= 3; face2
= 4; }
469 if (face1
== -1 || face2
== -1) {
474 void PolyMesh::getSortedEdgeCells(vtkIdType id_node1
, vtkIdType id_node2
, QList
<vtkIdType
> &cells
, bool &is_loop
)
477 vtkIdType id_start
= -1;
478 for (int i
= 0; i
< m_Part
.n2cGSize(id_node1
); ++i
) {
479 vtkIdType id_cell
= m_Part
.n2cGG(id_node1
, i
);
480 if (isVolume(id_cell
, m_Grid
)) {
481 if (m_Cell2PCell
[id_cell
] == -1) {
482 vtkIdType num_pts
, *pts
;
483 m_Grid
->GetCellPoints(id_cell
, num_pts
, pts
);
484 for (int j
= 0; j
< num_pts
; ++j
) {
485 if (pts
[j
] == id_node2
) {
493 if (id_start
== -1) {
496 vtkIdType id_cell_old
= id_start
;
497 vtkIdType id_cell_new
= id_start
;
498 cells
.append(id_cell_new
);
499 bool finished
= false;
500 bool prepend
= false;
504 getFacesOfEdgeInsideCell(id_cell_new
, id_node1
, id_node2
, f1
, f2
);
505 vtkIdType id_neigh1
= m_Part
.c2cGG(id_cell_new
, f1
);
506 vtkIdType id_neigh2
= m_Part
.c2cGG(id_cell_new
, f2
);
507 if (id_neigh1
== id_cell_old
) {
508 id_cell_old
= id_cell_new
;
509 id_cell_new
= id_neigh2
;
511 id_cell_old
= id_cell_new
;
512 id_cell_new
= id_neigh1
;
515 cells
.prepend(id_cell_new
);
517 cells
.append(id_cell_new
);
519 } while (id_cell_new
!= id_start
&& !isSurface(id_cell_new
, m_Grid
) && m_Cell2PCell
[id_cell_new
] == -1);
520 if ((isSurface(id_cell_new
, m_Grid
) || m_Cell2PCell
[id_cell_new
] != -1) && !prepend
) {
521 id_cell_new
= cells
[0];
522 id_cell_old
= cells
[1];
529 // remove last cell for loops
530 if (cells
.size() > 1 && cells
.first() == cells
.last()) {
538 bool PolyMesh::isDualFace(vtkIdType id_face
)
540 vtkIdType id_cell
= m_Part
.getVolumeCell(id_face
);
541 if (m_Cell2PCell
[id_cell
] == -1) {
547 void PolyMesh::getSortedPointFaces(vtkIdType id_node
, int bc
, QList
<vtkIdType
> &faces
, bool &is_loop
)
549 EG_VTKDCC(vtkIntArray
, cell_code
, m_Grid
, "cell_code");
551 vtkIdType id_start
= -1;
552 for (int i
= 0; i
< m_Part
.n2cGSize(id_node
); ++i
) {
553 vtkIdType id_cell
= m_Part
.n2cGG(id_node
, i
);
554 if (isSurface(id_cell
, m_Grid
)) {
555 if (cell_code
->GetValue(id_cell
) == bc
&& isDualFace(id_cell
)) {
561 if (id_start
== -1) {
564 vtkIdType id_face_old
= id_start
;
565 vtkIdType id_face_new
= id_start
;
566 faces
.append(id_face_new
);
567 bool finished
= false;
568 bool prepend
= false;
571 QList
<vtkIdType
> id_neigh
;
572 for (int i
= 0; i
< m_Part
.c2cGSize(id_face_new
); ++i
) {
573 if (cellContainsNode(m_Grid
, m_Part
.c2cGG(id_face_new
, i
), id_node
)) {
574 id_neigh
.append(m_Part
.c2cGG(id_face_new
, i
));
577 if (id_neigh
[0] == id_face_old
) {
578 id_face_old
= id_face_new
;
579 id_face_new
= id_neigh
[1];
581 id_face_old
= id_face_new
;
582 id_face_new
= id_neigh
[0];
584 if (cell_code
->GetValue(id_face_new
) == bc
&& isDualFace(id_face_new
)) {
586 faces
.prepend(id_face_new
);
588 faces
.append(id_face_new
);
591 } while (id_face_new
!= id_start
&& cell_code
->GetValue(id_face_new
) == bc
&& isDualFace(id_face_new
));
592 if ((cell_code
->GetValue(id_face_new
) != bc
|| !isDualFace(id_face_new
)) && !prepend
) {
593 id_face_old
= id_face_new
;
594 id_face_new
= faces
[0];
595 if (faces
.size() > 1) {
596 id_face_old
= faces
[1];
604 // remove last face for loops
605 if (faces
.size() > 1 && faces
.first() == faces
.last()) {
614 void PolyMesh::findPolyCells()
616 m_Cell2PCell
.fill(-1, m_Grid
->GetNumberOfCells());
617 m_Node2PCell
.fill(-1, m_Grid
->GetNumberOfPoints());
619 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
620 if (!isHexCoreNode(id_node
)) {
621 bool tetra_or_pyramid
= false;
622 for (int j
= 0; j
< m_Part
.n2cGSize(id_node
); ++j
) {
623 vtkIdType id_cell
= m_Part
.n2cGG(id_node
, j
);
624 vtkIdType type_cell
= m_Grid
->GetCellType(id_cell
);
625 if (type_cell
== VTK_TETRA
|| type_cell
== VTK_PYRAMID
) {
626 tetra_or_pyramid
= true;
629 if (tetra_or_pyramid
) {
630 m_Node2PCell
[id_node
] = m_NumPolyCells
;
635 for (vtkIdType id_cell
= 0; id_cell
< m_Grid
->GetNumberOfCells(); ++id_cell
) {
636 vtkIdType type_cell
= m_Grid
->GetCellType(id_cell
);
637 if (type_cell
== VTK_WEDGE
|| type_cell
== VTK_HEXAHEDRON
) {
638 m_Cell2PCell
[id_cell
] = m_NumPolyCells
;
642 m_CellCentre
.resize(m_NumPolyCells
);
643 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
644 if (m_Node2PCell
[id_node
] != -1) {
645 m_Grid
->GetPoint(id_node
, m_CellCentre
[m_Node2PCell
[id_node
]].data());
648 for (vtkIdType id_cell
= 0; id_cell
< m_Grid
->GetNumberOfCells(); ++id_cell
) {
649 if (m_Cell2PCell
[id_cell
] != -1) {
650 m_CellCentre
[m_Cell2PCell
[id_cell
]] = cellCentre(m_Grid
, id_cell
);
655 void PolyMesh::createFace(QList
<node_t
> nodes
, int owner
, int neighbour
, vec3_t ref_vec
, int bc
)
657 if (owner
> neighbour
&& neighbour
!= -1) {
658 swap(owner
, neighbour
);
661 face_t
face(nodes
.size(), owner
, neighbour
, ref_vec
, bc
);
662 for (int i
= 0; i
< nodes
.size(); ++i
) {
663 int idx
= m_Nodes
.insert(nodes
[i
]);
666 m_Faces
.append(face
);
669 void PolyMesh::createCornerFace(vtkIdType id_cell
, int i_face
, vtkIdType id_node
)
671 QList
<vtkIdType
> edge_nodes
;
672 QVector
<vtkIdType
> face_nodes
;
673 getFaceOfCell(m_Grid
, id_cell
, i_face
, face_nodes
);
674 vtkIdType num_pts
, *pts
;
675 m_Grid
->GetCellPoints(id_cell
, num_pts
, pts
);
676 for (int i
= 0; i
< m_Part
.n2nGSize(id_node
); ++i
) {
677 vtkIdType id_neigh
= m_Part
.n2nGG(id_node
, i
);
678 if (face_nodes
.contains(id_neigh
)) {
679 edge_nodes
.append(id_neigh
);
682 if (edge_nodes
.size() != 2) {
685 int owner
= m_Cell2PCell
[id_cell
];
689 int neighbour
= m_Node2PCell
[id_node
];
691 if (neighbour
== -1) {
692 EG_VTKDCC(vtkIntArray
, cell_code
, m_Grid
, "cell_code");
693 vtkIdType id_face
= m_Part
.c2cGG(id_cell
, i_face
);
697 if (!isSurface(id_cell
, m_Grid
)) {
700 bc
= cell_code
->GetValue(id_face
);
703 nodes
.append(node_t(id_node
));
704 nodes
.append(node_t(id_node
, edge_nodes
[0]));
705 nodes
.append(node_t(face_nodes
));
706 nodes
.append(node_t(id_node
, edge_nodes
[1]));
707 vec3_t n
= getNormalOfCell(m_Grid
, id_cell
, i_face
);
709 createFace(nodes
, owner
, neighbour
, n
, bc
);
712 void PolyMesh::createEdgeFace(vtkIdType id_node1
, vtkIdType id_node2
)
714 // check id additional edge node needs to be created
715 // (transition from boundary layer to far-field)
716 // try to find a boundary layer cell which contains both nodes
717 bool add_edge_node
= false;
718 for (int i
= 0; i
< m_Part
.n2cGSize(id_node1
); ++i
) {
719 vtkIdType id_cell
= m_Part
.n2cGG(id_node1
, i
);
720 if (m_Cell2PCell
[id_cell
] != -1) {
721 if (cellContainsNode(m_Grid
, id_cell
, id_node1
) && cellContainsNode(m_Grid
, id_cell
, id_node2
)) {
722 add_edge_node
= true;
727 if (!add_edge_node
) {
728 for (int i
= 0; i
< m_Part
.n2cGSize(id_node2
); ++i
) {
729 vtkIdType id_cell
= m_Part
.n2cGG(id_node2
, i
);
730 if (m_Cell2PCell
[id_cell
] != -1) {
731 if (cellContainsNode(m_Grid
, id_cell
, id_node1
) && cellContainsNode(m_Grid
, id_cell
, id_node2
)) {
732 add_edge_node
= true;
739 QList
<vtkIdType
> cells
;
741 getSortedEdgeCells(id_node1
, id_node2
, cells
, loop
);
742 int owner
= m_Node2PCell
[id_node1
];
743 int neighbour
= m_Node2PCell
[id_node2
];
747 if (neighbour
== -1) {
750 if (owner
> neighbour
) {
751 swap(id_node1
, id_node2
);
752 swap(owner
, neighbour
);
755 for (int i_cells
= 0; i_cells
< cells
.size(); ++i_cells
) {
757 vtkIdType num_pts1
, *pts1
;
758 m_Grid
->GetCellPoints(cells
[i_cells
], num_pts1
, pts1
);
759 if (m_Cell2PCell
[cells
[i_cells
]] != -1) {
763 } else if (i_cells
== cells
.size() - 1) {
764 i_cells2
= cells
.size() - 2;
768 vtkIdType num_pts2
, *pts2
;
769 m_Grid
->GetCellPoints(cells
[i_cells2
], num_pts2
, pts2
);
770 QSet
<vtkIdType
> p1
, p2
;
771 for (int i_pts1
= 0; i_pts1
< num_pts1
; ++i_pts1
) {
772 p1
.insert(pts1
[i_pts1
]);
774 for (int i_pts2
= 0; i_pts2
< num_pts2
; ++i_pts2
) {
775 p2
.insert(pts2
[i_pts2
]);
777 QSet
<vtkIdType
> face_nodes
= p1
.intersect(p2
);
778 node
.id
.resize(face_nodes
.size());
780 foreach (vtkIdType id_node
, face_nodes
) {
781 node
.id
[i
] = id_node
;
785 node
.id
.resize(num_pts1
);
786 for (int i_pts1
= 0; i_pts1
< num_pts1
; ++i_pts1
) {
787 node
.id
[i_pts1
] = pts1
[i_pts1
];
794 EG_VTKDCC(vtkIntArray
, cell_code
, m_Grid
, "cell_code");
795 if (cell_code
->GetValue(cells
.first()) != cell_code
->GetValue(cells
.last()) || add_edge_node
) {
796 nodes
.append(node_t(id_node1
, id_node2
));
800 m_Grid
->GetPoint(id_node1
, x1
.data());
801 m_Grid
->GetPoint(id_node2
, x2
.data());
802 createFace(nodes
, owner
, neighbour
, x2
- x1
, 0);
805 void PolyMesh::createFaceFace(vtkIdType id_cell
, int i_face
)
807 if (m_Cell2PCell
[id_cell
] == -1) {
810 int owner
= m_Cell2PCell
[id_cell
];
812 vtkIdType id_neigh
= m_Part
.c2cGG(id_cell
, i_face
);
814 if (id_neigh
!= -1) {
815 if (isVolume(id_neigh
, m_Grid
)) {
816 if (m_Cell2PCell
[id_neigh
] == -1) {
819 neighbour
= m_Cell2PCell
[id_neigh
];
821 EG_VTKDCC(vtkIntArray
, cell_code
, m_Grid
, "cell_code");
822 bc
= cell_code
->GetValue(id_neigh
);
825 QVector
<vtkIdType
> tmp_node_ids
;
826 getFaceOfCell(m_Grid
, id_cell
, i_face
, tmp_node_ids
);
827 vec3_t n
= getNormalOfCell(m_Grid
, id_cell
, i_face
);
829 QVector
<vtkIdType
> node_ids(tmp_node_ids
.size() + 1);
830 for (int i
= 0; i
< tmp_node_ids
.size(); ++i
) {
831 node_ids
[i
] = tmp_node_ids
[i
];
833 node_ids
[node_ids
.size() - 1] = node_ids
[0];
835 for (int i
= 0; i
< node_ids
.size() - 1; ++i
) {
836 nodes
.append(node_t(node_ids
[i
]));
837 if (m_Node2PCell
[node_ids
[i
]] != -1 && m_Node2PCell
[node_ids
[i
+1]] != -1) {
838 nodes
.append(node_t(node_ids
[i
], node_ids
[i
+1]));
841 createFace(nodes
, owner
, neighbour
, n
, bc
);
844 void PolyMesh::createPointFace(vtkIdType id_node
, int bc
)
847 QList
<vtkIdType
> faces
;
848 getSortedPointFaces(id_node
, bc
, faces
, is_loop
);
849 if (faces
.size() == 0) {
854 if (faces
.size() == 0) {
857 foreach (vtkIdType id_face
, faces
) {
859 vtkIdType num_pts
, *pts
;
860 m_Grid
->GetCellPoints(id_face
, num_pts
, pts
);
861 node
.id
.resize(num_pts
);
862 for (int i_pts
= 0; i_pts
< num_pts
; ++i_pts
) {
863 node
.id
[i_pts
] = pts
[i_pts
];
867 n
+= GeometryTools::cellNormal(m_Grid
, id_face
);
871 bool prepend
= false;
872 vtkIdType id_face
= faces
.last();
873 while (id_face
!= -1) {
875 vtkIdType num_pts
, *pts
;
876 m_Grid
->GetCellPoints(id_face
, num_pts
, pts
);
877 QList
<vtkIdType
> id_neigh_node
;
878 vtkIdType id_neigh
= -1;
879 for (int i
= 0; i
< m_Part
.c2cGSize(id_face
); ++i
) {
880 id_neigh
= m_Part
.c2cGG(id_face
, i
);
881 if (id_neigh
== -1) {
884 if (!isSurface(id_neigh
, m_Grid
)) {
887 if (cellContainsNode(m_Grid
, id_neigh
, id_node
)) {
888 if (!faces
.contains(id_neigh
)) {
892 for (int j
= 0; j
< num_pts
; ++j
) {
893 if (pts
[j
] != id_node
&& cellContainsNode(m_Grid
, id_neigh
, pts
[j
])) {
894 id_neigh_node
.append(pts
[j
]);
901 if (id_neigh_node
.size() == 0) {
905 if (id_neigh_node
.size() == 1) {
907 nodes
.prepend(node_t(id_node
, id_neigh_node
[0]));
910 nodes
.append(node_t(id_node
, id_neigh_node
[0]));
912 id_face
= faces
.first();
915 if (id_neigh_node
.size() > 2) {
918 if (faces
.size() != 1) {
921 nodes
.prepend(node_t(id_node
, id_neigh_node
[0]));
922 nodes
.append(node_t(id_node
, id_neigh_node
[1]));
926 nodes
.prepend(node_t(id_node
));
928 int owner
= m_Node2PCell
[id_node
];
930 createFace(nodes
, owner
, neighbour
, n
, bc
);
933 void PolyMesh::computePoints()
935 QVector
<node_t
> nodes
;
936 m_Nodes
.getQVector(nodes
);
937 m_Points
.resize(nodes
.size());
938 m_PointWeights
.fill(1.0, m_Grid
->GetNumberOfPoints());
940 // find transition nodes
941 QVector
<bool> is_transition_node(m_Grid
->GetNumberOfPoints(), false);
942 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
943 if (m_Node2PCell
[id_node
] != -1) {
944 for (int i_cell
= 0; i_cell
< m_Part
.n2cGSize(id_node
); ++i_cell
) {
945 vtkIdType id_cell
= m_Part
.n2cGG(id_node
, i_cell
);
946 if (m_Cell2PCell
[id_cell
] != -1) {
947 is_transition_node
[id_node
] = true;
954 // compute weights (attraction for convex corners)
955 // mark convex edge nodes
956 QVector
<bool> is_convex_node(m_Grid
->GetNumberOfPoints(), false);
957 for (vtkIdType id_cell
= 0; id_cell
< m_Grid
->GetNumberOfCells(); ++id_cell
) {
958 if (m_Cell2PCell
[id_cell
] != -1) {
959 vec3_t xc1
= cellCentre(m_Grid
, id_cell
);
962 for (int i_face
= 0; i_face
< m_Part
.c2cGSize(id_cell
); ++i_face
) {
963 vtkIdType id_neigh
= m_Part
.c2cGG(id_cell
, i_face
);
964 if (id_neigh
!= -1) {
965 if (m_Cell2PCell
[id_neigh
] == -1 && !isSurface(id_neigh
, m_Grid
)) {
967 n_out
+= getNormalOfCell(m_Grid
, id_cell
, i_face
);
973 for (int i_face
= 0; i_face
< m_Part
.c2cGSize(id_cell
); ++i_face
) {
974 vtkIdType id_neigh
= m_Part
.c2cGG(id_cell
, i_face
);
975 if (id_neigh
!= -1) {
976 if (m_Cell2PCell
[id_neigh
] != -1) {
977 vec3_t xc2
= cellCentre(m_Grid
, id_neigh
);
978 QVector
<vtkIdType
> face_nodes
;
979 getFaceOfCell(m_Grid
, id_cell
, i_face
, face_nodes
);
980 if (face_nodes
.size() == 0) {
984 foreach (vtkIdType id_node
, face_nodes
) {
986 m_Grid
->GetPoint(id_node
, x
.data());
989 xf
*= 1.0/face_nodes
.size();
990 vec3_t v1
= xc1
- xf
;
991 vec3_t v2
= xc2
- xf
;
994 if ((v1
+v2
)*n_out
< 0) {
995 double w
= 1 + m_AttractorWeight
*(1.0 + v1
*v2
);
996 foreach (vtkIdType id_node
, face_nodes
) {
997 if (m_Node2PCell
[id_node
] != -1) {
998 m_PointWeights
[id_node
] = max(m_PointWeights
[id_node
], w
);
1000 //is_convex_node[id_node] = true;
1012 // mapping for simple nodes
1013 QVector
<int> prime2dual(m_Grid
->GetNumberOfPoints(), -1);
1015 // compute the point locations
1016 for (int i
= 0; i
< nodes
.size(); ++i
) {
1017 if (nodes
[i
].id
.size() == 0) {
1020 m_Points
[i
] = vec3_t(0,0,0);
1021 double weight_sum
= 0.0;
1022 foreach (vtkIdType id
, nodes
[i
].id
) {
1024 m_Grid
->GetPoint(id
, x
.data());
1025 weight_sum
+= m_PointWeights
[id
];
1026 m_Points
[i
] += m_PointWeights
[id
]*x
;
1028 //m_Points[i] *= 1.0/nodes[i].id.size();
1029 m_Points
[i
] *= 1.0/weight_sum
;
1030 if (nodes
[i
].id
.size() == 1) {
1031 prime2dual
[nodes
[i
].id
[0]] = i
;
1035 // correct transition nodes (pull-in)
1036 for (vtkIdType id_node1
= 0; id_node1
< m_Grid
->GetNumberOfPoints(); ++id_node1
) {
1037 if (is_transition_node
[id_node1
]) {
1038 if (prime2dual
[id_node1
] == -1) {
1041 vtkIdType id_node2
= -1;
1042 bool pull_in
= true;
1043 for (int i_neigh
= 0; i_neigh
< m_Part
.n2nGSize(id_node1
); ++i_neigh
) {
1044 vtkIdType id_neigh
= m_Part
.n2nGG(id_node1
, i_neigh
);
1045 if (m_Node2PCell
[id_neigh
] == -1 && !is_transition_node
[id_neigh
]) {
1046 if (id_node2
!= -1) {
1049 id_node2
= id_neigh
;
1053 double w
= m_PullInFactor
;
1054 m_Points
[prime2dual
[id_node1
]] = w
*m_Points
[prime2dual
[id_node2
]] + (1-w
)*m_Points
[prime2dual
[id_node1
]];
1061 void PolyMesh::splitConcaveFaces()
1063 QList
<face_t
> new_faces
;
1064 for (int i_face
= 0; i_face
< m_Faces
.size(); ++i_face
) {
1065 face_t face
= m_Faces
[i_face
];
1066 int num_nodes
= face
.node
.size();
1067 if (num_nodes
> 4) {
1068 QVector
<vec3_t
> x(num_nodes
);
1069 for (int i
= 0; i
< num_nodes
; ++i
) {
1070 x
[i
] = nodeVector(face
.node
[i
]);
1073 GeometryTools::planeFit(x
, xc
, n
);
1074 QVector
<vec3_t
> x_proj
= x
;
1075 for (int i
= 0; i
< num_nodes
; ++i
) {
1076 x_proj
[i
] -= ((x_proj
[i
] - xc
)*n
)*n
;
1078 if (convexRatio(x_proj
, n
) <= -1e-3) {
1079 double max_ratio
= -1e99
;
1080 bool debug_dump
= false;
1081 QList
<int> poly1_best
, poly2_best
;
1082 for (int i_split1
= 0; i_split1
< x_proj
.size() - 3; ++i_split1
) {
1083 for (int i_split2
= i_split1
+ 2; i_split2
< x_proj
.size(); ++i_split2
) {
1084 QList
<int> poly1
, poly2
;
1085 QList
<vec3_t
> x1
, x2
;
1086 for (int i
= i_split1
; i
<= i_split2
; ++i
) {
1087 x1
.append(x_proj
[i
]);
1090 for (int i
= i_split2
; i
< x_proj
.size(); ++i
) {
1091 x2
.append(x_proj
[i
]);
1094 for (int i
= 0; i
<= i_split1
; ++i
) {
1095 x2
.append(x_proj
[i
]);
1098 vec3_t n1
= polyNormal(x1
);
1099 vec3_t n2
= polyNormal(x2
);
1101 double ratio
= min(convexRatio(x1
, n
), convexRatio(x2
, n
));
1102 if (ratio
> max_ratio
) {
1111 if (poly1_best
.size() < 3) {
1112 debug_dump
= true;//EG_BUG;
1114 if (poly2_best
.size() < 3) {
1115 debug_dump
= true;//EG_BUG;
1117 if (poly1_best
.size() + poly2_best
.size() != x
.size() + 2) {
1118 debug_dump
= true;//EG_BUG;
1123 //EG_VTKSP(vtkXMLPolyDataWriter, vtp);
1124 //vtp->SetFileName(qPrintable(GuiMainWindow::pointer()->getCwd() + "/test_cell.vtp"));
1125 //vtp->SetInput(triangulate->GetOutput());
1129 EG_VTKSP(vtkPolyData
, pdata1
);
1130 createPolyData(x
, pdata1
);
1131 EG_VTKSP(vtkXMLPolyDataWriter
, vtp1
);
1132 vtp1
->SetFileName(qPrintable(GuiMainWindow::pointer()->getCwd() + "/test_cell1.vtp"));
1133 vtp1
->SetInput(pdata1
);
1136 EG_VTKSP(vtkPolyData
, pdata2
);
1137 createPolyData(x_proj
, pdata2
);
1138 EG_VTKSP(vtkXMLPolyDataWriter
, vtp2
);
1139 vtp2
->SetFileName(qPrintable(GuiMainWindow::pointer()->getCwd() + "/test_cell2.vtp"));
1140 vtp2
->SetInput(pdata2
);
1144 foreach (int i
, poly1_best
) xp1
.append(x_proj
[i
]);
1145 EG_VTKSP(vtkPolyData
, pdata3
);
1146 createPolyData(xp1
, pdata3
);
1147 EG_VTKSP(vtkXMLPolyDataWriter
, vtp3
);
1148 vtp3
->SetFileName(qPrintable(GuiMainWindow::pointer()->getCwd() + "/test_cell3.vtp"));
1149 vtp3
->SetInput(pdata3
);
1153 foreach (int i
, poly2_best
) xp2
.append(x_proj
[i
]);
1154 EG_VTKSP(vtkPolyData
, pdata4
);
1155 createPolyData(xp2
, pdata4
);
1156 EG_VTKSP(vtkXMLPolyDataWriter
, vtp4
);
1157 vtp4
->SetFileName(qPrintable(GuiMainWindow::pointer()->getCwd() + "/test_cell4.vtp"));
1158 vtp4
->SetInput(pdata4
);
1166 if (poly1_best
.size() >= 3 && poly2_best
.size() >= 3) {
1167 QVector
<int> old_node
= face
.node
;
1168 face
.node
.resize(poly1_best
.size());
1169 for (int i
= 0; i
< poly1_best
.size(); ++i
) {
1170 face
.node
[i
] = old_node
[poly1_best
[i
]];
1172 m_Faces
[i_face
] = face
;
1173 face
.node
.resize(poly2_best
.size());
1174 for (int i
= 0; i
< poly2_best
.size(); ++i
) {
1175 face
.node
[i
] = old_node
[poly2_best
[i
]];
1177 new_faces
.append(face
);
1187 m_Faces
.append(new_faces
);
1193 void PolyMesh::createNodesAndFaces()
1195 m_Nodes
.resize(m_Grid
->GetNumberOfPoints());
1196 EG_VTKDCC(vtkIntArray
, cell_code
, m_Grid
, "cell_code");
1198 // create all prismatic elements (hexes and prisms)
1199 for (vtkIdType id_cell
= 0; id_cell
< m_Grid
->GetNumberOfCells(); ++id_cell
) {
1200 if (m_Cell2PCell
[id_cell
] != -1) {
1201 for (int i_face
= 0; i_face
< m_Part
.c2cGSize(id_cell
); ++i_face
) {
1202 vtkIdType id_neigh
= m_Part
.c2cGG(id_cell
, i_face
);
1203 if (id_neigh
== -1) {
1206 bool create_corner_faces
= false;
1207 if (m_Cell2PCell
[id_neigh
] == -1 && !isSurface(id_neigh
, m_Grid
)) {
1208 create_corner_faces
= true;
1210 if (create_corner_faces
) {
1211 QVector
<vtkIdType
> face_nodes
;
1212 getFaceOfCell(m_Grid
, id_cell
, i_face
, face_nodes
);
1213 foreach (vtkIdType id_node
, face_nodes
) {
1214 if (m_Node2PCell
[id_node
] == -1) {
1217 createCornerFace(id_cell
, i_face
, id_node
);
1220 if (id_neigh
> id_cell
|| isSurface(id_neigh
, m_Grid
)) {
1221 createFaceFace(id_cell
, i_face
);
1228 // create all dual cells
1229 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
1230 if (m_Node2PCell
[id_node
] != -1) {
1231 for (int i_neigh
= 0; i_neigh
< m_Part
.n2nGSize(id_node
); ++i_neigh
) {
1232 vtkIdType id_neigh
= m_Part
.n2nGG(id_node
, i_neigh
);
1233 if (m_Node2PCell
[id_neigh
] != -1 && id_neigh
> id_node
) {
1234 createEdgeFace(id_node
, id_neigh
);
1238 for (int i_cell
= 0; i_cell
< m_Part
.n2cGSize(id_node
); ++i_cell
) {
1239 vtkIdType id_cell
= m_Part
.n2cGG(id_node
, i_cell
);
1240 if (isSurface(id_cell
, m_Grid
)) {
1241 bcs
.insert(cell_code
->GetValue(id_cell
));
1244 foreach (int bc
, bcs
) {
1245 createPointFace(id_node
, bc
);
1252 //splitConcaveFaces();
1256 foreach (face_t face
, m_Faces
) {
1258 bcs
.insert(face
.bc
);
1261 m_BCs
.resize(bcs
.size());
1262 qCopy(bcs
.begin(), bcs
.end(), m_BCs
.begin());
1266 vec3_t
PolyMesh::faceNormal(int i
)
1268 int N
= m_Faces
[i
].node
.size();
1269 QVector
<vec3_t
> x(N
+ 1);
1271 for (int j
= 0; j
< N
; ++j
) {
1272 x
[j
] = m_Points
[m_Faces
[i
].node
[j
]];
1278 for (int j
= 0; j
< N
; ++j
) {
1279 vec3_t u
= x
[j
] - xc
;
1280 vec3_t v
= x
[j
+1] - xc
;
1281 n
+= 0.5*u
.cross(v
);
1286 void PolyMesh::checkFaceOrientation()
1288 for (int i
= 0; i
< m_Faces
.size(); ++i
) {
1289 int N
= m_Faces
[i
].node
.size();
1290 vec3_t n
= faceNormal(i
);
1292 if (n
*m_Faces
[i
].ref_vec
< 0) {
1293 QVector
<int> old_node
= m_Faces
[i
].node
;
1294 for (int j
= 0; j
< N
; ++j
) {
1295 m_Faces
[i
].node
[j
] = old_node
[N
-1-j
];
1301 void PolyMesh::buildPoint2Face()
1303 m_Point2Face
.clear();
1304 m_Point2Face
.resize(totalNumNodes());
1305 for (int face
= 0; face
< numFaces(); ++face
) {
1306 foreach (int node
, m_Faces
[face
].node
) {
1307 m_Point2Face
[node
].append(face
);
1312 void PolyMesh::buildPCell2Face()
1314 m_PCell2Face
.clear();
1315 m_PCell2Face
.resize(m_NumPolyCells
);
1316 for (int i
= 0; i
< m_Faces
.size(); ++i
) {
1317 if (owner(i
) >= m_NumPolyCells
) {
1320 m_PCell2Face
[owner(i
)].append(i
);
1321 if (neighbour(i
) >= 0) {
1322 if (neighbour(i
) >= m_NumPolyCells
) {
1325 m_PCell2Face
[neighbour(i
)].append(i
);