Salome HOME
21accdc9da2d9f4dbe4ef2af020b3992526e26ee
[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     SaveFacet savedFacet( myCurFace );
710
711     // get an origin not lying in plane of any facet
712     int faceIndex = std::max( myCurFace.myIndex, 0 );
713     setFace( faceIndex );
714     double minProj, maxProj;
715     XYZ normal, origin;
716     projectNodesToNormal( faceIndex, minProj, maxProj, normal.data());
717     GetFaceBaryCenter( faceIndex, origin.x, origin.y, origin.z );
718     origin.x += normal.x * ( maxProj - minProj ) * 1e-1;
719     origin.y += normal.y * ( maxProj - minProj ) * 1e-2;
720     origin.z += normal.z * ( maxProj - minProj ) * 1e-3;
721
722     // split a polyhedron into tetrahedrons
723
724     SMDS_VolumeTool* me = const_cast< SMDS_VolumeTool* > ( this );
725     for ( int f = 0; f < NbFaces(); ++f )
726     {
727       me->setFace( f );
728       XYZ area (0,0,0), p1( myCurFace.myNodes[0] );
729       for ( int n = 0; n < myCurFace.myNbNodes; ++n )
730       {
731         XYZ p2( myCurFace.myNodes[ n+1 ]);
732         area = area + p1.Crossed( p2 );
733         p1 = p2;
734       }
735       V += ( p1 - origin ).Dot( area );
736     }
737     V /= 6;
738   }
739   else 
740   {
741     const static int ind[] = {
742       0, 1, 3, 6, 11, 23, 31, 44, 58, 78 };
743     const static int vtab[][4] = { // decomposition into tetra in the order of enum VolumeType
744       // tetrahedron
745       { 0, 1, 2, 3 },
746       // pyramid
747       { 0, 1, 3, 4 },
748       { 1, 2, 3, 4 },
749       // pentahedron
750       { 0, 1, 2, 3 },
751       { 1, 5, 3, 4 },
752       { 1, 5, 2, 3 },
753       // hexahedron
754       { 1, 4, 3, 0 },
755       { 4, 1, 6, 5 },
756       { 1, 3, 6, 2 },
757       { 4, 6, 3, 7 },
758       { 1, 4, 6, 3 },
759       // hexagonal prism
760       { 0, 1, 2, 7 },
761       { 0, 7, 8, 6 },
762       { 2, 7, 8, 0 },
763
764       { 0, 3, 4, 9 },
765       { 0, 9, 10, 6 },
766       { 4, 9, 10, 0 },
767
768       { 0, 3, 4, 9 },
769       { 0, 9, 10, 6 },
770       { 4, 9, 10, 0 },
771
772       { 0, 4, 5, 10 },
773       { 0, 10, 11, 6 },
774       { 5, 10, 11, 0 },
775
776       // quadratic tetrahedron
777       { 0, 4, 6, 7 },
778       { 1, 5, 4, 8 },
779       { 2, 6, 5, 9 },
780       { 7, 8, 9, 3 },
781       { 4, 6, 7, 9 },
782       { 4, 5, 6, 9 },
783       { 4, 7, 8, 9 },
784       { 4, 5, 9, 8 },
785
786       // quadratic pyramid
787       { 0, 5, 8, 9 },
788       { 1, 5,10, 6 },
789       { 2, 6,11, 7 },
790       { 3, 7,12, 8 },
791       { 4, 9,11,10 },
792       { 4, 9,12,11 },
793       { 10, 5, 9, 8 },
794       { 10, 8, 9,12 },
795       { 10, 8,12, 7 },
796       { 10, 7,12,11 },
797       { 10, 7,11, 6 },
798       { 10, 5, 8, 6 },
799       { 10, 6, 8, 7 },
800
801       // quadratic pentahedron
802       { 12, 0, 8, 6 },
803       { 12, 8, 7, 6 },
804       { 12, 8, 2, 7 },
805       { 12, 6, 7, 1 },
806       { 12, 1, 7,13 },
807       { 12, 7, 2,13 },
808       { 12, 2,14,13 },
809
810       { 12, 3, 9,11 },
811       { 12,11, 9,10 },
812       { 12,11,10, 5 },
813       { 12, 9, 4,10 },
814       { 12,14, 5,10 },
815       { 12,14,10, 4 },
816       { 12,14, 4,13 },
817
818       // quadratic hexahedron
819       { 16, 0,11, 8 },
820       { 16,11, 9, 8 },
821       { 16, 8, 9, 1 },
822       { 16,11, 3,10 },
823       { 16,11,10, 9 },
824       { 16,10, 2, 9 },
825       { 16, 3,19, 2 },
826       { 16, 2,19,18 },
827       { 16, 2,18,17 },
828       { 16, 2,17, 1 },
829
830       { 16, 4,12,15 },
831       { 16,12, 5,13 },
832       { 16,12,13,15 },
833       { 16,13, 6,14 },
834       { 16,13,14,15 },
835       { 16,14, 7,15 },
836       { 16, 6, 5,17 },
837       { 16,18, 6,17 },
838       { 16,18, 7, 6 },
839       { 16,18,19, 7 },
840
841     };
842
843     int type = GetVolumeType();
844     int n1 = ind[type];
845     int n2 = ind[type+1];
846
847     for (int i = n1; i <  n2; i++) {
848       V -= getTetraVolume( myVolumeNodes[ vtab[i][0] ],
849                            myVolumeNodes[ vtab[i][1] ],
850                            myVolumeNodes[ vtab[i][2] ],
851                            myVolumeNodes[ vtab[i][3] ]);
852     }
853   }
854   return V;
855 }
856
857 //=======================================================================
858 //function : GetBaryCenter
859 //purpose  : 
860 //=======================================================================
861
862 bool SMDS_VolumeTool::GetBaryCenter(double & X, double & Y, double & Z) const
863 {
864   X = Y = Z = 0.;
865   if ( !myVolume )
866     return false;
867
868   for ( size_t i = 0; i < myVolumeNodes.size(); i++ ) {
869     X += myVolumeNodes[ i ]->X();
870     Y += myVolumeNodes[ i ]->Y();
871     Z += myVolumeNodes[ i ]->Z();
872   }
873   X /= myVolumeNodes.size();
874   Y /= myVolumeNodes.size();
875   Z /= myVolumeNodes.size();
876
877   return true;
878 }
879
880 //================================================================================
881 /*!
882  * \brief Classify a point
883  *  \param tol - thickness of faces
884  */
885 //================================================================================
886
887 bool SMDS_VolumeTool::IsOut(double X, double Y, double Z, double tol) const
888 {
889   // LIMITATION: for convex volumes only
890   XYZ p( X,Y,Z );
891   for ( int iF = 0; iF < myNbFaces; ++iF )
892   {
893     XYZ faceNormal;
894     if ( !GetFaceNormal( iF, faceNormal.x, faceNormal.y, faceNormal.z ))
895       continue;
896     if ( !IsFaceExternal( iF ))
897       faceNormal = XYZ() - faceNormal; // reverse
898
899     XYZ face2p( p - XYZ( myCurFace.myNodes[0] ));
900     if ( face2p.Dot( faceNormal ) > tol )
901       return true;
902   }
903   return false;
904 }
905
906 //=======================================================================
907 //function : SetExternalNormal
908 //purpose  : Node order will be so that faces normals are external
909 //=======================================================================
910
911 void SMDS_VolumeTool::SetExternalNormal ()
912 {
913   myExternalFaces = true;
914   myCurFace.myIndex = -1;
915 }
916
917 //=======================================================================
918 //function : NbFaceNodes
919 //purpose  : Return number of nodes in the array of face nodes
920 //=======================================================================
921
922 int SMDS_VolumeTool::NbFaceNodes( int faceIndex ) const
923 {
924   if ( !setFace( faceIndex ))
925     return 0;
926   return myCurFace.myNbNodes;
927 }
928
929 //=======================================================================
930 //function : GetFaceNodes
931 //purpose  : Return pointer to the array of face nodes.
932 //           To comfort link iteration, the array
933 //           length == NbFaceNodes( faceIndex ) + 1 and
934 //           the last node == the first one.
935 //=======================================================================
936
937 const SMDS_MeshNode** SMDS_VolumeTool::GetFaceNodes( int faceIndex ) const
938 {
939   if ( !setFace( faceIndex ))
940     return 0;
941   return &myCurFace.myNodes[0];
942 }
943
944 //=======================================================================
945 //function : GetFaceNodesIndices
946 //purpose  : Return pointer to the array of face nodes indices
947 //           To comfort link iteration, the array
948 //           length == NbFaceNodes( faceIndex ) + 1 and
949 //           the last node index == the first one.
950 //=======================================================================
951
952 const int* SMDS_VolumeTool::GetFaceNodesIndices( int faceIndex ) const
953 {
954   if ( !setFace( faceIndex ))
955     return 0;
956
957   return myCurFace.myNodeIndices;
958 }
959
960 //=======================================================================
961 //function : GetFaceNodes
962 //purpose  : Return a set of face nodes.
963 //=======================================================================
964
965 bool SMDS_VolumeTool::GetFaceNodes (int                             faceIndex,
966                                     std::set<const SMDS_MeshNode*>& theFaceNodes ) const
967 {
968   if ( !setFace( faceIndex ))
969     return false;
970
971   theFaceNodes.clear();
972   theFaceNodes.insert( myCurFace.myNodes.begin(), myCurFace.myNodes.end() );
973
974   return true;
975 }
976
977 namespace
978 {
979   struct NLink : public std::pair<int,int>
980   {
981     int myOri;
982     NLink(const SMDS_MeshNode* n1=0, const SMDS_MeshNode* n2=0, int ori=1 )
983     {
984       if ( n1 )
985       {
986         if (( myOri = ( n1->GetID() < n2->GetID() )))
987         {
988           first  = n1->GetID();
989           second = n2->GetID();
990         }
991         else
992         {
993           myOri  = -1;
994           first  = n2->GetID();
995           second = n1->GetID();
996         }
997         myOri *= ori;
998       }
999       else
1000       {
1001         myOri = first = second = 0;
1002       }
1003     }
1004     //int Node1() const { return myOri == -1 ? second : first; }
1005
1006     //bool IsSameOri( const std::pair<int,int>& link ) const { return link.first == Node1(); }
1007   };
1008 }
1009
1010 //=======================================================================
1011 //function : IsFaceExternal
1012 //purpose  : Check normal orientation of a given face
1013 //=======================================================================
1014
1015 bool SMDS_VolumeTool::IsFaceExternal( int faceIndex ) const
1016 {
1017   if ( myExternalFaces || !myVolume )
1018     return true;
1019
1020   if ( !myPolyedre ) // all classical volumes have external facet normals
1021     return true;
1022
1023   SMDS_VolumeTool* me = const_cast< SMDS_VolumeTool* >( this );
1024
1025   if ( myPolyFacetOri[ faceIndex ])
1026     return myPolyFacetOri[ faceIndex ] > 0;
1027
1028   int ori = 0; // -1-in, +1-out, 0-undef
1029   double minProj, maxProj;
1030   if ( projectNodesToNormal( faceIndex, minProj, maxProj ))
1031   {
1032     // all nodes are on the same side of the facet
1033     ori = ( minProj < 0 ? +1 : -1 );
1034     me->myPolyFacetOri[ faceIndex ] = ori;
1035
1036     if ( !myFwdLinks.empty() ) // concave polyhedron; collect oriented links
1037       for ( int i = 0; i < myCurFace.myNbNodes; ++i )
1038       {
1039         NLink link( myCurFace.myNodes[i], myCurFace.myNodes[i+1], ori );
1040         me->myFwdLinks.insert( make_pair( link, link.myOri ));
1041       }
1042     return ori > 0;
1043   }
1044
1045   SaveFacet savedFacet( myCurFace );
1046
1047   // concave polyhedron
1048
1049   if ( myFwdLinks.empty() ) // get links of the least ambiguously oriented facet
1050   {
1051     for ( size_t i = 0; i < myPolyFacetOri.size() && !ori; ++i )
1052       ori = myPolyFacetOri[ i ];
1053
1054     if ( !ori ) // none facet is oriented yet
1055     {
1056       // find the least ambiguously oriented facet
1057       int faceMostConvex = -1;
1058       std::map< double, int > convexity2face;
1059       for ( size_t iF = 0; iF < myPolyFacetOri.size() && faceMostConvex < 0; ++iF )
1060       {
1061         if ( projectNodesToNormal( iF, minProj, maxProj ))
1062         {
1063           // all nodes are on the same side of the facet
1064           me->myPolyFacetOri[ iF ] = ( minProj < 0 ? +1 : -1 );
1065           faceMostConvex = iF;
1066         }
1067         else
1068         {
1069           ori = ( -minProj < maxProj ? -1 : +1 );
1070           double convexity = std::min( -minProj, maxProj ) / std::max( -minProj, maxProj );
1071           convexity2face.insert( std::make_pair( convexity, iF * ori ));
1072         }
1073       }
1074       if ( faceMostConvex < 0 ) // none facet has nodes on the same side
1075       {
1076         // use the least ambiguous facet
1077         faceMostConvex = convexity2face.begin()->second;
1078         ori = ( faceMostConvex < 0 ? -1 : +1 );
1079         faceMostConvex = std::abs( faceMostConvex );
1080         me->myPolyFacetOri[ faceMostConvex ] = ori;
1081       }
1082     }
1083     // collect links of the oriented facets in myFwdLinks
1084     for ( size_t iF = 0; iF < myPolyFacetOri.size(); ++iF )
1085     {
1086       ori = myPolyFacetOri[ iF ];
1087       if ( !ori ) continue;
1088       setFace( iF );
1089       for ( int i = 0; i < myCurFace.myNbNodes; ++i )
1090       {
1091         NLink link( myCurFace.myNodes[i], myCurFace.myNodes[i+1], ori );
1092         me->myFwdLinks.insert( make_pair( link, link.myOri ));
1093       }
1094     }
1095   }
1096
1097   // compare orientation of links of the facet with myFwdLinks
1098   ori = 0;
1099   setFace( faceIndex );
1100   std::vector< NLink > links( myCurFace.myNbNodes ), links2;
1101   for ( int i = 0; i < myCurFace.myNbNodes && !ori; ++i )
1102   {
1103     NLink link( myCurFace.myNodes[i], myCurFace.myNodes[i+1] );
1104     std::map<Link, int>::const_iterator l2o = myFwdLinks.find( link );
1105     if ( l2o != myFwdLinks.end() )
1106       ori = link.myOri * l2o->second * -1;
1107     links[ i ] = link;
1108   }
1109   while ( !ori ) // the facet has no common links with already oriented facets
1110   {
1111     // orient and collect links of other non-oriented facets
1112     for ( size_t iF = 0; iF < myPolyFacetOri.size(); ++iF )
1113     {
1114       if ( myPolyFacetOri[ iF ] ) continue; // already oriented
1115       setFace( iF );
1116       links2.clear();
1117       ori = 0;
1118       for ( int i = 0; i < myCurFace.myNbNodes && !ori; ++i )
1119       {
1120         NLink link( myCurFace.myNodes[i], myCurFace.myNodes[i+1] );
1121         std::map<Link, int>::const_iterator l2o = myFwdLinks.find( link );
1122         if ( l2o != myFwdLinks.end() )
1123           ori = link.myOri * l2o->second * -1;
1124         links2.push_back( link );
1125       }
1126       if ( ori ) // one more facet oriented
1127       {
1128         me->myPolyFacetOri[ iF ] = ori;
1129         for ( size_t i = 0; i < links2.size(); ++i )
1130           me->myFwdLinks.insert( make_pair( links2[i], links2[i].myOri * ori ));
1131         break;
1132       }
1133     }
1134     if ( !ori )
1135       return false; // error in algorithm: infinite loop
1136
1137     // try to orient the facet again
1138     ori = 0;
1139     for ( size_t i = 0; i < links.size() && !ori; ++i )
1140     {
1141       std::map<Link, int>::const_iterator l2o = myFwdLinks.find( links[i] );
1142       if ( l2o != myFwdLinks.end() )
1143         ori = links[i].myOri * l2o->second * -1;
1144     }
1145     me->myPolyFacetOri[ faceIndex ] = ori;
1146   }
1147
1148   return ori > 0;
1149 }
1150
1151 //=======================================================================
1152 //function : projectNodesToNormal
1153 //purpose  : compute min and max projections of all nodes to normal of a facet.
1154 //=======================================================================
1155
1156 bool SMDS_VolumeTool::projectNodesToNormal( int     faceIndex,
1157                                             double& minProj,
1158                                             double& maxProj,
1159                                             double* normalXYZ ) const
1160 {
1161   minProj = std::numeric_limits<double>::max();
1162   maxProj = std::numeric_limits<double>::min();
1163
1164   XYZ normal;
1165   if ( !GetFaceNormal( faceIndex, normal.x, normal.y, normal.z ))
1166     return false;
1167   if ( normalXYZ )
1168     memcpy( normalXYZ, normal.data(), 3*sizeof(double));
1169
1170   XYZ p0 ( myCurFace.myNodes[0] );
1171   for ( size_t i = 0; i < myVolumeNodes.size(); ++i )
1172   {
1173     if ( std::find( myCurFace.myNodes.begin() + 1,
1174                     myCurFace.myNodes.end(),
1175                     myVolumeNodes[ i ] ) != myCurFace.myNodes.end() )
1176       continue; // node of the faceIndex-th facet
1177
1178     double proj = normal.Dot( XYZ( myVolumeNodes[ i ]) - p0 );
1179     if ( proj < minProj ) minProj = proj;
1180     if ( proj > maxProj ) maxProj = proj;
1181   }
1182   const double tol = 1e-7;
1183   minProj += tol;
1184   maxProj -= tol;
1185   bool diffSize = ( minProj * maxProj < 0 );
1186   // if ( diffSize )
1187   // {
1188   //   minProj = -minProj;
1189   // }
1190   // else if ( minProj < 0 )
1191   // {
1192   //   minProj = -minProj;
1193   //   maxProj = -maxProj;
1194   // }
1195
1196   return !diffSize; // ? 0 : (minProj >= 0);
1197 }
1198
1199 //=======================================================================
1200 //function : GetFaceNormal
1201 //purpose  : Return a normal to a face
1202 //=======================================================================
1203
1204 bool SMDS_VolumeTool::GetFaceNormal (int faceIndex, double & X, double & Y, double & Z) const
1205 {
1206   if ( !setFace( faceIndex ))
1207     return false;
1208
1209   const int iQuad = ( !myPolyedre && myCurFace.myNbNodes > 6 ) ? 2 : 1;
1210   XYZ p1 ( myCurFace.myNodes[0*iQuad] );
1211   XYZ p2 ( myCurFace.myNodes[1*iQuad] );
1212   XYZ p3 ( myCurFace.myNodes[2*iQuad] );
1213   XYZ aVec12( p2 - p1 );
1214   XYZ aVec13( p3 - p1 );
1215   XYZ cross = aVec12.Crossed( aVec13 );
1216
1217   for ( int i = 3*iQuad; i < myCurFace.myNbNodes; i += iQuad )
1218   {
1219     XYZ p4 ( myCurFace.myNodes[i] );
1220     XYZ aVec14( p4 - p1 );
1221     XYZ cross2 = aVec13.Crossed( aVec14 );
1222     cross = cross + cross2;
1223     aVec13 = aVec14;
1224   }
1225
1226   double size = cross.Magnitude();
1227   if ( size <= std::numeric_limits<double>::min() )
1228     return false;
1229
1230   X = cross.x / size;
1231   Y = cross.y / size;
1232   Z = cross.z / size;
1233
1234   return true;
1235 }
1236
1237 //================================================================================
1238 /*!
1239  * \brief Return barycenter of a face
1240  */
1241 //================================================================================
1242
1243 bool SMDS_VolumeTool::GetFaceBaryCenter (int faceIndex, double & X, double & Y, double & Z) const
1244 {
1245   if ( !setFace( faceIndex ))
1246     return false;
1247
1248   X = Y = Z = 0.0;
1249   for ( int i = 0; i < myCurFace.myNbNodes; ++i )
1250   {
1251     X += myCurFace.myNodes[i]->X() / myCurFace.myNbNodes;
1252     Y += myCurFace.myNodes[i]->Y() / myCurFace.myNbNodes;
1253     Z += myCurFace.myNodes[i]->Z() / myCurFace.myNbNodes;
1254   }
1255   return true;
1256 }
1257
1258 //=======================================================================
1259 //function : GetFaceArea
1260 //purpose  : Return face area
1261 //=======================================================================
1262
1263 double SMDS_VolumeTool::GetFaceArea( int faceIndex ) const
1264 {
1265   double area = 0;
1266   if ( !setFace( faceIndex ))
1267     return area;
1268
1269   XYZ p1 ( myCurFace.myNodes[0] );
1270   XYZ p2 ( myCurFace.myNodes[1] );
1271   XYZ p3 ( myCurFace.myNodes[2] );
1272   XYZ aVec12( p2 - p1 );
1273   XYZ aVec13( p3 - p1 );
1274   area += aVec12.Crossed( aVec13 ).Magnitude();
1275
1276   if (myVolume->IsPoly())
1277   {
1278     for ( int i = 3; i < myCurFace.myNbNodes; ++i )
1279     {
1280       XYZ pI ( myCurFace.myNodes[i] );
1281       XYZ aVecI( pI - p1 );
1282       area += aVec13.Crossed( aVecI ).Magnitude();
1283       aVec13 = aVecI;
1284     }
1285   }
1286   else
1287   {
1288     if ( myCurFace.myNbNodes == 4 ) {
1289       XYZ p4 ( myCurFace.myNodes[3] );
1290       XYZ aVec14( p4 - p1 );
1291       area += aVec14.Crossed( aVec13 ).Magnitude();
1292     }
1293   }
1294   return area / 2;
1295 }
1296
1297 //================================================================================
1298 /*!
1299  * \brief Return index of the node located at face center of a quadratic element like HEX27
1300  */
1301 //================================================================================
1302
1303 int SMDS_VolumeTool::GetCenterNodeIndex( int faceIndex ) const
1304 {
1305   if ( myAllFacesNbNodes && myVolumeNodes.size() == 27 ) // classic element with 27 nodes
1306   {
1307     switch ( faceIndex ) {
1308     case 0: return 20;
1309     case 1: return 25;
1310     default:
1311       return faceIndex + 19;
1312     }
1313   }
1314   return -1;
1315 }
1316
1317 //=======================================================================
1318 //function : GetOppFaceIndex
1319 //purpose  : Return index of the opposite face if it exists, else -1.
1320 //=======================================================================
1321
1322 int SMDS_VolumeTool::GetOppFaceIndex( int faceIndex ) const
1323 {
1324   int ind = -1;
1325   if (myPolyedre) {
1326     MESSAGE("Warning: attempt to obtain opposite face on polyhedral volume");
1327     return ind;
1328   }
1329
1330   const int nbHoriFaces = 2;
1331
1332   if ( faceIndex >= 0 && faceIndex < NbFaces() ) {
1333     switch ( myVolumeNodes.size() ) {
1334     case 6:
1335     case 15:
1336       if ( faceIndex == 0 || faceIndex == 1 )
1337         ind = 1 - faceIndex;
1338         break;
1339     case 8:
1340     case 12:
1341       if ( faceIndex <= 1 ) // top or bottom
1342         ind = 1 - faceIndex;
1343       else {
1344         const int nbSideFaces = myAllFacesNbNodes[0];
1345         ind = ( faceIndex - nbHoriFaces + nbSideFaces/2 ) % nbSideFaces + nbHoriFaces;
1346       }
1347       break;
1348     case 20:
1349     case 27:
1350       ind = GetOppFaceIndexOfHex( faceIndex );
1351       break;
1352     default:;
1353     }
1354   }
1355   return ind;
1356 }
1357
1358 //=======================================================================
1359 //function : GetOppFaceIndexOfHex
1360 //purpose  : Return index of the opposite face of the hexahedron
1361 //=======================================================================
1362
1363 int SMDS_VolumeTool::GetOppFaceIndexOfHex( int faceIndex )
1364 {
1365   return Hexa_oppF[ faceIndex ];
1366 }
1367
1368 //=======================================================================
1369 //function : IsLinked
1370 //purpose  : return true if theNode1 is linked with theNode2
1371 // If theIgnoreMediumNodes then corner nodes of quadratic cell are considered linked as well
1372 //=======================================================================
1373
1374 bool SMDS_VolumeTool::IsLinked (const SMDS_MeshNode* theNode1,
1375                                 const SMDS_MeshNode* theNode2,
1376                                 const bool           theIgnoreMediumNodes) const
1377 {
1378   if ( !myVolume )
1379     return false;
1380
1381   if (myVolume->IsPoly()) {
1382     if (!myPolyedre) {
1383       MESSAGE("Warning: bad volumic element");
1384       return false;
1385     }
1386     if ( !myAllFacesNbNodes ) {
1387       SMDS_VolumeTool*  me = const_cast< SMDS_VolumeTool* >( this );
1388       me->myPolyQuantities = myPolyedre->GetQuantities();
1389       myAllFacesNbNodes    = &myPolyQuantities[0];
1390     }
1391     int from, to = 0, d1 = 1, d2 = 2;
1392     if ( myPolyedre->IsQuadratic() ) {
1393       if ( theIgnoreMediumNodes ) {
1394         d1 = 2; d2 = 0;
1395       }
1396     } else {
1397       d2 = 0;
1398     }
1399     std::vector<const SMDS_MeshNode*>::const_iterator i;
1400     for (int iface = 0; iface < myNbFaces; iface++)
1401     {
1402       from = to;
1403       to  += myPolyQuantities[iface];
1404       i    = std::find( myVolumeNodes.begin() + from, myVolumeNodes.begin() + to, theNode1 );
1405       if ( i != myVolumeNodes.end() )
1406       {
1407         if ((  theNode2 == *( i-d1 ) ||
1408                theNode2 == *( i+d1 )))
1409           return true;
1410         if (( d2 ) &&
1411             (( theNode2 == *( i-d2 ) ||
1412                theNode2 == *( i+d2 ))))
1413           return true;
1414       }
1415     }
1416     return false;
1417   }
1418
1419   // find nodes indices
1420   int i1 = -1, i2 = -1, nbFound = 0;
1421   for ( size_t i = 0; i < myVolumeNodes.size() && nbFound < 2; i++ )
1422   {
1423     if ( myVolumeNodes[ i ] == theNode1 )
1424       i1 = i, ++nbFound;
1425     else if ( myVolumeNodes[ i ] == theNode2 )
1426       i2 = i, ++nbFound;
1427   }
1428   return IsLinked( i1, i2 );
1429 }
1430
1431 //=======================================================================
1432 //function : IsLinked
1433 //purpose  : return true if the node with theNode1Index is linked
1434 //           with the node with theNode2Index
1435 // If theIgnoreMediumNodes then corner nodes of quadratic cell are considered linked as well
1436 //=======================================================================
1437
1438 bool SMDS_VolumeTool::IsLinked (const int theNode1Index,
1439                                 const int theNode2Index,
1440                                 bool      theIgnoreMediumNodes) const
1441 {
1442   if ( myVolume->IsPoly() ) {
1443     return IsLinked(myVolumeNodes[theNode1Index], myVolumeNodes[theNode2Index]);
1444   }
1445
1446   int minInd = std::min( theNode1Index, theNode2Index );
1447   int maxInd = std::max( theNode1Index, theNode2Index );
1448
1449   if ( minInd < 0 || maxInd > (int)myVolumeNodes.size() - 1 || maxInd == minInd )
1450     return false;
1451
1452   VolumeType type = GetVolumeType();
1453   if ( myVolume->IsQuadratic() )
1454   {
1455     int firstMediumInd = myVolume->NbCornerNodes();
1456     if ( minInd >= firstMediumInd )
1457       return false; // both nodes are medium - not linked
1458     if ( maxInd < firstMediumInd ) // both nodes are corners
1459     {
1460       if ( theIgnoreMediumNodes )
1461         type = quadToLinear(type); // to check linkage of corner nodes only
1462       else
1463         return false; // corner nodes are not linked directly in a quadratic cell
1464     }
1465   }
1466
1467   switch ( type ) {
1468   case TETRA:
1469     return true;
1470   case HEXA:
1471     switch ( maxInd - minInd ) {
1472     case 1: return minInd != 3;
1473     case 3: return minInd == 0 || minInd == 4;
1474     case 4: return true;
1475     default:;
1476     }
1477     break;
1478   case PYRAM:
1479     if ( maxInd == 4 )
1480       return true;
1481     switch ( maxInd - minInd ) {
1482     case 1:
1483     case 3: return true;
1484     default:;
1485     }
1486     break;
1487   case PENTA:
1488     switch ( maxInd - minInd ) {
1489     case 1: return minInd != 2;
1490     case 2: return minInd == 0 || minInd == 3;
1491     case 3: return true;
1492     default:;
1493     }
1494     break;
1495   case QUAD_TETRA:
1496     {
1497       switch ( minInd ) {
1498       case 0: if( maxInd==4 ||  maxInd==6 ||  maxInd==7 ) return true;
1499       case 1: if( maxInd==4 ||  maxInd==5 ||  maxInd==8 ) return true;
1500       case 2: if( maxInd==5 ||  maxInd==6 ||  maxInd==9 ) return true;
1501       case 3: if( maxInd==7 ||  maxInd==8 ||  maxInd==9 ) return true;
1502       default:;
1503       }
1504       break;
1505     }
1506   case QUAD_HEXA:
1507     {
1508       switch ( minInd ) {
1509       case 0: if( maxInd==8 ||  maxInd==11 ||  maxInd==16 ) return true;
1510       case 1: if( maxInd==8 ||  maxInd==9 ||  maxInd==17 ) return true;
1511       case 2: if( maxInd==9 ||  maxInd==10 ||  maxInd==18 ) return true;
1512       case 3: if( maxInd==10 ||  maxInd==11 ||  maxInd==19 ) return true;
1513       case 4: if( maxInd==12 ||  maxInd==15 ||  maxInd==16 ) return true;
1514       case 5: if( maxInd==12 ||  maxInd==13 ||  maxInd==17 ) return true;
1515       case 6: if( maxInd==13 ||  maxInd==14 ||  maxInd==18 ) return true;
1516       case 7: if( maxInd==14 ||  maxInd==15 ||  maxInd==19 ) return true;
1517       default:;
1518       }
1519       break;
1520     }
1521   case QUAD_PYRAM:
1522     {
1523       switch ( minInd ) {
1524       case 0: if( maxInd==5 ||  maxInd==8 ||  maxInd==9 ) return true;
1525       case 1: if( maxInd==5 ||  maxInd==6 ||  maxInd==10 ) return true;
1526       case 2: if( maxInd==6 ||  maxInd==7 ||  maxInd==11 ) return true;
1527       case 3: if( maxInd==7 ||  maxInd==8 ||  maxInd==12 ) return true;
1528       case 4: if( maxInd==9 ||  maxInd==10 ||  maxInd==11 ||  maxInd==12 ) return true;
1529       default:;
1530       }
1531       break;
1532     }
1533   case QUAD_PENTA:
1534     {
1535       switch ( minInd ) {
1536       case 0: if( maxInd==6 ||  maxInd==8 ||  maxInd==12 ) return true;
1537       case 1: if( maxInd==6 ||  maxInd==7 ||  maxInd==13 ) return true;
1538       case 2: if( maxInd==7 ||  maxInd==8 ||  maxInd==14 ) return true;
1539       case 3: if( maxInd==9 ||  maxInd==11 ||  maxInd==12 ) return true;
1540       case 4: if( maxInd==9 ||  maxInd==10 ||  maxInd==13 ) return true;
1541       case 5: if( maxInd==10 ||  maxInd==11 ||  maxInd==14 ) return true;
1542       default:;
1543       }
1544       break;
1545     }
1546   case HEX_PRISM:
1547     {
1548       const int diff = maxInd-minInd;
1549       if ( diff > 6  ) return false;// not linked top and bottom
1550       if ( diff == 6 ) return true; // linked top and bottom
1551       return diff == 1 || diff == 7;
1552     }
1553   default:;
1554   }
1555   return false;
1556 }
1557
1558 //=======================================================================
1559 //function : GetNodeIndex
1560 //purpose  : Return an index of theNode
1561 //=======================================================================
1562
1563 int SMDS_VolumeTool::GetNodeIndex(const SMDS_MeshNode* theNode) const
1564 {
1565   if ( myVolume ) {
1566     for ( size_t i = 0; i < myVolumeNodes.size(); i++ ) {
1567       if ( myVolumeNodes[ i ] == theNode )
1568         return i;
1569     }
1570   }
1571   return -1;
1572 }
1573
1574 //================================================================================
1575 /*!
1576  * \brief Fill vector with boundary faces existing in the mesh
1577   * \param faces - vector of found nodes
1578   * \retval int - nb of found faces
1579  */
1580 //================================================================================
1581
1582 int SMDS_VolumeTool::GetAllExistingFaces(std::vector<const SMDS_MeshElement*> & faces) const
1583 {
1584   faces.clear();
1585   SaveFacet savedFacet( myCurFace );
1586   if ( IsPoly() )
1587     for ( int iF = 0; iF < NbFaces(); ++iF ) {
1588       if ( setFace( iF ))
1589         if ( const SMDS_MeshElement* face = SMDS_Mesh::FindFace( myCurFace.myNodes ))
1590           faces.push_back( face );
1591     }
1592   else
1593     for ( int iF = 0; iF < NbFaces(); ++iF ) {
1594       const SMDS_MeshFace* face = 0;
1595       const SMDS_MeshNode** nodes = GetFaceNodes( iF );
1596       switch ( NbFaceNodes( iF )) {
1597       case 3:
1598         face = SMDS_Mesh::FindFace( nodes[0], nodes[1], nodes[2] ); break;
1599       case 4:
1600         face = SMDS_Mesh::FindFace( nodes[0], nodes[1], nodes[2], nodes[3] ); break;
1601       case 6:
1602         face = SMDS_Mesh::FindFace( nodes[0], nodes[1], nodes[2],
1603                                     nodes[3], nodes[4], nodes[5]); break;
1604       case 8:
1605         face = SMDS_Mesh::FindFace( nodes[0], nodes[1], nodes[2], nodes[3],
1606                                     nodes[4], nodes[5], nodes[6], nodes[7]); break;
1607       }
1608       if ( face )
1609         faces.push_back( face );
1610     }
1611   return faces.size();
1612 }
1613
1614
1615 //================================================================================
1616 /*!
1617  * \brief Fill vector with boundary edges existing in the mesh
1618  * \param edges - vector of found edges
1619  * \retval int - nb of found faces
1620  */
1621 //================================================================================
1622
1623 int SMDS_VolumeTool::GetAllExistingEdges(std::vector<const SMDS_MeshElement*> & edges) const
1624 {
1625   edges.clear();
1626   edges.reserve( myVolumeNodes.size() * 2 );
1627   for ( size_t i = 0; i < myVolumeNodes.size()-1; ++i ) {
1628     for ( size_t j = i + 1; j < myVolumeNodes.size(); ++j ) {
1629       if ( IsLinked( i, j )) {
1630         const SMDS_MeshElement* edge =
1631           SMDS_Mesh::FindEdge( myVolumeNodes[i], myVolumeNodes[j] );
1632         if ( edge )
1633           edges.push_back( edge );
1634       }
1635     }
1636   }
1637   return edges.size();
1638 }
1639
1640 //================================================================================
1641 /*!
1642  * \brief Return minimal square distance between connected corner nodes
1643  */
1644 //================================================================================
1645
1646 double SMDS_VolumeTool::MinLinearSize2() const
1647 {
1648   double minSize = 1e+100;
1649   int iQ = myVolume->IsQuadratic() ? 2 : 1;
1650
1651   SaveFacet savedFacet( myCurFace );
1652
1653   // it seems that compute distance twice is faster than organization of a sole computing
1654   myCurFace.myIndex = -1;
1655   for ( int iF = 0; iF < myNbFaces; ++iF )
1656   {
1657     setFace( iF );
1658     for ( int iN = 0; iN < myCurFace.myNbNodes; iN += iQ )
1659     {
1660       XYZ n1( myCurFace.myNodes[ iN ]);
1661       XYZ n2( myCurFace.myNodes[(iN + iQ) % myCurFace.myNbNodes]);
1662       minSize = std::min( minSize, (n1 - n2).SquareMagnitude());
1663     }
1664   }
1665
1666   return minSize;
1667 }
1668
1669 //================================================================================
1670 /*!
1671  * \brief Return maximal square distance between connected corner nodes
1672  */
1673 //================================================================================
1674
1675 double SMDS_VolumeTool::MaxLinearSize2() const
1676 {
1677   double maxSize = -1e+100;
1678   int iQ = myVolume->IsQuadratic() ? 2 : 1;
1679
1680   SaveFacet savedFacet( myCurFace );
1681   
1682   // it seems that compute distance twice is faster than organization of a sole computing
1683   myCurFace.myIndex = -1;
1684   for ( int iF = 0; iF < myNbFaces; ++iF )
1685   {
1686     setFace( iF );
1687     for ( int iN = 0; iN < myCurFace.myNbNodes; iN += iQ )
1688     {
1689       XYZ n1( myCurFace.myNodes[ iN ]);
1690       XYZ n2( myCurFace.myNodes[(iN + iQ) % myCurFace.myNbNodes]);
1691       maxSize = std::max( maxSize, (n1 - n2).SquareMagnitude());
1692     }
1693   }
1694
1695   return maxSize;
1696 }
1697
1698 //================================================================================
1699 /*!
1700  * \brief Fast quickly check that only one volume is built on the face nodes
1701  *        This check is valid for conformal meshes only
1702  */
1703 //================================================================================
1704
1705 bool SMDS_VolumeTool::IsFreeFace( int faceIndex, const SMDS_MeshElement** otherVol/*=0*/ ) const
1706 {
1707   const bool isFree = true;
1708
1709   if ( !setFace( faceIndex ))
1710     return !isFree;
1711
1712   const SMDS_MeshNode** nodes = GetFaceNodes( faceIndex );
1713
1714   const int  di = myVolume->IsQuadratic() ? 2 : 1;
1715   const int nbN = ( myCurFace.myNbNodes/di <= 4 && !IsPoly()) ? 3 : myCurFace.myNbNodes/di; // nb nodes to check
1716
1717   SMDS_ElemIteratorPtr eIt = nodes[0]->GetInverseElementIterator( SMDSAbs_Volume );
1718   while ( eIt->more() )
1719   {
1720     const SMDS_MeshElement* vol = eIt->next();
1721     if ( vol == myVolume )
1722       continue;
1723     int iN;
1724     for ( iN = 1; iN < nbN; ++iN )
1725       if ( vol->GetNodeIndex( nodes[ iN*di ]) < 0 )
1726         break;
1727     if ( iN == nbN ) // nbN nodes are shared with vol
1728     {
1729       // if ( vol->IsPoly() || vol->NbFaces() > 6 ) // vol is polyhed or hex prism 
1730       // {
1731       //   int nb = myCurFace.myNbNodes;
1732       //   if ( myVolume->GetEntityType() != vol->GetEntityType() )
1733       //     nb -= ( GetCenterNodeIndex(0) > 0 );
1734       //   std::set<const SMDS_MeshNode*> faceNodes( nodes, nodes + nb );
1735       //   if ( SMDS_VolumeTool( vol ).GetFaceIndex( faceNodes ) < 0 )
1736       //     continue;
1737       // }
1738       if ( otherVol ) *otherVol = vol;
1739       return !isFree;
1740     }
1741   }
1742   if ( otherVol ) *otherVol = 0;
1743   return isFree;
1744 }
1745
1746 //================================================================================
1747 /*!
1748  * \brief Thorough check that only one volume is built on the face nodes
1749  */
1750 //================================================================================
1751
1752 bool SMDS_VolumeTool::IsFreeFaceAdv( int faceIndex, const SMDS_MeshElement** otherVol/*=0*/ ) const
1753 {
1754   const bool isFree = true;
1755
1756   if (!setFace( faceIndex ))
1757     return !isFree;
1758
1759   const SMDS_MeshNode** nodes = GetFaceNodes( faceIndex );
1760   const int nbFaceNodes = myCurFace.myNbNodes;
1761
1762   // evaluate nb of face nodes shared by other volumes
1763   int maxNbShared = -1;
1764   typedef std::map< const SMDS_MeshElement*, int > TElemIntMap;
1765   TElemIntMap volNbShared;
1766   TElemIntMap::iterator vNbIt;
1767   for ( int iNode = 0; iNode < nbFaceNodes; iNode++ ) {
1768     const SMDS_MeshNode* n = nodes[ iNode ];
1769     SMDS_ElemIteratorPtr eIt = n->GetInverseElementIterator( SMDSAbs_Volume );
1770     while ( eIt->more() ) {
1771       const SMDS_MeshElement* elem = eIt->next();
1772       if ( elem != myVolume ) {
1773         vNbIt = volNbShared.insert( std::make_pair( elem, 0 )).first;
1774         (*vNbIt).second++;
1775         if ( vNbIt->second > maxNbShared )
1776           maxNbShared = vNbIt->second;
1777       }
1778     }
1779   }
1780   if ( maxNbShared < 3 )
1781     return isFree; // is free
1782
1783   // find volumes laying on the opposite side of the face
1784   // and sharing all nodes
1785   XYZ intNormal; // internal normal
1786   GetFaceNormal( faceIndex, intNormal.x, intNormal.y, intNormal.z );
1787   if ( IsFaceExternal( faceIndex ))
1788     intNormal = XYZ( -intNormal.x, -intNormal.y, -intNormal.z );
1789   XYZ p0 ( nodes[0] ), baryCenter;
1790   for ( vNbIt = volNbShared.begin(); vNbIt != volNbShared.end();  ) {
1791     const int& nbShared = (*vNbIt).second;
1792     if ( nbShared >= 3 ) {
1793       SMDS_VolumeTool volume( (*vNbIt).first );
1794       volume.GetBaryCenter( baryCenter.x, baryCenter.y, baryCenter.z );
1795       XYZ intNormal2( baryCenter - p0 );
1796       if ( intNormal.Dot( intNormal2 ) < 0 ) {
1797         // opposite side
1798         if ( nbShared >= nbFaceNodes )
1799         {
1800           // a volume shares the whole facet
1801           if ( otherVol ) *otherVol = vNbIt->first;
1802           return !isFree; 
1803         }
1804         ++vNbIt;
1805         continue;
1806       }
1807     }
1808     // remove a volume from volNbShared map
1809     volNbShared.erase( vNbIt++ );
1810   }
1811
1812   // here volNbShared contains only volumes laying on the opposite side of
1813   // the face and sharing 3 or more but not all face nodes with myVolume
1814   if ( volNbShared.size() < 2 ) {
1815     return isFree; // is free
1816   }
1817
1818   // check if the whole area of a face is shared
1819   for ( int iNode = 0; iNode < nbFaceNodes; iNode++ )
1820   {
1821     const SMDS_MeshNode* n = nodes[ iNode ];
1822     // check if n is shared by one of volumes of volNbShared
1823     bool isShared = false;
1824     SMDS_ElemIteratorPtr eIt = n->GetInverseElementIterator( SMDSAbs_Volume );
1825     while ( eIt->more() && !isShared )
1826       isShared = volNbShared.count( eIt->next() );
1827     if ( !isShared )
1828       return isFree;
1829   }
1830   if ( otherVol ) *otherVol = volNbShared.begin()->first;
1831   return !isFree;
1832
1833 //   if ( !myVolume->IsPoly() )
1834 //   {
1835 //     bool isShared[] = { false, false, false, false }; // 4 triangle parts of a quadrangle
1836 //     for ( vNbIt = volNbShared.begin(); vNbIt != volNbShared.end(); vNbIt++ ) {
1837 //       SMDS_VolumeTool volume( (*vNbIt).first );
1838 //       bool prevLinkShared = false;
1839 //       int nbSharedLinks = 0;
1840 //       for ( int iNode = 0; iNode < nbFaceNodes; iNode++ ) {
1841 //         bool linkShared = volume.IsLinked( nodes[ iNode ], nodes[ iNode + 1] );
1842 //         if ( linkShared )
1843 //           nbSharedLinks++;
1844 //         if ( linkShared && prevLinkShared &&
1845 //              volume.IsLinked( nodes[ iNode - 1 ], nodes[ iNode + 1] ))
1846 //           isShared[ iNode ] = true;
1847 //         prevLinkShared = linkShared;
1848 //       }
1849 //       if ( nbSharedLinks == nbFaceNodes )
1850 //         return !free; // is not free
1851 //       if ( nbFaceNodes == 4 ) {
1852 //         // check traingle parts 1 & 3
1853 //         if ( isShared[1] && isShared[3] )
1854 //           return !free; // is not free
1855 //         // check triangle parts 0 & 2;
1856 //         // 0 part could not be checked in the loop; check it here
1857 //         if ( isShared[2] && prevLinkShared &&
1858 //              volume.IsLinked( nodes[ 0 ], nodes[ 1 ] ) &&
1859 //              volume.IsLinked( nodes[ 1 ], nodes[ 3 ] ) )
1860 //           return !free; // is not free
1861 //       }
1862 //     }
1863 //   }
1864 //  return free;
1865 }
1866
1867 //=======================================================================
1868 //function : GetFaceIndex
1869 //purpose  : Return index of a face formed by theFaceNodes
1870 //=======================================================================
1871
1872 int SMDS_VolumeTool::GetFaceIndex( const std::set<const SMDS_MeshNode*>& theFaceNodes,
1873                                    const int                        theFaceIndexHint ) const
1874 {
1875   if ( theFaceIndexHint >= 0 )
1876   {
1877     int nbNodes = NbFaceNodes( theFaceIndexHint );
1878     if ( nbNodes == (int) theFaceNodes.size() )
1879     {
1880       const SMDS_MeshNode** nodes = GetFaceNodes( theFaceIndexHint );
1881       while ( nbNodes )
1882         if ( theFaceNodes.count( nodes[ nbNodes-1 ]))
1883           --nbNodes;
1884         else
1885           break;
1886       if ( nbNodes == 0 )
1887         return theFaceIndexHint;
1888     }
1889   }
1890   for ( int iFace = 0; iFace < myNbFaces; iFace++ )
1891   {
1892     if ( iFace == theFaceIndexHint )
1893       continue;
1894     int nbNodes = NbFaceNodes( iFace );
1895     if ( nbNodes == (int) theFaceNodes.size() )
1896     {
1897       const SMDS_MeshNode** nodes = GetFaceNodes( iFace );
1898       while ( nbNodes )
1899         if ( theFaceNodes.count( nodes[ nbNodes-1 ]))
1900           --nbNodes;
1901         else
1902           break;
1903       if ( nbNodes == 0 )
1904         return iFace;
1905     }
1906   }
1907   return -1;
1908 }
1909
1910 //=======================================================================
1911 //function : GetFaceIndex
1912 //purpose  : Return index of a face formed by theFaceNodes
1913 //=======================================================================
1914
1915 /*int SMDS_VolumeTool::GetFaceIndex( const std::set<int>& theFaceNodesIndices )
1916 {
1917   for ( int iFace = 0; iFace < myNbFaces; iFace++ ) {
1918     const int* nodes = GetFaceNodesIndices( iFace );
1919     int nbFaceNodes = NbFaceNodes( iFace );
1920     std::set<int> nodeSet;
1921     for ( int iNode = 0; iNode < nbFaceNodes; iNode++ )
1922       nodeSet.insert( nodes[ iNode ] );
1923     if ( theFaceNodesIndices == nodeSet )
1924       return iFace;
1925   }
1926   return -1;
1927 }*/
1928
1929 //=======================================================================
1930 //function : setFace
1931 //purpose  : 
1932 //=======================================================================
1933
1934 bool SMDS_VolumeTool::setFace( int faceIndex ) const
1935 {
1936   if ( !myVolume )
1937     return false;
1938
1939   if ( myCurFace.myIndex == faceIndex )
1940     return true;
1941
1942   myCurFace.myIndex = -1;
1943
1944   if ( faceIndex < 0 || faceIndex >= NbFaces() )
1945     return false;
1946
1947   if (myVolume->IsPoly())
1948   {
1949     if ( !myPolyedre ) {
1950       MESSAGE("Warning: bad volumic element");
1951       return false;
1952     }
1953
1954     // set face nodes
1955     SMDS_VolumeTool* me = const_cast< SMDS_VolumeTool* >( this );
1956     if ( !myAllFacesNbNodes ) {
1957       me->myPolyQuantities = myPolyedre->GetQuantities();
1958       myAllFacesNbNodes    = &myPolyQuantities[0];
1959     }
1960     myCurFace.myNbNodes = myAllFacesNbNodes[ faceIndex ];
1961     myCurFace.myNodes.resize( myCurFace.myNbNodes + 1 );
1962     me->myPolyIndices.resize( myCurFace.myNbNodes + 1 );
1963     myCurFace.myNodeIndices = & me->myPolyIndices[0];
1964     int shift = std::accumulate( myAllFacesNbNodes, myAllFacesNbNodes+faceIndex, 0 );
1965     for ( int iNode = 0; iNode < myCurFace.myNbNodes; iNode++ )
1966     {
1967       myCurFace.myNodes      [ iNode ] = myVolumeNodes[ shift + iNode ];
1968       myCurFace.myNodeIndices[ iNode ] = shift + iNode;
1969     }
1970     myCurFace.myNodes      [ myCurFace.myNbNodes ] = myCurFace.myNodes[ 0 ]; // last = first
1971     myCurFace.myNodeIndices[ myCurFace.myNbNodes ] = myCurFace.myNodeIndices[ 0 ];
1972
1973     // check orientation
1974     if (myExternalFaces)
1975     {
1976       myCurFace.myIndex = faceIndex; // avoid infinite recursion in IsFaceExternal()
1977       myExternalFaces = false; // force normal computation by IsFaceExternal()
1978       if ( !IsFaceExternal( faceIndex ))
1979         std::reverse( myCurFace.myNodes.begin(), myCurFace.myNodes.end() );
1980       myExternalFaces = true;
1981     }
1982   }
1983   else
1984   {
1985     if ( !myAllFacesNodeIndices_F )
1986     {
1987       // choose data for an element type
1988       switch ( myVolumeNodes.size() ) {
1989       case 4:
1990         myAllFacesNodeIndices_F  = &Tetra_F [0][0];
1991         //myAllFacesNodeIndices_FE = &Tetra_F [0][0];
1992         myAllFacesNodeIndices_RE = &Tetra_RE[0][0];
1993         myAllFacesNbNodes        = Tetra_nbN;
1994         myMaxFaceNbNodes         = sizeof(Tetra_F[0])/sizeof(Tetra_F[0][0]);
1995         break;
1996       case 5:
1997         myAllFacesNodeIndices_F  = &Pyramid_F [0][0];
1998         //myAllFacesNodeIndices_FE = &Pyramid_F [0][0];
1999         myAllFacesNodeIndices_RE = &Pyramid_RE[0][0];
2000         myAllFacesNbNodes        = Pyramid_nbN;
2001         myMaxFaceNbNodes         = sizeof(Pyramid_F[0])/sizeof(Pyramid_F[0][0]);
2002         break;
2003       case 6:
2004         myAllFacesNodeIndices_F  = &Penta_F [0][0];
2005         //myAllFacesNodeIndices_FE = &Penta_FE[0][0];
2006         myAllFacesNodeIndices_RE = &Penta_RE[0][0];
2007         myAllFacesNbNodes        = Penta_nbN;
2008         myMaxFaceNbNodes         = sizeof(Penta_F[0])/sizeof(Penta_F[0][0]);
2009         break;
2010       case 8:
2011         myAllFacesNodeIndices_F  = &Hexa_F [0][0];
2012         ///myAllFacesNodeIndices_FE = &Hexa_FE[0][0];
2013         myAllFacesNodeIndices_RE = &Hexa_RE[0][0];
2014         myAllFacesNbNodes        = Hexa_nbN;
2015         myMaxFaceNbNodes         = sizeof(Hexa_F[0])/sizeof(Hexa_F[0][0]);
2016         break;
2017       case 10:
2018         myAllFacesNodeIndices_F  = &QuadTetra_F [0][0];
2019         //myAllFacesNodeIndices_FE = &QuadTetra_F [0][0];
2020         myAllFacesNodeIndices_RE = &QuadTetra_RE[0][0];
2021         myAllFacesNbNodes        = QuadTetra_nbN;
2022         myMaxFaceNbNodes         = sizeof(QuadTetra_F[0])/sizeof(QuadTetra_F[0][0]);
2023         break;
2024       case 13:
2025         myAllFacesNodeIndices_F  = &QuadPyram_F [0][0];
2026         //myAllFacesNodeIndices_FE = &QuadPyram_F [0][0];
2027         myAllFacesNodeIndices_RE = &QuadPyram_RE[0][0];
2028         myAllFacesNbNodes        = QuadPyram_nbN;
2029         myMaxFaceNbNodes         = sizeof(QuadPyram_F[0])/sizeof(QuadPyram_F[0][0]);
2030         break;
2031       case 15:
2032         myAllFacesNodeIndices_F  = &QuadPenta_F [0][0];
2033         //myAllFacesNodeIndices_FE = &QuadPenta_FE[0][0];
2034         myAllFacesNodeIndices_RE = &QuadPenta_RE[0][0];
2035         myAllFacesNbNodes        = QuadPenta_nbN;
2036         myMaxFaceNbNodes         = sizeof(QuadPenta_F[0])/sizeof(QuadPenta_F[0][0]);
2037         break;
2038       case 20:
2039       case 27:
2040         myAllFacesNodeIndices_F  = &QuadHexa_F [0][0];
2041         //myAllFacesNodeIndices_FE = &QuadHexa_FE[0][0];
2042         myAllFacesNodeIndices_RE = &QuadHexa_RE[0][0];
2043         myAllFacesNbNodes        = QuadHexa_nbN;
2044         myMaxFaceNbNodes         = sizeof(QuadHexa_F[0])/sizeof(QuadHexa_F[0][0]);
2045         if ( !myIgnoreCentralNodes && myVolumeNodes.size() == 27 )
2046         {
2047           myAllFacesNodeIndices_F  = &TriQuadHexa_F [0][0];
2048           //myAllFacesNodeIndices_FE = &TriQuadHexa_FE[0][0];
2049           myAllFacesNodeIndices_RE = &TriQuadHexa_RE[0][0];
2050           myAllFacesNbNodes        = TriQuadHexa_nbN;
2051           myMaxFaceNbNodes         = sizeof(TriQuadHexa_F[0])/sizeof(TriQuadHexa_F[0][0]);
2052         }
2053         break;
2054       case 12:
2055         myAllFacesNodeIndices_F  = &HexPrism_F [0][0];
2056         //myAllFacesNodeIndices_FE = &HexPrism_FE[0][0];
2057         myAllFacesNodeIndices_RE = &HexPrism_RE[0][0];
2058         myAllFacesNbNodes        = HexPrism_nbN;
2059         myMaxFaceNbNodes         = sizeof(HexPrism_F[0])/sizeof(HexPrism_F[0][0]);
2060         break;
2061       default:
2062         return false;
2063       }
2064     }
2065     myCurFace.myNbNodes = myAllFacesNbNodes[ faceIndex ];
2066     // if ( myExternalFaces )
2067     //   myCurFace.myNodeIndices = (int*)( myVolForward ? myAllFacesNodeIndices_FE + faceIndex*myMaxFaceNbNodes : myAllFacesNodeIndices_RE + faceIndex*myMaxFaceNbNodes );
2068     // else
2069     //   myCurFace.myNodeIndices = (int*)( myAllFacesNodeIndices_F + faceIndex*myMaxFaceNbNodes );
2070     myCurFace.myNodeIndices = (int*)( myVolForward ? myAllFacesNodeIndices_F + faceIndex*myMaxFaceNbNodes : myAllFacesNodeIndices_RE + faceIndex*myMaxFaceNbNodes );
2071
2072     // set face nodes
2073     myCurFace.myNodes.resize( myCurFace.myNbNodes + 1 );
2074     for ( int iNode = 0; iNode < myCurFace.myNbNodes; iNode++ )
2075       myCurFace.myNodes[ iNode ] = myVolumeNodes[ myCurFace.myNodeIndices[ iNode ]];
2076     myCurFace.myNodes[ myCurFace.myNbNodes ] = myCurFace.myNodes[ 0 ];
2077   }
2078
2079   myCurFace.myIndex = faceIndex;
2080
2081   return true;
2082 }
2083
2084 //=======================================================================
2085 //function : GetType
2086 //purpose  : return VolumeType by nb of nodes in a volume
2087 //=======================================================================
2088
2089 SMDS_VolumeTool::VolumeType SMDS_VolumeTool::GetType(int nbNodes)
2090 {
2091   switch ( nbNodes ) {
2092   case 4: return TETRA;
2093   case 5: return PYRAM;
2094   case 6: return PENTA;
2095   case 8: return HEXA;
2096   case 10: return QUAD_TETRA;
2097   case 13: return QUAD_PYRAM;
2098   case 15: return QUAD_PENTA;
2099   case 20:
2100   case 27: return QUAD_HEXA;
2101   case 12: return HEX_PRISM;
2102   default:return UNKNOWN;
2103   }
2104 }
2105
2106 //=======================================================================
2107 //function : NbFaces
2108 //purpose  : return nb of faces by volume type
2109 //=======================================================================
2110
2111 int SMDS_VolumeTool::NbFaces( VolumeType type )
2112 {
2113   switch ( type ) {
2114   case TETRA     :
2115   case QUAD_TETRA: return 4;
2116   case PYRAM     :
2117   case QUAD_PYRAM: return 5;
2118   case PENTA     :
2119   case QUAD_PENTA: return 5;
2120   case HEXA      :
2121   case QUAD_HEXA : return 6;
2122   case HEX_PRISM : return 8;
2123   default:         return 0;
2124   }
2125 }
2126
2127 //================================================================================
2128 /*!
2129  * \brief Useful to know nb of corner nodes of a quadratic volume
2130   * \param type - volume type
2131   * \retval int - nb of corner nodes
2132  */
2133 //================================================================================
2134
2135 int SMDS_VolumeTool::NbCornerNodes(VolumeType type)
2136 {
2137   switch ( type ) {
2138   case TETRA     :
2139   case QUAD_TETRA: return 4;
2140   case PYRAM     :
2141   case QUAD_PYRAM: return 5;
2142   case PENTA     :
2143   case QUAD_PENTA: return 6;
2144   case HEXA      :
2145   case QUAD_HEXA : return 8;
2146   case HEX_PRISM : return 12;
2147   default:         return 0;
2148   }
2149   return 0;
2150 }
2151   // 
2152
2153 //=======================================================================
2154 //function : GetFaceNodesIndices
2155 //purpose  : Return the array of face nodes indices
2156 //           To comfort link iteration, the array
2157 //           length == NbFaceNodes( faceIndex ) + 1 and
2158 //           the last node index == the first one.
2159 //=======================================================================
2160
2161 const int* SMDS_VolumeTool::GetFaceNodesIndices(VolumeType type,
2162                                                 int        faceIndex,
2163                                                 bool       external)
2164 {
2165   switch ( type ) {
2166   case TETRA: return Tetra_F[ faceIndex ];
2167   case PYRAM: return Pyramid_F[ faceIndex ];
2168   case PENTA: return external ? Penta_F[ faceIndex ] : Penta_F[ faceIndex ];
2169   case HEXA:  return external ? Hexa_F[ faceIndex ] : Hexa_F[ faceIndex ];
2170   case QUAD_TETRA: return QuadTetra_F[ faceIndex ];
2171   case QUAD_PYRAM: return QuadPyram_F[ faceIndex ];
2172   case QUAD_PENTA: return external ? QuadPenta_F[ faceIndex ] : QuadPenta_F[ faceIndex ];
2173     // what about SMDSEntity_TriQuad_Hexa?
2174   case QUAD_HEXA:  return external ? QuadHexa_F[ faceIndex ] : QuadHexa_F[ faceIndex ];
2175   case HEX_PRISM:  return external ? HexPrism_F[ faceIndex ] : HexPrism_F[ faceIndex ];
2176   default:;
2177   }
2178   return 0;
2179 }
2180
2181 //=======================================================================
2182 //function : NbFaceNodes
2183 //purpose  : Return number of nodes in the array of face nodes
2184 //=======================================================================
2185
2186 int SMDS_VolumeTool::NbFaceNodes(VolumeType type,
2187                                  int        faceIndex )
2188 {
2189   switch ( type ) {
2190   case TETRA: return Tetra_nbN[ faceIndex ];
2191   case PYRAM: return Pyramid_nbN[ faceIndex ];
2192   case PENTA: return Penta_nbN[ faceIndex ];
2193   case HEXA:  return Hexa_nbN[ faceIndex ];
2194   case QUAD_TETRA: return QuadTetra_nbN[ faceIndex ];
2195   case QUAD_PYRAM: return QuadPyram_nbN[ faceIndex ];
2196   case QUAD_PENTA: return QuadPenta_nbN[ faceIndex ];
2197     // what about SMDSEntity_TriQuad_Hexa?
2198   case QUAD_HEXA:  return QuadHexa_nbN[ faceIndex ];
2199   case HEX_PRISM:  return HexPrism_nbN[ faceIndex ];
2200   default:;
2201   }
2202   return 0;
2203 }
2204
2205 //=======================================================================
2206 //function : Element
2207 //purpose  : return element
2208 //=======================================================================
2209
2210 const SMDS_MeshVolume* SMDS_VolumeTool::Element() const
2211 {
2212   return static_cast<const SMDS_MeshVolume*>( myVolume );
2213 }
2214
2215 //=======================================================================
2216 //function : ID
2217 //purpose  : return element ID
2218 //=======================================================================
2219
2220 int SMDS_VolumeTool::ID() const
2221 {
2222   return myVolume ? myVolume->GetID() : 0;
2223 }