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 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
24 #include "polymolecule.h"
25 #include "guimainwindow.h"
27 #include <vtkUnstructuredGridWriter.h>
28 #include <vtkGeometryFilter.h>
29 #include <vtkXMLPolyDataWriter.h>
30 #include <vtkDelaunay3D.h>
32 #include <vtkTriangleFilter.h>
34 PolyMolecule::PolyMolecule()
36 m_AllPositive
= false;
42 PolyMolecule::PolyMolecule(PolyMesh
*poly_mesh
, int i_pcell
)
45 QVector
<QList
<int> > faces(poly_mesh
->numFacesOfPCell(i_pcell
));
46 for (int i
= 0; i
< poly_mesh
->numFacesOfPCell(i_pcell
); ++i
) {
47 int i_face
= poly_mesh
->pcell2Face(i_pcell
, i
);
48 int num_nodes
= poly_mesh
->numNodes(i_face
);
49 for (int j
= 0; j
< num_nodes
; ++j
) {
50 int node
= poly_mesh
->nodeIndex(i_face
, j
);
51 if (poly_mesh
->owner(i_face
) == i_pcell
) {
52 faces
[i
].append(node
);
54 faces
[i
].prepend(node
);
58 m_AllPositive
= false;
61 init(poly_mesh
, faces
);
64 void PolyMolecule::writeVtkFile(QString file_name
)
66 EG_VTKSP(vtkUnstructuredGrid
, grid
);
67 allocateGrid(grid
, m_Faces
.size(), m_Nodes
.size());
69 EG_VTKSP(vtkDoubleArray
, normals
);
70 normals
->SetNumberOfComponents(3);
71 normals
->SetNumberOfTuples(m_Nodes
.size());
72 normals
->SetName("N");
74 EG_VTKSP(vtkDoubleArray
, badness
);
75 badness
->SetNumberOfComponents(1);
76 badness
->SetNumberOfTuples(m_Nodes
.size());
77 badness
->SetName("badness");
79 for (int i
= 0; i
< m_Nodes
.size(); ++i
) {
80 vec3_t x
= getXNode(i
);
81 grid
->GetPoints()->SetPoint(i
, x
.data());
82 normals
->SetTuple(i
, m_NodeNormals
[i
].data());
83 badness
->SetValue(i
, double(m_N2BadFaces
[i
].size()));
85 grid
->GetPointData()->AddArray(normals
);
86 grid
->GetPointData()->AddArray(badness
);
88 for (int i
= 0; i
< m_Faces
.size(); ++i
) {
89 QVector
<vtkIdType
> pts(m_Faces
[i
].size());
90 for (int j
= 0; j
< m_Faces
[i
].size(); ++j
) {
91 pts
[j
] = m_Faces
[i
][j
];
93 grid
->InsertNextCell(VTK_POLYGON
, m_Faces
[i
].size(), pts
.data());
95 EG_VTKSP(vtkXMLUnstructuredGridWriter
, vtu
);
96 file_name
= GuiMainWindow::pointer()->getCwd() + "/" + file_name
+ ".vtu";
97 vtu
->SetFileName(qPrintable(file_name
));
98 vtu
->SetDataModeToBinary();
103 void PolyMolecule::createPolyData(vtkPolyData
*poly_data
)
105 int N
= m_Nodes
.size();
106 EG_VTKSP(vtkPoints
, points
);
107 points
->SetNumberOfPoints(N
);
108 for (int i
= 0; i
< N
; ++i
) {
109 vec3_t x
= getXNode(i
);
110 points
->SetPoint(i
, x
.data());
112 poly_data
->Allocate(m_Faces
.size());
113 poly_data
->SetPoints(points
);
114 for (int i
= 0; i
< m_Faces
.size(); ++i
) {
115 QVector
<vtkIdType
> pts(m_Faces
[i
].size());
116 for (int j
= 0; j
< m_Faces
[i
].size(); ++j
) {
117 pts
[j
] = m_Faces
[i
][j
];
119 if (pts
.size() == 3) {
120 poly_data
->InsertNextCell(VTK_TRIANGLE
, pts
.size(), pts
.data());
121 } else if (pts
.size() == 4) {
122 poly_data
->InsertNextCell(VTK_QUAD
, pts
.size(), pts
.data());
124 poly_data
->InsertNextCell(VTK_POLYGON
, pts
.size(), pts
.data());
129 void PolyMolecule::computeCentreOfGravity()
131 m_AllPositive
= true;
132 // first guess of centre of gravity
133 vec3_t
x_centre(0,0,0);
134 for (int i
= 0; i
< m_Faces
.size(); ++i
) {
135 x_centre
+= getXFace(i
);
137 m_N2BadFaces
.resize(m_Nodes
.size());
138 for (int i
= 0; i
< m_Nodes
.size(); ++i
) {
139 m_N2BadFaces
[i
].clear();
141 x_centre
*= 1.0/m_Faces
.size();
142 for (int iter
= 0; iter
< 2; ++iter
) {
144 m_CentreOfGravity
= vec3_t(0,0,0);
145 m_MaxPyramidVolume
= -1e99
;
146 m_MinPyramidVolume
= 1e99
;
147 for (int i
= 0; i
< m_Faces
.size(); ++i
) {
148 double V_pyramid
= 0;
149 vec3_t x_face
= getXFace(i
);
150 QVector
<vec3_t
> x(m_Faces
[i
].size() + 1);
151 for (int j
= 0; j
< m_Faces
[i
].size(); ++j
) {
152 x
[j
] = getXNode(m_Faces
[i
][j
]);
154 x
.last() = x
.first();
155 for (int j
= 0; j
< m_Faces
[i
].size(); ++j
) {
156 double V
= GeometryTools::tetraVol(x_face
, x
[j
+1], x
[j
], x_centre
, true);
158 m_AllPositive
= false;
162 m_CentreOfGravity
+= 0.25*V
*(x_face
+ x
[j
+1] + x
[j
] + x_centre
);
164 if (V_pyramid
<= 0) {
165 for (int j
= 0; j
< m_Faces
[i
].size(); ++j
) {
166 m_N2BadFaces
[m_Faces
[i
][j
]].insert(i
);
169 m_MaxPyramidVolume
= max(V_pyramid
, m_MaxPyramidVolume
);
170 m_MinPyramidVolume
= min(V_pyramid
, m_MinPyramidVolume
);
172 m_CentreOfGravity
*= 1.0/V_total
;
173 x_centre
= m_CentreOfGravity
;
177 void PolyMolecule::buildNode2Node()
180 m_N2N
.resize(m_Nodes
.size());
181 for (int i
= 0; i
< m_Faces
.size(); ++i
) {
182 for (int j
= 0; j
< m_Faces
[i
].size() - 1; ++j
) {
183 m_N2N
[m_Faces
[i
][j
]].insert(m_Faces
[i
][j
+1]);
184 m_N2N
[m_Faces
[i
][j
+1]].insert(m_Faces
[i
][j
]);
189 void PolyMolecule::computeNormals()
191 m_FaceNormals
.fill(vec3_t(0,0,0), m_Faces
.size());
192 m_NodeNormals
.fill(vec3_t(0,0,0), m_Nodes
.size());
193 for (int i
= 0; i
< m_Faces
.size(); ++i
) {
194 QVector
<vec3_t
> x(m_Faces
[i
].size() + 1);
195 vec3_t
x_face(0,0,0);
196 for (int j
= 0; j
< m_Faces
[i
].size(); ++j
) {
197 x
[j
] = getXNode(m_Faces
[i
][j
]);
200 x
.last() = x
.first();
201 x_face
*= 1.0/m_Faces
[i
].size();
202 m_FaceNormals
[i
] = vec3_t(0,0,0);
203 for (int j
= 0; j
< m_Faces
[i
].size(); ++j
) {
204 m_FaceNormals
[i
] += triNormal(x_face
, x
[j
], x
[j
+1]);
206 for (int j
= 0; j
< m_Faces
[i
].size(); ++j
) {
207 vec3_t n
= m_FaceNormals
[i
];
209 m_NodeNormals
[m_Faces
[i
][j
]] += n
;
212 for (int i
= 0; i
< m_Nodes
.size(); ++i
) {
213 m_NodeNormals
[i
].normalise();
217 vec3_t
PolyMolecule::getXFace(int face
)
220 foreach (int i
, m_Faces
[face
]) {
223 if (m_Nodes
.size() == 0) {
226 return (1.0/m_Faces
[face
].size())*x
;
229 void PolyMolecule::smooth(bool delaunay
, bool write
)
232 if (write
) writeVtkFile("bad_cell");
234 // collect all "healthy" cells in the neighbourhood
235 // they will be checked for volume inversion caused by the smoothing of this cell
237 QSet
<int> check_cells
;
238 check_cells
.insert(m_PCell
);
239 for (int i
= 0; i
< m_PolyMesh
->numFacesOfPCell(m_PCell
); ++i
) {
240 int i_face
= m_PolyMesh
->pcell2Face(m_PCell
, i
);
241 int i_cell
= m_PolyMesh
->owner(i_face
);
242 PolyMolecule
pm(m_PolyMesh
, i_cell
);
243 if (pm
.minPyramidVolume() > 0) {
244 check_cells
.insert(i_cell
);
246 i_cell
= m_PolyMesh
->neighbour(i_face
);
248 PolyMolecule
pm(m_PolyMesh
, i_cell
);
249 if (pm
.minPyramidVolume() > 0) {
250 check_cells
.insert(i_cell
);
255 // VTK pipeline to get convex hull
256 // The cell will be "inflated" into this convex hull.
258 QVector
<QVector
<vec3_t
> > convex_hull
;
260 EG_VTKSP(vtkPolyData
, poly_data
);
261 EG_VTKSP(vtkPoints
, points
);
262 points
->SetNumberOfPoints(m_Nodes
.size());
263 for (vtkIdType id_node
= 0; id_node
< m_Nodes
.size(); ++id_node
) {
264 vec3_t x
= getXNode(id_node
);
265 points
->SetPoint(id_node
, x
.data());
267 poly_data
->Allocate(m_Faces
.size());
268 poly_data
->SetPoints(points
);
269 for (int i
= 0; i
< m_Faces
.size(); ++i
) {
270 QVector
<vtkIdType
> pts(m_Faces
[i
].size());
271 for (int j
= 0; j
< m_Faces
[i
].size(); ++j
) {
272 pts
[j
] = m_Faces
[i
][j
];
274 poly_data
->InsertNextCell(VTK_POLYGON
, pts
.size(), pts
.data());
276 EG_VTKSP(vtkGeometryFilter
, surface
);
278 EG_VTKSP(vtkDelaunay3D
, delaunay
);
279 delaunay
->SetOffset(100);
280 delaunay
->SetInput(poly_data
);
281 surface
->SetInput(delaunay
->GetOutput());
283 EG_VTKSP(vtkHull
, hull
);
284 hull
->SetInput(poly_data
);
285 hull
->AddRecursiveSpherePlanes(5);
286 EG_VTKSP(vtkTriangleFilter
, tri
);
287 tri
->SetInput(hull
->GetOutput());
288 surface
->SetInput(tri
->GetOutput());
291 if (surface
->GetOutput()->GetNumberOfCells() < 4) {
294 EG_VTKSP(vtkXMLPolyDataWriter
, vtp
);
296 QString file_name
= GuiMainWindow::pointer()->getCwd() + "/" + "convex_hull.vtp";
297 vtp
->SetFileName(qPrintable(file_name
));
298 vtp
->SetDataModeToAscii();
299 vtp
->SetInput(surface
->GetOutput());
302 convex_hull
.fill(QVector
<vec3_t
>(3), surface
->GetOutput()->GetNumberOfCells());
303 for (vtkIdType id_tri
= 0; id_tri
< surface
->GetOutput()->GetNumberOfCells(); ++id_tri
) {
304 EG_GET_CELL(id_tri
, surface
->GetOutput());
305 if (type_cell
!= VTK_TRIANGLE
) {
308 for (int i
= 0; i
< 3; ++i
) {
310 surface
->GetOutput()->GetPoint(pts
[i
], x
.data());
311 convex_hull
[id_tri
][i
] = x
;
316 // Save old nodes for under-relaxations
318 QVector
<vec3_t
> x_old(m_Nodes
.size());
319 for (int i
= 0; i
< m_Nodes
.size(); ++i
) {
320 x_old
[i
] = getXNode(i
);
325 for (int iter
= 1; iter
<= 100; ++iter
) {
326 for (int i
= 0; i
< m_Nodes
.size(); ++i
) {
327 vec3_t x
= getXNode(i
);
328 double L
= (x
- m_CentreOfGravity
).abs();
330 vec3_t Dx_n
= 0.1*L
*m_NodeNormals
[i
];
332 foreach (int neigh
, m_N2N
[i
]) {
333 Dx_lp
+= 0.1*(getXNode(neigh
) - x
);
335 //Dx_lp -= (Dx_lp * m_NodeNormals[i])*m_NodeNormals[i];
336 vec3_t Dx
= Dx_n
+ Dx_lp
;
338 for (int j
= 0; j
< convex_hull
.size(); ++j
) {
339 vec3_t np
= triNormal(convex_hull
[j
][0], convex_hull
[j
][1], convex_hull
[j
][2]);
341 double d
= GeometryTools::intersection(x
, Dx
, convex_hull
[j
][0], np
);
342 if (d
> -1e-3 && d
< 1) {
347 for (int j
= 0; j
< convex_hull
.size(); ++j
) {
348 vec3_t np
= triNormal(convex_hull
[j
][0], convex_hull
[j
][1], convex_hull
[j
][2]);
350 double d
= GeometryTools::intersection(x
, Dx
, convex_hull
[j
][0], np
);
351 if (d
> -1e-3 && d
< 1) {
357 m_PolyMesh
->setNodeVector(m_Nodes
[i
], x
);
360 file_name
.setNum(iter
);
361 file_name
= file_name
.rightJustified(4, '0');
362 file_name
= "improved_cell." + file_name
;
363 if (write
) writeVtkFile(file_name
);
364 computeCentreOfGravity();
368 // get new state of the cell for under relaxation
370 QVector
<vec3_t
> x_new(m_Nodes
.size());
371 for (int i
= 0; i
< m_Nodes
.size(); ++i
) {
372 x_new
[i
] = getXNode(i
);
375 // check all formerly "healthy" cells and under relax as rquired
381 foreach (int i_pcell
, check_cells
) {
382 PolyMolecule
pm(m_PolyMesh
, i_pcell
);
383 if (!pm
.allPositive()) {
394 for (int i
= 0; i
< m_Nodes
.size(); ++i
) {
395 m_PolyMesh
->setNodeVector(m_Nodes
[i
], x_old
[i
] + relax
*(x_new
[i
] - x_old
[i
]));
401 if (write
) writeVtkFile("improved_cell");
404 void PolyMolecule::split(bool write
)
406 if (write
) writeVtkFile("concave_cell");
409 void PolyMolecule::fix(bool write
)
412 if (m_MinPyramidVolume/m_MaxPyramidVolume < -0.1) {
419 if (m_MinPyramidVolume
<= 0) {
420 smooth(false, write
);