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