Salome HOME
Typo-fix by Kunda
[modules/smesh.git] / src / SMDS / SMDS_VolumeTool.cxx
1 // Copyright (C) 2007-2016  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22
23 // File      : SMDS_VolumeTool.cxx
24 // Created   : Tue Jul 13 12:22:13 2004
25 // Author    : Edward AGAPOV (eap)
26 //
27 #ifdef _MSC_VER
28 #pragma warning(disable:4786)
29 #endif
30
31 #include "SMDS_VolumeTool.hxx"
32
33 #include "SMDS_MeshElement.hxx"
34 #include "SMDS_MeshNode.hxx"
35 #include "SMDS_VtkVolume.hxx"
36 #include "SMDS_Mesh.hxx"
37
38 #include "utilities.h"
39
40 #include <map>
41 #include <limits>
42 #include <cmath>
43 #include <numeric>
44 #include <algorithm>
45
46 using namespace std;
47
48 namespace
49 {
50 // ======================================================
51 // Node indices in faces depending on volume orientation
52 // making most faces normals external
53 // ======================================================
54 // For all elements, 0-th face is bottom based on the first nodes.
55 // For prismatic elements (tetra,hexa,prisms), 1-th face is a top one.
56 // For all elements, side faces follow order of bottom nodes
57 // ======================================================
58
59 /*
60 //           N3
61 //           +
62 //          /|\
63 //         / | \
64 //        /  |  \
65 //    N0 +---|---+ N1                TETRAHEDRON
66 //       \   |   /
67 //        \  |  /
68 //         \ | /
69 //          \|/
70 //           +
71 //           N2
72 */
73 static int Tetra_F [4][4] = { // FORWARD == EXTERNAL
74   { 0, 1, 2, 0 },              // All faces have external normals
75   { 0, 3, 1, 0 },
76   { 1, 3, 2, 1 },
77   { 0, 2, 3, 0 }}; 
78 static int Tetra_RE [4][4] = { // REVERSED -> FORWARD (EXTERNAL)
79   { 0, 2, 1, 0 },              // All faces have external normals
80   { 0, 1, 3, 0 },
81   { 1, 2, 3, 1 },
82   { 0, 3, 2, 0 }};
83 static int Tetra_nbN [] = { 3, 3, 3, 3 };
84
85 //
86 //     PYRAMID
87 //
88 static int Pyramid_F [5][5] = { // FORWARD == EXTERNAL
89   { 0, 1, 2, 3, 0 },            // All faces have external normals
90   { 0, 4, 1, 0, 4 },
91   { 1, 4, 2, 1, 4 },
92   { 2, 4, 3, 2, 4 },
93   { 3, 4, 0, 3, 4 }
94 }; 
95 static int Pyramid_RE [5][5] = { // REVERSED -> FORWARD (EXTERNAL)
96   { 0, 3, 2, 1, 0 },             // All faces but a bottom have external normals
97   { 0, 1, 4, 0, 4 },
98   { 1, 2, 4, 1, 4 },
99   { 2, 3, 4, 2, 4 },
100   { 3, 0, 4, 3, 4 }}; 
101 static int Pyramid_nbN [] = { 4, 3, 3, 3, 3 };
102
103 /*   
104 //            + N4
105 //           /|\
106 //          / | \
107 //         /  |  \
108 //        /   |   \
109 //    N3 +---------+ N5
110 //       |    |    |
111 //       |    + N1 |
112 //       |   / \   |                PENTAHEDRON
113 //       |  /   \  |
114 //       | /     \ |
115 //       |/       \|
116 //    N0 +---------+ N2
117 */
118 static int Penta_F [5][5] = { // FORWARD
119   { 0, 1, 2, 0, 0 },          // All faces have external normals
120   { 3, 5, 4, 3, 3 },          // 0 is bottom, 1 is top face
121   { 0, 3, 4, 1, 0 },
122   { 1, 4, 5, 2, 1 },
123   { 0, 2, 5, 3, 0 }}; 
124 static int Penta_RE [5][5] = { // REVERSED -> EXTERNAL
125   { 0, 2, 1, 0, 0 },
126   { 3, 4, 5, 3, 3 },
127   { 0, 1, 4, 3, 0 },
128   { 1, 2, 5, 4, 1 },
129   { 0, 3, 5, 2, 0 }}; 
130 static int Penta_nbN [] = { 3, 3, 4, 4, 4 };
131
132 /*
133 //         N5+----------+N6
134 //          /|         /|
135 //         / |        / |
136 //        /  |       /  |
137 //     N4+----------+N7 |
138 //       |   |      |   |           HEXAHEDRON
139 //       | N1+------|---+N2
140 //       |  /       |  /
141 //       | /        | /
142 //       |/         |/
143 //     N0+----------+N3
144 */
145 static int Hexa_F [6][5] = { // FORWARD
146   { 0, 1, 2, 3, 0 },
147   { 4, 7, 6, 5, 4 },          // all face normals are external
148   { 0, 4, 5, 1, 0 },
149   { 1, 5, 6, 2, 1 },
150   { 3, 2, 6, 7, 3 }, 
151   { 0, 3, 7, 4, 0 }};
152 static int Hexa_RE [6][5] = { // REVERSED -> EXTERNAL
153   { 0, 3, 2, 1, 0 },
154   { 4, 5, 6, 7, 4 },          // all face normals are external
155   { 0, 1, 5, 4, 0 },
156   { 1, 2, 6, 5, 1 },
157   { 3, 7, 6, 2, 3 }, 
158   { 0, 4, 7, 3, 0 }};
159 static int Hexa_nbN [] = { 4, 4, 4, 4, 4, 4 };
160 static int Hexa_oppF[] = { 1, 0, 4, 5, 2, 3 }; // oppopsite facet indices
161
162 /*   
163 //      N8 +------+ N9
164 //        /        \
165 //       /          \
166 //   N7 +            + N10
167 //       \          /
168 //        \        /
169 //      N6 +------+ N11
170 //                             HEXAGONAL PRISM
171 //      N2 +------+ N3
172 //        /        \
173 //       /          \
174 //   N1 +            + N4
175 //       \          /
176 //        \        /
177 //      N0 +------+ N5
178 */
179 static int HexPrism_F [8][7] = { // FORWARD
180   { 0, 1, 2, 3, 4, 5, 0 },
181   { 6,11,10, 9, 8, 7, 6 },
182   { 0, 6, 7, 1, 0, 0, 0 },
183   { 1, 7, 8, 2, 1, 1, 1 },
184   { 2, 8, 9, 3, 2, 2, 2 },
185   { 3, 9,10, 4, 3, 3, 3 },
186   { 4,10,11, 5, 4, 4, 4 },
187   { 5,11, 6, 0, 5, 5, 5 }}; 
188 static int HexPrism_RE [8][7] = { // REVERSED -> EXTERNAL
189   { 0, 5, 4, 3, 2, 1, 0 },
190   { 6,11,10, 9, 8, 7, 6 },
191   { 0, 6, 7, 1, 0, 0, 0 },
192   { 1, 7, 8, 2, 1, 1, 1 },
193   { 2, 8, 9, 3, 2, 2, 2 },
194   { 3, 9,10, 4, 3, 3, 3 },
195   { 4,10,11, 5, 4, 4, 4 },
196   { 5,11, 6, 0, 5, 5, 5 }}; 
197 static int HexPrism_nbN [] = { 6, 6, 4, 4, 4, 4, 4, 4 };
198
199
200 /*
201 //           N3
202 //           +
203 //          /|\
204 //        7/ | \8
205 //        /  |4 \                    QUADRATIC
206 //    N0 +---|---+ N1                TETRAHEDRON
207 //       \   +9  /
208 //        \  |  /
209 //        6\ | /5
210 //          \|/
211 //           +
212 //           N2
213 */
214 static int QuadTetra_F [4][7] = { // FORWARD
215   { 0, 4, 1, 5, 2, 6, 0 },        // All faces have external normals
216   { 0, 7, 3, 8, 1, 4, 0 },
217   { 1, 8, 3, 9, 2, 5, 1 },
218   { 0, 6, 2, 9, 3, 7, 0 }}; 
219 static int QuadTetra_RE [4][7] = { // REVERSED -> FORWARD (EXTERNAL)
220   { 0, 6, 2, 5, 1, 4, 0 },         // All faces have external normals
221   { 0, 4, 1, 8, 3, 7, 0 },
222   { 1, 5, 2, 9, 3, 8, 1 },
223   { 0, 7, 3, 9, 2, 6, 0 }};
224 static int QuadTetra_nbN [] = { 6, 6, 6, 6 };
225
226 //
227 //     QUADRATIC
228 //     PYRAMID
229 //
230 //            +4
231 //
232 //            
233 //       10+-----+11
234 //         |     |        9 - middle point for (0,4) etc.
235 //         |     |
236 //        9+-----+12
237 //
238 //            6
239 //      1+----+----+2
240 //       |         |
241 //       |         |
242 //      5+         +7
243 //       |         |
244 //       |         |
245 //      0+----+----+3
246 //            8
247 static int QuadPyram_F [5][9] = {  // FORWARD
248   { 0, 5, 1, 6, 2, 7, 3, 8, 0 },   // All faces have external normals
249   { 0, 9, 4, 10,1, 5, 0, 4, 4 },
250   { 1, 10,4, 11,2, 6, 1, 4, 4 },
251   { 2, 11,4, 12,3, 7, 2, 4, 4 },
252   { 3, 12,4, 9, 0, 8, 3, 4, 4 }}; 
253 static int QuadPyram_RE [5][9] = { // REVERSED -> FORWARD (EXTERNAL)
254   { 0, 8, 3, 7, 2, 6, 1, 5, 0 },   // All faces but a bottom have external normals
255   { 0, 5, 1, 10,4, 9, 0, 4, 4 },
256   { 1, 6, 2, 11,4, 10,1, 4, 4 },
257   { 2, 7, 3, 12,4, 11,2, 4, 4 },
258   { 3, 8, 0, 9, 4, 12,3, 4, 4 }}; 
259 static int QuadPyram_nbN [] = { 8, 6, 6, 6, 6 };
260
261 /*   
262 //            + N4
263 //           /|\
264 //         9/ | \10
265 //         /  |  \
266 //        /   |   \
267 //    N3 +----+----+ N5
268 //       |    |11  |
269 //       |    |    |
270 //       |    +13  |                QUADRATIC
271 //       |    |    |                PENTAHEDRON
272 //     12+    |    +14
273 //       |    |    |
274 //       |    |    |
275 //       |    + N1 |
276 //       |   / \   |               
277 //       | 6/   \7 |
278 //       | /     \ |
279 //       |/       \|
280 //    N0 +---------+ N2
281 //            8
282 */
283 static int QuadPenta_F [5][9] = {  // FORWARD
284   { 0, 6, 1, 7, 2, 8, 0, 0, 0 },
285   { 3, 11,5, 10,4, 9, 3, 3, 3 },
286   { 0, 12,3, 9, 4, 13,1, 6, 0 },
287   { 1, 13,4, 10,5, 14,2, 7, 1 },
288   { 0, 8, 2, 14,5, 11,3, 12,0 }}; 
289 static int QuadPenta_RE [5][9] = { // REVERSED -> EXTERNAL
290   { 0, 8, 2, 7, 1, 6, 0, 0, 0 },
291   { 3, 9, 4, 10,5, 11,3, 3, 3 },
292   { 0, 6, 1, 13,4, 9, 3, 12,0 },
293   { 1, 7, 2, 14,5, 10,4, 13,1 },
294   { 0, 12,3, 11,5, 14,2, 8, 0 }}; 
295 static int QuadPenta_nbN [] = { 6, 6, 8, 8, 8 };
296
297 /*
298 //                 13                                                         
299 //         N5+-----+-----+N6                          +-----+-----+
300 //          /|          /|                           /|          /| 
301 //       12+ |       14+ |                          + |   +25   + |    
302 //        /  |        /  |                         /  |        /  |    
303 //     N4+-----+-----+N7 |       QUADRATIC        +-----+-----+   |  Central nodes
304 //       |   | 15    |   |       HEXAHEDRON       |   |       |   |  of tri-quadratic
305 //       |   |       |   |                        |   |       |   |  HEXAHEDRON
306 //       | 17+       |   +18                      |   +   22+ |   +  
307 //       |   |       |   |                        |21 |       |   | 
308 //       |   |       |   |                        | + | 26+   | + |    
309 //       |   |       |   |                        |   |       |23 |    
310 //     16+   |       +19 |                        +   | +24   +   |    
311 //       |   |       |   |                        |   |       |   |    
312 //       |   |     9 |   |                        |   |       |   |    
313 //       | N1+-----+-|---+N2                      |   +-----+-|---+    
314 //       |  /        |  /                         |  /        |  /  
315 //       | +8        | +10                        | +   20+   | +      
316 //       |/          |/                           |/          |/       
317 //     N0+-----+-----+N3                          +-----+-----+    
318 //             11                              
319 */
320 static int QuadHexa_F [6][9] = {  // FORWARD
321   { 0, 8, 1, 9, 2, 10,3, 11,0 },   // all face normals are external,
322   { 4, 15,7, 14,6, 13,5, 12,4 },
323   { 0, 16,4, 12,5, 17,1, 8, 0 },
324   { 1, 17,5, 13,6, 18,2, 9, 1 },
325   { 3, 10,2, 18,6, 14,7, 19,3 }, 
326   { 0, 11,3, 19,7, 15,4, 16,0 }};
327 static int QuadHexa_RE [6][9] = {  // REVERSED -> EXTERNAL
328   { 0, 11,3, 10,2, 9, 1, 8, 0 },   // all face normals are external
329   { 4, 12,5, 13,6, 14,7, 15,4 },
330   { 0, 8, 1, 17,5, 12,4, 16,0 },
331   { 1, 9, 2, 18,6, 13,5, 17,1 },
332   { 3, 19,7, 14,6, 18,2, 10,3 }, 
333   { 0, 16,4, 15,7, 19,3, 11,0 }};
334 static int QuadHexa_nbN [] = { 8, 8, 8, 8, 8, 8 };
335
336 static int TriQuadHexa_F [6][9] = {  // FORWARD
337   { 0, 8, 1, 9, 2, 10,3, 11, 20 },   // all face normals are external
338   { 4, 15,7, 14,6, 13,5, 12, 25 },
339   { 0, 16,4, 12,5, 17,1, 8,  21 },
340   { 1, 17,5, 13,6, 18,2, 9,  22 },
341   { 3, 10,2, 18,6, 14,7, 19, 23 }, 
342   { 0, 11,3, 19,7, 15,4, 16, 24 }};
343 static int TriQuadHexa_RE [6][9] = {  // REVERSED -> EXTERNAL
344   { 0, 11,3, 10,2, 9, 1, 8,  20 },   // opposite faces are neighbouring,
345   { 4, 12,5, 13,6, 14,7, 15, 25 },   // all face normals are external
346   { 0, 8, 1, 17,5, 12,4, 16, 21 },
347   { 1, 9, 2, 18,6, 13,5, 17, 22 },
348   { 3, 19,7, 14,6, 18,2, 10, 23 }, 
349   { 0, 16,4, 15,7, 19,3, 11, 24 }};
350 static int TriQuadHexa_nbN [] = { 9, 9, 9, 9, 9, 9 };
351
352
353 // ========================================================
354 // to perform some calculations without linkage to CASCADE
355 // ========================================================
356 struct XYZ {
357   double x;
358   double y;
359   double z;
360   XYZ()                               { x = 0; y = 0; z = 0; }
361   XYZ( double X, double Y, double Z ) { x = X; y = Y; z = Z; }
362   XYZ( const XYZ& other )             { x = other.x; y = other.y; z = other.z; }
363   XYZ( const SMDS_MeshNode* n )       { x = n->X(); y = n->Y(); z = n->Z(); }
364   inline XYZ operator-( const XYZ& other );
365   inline XYZ operator+( const XYZ& other );
366   inline XYZ Crossed( const XYZ& other );
367   inline double Dot( const XYZ& other );
368   inline double Magnitude();
369   inline double SquareMagnitude();
370 };
371 inline XYZ XYZ::operator-( const XYZ& Right ) {
372   return XYZ(x - Right.x, y - Right.y, z - Right.z);
373 }
374 inline XYZ XYZ::operator+( const XYZ& Right ) {
375   return XYZ(x + Right.x, y + Right.y, z + Right.z);
376 }
377 inline XYZ XYZ::Crossed( const XYZ& Right ) {
378   return XYZ (y * Right.z - z * Right.y,
379               z * Right.x - x * Right.z,
380               x * Right.y - y * Right.x);
381 }
382 inline double XYZ::Dot( const XYZ& Other ) {
383   return(x * Other.x + y * Other.y + z * Other.z);
384 }
385 inline double XYZ::Magnitude() {
386   return sqrt (x * x + y * y + z * z);
387 }
388 inline double XYZ::SquareMagnitude() {
389   return (x * x + y * y + z * z);
390 }
391
392   //================================================================================
393   /*!
394    * \brief Return linear type corresponding to a quadratic one
395    */
396   //================================================================================
397
398   SMDS_VolumeTool::VolumeType quadToLinear(SMDS_VolumeTool::VolumeType quadType)
399   {
400     SMDS_VolumeTool::VolumeType linType = SMDS_VolumeTool::VolumeType( int(quadType)-4 );
401     const int           nbCornersByQuad = SMDS_VolumeTool::NbCornerNodes( quadType );
402     if ( SMDS_VolumeTool::NbCornerNodes( linType ) == nbCornersByQuad )
403       return linType;
404
405     int iLin = 0;
406     for ( ; iLin < SMDS_VolumeTool::NB_VOLUME_TYPES; ++iLin )
407       if ( SMDS_VolumeTool::NbCornerNodes( SMDS_VolumeTool::VolumeType( iLin )) == nbCornersByQuad)
408         return SMDS_VolumeTool::VolumeType( iLin );
409
410     return SMDS_VolumeTool::UNKNOWN;
411   }
412
413 } // namespace
414
415 //================================================================================
416 /*!
417  * \brief Saver/restorer of a SMDS_VolumeTool::myCurFace
418  */
419 //================================================================================
420
421 struct SMDS_VolumeTool::SaveFacet
422 {
423   SMDS_VolumeTool::Facet  mySaved;
424   SMDS_VolumeTool::Facet& myToRestore;
425   SaveFacet( SMDS_VolumeTool::Facet& facet ): myToRestore( facet )
426   {
427     mySaved = facet;
428     mySaved.myNodes.swap( facet.myNodes );
429   }
430   ~SaveFacet()
431   {
432     if ( myToRestore.myIndex != mySaved.myIndex )
433       myToRestore = mySaved;
434     myToRestore.myNodes.swap( mySaved.myNodes );
435   }
436 };
437
438 //=======================================================================
439 //function : SMDS_VolumeTool
440 //purpose  :
441 //=======================================================================
442
443 SMDS_VolumeTool::SMDS_VolumeTool ()
444 {
445   Set( 0 );
446 }
447
448 //=======================================================================
449 //function : SMDS_VolumeTool
450 //purpose  : 
451 //=======================================================================
452
453 SMDS_VolumeTool::SMDS_VolumeTool (const SMDS_MeshElement* theVolume,
454                                   const bool              ignoreCentralNodes)
455 {
456   Set( theVolume, ignoreCentralNodes );
457 }
458
459 //=======================================================================
460 //function : SMDS_VolumeTool
461 //purpose  : 
462 //=======================================================================
463
464 SMDS_VolumeTool::~SMDS_VolumeTool()
465 {
466   myCurFace.myNodeIndices = NULL;
467 }
468
469 //=======================================================================
470 //function : SetVolume
471 //purpose  : Set volume to iterate on
472 //=======================================================================
473
474 bool SMDS_VolumeTool::Set (const SMDS_MeshElement*                  theVolume,
475                            const bool                               ignoreCentralNodes,
476                            const std::vector<const SMDS_MeshNode*>* otherNodes)
477 {
478   // reset fields
479   myVolume = 0;
480   myPolyedre = 0;
481   myIgnoreCentralNodes = ignoreCentralNodes;
482
483   myVolForward = true;
484   myNbFaces = 0;
485   myVolumeNodes.clear();
486   myPolyIndices.clear();
487   myPolyQuantities.clear();
488   myPolyFacetOri.clear();
489   myFwdLinks.clear();
490
491   myExternalFaces = false;
492
493   myAllFacesNodeIndices_F  = 0;
494   myAllFacesNodeIndices_RE = 0;
495   myAllFacesNbNodes        = 0;
496
497   myCurFace.myIndex = -1;
498   myCurFace.myNodeIndices = NULL;
499   myCurFace.myNodes.clear();
500
501   // set volume data
502   if ( !theVolume || theVolume->GetType() != SMDSAbs_Volume )
503     return false;
504
505   myVolume = theVolume;
506   myNbFaces = theVolume->NbFaces();
507   if ( myVolume->IsPoly() )
508   {
509     myPolyedre = dynamic_cast<const SMDS_VtkVolume*>( myVolume );
510     myPolyFacetOri.resize( myNbFaces, 0 );
511   }
512
513   // set nodes
514   myVolumeNodes.resize( myVolume->NbNodes() );
515   if ( otherNodes )
516   {
517     if ( otherNodes->size() != myVolumeNodes.size() )
518       return ( myVolume = 0 );
519     for ( size_t i = 0; i < otherNodes->size(); ++i )
520       if ( ! ( myVolumeNodes[i] = (*otherNodes)[0] ))
521         return ( myVolume = 0 );
522   }
523   else
524   {
525     int iNode = 0;
526     SMDS_ElemIteratorPtr nodeIt = myVolume->nodesIterator();
527     while ( nodeIt->more() )
528       myVolumeNodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
529   }
530
531   // check validity
532   if ( !setFace(0) )
533     return ( myVolume = 0 );
534
535   if ( !myPolyedre )
536   {
537     // define volume orientation
538     XYZ botNormal;
539     if ( GetFaceNormal( 0, botNormal.x, botNormal.y, botNormal.z ))
540     {
541       const SMDS_MeshNode* botNode = myVolumeNodes[ 0 ];
542       int topNodeIndex = myVolume->NbCornerNodes() - 1;
543       while ( !IsLinked( 0, topNodeIndex, /*ignoreMediumNodes=*/true )) --topNodeIndex;
544       const SMDS_MeshNode* topNode = myVolumeNodes[ topNodeIndex ];
545       XYZ upDir (topNode->X() - botNode->X(),
546                  topNode->Y() - botNode->Y(),
547                  topNode->Z() - botNode->Z() );
548       myVolForward = ( botNormal.Dot( upDir ) < 0 );
549     }
550     if ( !myVolForward )
551       myCurFace.myIndex = -1; // previous setFace(0) didn't take myVolForward into account
552   }
553   return true;
554 }
555
556 //=======================================================================
557 //function : Inverse
558 //purpose  : Inverse volume
559 //=======================================================================
560
561 #define SWAP_NODES(nodes,i1,i2)           \
562 {                                         \
563   const SMDS_MeshNode* tmp = nodes[ i1 ]; \
564   nodes[ i1 ] = nodes[ i2 ];              \
565   nodes[ i2 ] = tmp;                      \
566 }
567 void SMDS_VolumeTool::Inverse ()
568 {
569   if ( !myVolume ) return;
570
571   if (myVolume->IsPoly()) {
572     MESSAGE("Warning: attempt to inverse polyhedral volume");
573     return;
574   }
575
576   myVolForward = !myVolForward;
577   myCurFace.myIndex = -1;
578
579   // inverse top and bottom faces
580   switch ( myVolumeNodes.size() ) {
581   case 4:
582     SWAP_NODES( myVolumeNodes, 1, 2 );
583     break;
584   case 5:
585     SWAP_NODES( myVolumeNodes, 1, 3 );
586     break;
587   case 6:
588     SWAP_NODES( myVolumeNodes, 1, 2 );
589     SWAP_NODES( myVolumeNodes, 4, 5 );
590     break;
591   case 8:
592     SWAP_NODES( myVolumeNodes, 1, 3 );
593     SWAP_NODES( myVolumeNodes, 5, 7 );
594     break;
595   case 12:
596     SWAP_NODES( myVolumeNodes, 1, 5 );
597     SWAP_NODES( myVolumeNodes, 2, 4 );
598     SWAP_NODES( myVolumeNodes, 7, 11 );
599     SWAP_NODES( myVolumeNodes, 8, 10 );
600     break;
601
602   case 10:
603     SWAP_NODES( myVolumeNodes, 1, 2 );
604     SWAP_NODES( myVolumeNodes, 4, 6 );
605     SWAP_NODES( myVolumeNodes, 8, 9 );
606     break;
607   case 13:
608     SWAP_NODES( myVolumeNodes, 1, 3 );
609     SWAP_NODES( myVolumeNodes, 5, 8 );
610     SWAP_NODES( myVolumeNodes, 6, 7 );
611     SWAP_NODES( myVolumeNodes, 10, 12 );
612     break;
613   case 15:
614     SWAP_NODES( myVolumeNodes, 1, 2 );
615     SWAP_NODES( myVolumeNodes, 4, 5 );
616     SWAP_NODES( myVolumeNodes, 6, 8 );
617     SWAP_NODES( myVolumeNodes, 9, 11 );
618     SWAP_NODES( myVolumeNodes, 13, 14 );
619     break;
620   case 20:
621     SWAP_NODES( myVolumeNodes, 1, 3 );
622     SWAP_NODES( myVolumeNodes, 5, 7 );
623     SWAP_NODES( myVolumeNodes, 8, 11 );
624     SWAP_NODES( myVolumeNodes, 9, 10 );
625     SWAP_NODES( myVolumeNodes, 12, 15 );
626     SWAP_NODES( myVolumeNodes, 13, 14 );
627     SWAP_NODES( myVolumeNodes, 17, 19 );
628     break;
629   case 27:
630     SWAP_NODES( myVolumeNodes, 1, 3 );
631     SWAP_NODES( myVolumeNodes, 5, 7 );
632     SWAP_NODES( myVolumeNodes, 8, 11 );
633     SWAP_NODES( myVolumeNodes, 9, 10 );
634     SWAP_NODES( myVolumeNodes, 12, 15 );
635     SWAP_NODES( myVolumeNodes, 13, 14 );
636     SWAP_NODES( myVolumeNodes, 17, 19 );
637     SWAP_NODES( myVolumeNodes, 21, 24 );
638     SWAP_NODES( myVolumeNodes, 22, 23 );
639     break;
640   default:;
641   }
642 }
643
644 //=======================================================================
645 //function : GetVolumeType
646 //purpose  : 
647 //=======================================================================
648
649 SMDS_VolumeTool::VolumeType SMDS_VolumeTool::GetVolumeType() const
650 {
651   if ( myPolyedre )
652     return POLYHEDA;
653
654   switch( myVolumeNodes.size() ) {
655   case 4: return TETRA;
656   case 5: return PYRAM;
657   case 6: return PENTA;
658   case 8: return HEXA;
659   case 12: return HEX_PRISM;
660   case 10: return QUAD_TETRA;
661   case 13: return QUAD_PYRAM;
662   case 15: return QUAD_PENTA;
663   case 20: return QUAD_HEXA;
664   case 27: return QUAD_HEXA;
665   default: break;
666   }
667
668   return UNKNOWN;
669 }
670
671 //=======================================================================
672 //function : getTetraVolume
673 //purpose  : 
674 //=======================================================================
675
676 static double getTetraVolume(const SMDS_MeshNode* n1,
677                              const SMDS_MeshNode* n2,
678                              const SMDS_MeshNode* n3,
679                              const SMDS_MeshNode* n4)
680 {
681   double p1[3], p2[3], p3[3], p4[3];
682   n1->GetXYZ( p1 );
683   n2->GetXYZ( p2 );
684   n3->GetXYZ( p3 );
685   n4->GetXYZ( p4 );
686
687   double Q1 = -(p1[ 0 ]-p2[ 0 ])*(p3[ 1 ]*p4[ 2 ]-p4[ 1 ]*p3[ 2 ]);
688   double Q2 =  (p1[ 0 ]-p3[ 0 ])*(p2[ 1 ]*p4[ 2 ]-p4[ 1 ]*p2[ 2 ]);
689   double R1 = -(p1[ 0 ]-p4[ 0 ])*(p2[ 1 ]*p3[ 2 ]-p3[ 1 ]*p2[ 2 ]);
690   double R2 = -(p2[ 0 ]-p3[ 0 ])*(p1[ 1 ]*p4[ 2 ]-p4[ 1 ]*p1[ 2 ]);
691   double S1 =  (p2[ 0 ]-p4[ 0 ])*(p1[ 1 ]*p3[ 2 ]-p3[ 1 ]*p1[ 2 ]);
692   double S2 = -(p3[ 0 ]-p4[ 0 ])*(p1[ 1 ]*p2[ 2 ]-p2[ 1 ]*p1[ 2 ]);
693
694   return (Q1+Q2+R1+R2+S1+S2)/6.0;
695 }
696
697 //=======================================================================
698 //function : GetSize
699 //purpose  : Return element volume
700 //=======================================================================
701
702 double SMDS_VolumeTool::GetSize() const
703 {
704   double V = 0.;
705   if ( !myVolume )
706     return 0.;
707
708   if ( myVolume->IsPoly() )
709   {
710     if ( !myPolyedre )
711       return 0.;
712
713     // split a polyhedron into tetrahedrons
714
715     SaveFacet savedFacet( myCurFace );
716     SMDS_VolumeTool* me = const_cast< SMDS_VolumeTool* > ( this );
717     for ( int f = 0; f < NbFaces(); ++f )
718     {
719       me->setFace( f );
720       XYZ area (0,0,0), p1( myCurFace.myNodes[0] );
721       for ( int n = 0; n < myCurFace.myNbNodes; ++n )
722       {
723         XYZ p2( myCurFace.myNodes[ n+1 ]);
724         area = area + p1.Crossed( p2 );
725         p1 = p2;
726       }
727       V += p1.Dot( area );
728     }
729     V /= 6;
730   }
731   else 
732   {
733     const static int ind[] = {
734       0, 1, 3, 6, 11, 23, 31, 44, 58, 78 };
735     const static int vtab[][4] = { // decomposition into tetra in the order of enum VolumeType
736       // tetrahedron
737       { 0, 1, 2, 3 },
738       // pyramid
739       { 0, 1, 3, 4 },
740       { 1, 2, 3, 4 },
741       // pentahedron
742       { 0, 1, 2, 3 },
743       { 1, 5, 3, 4 },
744       { 1, 5, 2, 3 },
745       // hexahedron
746       { 1, 4, 3, 0 },
747       { 4, 1, 6, 5 },
748       { 1, 3, 6, 2 },
749       { 4, 6, 3, 7 },
750       { 1, 4, 6, 3 },
751       // hexagonal prism
752       { 0, 1, 2, 7 },
753       { 0, 7, 8, 6 },
754       { 2, 7, 8, 0 },
755
756       { 0, 3, 4, 9 },
757       { 0, 9, 10, 6 },
758       { 4, 9, 10, 0 },
759
760       { 0, 3, 4, 9 },
761       { 0, 9, 10, 6 },
762       { 4, 9, 10, 0 },
763
764       { 0, 4, 5, 10 },
765       { 0, 10, 11, 6 },
766       { 5, 10, 11, 0 },
767
768       // quadratic tetrahedron
769       { 0, 4, 6, 7 },
770       { 1, 5, 4, 8 },
771       { 2, 6, 5, 9 },
772       { 7, 8, 9, 3 },
773       { 4, 6, 7, 9 },
774       { 4, 5, 6, 9 },
775       { 4, 7, 8, 9 },
776       { 4, 5, 9, 8 },
777
778       // quadratic pyramid
779       { 0, 5, 8, 9 },
780       { 1, 5,10, 6 },
781       { 2, 6,11, 7 },
782       { 3, 7,12, 8 },
783       { 4, 9,11,10 },
784       { 4, 9,12,11 },
785       { 10, 5, 9, 8 },
786       { 10, 8, 9,12 },
787       { 10, 8,12, 7 },
788       { 10, 7,12,11 },
789       { 10, 7,11, 6 },
790       { 10, 5, 8, 6 },
791       { 10, 6, 8, 7 },
792
793       // quadratic pentahedron
794       { 12, 0, 8, 6 },
795       { 12, 8, 7, 6 },
796       { 12, 8, 2, 7 },
797       { 12, 6, 7, 1 },
798       { 12, 1, 7,13 },
799       { 12, 7, 2,13 },
800       { 12, 2,14,13 },
801
802       { 12, 3, 9,11 },
803       { 12,11, 9,10 },
804       { 12,11,10, 5 },
805       { 12, 9, 4,10 },
806       { 12,14, 5,10 },
807       { 12,14,10, 4 },
808       { 12,14, 4,13 },
809
810       // quadratic hexahedron
811       { 16, 0,11, 8 },
812       { 16,11, 9, 8 },
813       { 16, 8, 9, 1 },
814       { 16,11, 3,10 },
815       { 16,11,10, 9 },
816       { 16,10, 2, 9 },
817       { 16, 3,19, 2 },
818       { 16, 2,19,18 },
819       { 16, 2,18,17 },
820       { 16, 2,17, 1 },
821
822       { 16, 4,12,15 },
823       { 16,12, 5,13 },
824       { 16,12,13,15 },
825       { 16,13, 6,14 },
826       { 16,13,14,15 },
827       { 16,14, 7,15 },
828       { 16, 6, 5,17 },
829       { 16,18, 6,17 },
830       { 16,18, 7, 6 },
831       { 16,18,19, 7 },
832
833     };
834
835     int type = GetVolumeType();
836     int n1 = ind[type];
837     int n2 = ind[type+1];
838
839     for (int i = n1; i <  n2; i++) {
840       V -= getTetraVolume( myVolumeNodes[ vtab[i][0] ],
841                            myVolumeNodes[ vtab[i][1] ],
842                            myVolumeNodes[ vtab[i][2] ],
843                            myVolumeNodes[ vtab[i][3] ]);
844     }
845   }
846   return V;
847 }
848
849 //=======================================================================
850 //function : GetBaryCenter
851 //purpose  : 
852 //=======================================================================
853
854 bool SMDS_VolumeTool::GetBaryCenter(double & X, double & Y, double & Z) const
855 {
856   X = Y = Z = 0.;
857   if ( !myVolume )
858     return false;
859
860   for ( size_t i = 0; i < myVolumeNodes.size(); i++ ) {
861     X += myVolumeNodes[ i ]->X();
862     Y += myVolumeNodes[ i ]->Y();
863     Z += myVolumeNodes[ i ]->Z();
864   }
865   X /= myVolumeNodes.size();
866   Y /= myVolumeNodes.size();
867   Z /= myVolumeNodes.size();
868
869   return true;
870 }
871
872 //================================================================================
873 /*!
874  * \brief Classify a point
875  *  \param tol - thickness of faces
876  */
877 //================================================================================
878
879 bool SMDS_VolumeTool::IsOut(double X, double Y, double Z, double tol) const
880 {
881   // LIMITATION: for convex volumes only
882   XYZ p( X,Y,Z );
883   for ( int iF = 0; iF < myNbFaces; ++iF )
884   {
885     XYZ faceNormal;
886     if ( !GetFaceNormal( iF, faceNormal.x, faceNormal.y, faceNormal.z ))
887       continue;
888     if ( !IsFaceExternal( iF ))
889       faceNormal = XYZ() - faceNormal; // reverse
890
891     XYZ face2p( p - XYZ( myCurFace.myNodes[0] ));
892     if ( face2p.Dot( faceNormal ) > tol )
893       return true;
894   }
895   return false;
896 }
897
898 //=======================================================================
899 //function : SetExternalNormal
900 //purpose  : Node order will be so that faces normals are external
901 //=======================================================================
902
903 void SMDS_VolumeTool::SetExternalNormal ()
904 {
905   myExternalFaces = true;
906   myCurFace.myIndex = -1;
907 }
908
909 //=======================================================================
910 //function : NbFaceNodes
911 //purpose  : Return number of nodes in the array of face nodes
912 //=======================================================================
913
914 int SMDS_VolumeTool::NbFaceNodes( int faceIndex ) const
915 {
916   if ( !setFace( faceIndex ))
917     return 0;
918   return myCurFace.myNbNodes;
919 }
920
921 //=======================================================================
922 //function : GetFaceNodes
923 //purpose  : Return pointer to the array of face nodes.
924 //           To comfort link iteration, the array
925 //           length == NbFaceNodes( faceIndex ) + 1 and
926 //           the last node == the first one.
927 //=======================================================================
928
929 const SMDS_MeshNode** SMDS_VolumeTool::GetFaceNodes( int faceIndex ) const
930 {
931   if ( !setFace( faceIndex ))
932     return 0;
933   return &myCurFace.myNodes[0];
934 }
935
936 //=======================================================================
937 //function : GetFaceNodesIndices
938 //purpose  : Return pointer to the array of face nodes indices
939 //           To comfort link iteration, the array
940 //           length == NbFaceNodes( faceIndex ) + 1 and
941 //           the last node index == the first one.
942 //=======================================================================
943
944 const int* SMDS_VolumeTool::GetFaceNodesIndices( int faceIndex ) const
945 {
946   if ( !setFace( faceIndex ))
947     return 0;
948
949   return myCurFace.myNodeIndices;
950 }
951
952 //=======================================================================
953 //function : GetFaceNodes
954 //purpose  : Return a set of face nodes.
955 //=======================================================================
956
957 bool SMDS_VolumeTool::GetFaceNodes (int                        faceIndex,
958                                     set<const SMDS_MeshNode*>& theFaceNodes ) const
959 {
960   if ( !setFace( faceIndex ))
961     return false;
962
963   theFaceNodes.clear();
964   theFaceNodes.insert( myCurFace.myNodes.begin(), myCurFace.myNodes.end() );
965
966   return true;
967 }
968
969 namespace
970 {
971   struct NLink : public std::pair<int,int>
972   {
973     int myOri;
974     NLink(const SMDS_MeshNode* n1=0, const SMDS_MeshNode* n2=0, int ori=1 )
975     {
976       if ( n1 )
977       {
978         if (( myOri = ( n1->GetID() < n2->GetID() )))
979         {
980           first  = n1->GetID();
981           second = n2->GetID();
982         }
983         else
984         {
985           myOri  = -1;
986           first  = n2->GetID();
987           second = n1->GetID();
988         }
989         myOri *= ori;
990       }
991       else
992       {
993         myOri = first = second = 0;
994       }
995     }
996     //int Node1() const { return myOri == -1 ? second : first; }
997
998     //bool IsSameOri( const std::pair<int,int>& link ) const { return link.first == Node1(); }
999   };
1000 }
1001
1002 //=======================================================================
1003 //function : IsFaceExternal
1004 //purpose  : Check normal orientation of a given face
1005 //=======================================================================
1006
1007 bool SMDS_VolumeTool::IsFaceExternal( int faceIndex ) const
1008 {
1009   if ( myExternalFaces || !myVolume )
1010     return true;
1011
1012   if ( !myPolyedre ) // all classical volumes have external facet normals
1013     return true;
1014
1015   SMDS_VolumeTool* me = const_cast< SMDS_VolumeTool* >( this );
1016
1017   if ( myPolyFacetOri[ faceIndex ])
1018     return myPolyFacetOri[ faceIndex ] > 0;
1019
1020   int ori = 0; // -1-in, +1-out, 0-undef
1021   double minProj, maxProj;
1022   if ( projectNodesToNormal( faceIndex, minProj, maxProj ))
1023   {
1024     // all nodes are on the same side of the facet
1025     ori = ( minProj < 0 ? +1 : -1 );
1026     me->myPolyFacetOri[ faceIndex ] = ori;
1027
1028     if ( !myFwdLinks.empty() ) // concave polyhedron; collect oriented links
1029       for ( int i = 0; i < myCurFace.myNbNodes; ++i )
1030       {
1031         NLink link( myCurFace.myNodes[i], myCurFace.myNodes[i+1], ori );
1032         me->myFwdLinks.insert( make_pair( link, link.myOri ));
1033       }
1034     return ori > 0;
1035   }
1036
1037   SaveFacet savedFacet( myCurFace );
1038
1039   // concave polyhedron
1040
1041   if ( myFwdLinks.empty() ) // get links of the least ambiguously oriented facet
1042   {
1043     for ( size_t i = 0; i < myPolyFacetOri.size() && !ori; ++i )
1044       ori = myPolyFacetOri[ i ];
1045
1046     if ( !ori ) // none facet is oriented yet
1047     {
1048       // find the least ambiguously oriented facet
1049       int faceMostConvex = -1;
1050       std::map< double, int > convexity2face;
1051       for ( size_t iF = 0; iF < myPolyFacetOri.size() && faceMostConvex < 0; ++iF )
1052       {
1053         if ( projectNodesToNormal( iF, minProj, maxProj ))
1054         {
1055           // all nodes are on the same side of the facet
1056           me->myPolyFacetOri[ iF ] = ( minProj < 0 ? +1 : -1 );
1057           faceMostConvex = iF;
1058         }
1059         else
1060         {
1061           ori = ( -minProj < maxProj ? -1 : +1 );
1062           double convexity = std::min( -minProj, maxProj ) / std::max( -minProj, maxProj );
1063           convexity2face.insert( make_pair( convexity, iF * ori ));
1064         }
1065       }
1066       if ( faceMostConvex < 0 ) // none facet has nodes on the same side
1067       {
1068         // use the least ambiguous facet
1069         faceMostConvex = convexity2face.begin()->second;
1070         ori = ( faceMostConvex < 0 ? -1 : +1 );
1071         faceMostConvex = std::abs( faceMostConvex );
1072         me->myPolyFacetOri[ faceMostConvex ] = ori;
1073       }
1074     }
1075     // collect links of the oriented facets in myFwdLinks
1076     for ( size_t iF = 0; iF < myPolyFacetOri.size(); ++iF )
1077     {
1078       ori = myPolyFacetOri[ iF ];
1079       if ( !ori ) continue;
1080       setFace( iF );
1081       for ( int i = 0; i < myCurFace.myNbNodes; ++i )
1082       {
1083         NLink link( myCurFace.myNodes[i], myCurFace.myNodes[i+1], ori );
1084         me->myFwdLinks.insert( make_pair( link, link.myOri ));
1085       }
1086     }
1087   }
1088
1089   // compare orientation of links of the facet with myFwdLinks
1090   ori = 0;
1091   setFace( faceIndex );
1092   vector< NLink > links( myCurFace.myNbNodes ), links2;
1093   for ( int i = 0; i < myCurFace.myNbNodes && !ori; ++i )
1094   {
1095     NLink link( myCurFace.myNodes[i], myCurFace.myNodes[i+1] );
1096     std::map<Link, int>::const_iterator l2o = myFwdLinks.find( link );
1097     if ( l2o != myFwdLinks.end() )
1098       ori = link.myOri * l2o->second * -1;
1099     links[ i ] = link;
1100   }
1101   while ( !ori ) // the facet has no common links with already oriented facets
1102   {
1103     // orient and collect links of other non-oriented facets
1104     for ( size_t iF = 0; iF < myPolyFacetOri.size(); ++iF )
1105     {
1106       if ( myPolyFacetOri[ iF ] ) continue; // already oriented
1107       setFace( iF );
1108       links2.clear();
1109       ori = 0;
1110       for ( int i = 0; i < myCurFace.myNbNodes && !ori; ++i )
1111       {
1112         NLink link( myCurFace.myNodes[i], myCurFace.myNodes[i+1] );
1113         std::map<Link, int>::const_iterator l2o = myFwdLinks.find( link );
1114         if ( l2o != myFwdLinks.end() )
1115           ori = link.myOri * l2o->second * -1;
1116         links2.push_back( link );
1117       }
1118       if ( ori ) // one more facet oriented
1119       {
1120         me->myPolyFacetOri[ iF ] = ori;
1121         for ( size_t i = 0; i < links2.size(); ++i )
1122           me->myFwdLinks.insert( make_pair( links2[i], links2[i].myOri * ori ));
1123         break;
1124       }
1125     }
1126     if ( !ori )
1127       return false; // error in algorithm: infinite loop
1128
1129     // try to orient the facet again
1130     ori = 0;
1131     for ( size_t i = 0; i < links.size() && !ori; ++i )
1132     {
1133       std::map<Link, int>::const_iterator l2o = myFwdLinks.find( links[i] );
1134       if ( l2o != myFwdLinks.end() )
1135         ori = links[i].myOri * l2o->second * -1;
1136     }
1137     me->myPolyFacetOri[ faceIndex ] = ori;
1138   }
1139
1140   return ori > 0;
1141 }
1142
1143 //=======================================================================
1144 //function : projectNodesToNormal
1145 //purpose  : compute min and max projections of all nodes to normal of a facet.
1146 //=======================================================================
1147
1148 bool SMDS_VolumeTool::projectNodesToNormal( int     faceIndex,
1149                                             double& minProj,
1150                                             double& maxProj ) const
1151 {
1152   minProj = std::numeric_limits<double>::max();
1153   maxProj = std::numeric_limits<double>::min();
1154
1155   XYZ normal;
1156   if ( !GetFaceNormal( faceIndex, normal.x, normal.y, normal.z ))
1157     return false;
1158   XYZ p0 ( myCurFace.myNodes[0] );
1159   for ( size_t i = 0; i < myVolumeNodes.size(); ++i )
1160   {
1161     if ( std::find( myCurFace.myNodes.begin() + 1,
1162                     myCurFace.myNodes.end(),
1163                     myVolumeNodes[ i ] ) != myCurFace.myNodes.end() )
1164       continue; // node of the faceIndex-th facet
1165
1166     double proj = normal.Dot( XYZ( myVolumeNodes[ i ]) - p0 );
1167     if ( proj < minProj ) minProj = proj;
1168     if ( proj > maxProj ) maxProj = proj;
1169   }
1170   const double tol = 1e-7;
1171   minProj += tol;
1172   maxProj -= tol;
1173   bool diffSize = ( minProj * maxProj < 0 );
1174   // if ( diffSize )
1175   // {
1176   //   minProj = -minProj;
1177   // }
1178   // else if ( minProj < 0 )
1179   // {
1180   //   minProj = -minProj;
1181   //   maxProj = -maxProj;
1182   // }
1183
1184   return !diffSize; // ? 0 : (minProj >= 0);
1185 }
1186
1187 //=======================================================================
1188 //function : GetFaceNormal
1189 //purpose  : Return a normal to a face
1190 //=======================================================================
1191
1192 bool SMDS_VolumeTool::GetFaceNormal (int faceIndex, double & X, double & Y, double & Z) const
1193 {
1194   if ( !setFace( faceIndex ))
1195     return false;
1196
1197   const int iQuad = ( !myPolyedre && myCurFace.myNbNodes > 6 ) ? 2 : 1;
1198   XYZ p1 ( myCurFace.myNodes[0*iQuad] );
1199   XYZ p2 ( myCurFace.myNodes[1*iQuad] );
1200   XYZ p3 ( myCurFace.myNodes[2*iQuad] );
1201   XYZ aVec12( p2 - p1 );
1202   XYZ aVec13( p3 - p1 );
1203   XYZ cross = aVec12.Crossed( aVec13 );
1204
1205   if ( myCurFace.myNbNodes >3*iQuad ) {
1206     XYZ p4 ( myCurFace.myNodes[3*iQuad] );
1207     XYZ aVec14( p4 - p1 );
1208     XYZ cross2 = aVec13.Crossed( aVec14 );
1209     cross = cross + cross2;
1210   }
1211
1212   double size = cross.Magnitude();
1213   if ( size <= numeric_limits<double>::min() )
1214     return false;
1215
1216   X = cross.x / size;
1217   Y = cross.y / size;
1218   Z = cross.z / size;
1219
1220   return true;
1221 }
1222
1223 //================================================================================
1224 /*!
1225  * \brief Return barycenter of a face
1226  */
1227 //================================================================================
1228
1229 bool SMDS_VolumeTool::GetFaceBaryCenter (int faceIndex, double & X, double & Y, double & Z) const
1230 {
1231   if ( !setFace( faceIndex ))
1232     return false;
1233
1234   X = Y = Z = 0.0;
1235   for ( int i = 0; i < myCurFace.myNbNodes; ++i )
1236   {
1237     X += myCurFace.myNodes[i]->X() / myCurFace.myNbNodes;
1238     Y += myCurFace.myNodes[i]->Y() / myCurFace.myNbNodes;
1239     Z += myCurFace.myNodes[i]->Z() / myCurFace.myNbNodes;
1240   }
1241   return true;
1242 }
1243
1244 //=======================================================================
1245 //function : GetFaceArea
1246 //purpose  : Return face area
1247 //=======================================================================
1248
1249 double SMDS_VolumeTool::GetFaceArea( int faceIndex ) const
1250 {
1251   double area = 0;
1252   if ( !setFace( faceIndex ))
1253     return area;
1254
1255   XYZ p1 ( myCurFace.myNodes[0] );
1256   XYZ p2 ( myCurFace.myNodes[1] );
1257   XYZ p3 ( myCurFace.myNodes[2] );
1258   XYZ aVec12( p2 - p1 );
1259   XYZ aVec13( p3 - p1 );
1260   area += aVec12.Crossed( aVec13 ).Magnitude();
1261
1262   if (myVolume->IsPoly())
1263   {
1264     for ( int i = 3; i < myCurFace.myNbNodes; ++i )
1265     {
1266       XYZ pI ( myCurFace.myNodes[i] );
1267       XYZ aVecI( pI - p1 );
1268       area += aVec13.Crossed( aVecI ).Magnitude();
1269       aVec13 = aVecI;
1270     }
1271   }
1272   else
1273   {
1274     if ( myCurFace.myNbNodes == 4 ) {
1275       XYZ p4 ( myCurFace.myNodes[3] );
1276       XYZ aVec14( p4 - p1 );
1277       area += aVec14.Crossed( aVec13 ).Magnitude();
1278     }
1279   }
1280   return area / 2;
1281 }
1282
1283 //================================================================================
1284 /*!
1285  * \brief Return index of the node located at face center of a quadratic element like HEX27
1286  */
1287 //================================================================================
1288
1289 int SMDS_VolumeTool::GetCenterNodeIndex( int faceIndex ) const
1290 {
1291   if ( myAllFacesNbNodes && myVolumeNodes.size() == 27 ) // classic element with 27 nodes
1292   {
1293     switch ( faceIndex ) {
1294     case 0: return 20;
1295     case 1: return 25;
1296     default:
1297       return faceIndex + 19;
1298     }
1299   }
1300   return -1;
1301 }
1302
1303 //=======================================================================
1304 //function : GetOppFaceIndex
1305 //purpose  : Return index of the opposite face if it exists, else -1.
1306 //=======================================================================
1307
1308 int SMDS_VolumeTool::GetOppFaceIndex( int faceIndex ) const
1309 {
1310   int ind = -1;
1311   if (myPolyedre) {
1312     MESSAGE("Warning: attempt to obtain opposite face on polyhedral volume");
1313     return ind;
1314   }
1315
1316   const int nbHoriFaces = 2;
1317
1318   if ( faceIndex >= 0 && faceIndex < NbFaces() ) {
1319     switch ( myVolumeNodes.size() ) {
1320     case 6:
1321     case 15:
1322       if ( faceIndex == 0 || faceIndex == 1 )
1323         ind = 1 - faceIndex;
1324         break;
1325     case 8:
1326     case 12:
1327       if ( faceIndex <= 1 ) // top or bottom
1328         ind = 1 - faceIndex;
1329       else {
1330         const int nbSideFaces = myAllFacesNbNodes[0];
1331         ind = ( faceIndex - nbHoriFaces + nbSideFaces/2 ) % nbSideFaces + nbHoriFaces;
1332       }
1333       break;
1334     case 20:
1335     case 27:
1336       ind = GetOppFaceIndexOfHex( faceIndex );
1337       break;
1338     default:;
1339     }
1340   }
1341   return ind;
1342 }
1343
1344 //=======================================================================
1345 //function : GetOppFaceIndexOfHex
1346 //purpose  : Return index of the opposite face of the hexahedron
1347 //=======================================================================
1348
1349 int SMDS_VolumeTool::GetOppFaceIndexOfHex( int faceIndex )
1350 {
1351   return Hexa_oppF[ faceIndex ];
1352 }
1353
1354 //=======================================================================
1355 //function : IsLinked
1356 //purpose  : return true if theNode1 is linked with theNode2
1357 // If theIgnoreMediumNodes then corner nodes of quadratic cell are considered linked as well
1358 //=======================================================================
1359
1360 bool SMDS_VolumeTool::IsLinked (const SMDS_MeshNode* theNode1,
1361                                 const SMDS_MeshNode* theNode2,
1362                                 const bool           theIgnoreMediumNodes) const
1363 {
1364   if ( !myVolume )
1365     return false;
1366
1367   if (myVolume->IsPoly()) {
1368     if (!myPolyedre) {
1369       MESSAGE("Warning: bad volumic element");
1370       return false;
1371     }
1372     if ( !myAllFacesNbNodes ) {
1373       SMDS_VolumeTool*  me = const_cast< SMDS_VolumeTool* >( this );
1374       me->myPolyQuantities = myPolyedre->GetQuantities();
1375       myAllFacesNbNodes    = &myPolyQuantities[0];
1376     }
1377     int from, to = 0, d1 = 1, d2 = 2;
1378     if ( myPolyedre->IsQuadratic() ) {
1379       if ( theIgnoreMediumNodes ) {
1380         d1 = 2; d2 = 0;
1381       }
1382     } else {
1383       d2 = 0;
1384     }
1385     vector<const SMDS_MeshNode*>::const_iterator i;
1386     for (int iface = 0; iface < myNbFaces; iface++)
1387     {
1388       from = to;
1389       to  += myPolyQuantities[iface];
1390       i    = std::find( myVolumeNodes.begin() + from, myVolumeNodes.begin() + to, theNode1 );
1391       if ( i != myVolumeNodes.end() )
1392       {
1393         if ((  theNode2 == *( i-d1 ) ||
1394                theNode2 == *( i+d1 )))
1395           return true;
1396         if (( d2 ) &&
1397             (( theNode2 == *( i-d2 ) ||
1398                theNode2 == *( i+d2 ))))
1399           return true;
1400       }
1401     }
1402     return false;
1403   }
1404
1405   // find nodes indices
1406   int i1 = -1, i2 = -1, nbFound = 0;
1407   for ( size_t i = 0; i < myVolumeNodes.size() && nbFound < 2; i++ )
1408   {
1409     if ( myVolumeNodes[ i ] == theNode1 )
1410       i1 = i, ++nbFound;
1411     else if ( myVolumeNodes[ i ] == theNode2 )
1412       i2 = i, ++nbFound;
1413   }
1414   return IsLinked( i1, i2 );
1415 }
1416
1417 //=======================================================================
1418 //function : IsLinked
1419 //purpose  : return true if the node with theNode1Index is linked
1420 //           with the node with theNode2Index
1421 // If theIgnoreMediumNodes then corner nodes of quadratic cell are considered linked as well
1422 //=======================================================================
1423
1424 bool SMDS_VolumeTool::IsLinked (const int theNode1Index,
1425                                 const int theNode2Index,
1426                                 bool      theIgnoreMediumNodes) const
1427 {
1428   if ( myVolume->IsPoly() ) {
1429     return IsLinked(myVolumeNodes[theNode1Index], myVolumeNodes[theNode2Index]);
1430   }
1431
1432   int minInd = min( theNode1Index, theNode2Index );
1433   int maxInd = max( theNode1Index, theNode2Index );
1434
1435   if ( minInd < 0 || maxInd > (int)myVolumeNodes.size() - 1 || maxInd == minInd )
1436     return false;
1437
1438   VolumeType type = GetVolumeType();
1439   if ( myVolume->IsQuadratic() )
1440   {
1441     int firstMediumInd = myVolume->NbCornerNodes();
1442     if ( minInd >= firstMediumInd )
1443       return false; // both nodes are medium - not linked
1444     if ( maxInd < firstMediumInd ) // both nodes are corners
1445     {
1446       if ( theIgnoreMediumNodes )
1447         type = quadToLinear(type); // to check linkage of corner nodes only
1448       else
1449         return false; // corner nodes are not linked directly in a quadratic cell
1450     }
1451   }
1452
1453   switch ( type ) {
1454   case TETRA:
1455     return true;
1456   case HEXA:
1457     switch ( maxInd - minInd ) {
1458     case 1: return minInd != 3;
1459     case 3: return minInd == 0 || minInd == 4;
1460     case 4: return true;
1461     default:;
1462     }
1463     break;
1464   case PYRAM:
1465     if ( maxInd == 4 )
1466       return true;
1467     switch ( maxInd - minInd ) {
1468     case 1:
1469     case 3: return true;
1470     default:;
1471     }
1472     break;
1473   case PENTA:
1474     switch ( maxInd - minInd ) {
1475     case 1: return minInd != 2;
1476     case 2: return minInd == 0 || minInd == 3;
1477     case 3: return true;
1478     default:;
1479     }
1480     break;
1481   case QUAD_TETRA:
1482     {
1483       switch ( minInd ) {
1484       case 0: if( maxInd==4 ||  maxInd==6 ||  maxInd==7 ) return true;
1485       case 1: if( maxInd==4 ||  maxInd==5 ||  maxInd==8 ) return true;
1486       case 2: if( maxInd==5 ||  maxInd==6 ||  maxInd==9 ) return true;
1487       case 3: if( maxInd==7 ||  maxInd==8 ||  maxInd==9 ) return true;
1488       default:;
1489       }
1490       break;
1491     }
1492   case QUAD_HEXA:
1493     {
1494       switch ( minInd ) {
1495       case 0: if( maxInd==8 ||  maxInd==11 ||  maxInd==16 ) return true;
1496       case 1: if( maxInd==8 ||  maxInd==9 ||  maxInd==17 ) return true;
1497       case 2: if( maxInd==9 ||  maxInd==10 ||  maxInd==18 ) return true;
1498       case 3: if( maxInd==10 ||  maxInd==11 ||  maxInd==19 ) return true;
1499       case 4: if( maxInd==12 ||  maxInd==15 ||  maxInd==16 ) return true;
1500       case 5: if( maxInd==12 ||  maxInd==13 ||  maxInd==17 ) return true;
1501       case 6: if( maxInd==13 ||  maxInd==14 ||  maxInd==18 ) return true;
1502       case 7: if( maxInd==14 ||  maxInd==15 ||  maxInd==19 ) return true;
1503       default:;
1504       }
1505       break;
1506     }
1507   case QUAD_PYRAM:
1508     {
1509       switch ( minInd ) {
1510       case 0: if( maxInd==5 ||  maxInd==8 ||  maxInd==9 ) return true;
1511       case 1: if( maxInd==5 ||  maxInd==6 ||  maxInd==10 ) return true;
1512       case 2: if( maxInd==6 ||  maxInd==7 ||  maxInd==11 ) return true;
1513       case 3: if( maxInd==7 ||  maxInd==8 ||  maxInd==12 ) return true;
1514       case 4: if( maxInd==9 ||  maxInd==10 ||  maxInd==11 ||  maxInd==12 ) return true;
1515       default:;
1516       }
1517       break;
1518     }
1519   case QUAD_PENTA:
1520     {
1521       switch ( minInd ) {
1522       case 0: if( maxInd==6 ||  maxInd==8 ||  maxInd==12 ) return true;
1523       case 1: if( maxInd==6 ||  maxInd==7 ||  maxInd==13 ) return true;
1524       case 2: if( maxInd==7 ||  maxInd==8 ||  maxInd==14 ) return true;
1525       case 3: if( maxInd==9 ||  maxInd==11 ||  maxInd==12 ) return true;
1526       case 4: if( maxInd==9 ||  maxInd==10 ||  maxInd==13 ) return true;
1527       case 5: if( maxInd==10 ||  maxInd==11 ||  maxInd==14 ) return true;
1528       default:;
1529       }
1530       break;
1531     }
1532   case HEX_PRISM:
1533     {
1534       const int diff = maxInd-minInd;
1535       if ( diff > 6  ) return false;// not linked top and bottom
1536       if ( diff == 6 ) return true; // linked top and bottom
1537       return diff == 1 || diff == 7;
1538     }
1539   default:;
1540   }
1541   return false;
1542 }
1543
1544 //=======================================================================
1545 //function : GetNodeIndex
1546 //purpose  : Return an index of theNode
1547 //=======================================================================
1548
1549 int SMDS_VolumeTool::GetNodeIndex(const SMDS_MeshNode* theNode) const
1550 {
1551   if ( myVolume ) {
1552     for ( size_t i = 0; i < myVolumeNodes.size(); i++ ) {
1553       if ( myVolumeNodes[ i ] == theNode )
1554         return i;
1555     }
1556   }
1557   return -1;
1558 }
1559
1560 //================================================================================
1561 /*!
1562  * \brief Fill vector with boundary faces existing in the mesh
1563   * \param faces - vector of found nodes
1564   * \retval int - nb of found faces
1565  */
1566 //================================================================================
1567
1568 int SMDS_VolumeTool::GetAllExistingFaces(vector<const SMDS_MeshElement*> & faces) const
1569 {
1570   faces.clear();
1571   SaveFacet savedFacet( myCurFace );
1572   if ( IsPoly() )
1573     for ( int iF = 0; iF < NbFaces(); ++iF ) {
1574       if ( setFace( iF ))
1575         if ( const SMDS_MeshElement* face = SMDS_Mesh::FindFace( myCurFace.myNodes ))
1576           faces.push_back( face );
1577     }
1578   else
1579     for ( int iF = 0; iF < NbFaces(); ++iF ) {
1580       const SMDS_MeshFace* face = 0;
1581       const SMDS_MeshNode** nodes = GetFaceNodes( iF );
1582       switch ( NbFaceNodes( iF )) {
1583       case 3:
1584         face = SMDS_Mesh::FindFace( nodes[0], nodes[1], nodes[2] ); break;
1585       case 4:
1586         face = SMDS_Mesh::FindFace( nodes[0], nodes[1], nodes[2], nodes[3] ); break;
1587       case 6:
1588         face = SMDS_Mesh::FindFace( nodes[0], nodes[1], nodes[2],
1589                                     nodes[3], nodes[4], nodes[5]); break;
1590       case 8:
1591         face = SMDS_Mesh::FindFace( nodes[0], nodes[1], nodes[2], nodes[3],
1592                                     nodes[4], nodes[5], nodes[6], nodes[7]); break;
1593       }
1594       if ( face )
1595         faces.push_back( face );
1596     }
1597   return faces.size();
1598 }
1599
1600
1601 //================================================================================
1602 /*!
1603  * \brief Fill vector with boundary edges existing in the mesh
1604  * \param edges - vector of found edges
1605  * \retval int - nb of found faces
1606  */
1607 //================================================================================
1608
1609 int SMDS_VolumeTool::GetAllExistingEdges(vector<const SMDS_MeshElement*> & edges) const
1610 {
1611   edges.clear();
1612   edges.reserve( myVolumeNodes.size() * 2 );
1613   for ( size_t i = 0; i < myVolumeNodes.size()-1; ++i ) {
1614     for ( size_t j = i + 1; j < myVolumeNodes.size(); ++j ) {
1615       if ( IsLinked( i, j )) {
1616         const SMDS_MeshElement* edge =
1617           SMDS_Mesh::FindEdge( myVolumeNodes[i], myVolumeNodes[j] );
1618         if ( edge )
1619           edges.push_back( edge );
1620       }
1621     }
1622   }
1623   return edges.size();
1624 }
1625
1626 //================================================================================
1627 /*!
1628  * \brief Return minimal square distance between connected corner nodes
1629  */
1630 //================================================================================
1631
1632 double SMDS_VolumeTool::MinLinearSize2() const
1633 {
1634   double minSize = 1e+100;
1635   int iQ = myVolume->IsQuadratic() ? 2 : 1;
1636
1637   SaveFacet savedFacet( myCurFace );
1638
1639   // it seems that compute distance twice is faster than organization of a sole computing
1640   myCurFace.myIndex = -1;
1641   for ( int iF = 0; iF < myNbFaces; ++iF )
1642   {
1643     setFace( iF );
1644     for ( int iN = 0; iN < myCurFace.myNbNodes; iN += iQ )
1645     {
1646       XYZ n1( myCurFace.myNodes[ iN ]);
1647       XYZ n2( myCurFace.myNodes[(iN + iQ) % myCurFace.myNbNodes]);
1648       minSize = std::min( minSize, (n1 - n2).SquareMagnitude());
1649     }
1650   }
1651
1652   return minSize;
1653 }
1654
1655 //================================================================================
1656 /*!
1657  * \brief Return maximal square distance between connected corner nodes
1658  */
1659 //================================================================================
1660
1661 double SMDS_VolumeTool::MaxLinearSize2() const
1662 {
1663   double maxSize = -1e+100;
1664   int iQ = myVolume->IsQuadratic() ? 2 : 1;
1665
1666   SaveFacet savedFacet( myCurFace );
1667   
1668   // it seems that compute distance twice is faster than organization of a sole computing
1669   myCurFace.myIndex = -1;
1670   for ( int iF = 0; iF < myNbFaces; ++iF )
1671   {
1672     setFace( iF );
1673     for ( int iN = 0; iN < myCurFace.myNbNodes; iN += iQ )
1674     {
1675       XYZ n1( myCurFace.myNodes[ iN ]);
1676       XYZ n2( myCurFace.myNodes[(iN + iQ) % myCurFace.myNbNodes]);
1677       maxSize = std::max( maxSize, (n1 - n2).SquareMagnitude());
1678     }
1679   }
1680
1681   return maxSize;
1682 }
1683
1684 //================================================================================
1685 /*!
1686  * \brief Fast quickly check that only one volume is built on the face nodes
1687  *        This check is valid for conformal meshes only
1688  */
1689 //================================================================================
1690
1691 bool SMDS_VolumeTool::IsFreeFace( int faceIndex, const SMDS_MeshElement** otherVol/*=0*/ ) const
1692 {
1693   const bool isFree = true;
1694
1695   if ( !setFace( faceIndex ))
1696     return !isFree;
1697
1698   const SMDS_MeshNode** nodes = GetFaceNodes( faceIndex );
1699
1700   const int  di = myVolume->IsQuadratic() ? 2 : 1;
1701   const int nbN = ( myCurFace.myNbNodes/di <= 4 && !IsPoly()) ? 3 : myCurFace.myNbNodes/di; // nb nodes to check
1702
1703   SMDS_ElemIteratorPtr eIt = nodes[0]->GetInverseElementIterator( SMDSAbs_Volume );
1704   while ( eIt->more() )
1705   {
1706     const SMDS_MeshElement* vol = eIt->next();
1707     if ( vol == myVolume )
1708       continue;
1709     int iN;
1710     for ( iN = 1; iN < nbN; ++iN )
1711       if ( vol->GetNodeIndex( nodes[ iN*di ]) < 0 )
1712         break;
1713     if ( iN == nbN ) // nbN nodes are shared with vol
1714     {
1715       // if ( vol->IsPoly() || vol->NbFaces() > 6 ) // vol is polyhed or hex prism 
1716       // {
1717       //   int nb = myCurFace.myNbNodes;
1718       //   if ( myVolume->GetEntityType() != vol->GetEntityType() )
1719       //     nb -= ( GetCenterNodeIndex(0) > 0 );
1720       //   set<const SMDS_MeshNode*> faceNodes( nodes, nodes + nb );
1721       //   if ( SMDS_VolumeTool( vol ).GetFaceIndex( faceNodes ) < 0 )
1722       //     continue;
1723       // }
1724       if ( otherVol ) *otherVol = vol;
1725       return !isFree;
1726     }
1727   }
1728   if ( otherVol ) *otherVol = 0;
1729   return isFree;
1730 }
1731
1732 //================================================================================
1733 /*!
1734  * \brief Thorough check that only one volume is built on the face nodes
1735  */
1736 //================================================================================
1737
1738 bool SMDS_VolumeTool::IsFreeFaceAdv( int faceIndex, const SMDS_MeshElement** otherVol/*=0*/ ) const
1739 {
1740   const bool isFree = true;
1741
1742   if (!setFace( faceIndex ))
1743     return !isFree;
1744
1745   const SMDS_MeshNode** nodes = GetFaceNodes( faceIndex );
1746   const int nbFaceNodes = myCurFace.myNbNodes;
1747
1748   // evaluate nb of face nodes shared by other volumes
1749   int maxNbShared = -1;
1750   typedef map< const SMDS_MeshElement*, int > TElemIntMap;
1751   TElemIntMap volNbShared;
1752   TElemIntMap::iterator vNbIt;
1753   for ( int iNode = 0; iNode < nbFaceNodes; iNode++ ) {
1754     const SMDS_MeshNode* n = nodes[ iNode ];
1755     SMDS_ElemIteratorPtr eIt = n->GetInverseElementIterator( SMDSAbs_Volume );
1756     while ( eIt->more() ) {
1757       const SMDS_MeshElement* elem = eIt->next();
1758       if ( elem != myVolume ) {
1759         vNbIt = volNbShared.insert( make_pair( elem, 0 )).first;
1760         (*vNbIt).second++;
1761         if ( vNbIt->second > maxNbShared )
1762           maxNbShared = vNbIt->second;
1763       }
1764     }
1765   }
1766   if ( maxNbShared < 3 )
1767     return isFree; // is free
1768
1769   // find volumes laying on the opposite side of the face
1770   // and sharing all nodes
1771   XYZ intNormal; // internal normal
1772   GetFaceNormal( faceIndex, intNormal.x, intNormal.y, intNormal.z );
1773   if ( IsFaceExternal( faceIndex ))
1774     intNormal = XYZ( -intNormal.x, -intNormal.y, -intNormal.z );
1775   XYZ p0 ( nodes[0] ), baryCenter;
1776   for ( vNbIt = volNbShared.begin(); vNbIt != volNbShared.end();  ) {
1777     const int& nbShared = (*vNbIt).second;
1778     if ( nbShared >= 3 ) {
1779       SMDS_VolumeTool volume( (*vNbIt).first );
1780       volume.GetBaryCenter( baryCenter.x, baryCenter.y, baryCenter.z );
1781       XYZ intNormal2( baryCenter - p0 );
1782       if ( intNormal.Dot( intNormal2 ) < 0 ) {
1783         // opposite side
1784         if ( nbShared >= nbFaceNodes )
1785         {
1786           // a volume shares the whole facet
1787           if ( otherVol ) *otherVol = vNbIt->first;
1788           return !isFree; 
1789         }
1790         ++vNbIt;
1791         continue;
1792       }
1793     }
1794     // remove a volume from volNbShared map
1795     volNbShared.erase( vNbIt++ );
1796   }
1797
1798   // here volNbShared contains only volumes laying on the opposite side of
1799   // the face and sharing 3 or more but not all face nodes with myVolume
1800   if ( volNbShared.size() < 2 ) {
1801     return isFree; // is free
1802   }
1803
1804   // check if the whole area of a face is shared
1805   for ( int iNode = 0; iNode < nbFaceNodes; iNode++ )
1806   {
1807     const SMDS_MeshNode* n = nodes[ iNode ];
1808     // check if n is shared by one of volumes of volNbShared
1809     bool isShared = false;
1810     SMDS_ElemIteratorPtr eIt = n->GetInverseElementIterator( SMDSAbs_Volume );
1811     while ( eIt->more() && !isShared )
1812       isShared = volNbShared.count( eIt->next() );
1813     if ( !isShared )
1814       return isFree;
1815   }
1816   if ( otherVol ) *otherVol = volNbShared.begin()->first;
1817   return !isFree;
1818
1819 //   if ( !myVolume->IsPoly() )
1820 //   {
1821 //     bool isShared[] = { false, false, false, false }; // 4 triangle parts of a quadrangle
1822 //     for ( vNbIt = volNbShared.begin(); vNbIt != volNbShared.end(); vNbIt++ ) {
1823 //       SMDS_VolumeTool volume( (*vNbIt).first );
1824 //       bool prevLinkShared = false;
1825 //       int nbSharedLinks = 0;
1826 //       for ( int iNode = 0; iNode < nbFaceNodes; iNode++ ) {
1827 //         bool linkShared = volume.IsLinked( nodes[ iNode ], nodes[ iNode + 1] );
1828 //         if ( linkShared )
1829 //           nbSharedLinks++;
1830 //         if ( linkShared && prevLinkShared &&
1831 //              volume.IsLinked( nodes[ iNode - 1 ], nodes[ iNode + 1] ))
1832 //           isShared[ iNode ] = true;
1833 //         prevLinkShared = linkShared;
1834 //       }
1835 //       if ( nbSharedLinks == nbFaceNodes )
1836 //         return !free; // is not free
1837 //       if ( nbFaceNodes == 4 ) {
1838 //         // check traingle parts 1 & 3
1839 //         if ( isShared[1] && isShared[3] )
1840 //           return !free; // is not free
1841 //         // check triangle parts 0 & 2;
1842 //         // 0 part could not be checked in the loop; check it here
1843 //         if ( isShared[2] && prevLinkShared &&
1844 //              volume.IsLinked( nodes[ 0 ], nodes[ 1 ] ) &&
1845 //              volume.IsLinked( nodes[ 1 ], nodes[ 3 ] ) )
1846 //           return !free; // is not free
1847 //       }
1848 //     }
1849 //   }
1850 //  return free;
1851 }
1852
1853 //=======================================================================
1854 //function : GetFaceIndex
1855 //purpose  : Return index of a face formed by theFaceNodes
1856 //=======================================================================
1857
1858 int SMDS_VolumeTool::GetFaceIndex( const set<const SMDS_MeshNode*>& theFaceNodes,
1859                                    const int                        theFaceIndexHint ) const
1860 {
1861   if ( theFaceIndexHint >= 0 )
1862   {
1863     int nbNodes = NbFaceNodes( theFaceIndexHint );
1864     if ( nbNodes == (int) theFaceNodes.size() )
1865     {
1866       const SMDS_MeshNode** nodes = GetFaceNodes( theFaceIndexHint );
1867       while ( nbNodes )
1868         if ( theFaceNodes.count( nodes[ nbNodes-1 ]))
1869           --nbNodes;
1870         else
1871           break;
1872       if ( nbNodes == 0 )
1873         return theFaceIndexHint;
1874     }
1875   }
1876   for ( int iFace = 0; iFace < myNbFaces; iFace++ )
1877   {
1878     if ( iFace == theFaceIndexHint )
1879       continue;
1880     int nbNodes = NbFaceNodes( iFace );
1881     if ( nbNodes == (int) theFaceNodes.size() )
1882     {
1883       const SMDS_MeshNode** nodes = GetFaceNodes( iFace );
1884       while ( nbNodes )
1885         if ( theFaceNodes.count( nodes[ nbNodes-1 ]))
1886           --nbNodes;
1887         else
1888           break;
1889       if ( nbNodes == 0 )
1890         return iFace;
1891     }
1892   }
1893   return -1;
1894 }
1895
1896 //=======================================================================
1897 //function : GetFaceIndex
1898 //purpose  : Return index of a face formed by theFaceNodes
1899 //=======================================================================
1900
1901 /*int SMDS_VolumeTool::GetFaceIndex( const set<int>& theFaceNodesIndices )
1902 {
1903   for ( int iFace = 0; iFace < myNbFaces; iFace++ ) {
1904     const int* nodes = GetFaceNodesIndices( iFace );
1905     int nbFaceNodes = NbFaceNodes( iFace );
1906     set<int> nodeSet;
1907     for ( int iNode = 0; iNode < nbFaceNodes; iNode++ )
1908       nodeSet.insert( nodes[ iNode ] );
1909     if ( theFaceNodesIndices == nodeSet )
1910       return iFace;
1911   }
1912   return -1;
1913 }*/
1914
1915 //=======================================================================
1916 //function : setFace
1917 //purpose  : 
1918 //=======================================================================
1919
1920 bool SMDS_VolumeTool::setFace( int faceIndex ) const
1921 {
1922   if ( !myVolume )
1923     return false;
1924
1925   if ( myCurFace.myIndex == faceIndex )
1926     return true;
1927
1928   myCurFace.myIndex = -1;
1929
1930   if ( faceIndex < 0 || faceIndex >= NbFaces() )
1931     return false;
1932
1933   if (myVolume->IsPoly())
1934   {
1935     if (!myPolyedre) {
1936       MESSAGE("Warning: bad volumic element");
1937       return false;
1938     }
1939
1940     // set face nodes
1941     SMDS_VolumeTool* me = const_cast< SMDS_VolumeTool* >( this );
1942     if ( !myAllFacesNbNodes ) {
1943       me->myPolyQuantities = myPolyedre->GetQuantities();
1944       myAllFacesNbNodes    = &myPolyQuantities[0];
1945     }
1946     myCurFace.myNbNodes = myAllFacesNbNodes[ faceIndex ];
1947     myCurFace.myNodes.resize( myCurFace.myNbNodes + 1 );
1948     me->myPolyIndices.resize( myCurFace.myNbNodes + 1 );
1949     myCurFace.myNodeIndices = & me->myPolyIndices[0];
1950     int shift = std::accumulate( myAllFacesNbNodes, myAllFacesNbNodes+faceIndex, 0 );
1951     for ( int iNode = 0; iNode < myCurFace.myNbNodes; iNode++ )
1952     {
1953       myCurFace.myNodes      [ iNode ] = myVolumeNodes[ shift + iNode ];
1954       myCurFace.myNodeIndices[ iNode ] = shift + iNode;
1955     }
1956     myCurFace.myNodes      [ myCurFace.myNbNodes ] = myCurFace.myNodes[ 0 ]; // last = first
1957     myCurFace.myNodeIndices[ myCurFace.myNbNodes ] = myCurFace.myNodeIndices[ 0 ];
1958
1959     // check orientation
1960     if (myExternalFaces)
1961     {
1962       myCurFace.myIndex = faceIndex; // avoid infinite recursion in IsFaceExternal()
1963       myExternalFaces = false; // force normal computation by IsFaceExternal()
1964       if ( !IsFaceExternal( faceIndex ))
1965         std::reverse( myCurFace.myNodes.begin(), myCurFace.myNodes.end() );
1966       myExternalFaces = true;
1967     }
1968   }
1969   else
1970   {
1971     if ( !myAllFacesNodeIndices_F )
1972     {
1973       // choose data for an element type
1974       switch ( myVolumeNodes.size() ) {
1975       case 4:
1976         myAllFacesNodeIndices_F  = &Tetra_F [0][0];
1977         //myAllFacesNodeIndices_FE = &Tetra_F [0][0];
1978         myAllFacesNodeIndices_RE = &Tetra_RE[0][0];
1979         myAllFacesNbNodes        = Tetra_nbN;
1980         myMaxFaceNbNodes         = sizeof(Tetra_F[0])/sizeof(Tetra_F[0][0]);
1981         break;
1982       case 5:
1983         myAllFacesNodeIndices_F  = &Pyramid_F [0][0];
1984         //myAllFacesNodeIndices_FE = &Pyramid_F [0][0];
1985         myAllFacesNodeIndices_RE = &Pyramid_RE[0][0];
1986         myAllFacesNbNodes        = Pyramid_nbN;
1987         myMaxFaceNbNodes         = sizeof(Pyramid_F[0])/sizeof(Pyramid_F[0][0]);
1988         break;
1989       case 6:
1990         myAllFacesNodeIndices_F  = &Penta_F [0][0];
1991         //myAllFacesNodeIndices_FE = &Penta_FE[0][0];
1992         myAllFacesNodeIndices_RE = &Penta_RE[0][0];
1993         myAllFacesNbNodes        = Penta_nbN;
1994         myMaxFaceNbNodes         = sizeof(Penta_F[0])/sizeof(Penta_F[0][0]);
1995         break;
1996       case 8:
1997         myAllFacesNodeIndices_F  = &Hexa_F [0][0];
1998         ///myAllFacesNodeIndices_FE = &Hexa_FE[0][0];
1999         myAllFacesNodeIndices_RE = &Hexa_RE[0][0];
2000         myAllFacesNbNodes        = Hexa_nbN;
2001         myMaxFaceNbNodes         = sizeof(Hexa_F[0])/sizeof(Hexa_F[0][0]);
2002         break;
2003       case 10:
2004         myAllFacesNodeIndices_F  = &QuadTetra_F [0][0];
2005         //myAllFacesNodeIndices_FE = &QuadTetra_F [0][0];
2006         myAllFacesNodeIndices_RE = &QuadTetra_RE[0][0];
2007         myAllFacesNbNodes        = QuadTetra_nbN;
2008         myMaxFaceNbNodes         = sizeof(QuadTetra_F[0])/sizeof(QuadTetra_F[0][0]);
2009         break;
2010       case 13:
2011         myAllFacesNodeIndices_F  = &QuadPyram_F [0][0];
2012         //myAllFacesNodeIndices_FE = &QuadPyram_F [0][0];
2013         myAllFacesNodeIndices_RE = &QuadPyram_RE[0][0];
2014         myAllFacesNbNodes        = QuadPyram_nbN;
2015         myMaxFaceNbNodes         = sizeof(QuadPyram_F[0])/sizeof(QuadPyram_F[0][0]);
2016         break;
2017       case 15:
2018         myAllFacesNodeIndices_F  = &QuadPenta_F [0][0];
2019         //myAllFacesNodeIndices_FE = &QuadPenta_FE[0][0];
2020         myAllFacesNodeIndices_RE = &QuadPenta_RE[0][0];
2021         myAllFacesNbNodes        = QuadPenta_nbN;
2022         myMaxFaceNbNodes         = sizeof(QuadPenta_F[0])/sizeof(QuadPenta_F[0][0]);
2023         break;
2024       case 20:
2025       case 27:
2026         myAllFacesNodeIndices_F  = &QuadHexa_F [0][0];
2027         //myAllFacesNodeIndices_FE = &QuadHexa_FE[0][0];
2028         myAllFacesNodeIndices_RE = &QuadHexa_RE[0][0];
2029         myAllFacesNbNodes        = QuadHexa_nbN;
2030         myMaxFaceNbNodes         = sizeof(QuadHexa_F[0])/sizeof(QuadHexa_F[0][0]);
2031         if ( !myIgnoreCentralNodes && myVolumeNodes.size() == 27 )
2032         {
2033           myAllFacesNodeIndices_F  = &TriQuadHexa_F [0][0];
2034           //myAllFacesNodeIndices_FE = &TriQuadHexa_FE[0][0];
2035           myAllFacesNodeIndices_RE = &TriQuadHexa_RE[0][0];
2036           myAllFacesNbNodes        = TriQuadHexa_nbN;
2037           myMaxFaceNbNodes         = sizeof(TriQuadHexa_F[0])/sizeof(TriQuadHexa_F[0][0]);
2038         }
2039         break;
2040       case 12:
2041         myAllFacesNodeIndices_F  = &HexPrism_F [0][0];
2042         //myAllFacesNodeIndices_FE = &HexPrism_FE[0][0];
2043         myAllFacesNodeIndices_RE = &HexPrism_RE[0][0];
2044         myAllFacesNbNodes        = HexPrism_nbN;
2045         myMaxFaceNbNodes         = sizeof(HexPrism_F[0])/sizeof(HexPrism_F[0][0]);
2046         break;
2047       default:
2048         return false;
2049       }
2050     }
2051     myCurFace.myNbNodes = myAllFacesNbNodes[ faceIndex ];
2052     // if ( myExternalFaces )
2053     //   myCurFace.myNodeIndices = (int*)( myVolForward ? myAllFacesNodeIndices_FE + faceIndex*myMaxFaceNbNodes : myAllFacesNodeIndices_RE + faceIndex*myMaxFaceNbNodes );
2054     // else
2055     //   myCurFace.myNodeIndices = (int*)( myAllFacesNodeIndices_F + faceIndex*myMaxFaceNbNodes );
2056     myCurFace.myNodeIndices = (int*)( myVolForward ? myAllFacesNodeIndices_F + faceIndex*myMaxFaceNbNodes : myAllFacesNodeIndices_RE + faceIndex*myMaxFaceNbNodes );
2057
2058     // set face nodes
2059     myCurFace.myNodes.resize( myCurFace.myNbNodes + 1 );
2060     for ( int iNode = 0; iNode < myCurFace.myNbNodes; iNode++ )
2061       myCurFace.myNodes[ iNode ] = myVolumeNodes[ myCurFace.myNodeIndices[ iNode ]];
2062     myCurFace.myNodes[ myCurFace.myNbNodes ] = myCurFace.myNodes[ 0 ];
2063   }
2064
2065   myCurFace.myIndex = faceIndex;
2066
2067   return true;
2068 }
2069
2070 //=======================================================================
2071 //function : GetType
2072 //purpose  : return VolumeType by nb of nodes in a volume
2073 //=======================================================================
2074
2075 SMDS_VolumeTool::VolumeType SMDS_VolumeTool::GetType(int nbNodes)
2076 {
2077   switch ( nbNodes ) {
2078   case 4: return TETRA;
2079   case 5: return PYRAM;
2080   case 6: return PENTA;
2081   case 8: return HEXA;
2082   case 10: return QUAD_TETRA;
2083   case 13: return QUAD_PYRAM;
2084   case 15: return QUAD_PENTA;
2085   case 20:
2086   case 27: return QUAD_HEXA;
2087   case 12: return HEX_PRISM;
2088   default:return UNKNOWN;
2089   }
2090 }
2091
2092 //=======================================================================
2093 //function : NbFaces
2094 //purpose  : return nb of faces by volume type
2095 //=======================================================================
2096
2097 int SMDS_VolumeTool::NbFaces( VolumeType type )
2098 {
2099   switch ( type ) {
2100   case TETRA     :
2101   case QUAD_TETRA: return 4;
2102   case PYRAM     :
2103   case QUAD_PYRAM: return 5;
2104   case PENTA     :
2105   case QUAD_PENTA: return 5;
2106   case HEXA      :
2107   case QUAD_HEXA : return 6;
2108   case HEX_PRISM : return 8;
2109   default:         return 0;
2110   }
2111 }
2112
2113 //================================================================================
2114 /*!
2115  * \brief Useful to know nb of corner nodes of a quadratic volume
2116   * \param type - volume type
2117   * \retval int - nb of corner nodes
2118  */
2119 //================================================================================
2120
2121 int SMDS_VolumeTool::NbCornerNodes(VolumeType type)
2122 {
2123   switch ( type ) {
2124   case TETRA     :
2125   case QUAD_TETRA: return 4;
2126   case PYRAM     :
2127   case QUAD_PYRAM: return 5;
2128   case PENTA     :
2129   case QUAD_PENTA: return 6;
2130   case HEXA      :
2131   case QUAD_HEXA : return 8;
2132   case HEX_PRISM : return 12;
2133   default:         return 0;
2134   }
2135   return 0;
2136 }
2137   // 
2138
2139 //=======================================================================
2140 //function : GetFaceNodesIndices
2141 //purpose  : Return the array of face nodes indices
2142 //           To comfort link iteration, the array
2143 //           length == NbFaceNodes( faceIndex ) + 1 and
2144 //           the last node index == the first one.
2145 //=======================================================================
2146
2147 const int* SMDS_VolumeTool::GetFaceNodesIndices(VolumeType type,
2148                                                 int        faceIndex,
2149                                                 bool       external)
2150 {
2151   switch ( type ) {
2152   case TETRA: return Tetra_F[ faceIndex ];
2153   case PYRAM: return Pyramid_F[ faceIndex ];
2154   case PENTA: return external ? Penta_F[ faceIndex ] : Penta_F[ faceIndex ];
2155   case HEXA:  return external ? Hexa_F[ faceIndex ] : Hexa_F[ faceIndex ];
2156   case QUAD_TETRA: return QuadTetra_F[ faceIndex ];
2157   case QUAD_PYRAM: return QuadPyram_F[ faceIndex ];
2158   case QUAD_PENTA: return external ? QuadPenta_F[ faceIndex ] : QuadPenta_F[ faceIndex ];
2159     // what about SMDSEntity_TriQuad_Hexa?
2160   case QUAD_HEXA:  return external ? QuadHexa_F[ faceIndex ] : QuadHexa_F[ faceIndex ];
2161   case HEX_PRISM:  return external ? HexPrism_F[ faceIndex ] : HexPrism_F[ faceIndex ];
2162   default:;
2163   }
2164   return 0;
2165 }
2166
2167 //=======================================================================
2168 //function : NbFaceNodes
2169 //purpose  : Return number of nodes in the array of face nodes
2170 //=======================================================================
2171
2172 int SMDS_VolumeTool::NbFaceNodes(VolumeType type,
2173                                  int        faceIndex )
2174 {
2175   switch ( type ) {
2176   case TETRA: return Tetra_nbN[ faceIndex ];
2177   case PYRAM: return Pyramid_nbN[ faceIndex ];
2178   case PENTA: return Penta_nbN[ faceIndex ];
2179   case HEXA:  return Hexa_nbN[ faceIndex ];
2180   case QUAD_TETRA: return QuadTetra_nbN[ faceIndex ];
2181   case QUAD_PYRAM: return QuadPyram_nbN[ faceIndex ];
2182   case QUAD_PENTA: return QuadPenta_nbN[ faceIndex ];
2183     // what about SMDSEntity_TriQuad_Hexa?
2184   case QUAD_HEXA:  return QuadHexa_nbN[ faceIndex ];
2185   case HEX_PRISM:  return HexPrism_nbN[ faceIndex ];
2186   default:;
2187   }
2188   return 0;
2189 }
2190
2191 //=======================================================================
2192 //function : Element
2193 //purpose  : return element
2194 //=======================================================================
2195
2196 const SMDS_MeshVolume* SMDS_VolumeTool::Element() const
2197 {
2198   return static_cast<const SMDS_MeshVolume*>( myVolume );
2199 }
2200
2201 //=======================================================================
2202 //function : ID
2203 //purpose  : return element ID
2204 //=======================================================================
2205
2206 int SMDS_VolumeTool::ID() const
2207 {
2208   return myVolume ? myVolume->GetID() : 0;
2209 }