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 "surfaceoperation.h"
25 #include "guimainwindow.h"
27 #include <vtkCharArray.h>
29 #include <vtkCellArray.h>
30 #include <vtkPolygon.h>
32 #include "geometrytools.h"
33 using namespace GeometryTools
;
35 SurfaceOperation::SurfaceOperation() : Operation()
37 //default values for determining node types and for smoothing operations
38 getSet("surface meshing", "edge angle to determine fixed vertices", 180, m_EdgeAngle
);
39 getSet("surface meshing", "feature angle", 180, m_FeatureAngle
);
40 m_FeatureAngle
= GeometryTools::deg2rad(m_FeatureAngle
);
41 m_EdgeAngle
= GeometryTools::deg2rad(m_EdgeAngle
);
42 setEdgeAngle(m_EdgeAngle
);
43 m_BoundarySmoothing
= 1;
44 m_StretchingFactor
= 0;
45 m_UniformSnapPoints
= false;
48 void SurfaceOperation::operate()
53 ostream
& operator<<(ostream
&out
, stencil_t S
)
55 out
<< "S.id_cell = " << S
.id_cell
<< " ";
56 out
<< "S.id_node = " << S
.id_node
<< " ";
57 out
<< "S.sameBC = " << S
.sameBC
<< " ";
58 out
<< "S.type = " << S
.type_cell
<< " ";
59 out
<< "S.p1 = " << S
.p1
<< " ";
60 out
<< "S.p2 = " << S
.p2
<< " ";
64 stencil_t
SurfaceOperation::getStencil(vtkIdType id_cell1
, int j1
)
68 vtkIdType N_pts
, *pts
;
69 m_Grid
->GetCellPoints(id_cell1
, N_pts
, pts
);
76 QSet
<vtkIdType
> cells_p1
;
77 for (int i
= 0; i
< m_Part
.n2cGSize(S
.p1
); ++i
) {
78 vtkIdType id_cell
= m_Part
.n2cGG(S
.p1
, i
);
79 if (id_cell
!= id_cell1
) {
80 cells_p1
.insert(id_cell
);
83 QSet
<vtkIdType
> cells_p2
;
84 for (int i
= 0; i
< m_Part
.n2cGSize(S
.p2
); ++i
) {
85 vtkIdType id_cell
= m_Part
.n2cGG(S
.p2
, i
);
86 if (id_cell
!= id_cell1
) {
87 cells_p2
.insert(id_cell
);
90 QSet
<vtkIdType
> cells
= cells_p1
.intersect(cells_p2
);
91 EG_VTKDCC(vtkIntArray
, cell_code
, m_Grid
, "cell_code");
94 S
.id_cell
[0] = id_cell1
;
95 foreach (vtkIdType id_cell
, cells
) {
96 if (isSurface(id_cell
, m_Grid
)) {
97 S
.id_cell
.push_back(id_cell
);
98 if (cell_code
->GetValue(id_cell
) != cell_code
->GetValue(id_cell1
)) {
103 S
.id_node
.resize(S
.id_cell
.size());
104 S
.type_cell
.resize(S
.id_cell
.size());
105 for (int i
= 0; i
< S
.id_cell
.size(); ++i
) {
106 vtkIdType N_pts
, *pts
;
107 m_Grid
->GetCellPoints(S
.id_cell
[i
], N_pts
, pts
);
108 S
.type_cell
[i
] = m_Grid
->GetCellType(S
.id_cell
[i
]);
109 for (int j
= 0; j
< N_pts
; ++j
) {
110 if (pts
[j
] != S
.p1
&& pts
[j
] != S
.p2
) {
111 S
.id_node
[i
] = pts
[j
];
119 int SurfaceOperation::UpdateCurrentMeshDensity()
121 if ( DebugLevel
> 0 ) {
122 cout
<< "===UpdateMeshDensity START===" << endl
;
124 QVector
<vtkIdType
> cells
;
125 getAllSurfaceCells( cells
, m_Grid
);
126 EG_VTKDCC( vtkIntArray
, cell_code
, m_Grid
, "cell_code" );
127 EG_VTKDCN( vtkDoubleArray
, characteristic_length_desired
, m_Grid
, "node_meshdensity_desired" );
130 if ( DebugLevel
> 5 ) {
131 cout
<< "cells.size()=" << cells
.size() << endl
;
133 EG_VTKDCN( vtkDoubleArray
, node_meshdensity_current
, m_Grid
, "node_meshdensity_current" );
134 l2g_t nodes
= getPartNodes();
135 foreach( vtkIdType node
, nodes
) {
136 node_meshdensity_current
->SetValue( node
, CurrentMeshDensity( node
) );
138 if ( DebugLevel
> 0 ) {
139 cout
<< "===UpdateMeshDensity END===" << endl
;
141 return( 0 ); ///\todo what for???
144 void SurfaceOperation::readVMD()
146 QString buffer
= GuiMainWindow::pointer()->getXmlSection("engrid/surface/table").replace("\n", " ");
148 int column_count
= 0;
151 if(!buffer
.isEmpty()) {
152 QTextStream
in(&buffer
, QIODevice::ReadOnly
);
153 in
>> row_count
>> column_count
;
154 QVector
<int> tmp_bcs
;
155 GuiMainWindow::pointer()->getAllBoundaryCodes(tmp_bcs
);
156 if (column_count
== tmp_bcs
.size() + 3) {
157 m_VMDvector
.fill(VertexMeshDensity(), row_count
);
158 for (int i
= 0; i
< row_count
; ++i
) {
161 foreach (int bc
, tmp_bcs
) {
162 in
>> row
>> column
>> formula
;
163 m_VMDvector
[row
].BCmap
[bc
] = formula
.toInt();
165 in
>> row
>> column
>> formula
;
166 m_VMDvector
[row
].type
= Str2VertexType(formula
);
167 in
>> row
>> column
>> formula
;
168 if (formula
== "{{{empty}}}") {
171 m_VMDvector
[i
].setNodes(formula
);
172 in
>> row
>> column
>> formula
;
173 m_VMDvector
[i
].density
= formula
.toDouble();
176 EG_ERR_RETURN(QObject::tr("Mismatch of number of boundary codes!"));
181 void SurfaceOperation::updateNodeInfo()
185 l2g_t nodes
= getPartNodes();
187 foreach (vtkIdType id_node
, nodes
) {
188 EG_VTKDCN(vtkCharArray
, node_type
, m_Grid
, "node_type");
189 node_type
->SetValue(id_node
, getNodeType(id_node
, true));
191 //density index from table
192 EG_VTKDCN(vtkIntArray
, node_specified_density
, m_Grid
, "node_specified_density");
194 VertexMeshDensity nodeVMD
= getVMD(id_node
);
195 int idx
= nodeVMD
.findSmallestVMD(m_VMDvector
);
196 node_specified_density
->SetValue(id_node
, idx
);
198 updatePotentialSnapPoints();
201 void SurfaceOperation::updatePotentialSnapPoints()
203 setAllSurfaceCells();
204 m_PotentialSnapPoints
.resize(m_Grid
->GetNumberOfPoints());
206 if (m_UniformSnapPoints
) {
207 m_PotentialSnapPoints
.resize(m_Grid
->GetNumberOfPoints());
208 EG_FORALL_NODES(id_node
, m_Grid
) {
209 for (int i
= 0; i
< m_Part
.n2nGSize(id_node
); ++i
) {
210 m_PotentialSnapPoints
[id_node
].append(m_Part
.n2nGG(id_node
, i
));
216 EG_VTKDCN(vtkCharArray
, node_type
, m_Grid
, "node_type");
217 EG_FORALL_NODES(id_node1
, m_Grid
) {
218 m_PotentialSnapPoints
[id_node1
].clear();
219 char type1
= node_type
->GetValue(id_node1
);
220 for (int i
= 0; i
< m_Part
.n2nGSize(id_node1
); ++i
) {
221 vtkIdType id_node2
= m_Part
.n2nGG(id_node1
, i
);
222 char type2
= node_type
->GetValue(id_node2
);
223 if ( (type1
== EG_SIMPLE_VERTEX
)
224 || (type1
== EG_FEATURE_EDGE_VERTEX
&& (type2
== EG_FEATURE_EDGE_VERTEX
|| type2
== EG_FEATURE_CORNER_VERTEX
))
225 || (type1
== EG_BOUNDARY_EDGE_VERTEX
&& (type2
== EG_BOUNDARY_EDGE_VERTEX
|| type2
== EG_FIXED_VERTEX
)))
227 m_PotentialSnapPoints
[id_node1
].append(id_node2
);
234 char SurfaceOperation::getNodeType(vtkIdType id_node
, bool fix_unselected
)
236 if (m_Part
.n2nGSize(id_node
) == 0) {
237 return EG_FIXED_VERTEX
;
240 //char type = EG_SIMPLE_VERTEX;
241 EG_VTKDCC(vtkIntArray
, cell_code
, m_Grid
, "cell_code");
244 for (int i
= 0; i
< m_Part
.n2cGSize(id_node
); ++i
) {
245 vtkIdType id_cell
= m_Part
.n2cGG(id_node
, i
);
247 // fix all vertices that are part of a volume element or a quad
248 if (m_Grid
->GetCellType(id_cell
) != VTK_TRIANGLE
) {
249 return EG_FIXED_VERTEX
;
252 int bc
= cell_code
->GetValue(id_cell
);
254 // fix nodes which belong to faces with unselected boundary codes
255 if (!m_BoundaryCodes
.contains(bc
) && fix_unselected
) {
256 return EG_FIXED_VERTEX
;
262 if (bcs
.size() >= 3 || bcs
.size() == 0) {
263 return EG_FIXED_VERTEX
;
265 if (bcs
.size() == 2) {
266 return EG_BOUNDARY_EDGE_VERTEX
;
269 SurfaceProjection
* proj
= GuiMainWindow::pointer()->getSurfProj(*bcs
.begin(), true);
273 m_Grid
->GetPoint(id_node
, x0
.data());
275 // compute average edge length
277 for (int i
= 0; i
< m_Part
.n2nGSize(id_node
); ++i
) {
279 m_Grid
->GetPoint(m_Part
.n2nGG(id_node
, i
), xn
.data());
280 L
+= (x0
- xn
).abs();
282 L
/= m_Part
.n2nGSize(id_node
);
284 bool convex
= isConvexNode(id_node
);
286 x0
= proj
->project(x0
, id_node
, true, m_NodeNormal
[id_node
]);
287 double radius
= proj
->lastProjRadius();
289 x
= x0
+ L
*m_NodeNormal
[id_node
];
291 x
= x0
- L
*m_NodeNormal
[id_node
];
293 vec3_t n
= proj
->lastProjNormal();
294 if (GeometryTools::angle(n
, m_NodeNormal
[id_node
]) > 0.5*m_FeatureAngle
&& !proj
->lastProjFailed()) {
295 double d
= L
/tan(0.5*m_FeatureAngle
);
296 static const int num_steps
= 36;
297 double D_alpha
= 2*M_PI
/num_steps
;
300 v
= GeometryTools::orthogonalVector(m_NodeNormal
[id_node
]);
302 for (int i
= 0; i
< num_steps
; ++i
) {
303 v
= GeometryTools::rotate(v
, m_NodeNormal
[id_node
], D_alpha
);
304 vec3_t xp
= proj
->project(x
, id_node
, true, v
, true);
305 if (proj
->lastProjFailed()) {
308 double l
= (x
- xp
).abs();
315 return EG_FEATURE_CORNER_VERTEX
;
323 v
= GeometryTools::orthogonalVector(n
);
325 for (int i
= 0; i
< num_steps
; ++i
) {
326 v
= GeometryTools::rotate(v
, n
, D_alpha
);
327 vec3_t xp
= proj
->project(x
, id_node
, true, v
, true);
328 if (!proj
->lastProjFailed()) {
329 double l
= (x
- xp
).abs();
336 return EG_FEATURE_EDGE_VERTEX
;
341 return EG_SIMPLE_VERTEX
;
344 int SurfaceOperation::getEdgeCells(vtkIdType id_node1
, vtkIdType id_node2
, QVector
<vtkIdType
> &EdgeCells
)
346 g2l_t _nodes
= getPartLocalNodes();
347 l2g_t cells
= getPartCells();
348 l2l_t n2c
= getPartN2C();
351 foreach (int i
, n2c
[_nodes
[id_node1
]]) {
356 foreach( int i
, n2c
[_nodes
[id_node2
]] ) {
361 EdgeCells
= Set2Vector(S2
, false);
362 return EdgeCells
.size();
365 int SurfaceOperation::getEdgeCells( vtkIdType id_node1
, vtkIdType id_node2
, QSet
<vtkIdType
> &EdgeCells
)
367 g2l_t _nodes
= getPartLocalNodes();
368 l2g_t cells
= getPartCells();
369 l2l_t n2c
= getPartN2C();
372 foreach( int i
, n2c
[_nodes
[id_node1
]] ) {
373 S1
.insert( cells
[i
] );
377 foreach( int i
, n2c
[_nodes
[id_node2
]] ) {
378 S2
.insert( cells
[i
] );
381 EdgeCells
= S2
.intersect( S1
);
382 return EdgeCells
.size();
385 char SurfaceOperation::getEdgeType(vtkIdType a_node1
, vtkIdType a_node2
, bool fix_unselected
)
387 double cos_feature_angle
= cos(this->m_FeatureAngle
);
388 bool feature_edges_disabled
= m_FeatureAngle
>= M_PI
;
390 // compute number of cells around edge [a_node,p2] and put them into neighbour_cells
391 QVector
<vtkIdType
> neighbour_cells
;
392 int numNei
= getEdgeCells(a_node1
, a_node2
, neighbour_cells
) - 1;
395 char edge
= EG_SIMPLE_VERTEX
;
398 edge
= EG_BOUNDARY_EDGE_VERTEX
;
399 } else if (numNei
>= 2) {
400 edge
= EG_BOUNDARY_EDGE_VERTEX
;
401 } else if (numNei
== 1) {
403 // check angle between cell1 and cell2 against FeatureAngle
404 double cosa
= cosAngle(m_Grid
, neighbour_cells
[0], neighbour_cells
[1]);
405 if (cosa
<= cos_feature_angle
&& !feature_edges_disabled
) {
406 edge
= EG_FEATURE_EDGE_VERTEX
;
409 // check the boundary codes
410 EG_VTKDCC( vtkIntArray
, cell_code
, m_Grid
, "cell_code" );
411 int cell_code_0
= cell_code
->GetValue( neighbour_cells
[0] );
412 int cell_code_1
= cell_code
->GetValue( neighbour_cells
[1] );
413 if (cell_code_0
!= cell_code_1
) {
414 edge
= EG_BOUNDARY_EDGE_VERTEX
;
417 if (fix_unselected
) {
418 if (!m_BoundaryCodes
.contains(cell_code_0
) || !m_BoundaryCodes
.contains(cell_code_1
)) {
419 // does not make sense, but should make the points of the edge fixed
420 edge
= EG_FIXED_VERTEX
;
428 VertexMeshDensity
SurfaceOperation::getVMD( vtkIdType id_node
)
430 g2l_t _nodes
= getPartLocalNodes();
431 l2g_t cells
= getPartCells();
432 l2l_t n2c
= getPartN2C();
434 EG_VTKDCN( vtkCharArray
, node_type
, m_Grid
, "node_type" );
435 EG_VTKDCC( vtkIntArray
, cell_code
, m_Grid
, "cell_code" );
437 VertexMeshDensity VMD
;
438 VMD
.type
= node_type
->GetValue( id_node
);
440 VMD
.CurrentNode
= id_node
;
442 foreach( int i_cell
, n2c
[_nodes
[id_node
]] ) {
443 vtkIdType id_cell
= cells
[i_cell
];
444 VMD
.BCmap
[cell_code
->GetValue( id_cell
)] = 2;
449 //////////////////////////////////////////////
450 double SurfaceOperation::currentVertexAvgDist( vtkIdType id_node
)
452 l2g_t nodes
= getPartNodes();
453 g2l_t _nodes
= getPartLocalNodes();
454 l2l_t n2n
= getPartN2N();
456 double total_dist
= 0;
458 int N
= n2n
[_nodes
[id_node
]].size();
460 m_Grid
->GetPoint( id_node
, C
.data() );
461 foreach( int i_node_neighbour
, n2n
[_nodes
[id_node
]] ) {
462 vtkIdType id_node_neighbour
= nodes
[i_node_neighbour
];
464 m_Grid
->GetPoint( id_node_neighbour
, M
.data() );
465 total_dist
+= ( M
- C
).abs();
467 avg_dist
= total_dist
/ ( double )N
;
471 double SurfaceOperation::CurrentMeshDensity( vtkIdType id_node
)
473 return 1.0 / currentVertexAvgDist( id_node
);
476 ///\todo change meshdensity fields to edgelength fields since this is what is mostly used?
478 /// desired edge length for id_node
479 double SurfaceOperation::desiredEdgeLength( vtkIdType id_node
)
481 EG_VTKDCN( vtkDoubleArray
, characteristic_length_desired
, m_Grid
, "node_meshdensity_desired" );
482 return( 1.0 / characteristic_length_desired
->GetValue( id_node
) );
485 /// mean desired edge length for id_cell
486 double SurfaceOperation::meanDesiredEdgeLength( vtkIdType id_cell
)
488 vtkIdType num_pts
, *pts
;
489 m_Grid
->GetCellPoints( id_cell
, num_pts
, pts
);
491 for ( int i
= 0; i
< num_pts
; i
++ ) {
492 total
+= desiredEdgeLength( pts
[i
] );
494 return total
/ ( double )num_pts
;
497 QVector
<vtkIdType
> SurfaceOperation::getPotentialSnapPoints( vtkIdType id_node
)
499 if ((id_node
< 0) || (id_node
>= m_PotentialSnapPoints
.size())) {
500 // UpdatePotentialSnapPoints should probably be called before using this function.
503 return m_PotentialSnapPoints
[id_node
];
506 bool SurfaceOperation::isCell(vtkIdType id_node1
, vtkIdType id_node2
, vtkIdType id_node3
)
508 QVector
<vtkIdType
> EdgeCells_12
;
509 QVector
<vtkIdType
> EdgeCells_13
;
510 QVector
<vtkIdType
> inter
;
512 getEdgeCells( id_node1
, id_node2
, EdgeCells_12
);
513 getEdgeCells( id_node1
, id_node3
, EdgeCells_13
);
514 qcontIntersection( EdgeCells_12
, EdgeCells_13
, inter
);
516 qWarning()<<"(id_node1, id_node2, id_node3)="<<"("<<id_node1
<<", "<<id_node2
<<", "<<id_node3
<<")";
517 qWarning()<<"EdgeCells_12="<<EdgeCells_12
;
518 qWarning()<<"EdgeCells_13="<<EdgeCells_13
;
519 qWarning()<<"inter="<<inter
;
520 writeGrid(m_Grid
, "abort");
521 EG_BUG
;// multiple cells in the same place
524 qDebug()<<"(id_node1, id_node2, id_node3)="<<"("<<id_node1
<<", "<<id_node2
<<", "<<id_node3
<<")";
525 qDebug()<<"EdgeCells_12="<<EdgeCells_12
;
526 qDebug()<<"EdgeCells_13="<<EdgeCells_13
;
527 qDebug()<<"inter="<<inter
;
529 return(inter
.size()>0);
532 void SurfaceOperation::computeNormals()
534 EG_VTKDCC(vtkIntArray
, cell_code
, m_Grid
, "cell_code");
535 m_NodeNormal
.fill(vec3_t(0,0,0), m_Grid
->GetNumberOfPoints());
536 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
537 if (m_Part
.localNode(id_node
) != -1) {
539 for (int i
= 0; i
< m_Part
.n2cGSize(id_node
); ++i
) {
540 vtkIdType id_cell
= m_Part
.n2cGG(id_node
, i
);
541 if (isSurface(id_cell
, m_Grid
)) {
542 int bc
= cell_code
->GetValue(id_cell
);
543 if (m_BoundaryCodes
.contains(bc
)) {
548 int num_bcs
= bcs
.size();
549 QVector
<vec3_t
> normal(num_bcs
, vec3_t(0,0,0));
552 foreach (int bc
, bcs
) {
556 for (int i
= 0; i
< m_Part
.n2cGSize(id_node
); ++i
) {
557 vtkIdType id_cell
= m_Part
.n2cGG(id_node
, i
);
558 if (isSurface(id_cell
, m_Grid
)) {
559 int bc
= cell_code
->GetValue(id_cell
);
560 if (m_BoundaryCodes
.contains(bc
)) {
561 vtkIdType N_pts
, *pts
;
562 m_Grid
->GetCellPoints(id_cell
, N_pts
, pts
);
564 for (int j
= 0; j
< N_pts
; ++j
) {
565 if (pts
[j
] == id_node
) {
566 m_Grid
->GetPoint(pts
[j
], a
.data());
568 m_Grid
->GetPoint(pts
[j
-1], b
.data());
570 m_Grid
->GetPoint(pts
[N_pts
-1], b
.data());
573 m_Grid
->GetPoint(pts
[j
+1], c
.data());
575 m_Grid
->GetPoint(pts
[0], c
.data());
581 double alpha
= GeometryTools::angle(u
, v
);
582 vec3_t n
= u
.cross(v
);
584 if (checkVector(n
)) {
585 normal
[bcmap
[bc
]] -= alpha
*n
;
590 for (int i
= 0; i
< num_bcs
; ++i
) {
591 normal
[i
].normalise();
596 for (int i
= 0; i
< num_bcs
; ++i
) {
597 for (int j
= i
+ 1; j
< num_bcs
; ++j
) {
598 vec3_t n
= normal
[i
] + normal
[j
];
600 m_NodeNormal
[id_node
] += n
;
604 for (int i
= 0; i
< num_bcs
; ++i
) {
605 m_NodeNormal
[id_node
] += normal
[i
];
609 m_NodeNormal
[id_node
] = normal
[0];
611 m_NodeNormal
[id_node
].normalise();
617 double SurfaceOperation::normalIrregularity(vtkIdType id_node
)
620 QVector
<vec3_t
> nc(m_Part
.n2cGSize(id_node
));
621 for (int i
= 0; i
< nc
.size(); ++i
) {
622 nc
[i
] = GeometryTools::cellNormal(m_Grid
, m_Part
.n2cGG(id_node
, i
));
625 for (int i
= 0; i
< nc
.size(); ++i
) {
626 for (int j
= i
+ 1; j
< nc
.size(); ++j
) {
627 nirr
+= 1.0 - nc
[i
]*nc
[j
];
633 bool SurfaceOperation::isConvexNode(vtkIdType id_node
)
635 int N
= m_Part
.n2nGSize(id_node
);
639 vec3_t x1
, x2(0,0,0);
640 m_Grid
->GetPoint(id_node
, x1
.data());
641 for (int i
= 0; i
< N
; ++i
) {
643 m_Grid
->GetPoint(m_Part
.n2nGG(id_node
, i
), x
.data());
647 if ((x1
- x2
)*m_NodeNormal
[id_node
] < 0) {