Salome HOME
Copyrights update
[modules/smesh.git] / src / SMDS / SMDS_VolumeTool.cxx
1 // Copyright (C) 2005  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
2 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
3 // 
4 // This library is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU Lesser General Public
6 // License as published by the Free Software Foundation; either 
7 // version 2.1 of the License.
8 // 
9 // This library is distributed in the hope that it will be useful 
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
12 // Lesser General Public License for more details.
13 //
14 // You should have received a copy of the GNU Lesser General Public  
15 // License along with this library; if not, write to the Free Software 
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
17 //
18 // See http://www.salome-platform.org/
19 //
20 // File      : SMDS_VolumeTool.cxx
21 // Created   : Tue Jul 13 12:22:13 2004
22 // Author    : Edward AGAPOV (eap)
23 // Copyright : Open CASCADE
24
25 #ifdef _MSC_VER
26 #pragma warning(disable:4786)
27 #endif
28
29 #include "SMDS_VolumeTool.hxx"
30
31 #include "SMDS_MeshElement.hxx"
32 #include "SMDS_MeshNode.hxx"
33 #include "SMDS_PolyhedralVolumeOfNodes.hxx"
34
35 #include "utilities.h"
36
37 #include <map>
38 #include <float.h>
39 #include <math.h>
40
41 using namespace std;
42
43 // ======================================================
44 // Node indices in faces depending on volume orientation
45 // making most faces normals external
46 // ======================================================
47
48 /*
49 //           N3
50 //           +
51 //          /|\
52 //         / | \
53 //        /  |  \
54 //    N0 +---|---+ N1                TETRAHEDRON
55 //       \   |   /
56 //        \  |  /
57 //         \ | /
58 //          \|/
59 //           +
60 //           N2
61 */
62 static int Tetra_F [4][4] = { // FORWARD == EXTERNAL
63   { 0, 1, 2, 0 },              // All faces have external normals
64   { 0, 3, 1, 0 },
65   { 1, 3, 2, 1 },
66   { 0, 2, 3, 0 }}; 
67 static int Tetra_R [4][4] = { // REVERSED
68   { 0, 1, 2, 0 },             // All faces but a bottom have external normals
69   { 0, 1, 3, 0 },
70   { 1, 2, 3, 1 },
71   { 0, 3, 2, 0 }};
72 static int Tetra_RE [4][4] = { // REVERSED -> FORWARD (EXTERNAL)
73   { 0, 2, 1, 0 },              // All faces have external normals
74   { 0, 1, 3, 0 },
75   { 1, 2, 3, 1 },
76   { 0, 3, 2, 0 }};
77 static int Tetra_nbN [] = { 3, 3, 3, 3 };
78
79 //
80 //     PYRAMID
81 //
82 static int Pyramid_F [5][5] = { // FORWARD == EXTERNAL
83   { 0, 1, 2, 3, 0 },            // All faces have external normals
84   { 0, 4, 1, 0, 4 },
85   { 1, 4, 2, 1, 4 },
86   { 2, 4, 3, 2, 4 },
87   { 3, 4, 0, 3, 4 }}; 
88 static int Pyramid_R [5][5] = { // REVERSED
89   { 0, 1, 2, 3, 0 },            // All faces but a bottom have external normals
90   { 0, 1, 4, 0, 4 },
91   { 1, 2, 4, 1, 4 },
92   { 2, 3, 4, 2, 4 },
93   { 3, 0, 4, 3, 4 }}; 
94 static int Pyramid_RE [5][5] = { // REVERSED -> FORWARD (EXTERNAL)
95   { 0, 3, 2, 1, 0 },             // All faces but a bottom have external normals
96   { 0, 1, 4, 0, 4 },
97   { 1, 2, 4, 1, 4 },
98   { 2, 3, 4, 2, 4 },
99   { 3, 0, 4, 3, 4 }}; 
100 static int Pyramid_nbN [] = { 4, 3, 3, 3, 3 };
101
102 /*   
103 //            + N4
104 //           /|\
105 //          / | \
106 //         /  |  \
107 //        /   |   \
108 //    N3 +---------+ N5
109 //       |    |    |
110 //       |    + N1 |
111 //       |   / \   |                PENTAHEDRON
112 //       |  /   \  |
113 //       | /     \ |
114 //       |/       \|
115 //    N0 +---------+ N2
116 */
117 static int Penta_F [5][5] = { // FORWARD
118   { 0, 1, 2, 0, 0 },          // Top face has an internal normal, other - external
119   { 3, 4, 5, 3, 3 },          // 0 is bottom, 1 is top face
120   { 0, 2, 5, 3, 0 },
121   { 1, 4, 5, 2, 1 },
122   { 0, 3, 4, 1, 0 }}; 
123 static int Penta_R [5][5] = { // REVERSED
124   { 0, 1, 2, 0, 0 },          // Bottom face has an internal normal, other - external
125   { 3, 4, 5, 3, 3 },          // 0 is bottom, 1 is top face
126   { 0, 3, 5, 2, 0 },
127   { 1, 2, 5, 4, 1 },
128   { 0, 1, 4, 3, 0 }}; 
129 static int Penta_FE [5][5] = { // FORWARD -> EXTERNAL
130   { 0, 1, 2, 0, 0 },
131   { 3, 5, 4, 3, 3 },
132   { 0, 2, 5, 3, 0 },
133   { 1, 4, 5, 2, 1 },
134   { 0, 3, 4, 1, 0 }}; 
135 static int Penta_RE [5][5] = { // REVERSED -> EXTERNAL
136   { 0, 2, 1, 0, 0 },
137   { 3, 4, 5, 3, 3 },
138   { 0, 3, 5, 2, 0 },
139   { 1, 2, 5, 4, 1 },
140   { 0, 1, 4, 3, 0 }}; 
141 static int Penta_nbN [] = { 3, 3, 4, 4, 4 };
142
143 /*
144 //         N5+----------+N6
145 //          /|         /|
146 //         / |        / |
147 //        /  |       /  |
148 //     N4+----------+N7 |
149 //       |   |      |   |           HEXAHEDRON
150 //       |   |      |   |
151 //       |   |      |   |
152 //       | N1+------|---+N2
153 //       |  /       |  /
154 //       | /        | /
155 //       |/         |/
156 //     N0+----------+N3
157 */
158 static int Hexa_F [6][5] = { // FORWARD
159   { 0, 1, 2, 3, 0 },         // opposite faces are neighbouring,
160   { 4, 5, 6, 7, 4 },         // odd face(1,3,5) normal is internal, even(0,2,4) - external
161   { 1, 0, 4, 5, 1 },         // same index nodes of opposite faces are linked
162   { 2, 3, 7, 6, 2 }, 
163   { 0, 3, 7, 4, 0 }, 
164   { 1, 2, 6, 5, 1 }};
165 // static int Hexa_R [6][5] = { // REVERSED
166 //   { 0, 3, 2, 1, 0 },         // opposite faces are neighbouring,
167 //   { 4, 7, 6, 5, 4 },         // odd face(1,3,5) normal is external, even(0,2,4) - internal
168 //   { 1, 5, 4, 0, 1 },         // same index nodes of opposite faces are linked
169 //   { 2, 6, 7, 3, 2 }, 
170 //   { 0, 4, 7, 3, 0 }, 
171 //   { 1, 5, 6, 2, 1 }};
172 static int Hexa_FE [6][5] = { // FORWARD -> EXTERNAL
173   { 0, 1, 2, 3, 0 } ,         // opposite faces are neighbouring,
174   { 4, 7, 6, 5, 4 },          // all face normals are external,
175   { 0, 4, 5, 1, 0 },          // links in opposite faces: 0-0, 1-3, 2-2, 3-1
176   { 3, 2, 6, 7, 3 }, 
177   { 0, 3, 7, 4, 0 },
178   { 1, 5, 6, 2, 1 }};
179 static int Hexa_RE [6][5] = { // REVERSED -> EXTERNAL
180   { 0, 3, 2, 1, 0 },          // opposite faces are neighbouring,
181   { 4, 5, 6, 7, 4 },          // all face normals are external,
182   { 0, 1, 5, 4, 0 },          // links in opposite faces: 0-0, 1-3, 2-2, 3-1
183   { 3, 7, 6, 2, 3 }, 
184   { 0, 4, 7, 3, 0 },
185   { 1, 2, 6, 5, 1 }};
186 static int Hexa_nbN [] = { 4, 4, 4, 4, 4, 4 };
187
188 // ========================================================
189 // to perform some calculations without linkage to CASCADE
190 // ========================================================
191 struct XYZ {
192   double x;
193   double y;
194   double z;
195   XYZ()                               { x = 0; y = 0; z = 0; }
196   XYZ( double X, double Y, double Z ) { x = X; y = Y; z = Z; }
197   XYZ( const XYZ& other )             { x = other.x; y = other.y; z = other.z; }
198   XYZ( const SMDS_MeshNode* n )       { x = n->X(); y = n->Y(); z = n->Z(); }
199   XYZ operator-( const XYZ& other );
200   XYZ Crossed( const XYZ& other );
201   double Dot( const XYZ& other );
202   double Magnitude();
203 };
204 XYZ XYZ::operator-( const XYZ& Right ) {
205   return XYZ(x - Right.x, y - Right.y, z - Right.z);
206 }
207 XYZ XYZ::Crossed( const XYZ& Right ) {
208   return XYZ (y * Right.z - z * Right.y,
209               z * Right.x - x * Right.z,
210               x * Right.y - y * Right.x);
211 }
212 double XYZ::Dot( const XYZ& Other ) {
213   return(x * Other.x + y * Other.y + z * Other.z);
214 }
215 double XYZ::Magnitude() {
216   return sqrt (x * x + y * y + z * z);
217 }
218
219 //=======================================================================
220 //function : SMDS_VolumeTool
221 //purpose  : 
222 //=======================================================================
223
224 SMDS_VolumeTool::SMDS_VolumeTool ()
225      : myVolume( 0 ),
226        myPolyedre( 0 ),
227        myVolForward( true ),
228        myNbFaces( 0 ),
229        myVolumeNbNodes( 0 ),
230        myVolumeNodes( NULL ),
231        myExternalFaces( false ),
232        myFaceNbNodes( 0 ),
233        myCurFace( -1 ),
234        myFaceNodeIndices( NULL ),
235        myFaceNodes( NULL )
236 {
237 }
238
239 //=======================================================================
240 //function : SMDS_VolumeTool
241 //purpose  : 
242 //=======================================================================
243
244 SMDS_VolumeTool::SMDS_VolumeTool (const SMDS_MeshElement* theVolume)
245      : myVolume( 0 ),
246        myPolyedre( 0 ),
247        myVolForward( true ),
248        myNbFaces( 0 ),
249        myVolumeNbNodes( 0 ),
250        myVolumeNodes( NULL ),
251        myExternalFaces( false ),
252        myFaceNbNodes( 0 ),
253        myCurFace( -1 ),
254        myFaceNodeIndices( NULL ),
255        myFaceNodes( NULL )
256 {
257   Set( theVolume );
258 }
259
260 //=======================================================================
261 //function : SMDS_VolumeTool
262 //purpose  : 
263 //=======================================================================
264
265 SMDS_VolumeTool::~SMDS_VolumeTool()
266 {
267   if (myVolumeNodes != NULL) {
268     delete [] myVolumeNodes;
269     myVolumeNodes = NULL;
270   }
271   if (myFaceNodes != NULL) {
272     delete [] myFaceNodes;
273     myFaceNodes = NULL;
274   }
275 }
276
277 //=======================================================================
278 //function : SetVolume
279 //purpose  : Set volume to iterate on
280 //=======================================================================
281
282 bool SMDS_VolumeTool::Set (const SMDS_MeshElement* theVolume)
283 {
284   myVolume = 0;
285   myPolyedre = 0;
286
287   myVolForward = true;
288   myNbFaces = 0;
289   myVolumeNbNodes = 0;
290   if (myVolumeNodes != NULL) {
291     delete [] myVolumeNodes;
292     myVolumeNodes = NULL;
293   }
294
295   myExternalFaces = false;
296   myFaceNbNodes = 0;
297
298   myCurFace = -1;
299   myFaceNodeIndices = NULL;
300   if (myFaceNodes != NULL) {
301     delete [] myFaceNodes;
302     myFaceNodes = NULL;
303   }
304
305   if ( theVolume && theVolume->GetType() == SMDSAbs_Volume )
306   {
307     myVolume = theVolume;
308
309     myNbFaces = theVolume->NbFaces();
310     myVolumeNbNodes = theVolume->NbNodes();
311
312     // set volume nodes
313     int iNode = 0;
314     myVolumeNodes = new const SMDS_MeshNode* [myVolumeNbNodes];
315     SMDS_ElemIteratorPtr nodeIt = myVolume->nodesIterator();
316     while ( nodeIt->more() ) {
317       myVolumeNodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
318     }
319
320     if (myVolume->IsPoly()) {
321       myPolyedre = static_cast<const SMDS_PolyhedralVolumeOfNodes*>( myVolume );
322       if (!myPolyedre) {
323         MESSAGE("Warning: bad volumic element");
324         return false;
325       }
326     } else {
327       switch ( myVolumeNbNodes ) {
328       case 4:
329       case 5:
330       case 6:
331       case 8: {
332         // define volume orientation
333         XYZ botNormal;
334         GetFaceNormal( 0, botNormal.x, botNormal.y, botNormal.z );
335         const SMDS_MeshNode* topNode = myVolumeNodes[ myVolumeNbNodes - 1 ];
336         const SMDS_MeshNode* botNode = myVolumeNodes[ 0 ];
337         XYZ upDir (topNode->X() - botNode->X(),
338                    topNode->Y() - botNode->Y(),
339                    topNode->Z() - botNode->Z() );
340         myVolForward = ( botNormal.Dot( upDir ) < 0 );
341         break;
342       }
343       default:
344         break;
345       }
346     }
347   }
348   return ( myVolume != 0 );
349 }
350
351 //=======================================================================
352 //function : Inverse
353 //purpose  : Inverse volume
354 //=======================================================================
355
356 #define SWAP_NODES(nodes,i1,i2)           \
357 {                                         \
358   const SMDS_MeshNode* tmp = nodes[ i1 ]; \
359   nodes[ i1 ] = nodes[ i2 ];              \
360   nodes[ i2 ] = tmp;                      \
361 }
362 void SMDS_VolumeTool::Inverse ()
363 {
364   if ( !myVolume ) return;
365
366   if (myVolume->IsPoly()) {
367     MESSAGE("Warning: attempt to inverse polyhedral volume");
368     return;
369   }
370
371   myVolForward = !myVolForward;
372   myCurFace = -1;
373
374   // inverse top and bottom faces
375   switch ( myVolumeNbNodes ) {
376   case 4:
377     SWAP_NODES( myVolumeNodes, 1, 2 );
378     break;
379   case 5:
380     SWAP_NODES( myVolumeNodes, 1, 3 );
381     break;
382   case 6:
383     SWAP_NODES( myVolumeNodes, 1, 2 );
384     SWAP_NODES( myVolumeNodes, 4, 5 );
385     break;
386   case 8:
387     SWAP_NODES( myVolumeNodes, 1, 3 );
388     SWAP_NODES( myVolumeNodes, 5, 7 );
389     break;
390   default:;
391   }
392 }
393
394 //=======================================================================
395 //function : GetVolumeType
396 //purpose  : 
397 //=======================================================================
398
399 SMDS_VolumeTool::VolumeType SMDS_VolumeTool::GetVolumeType() const
400 {
401   if ( myPolyedre )
402     return POLYHEDA;
403
404   if ( myVolume ) {
405     static const VolumeType types[] = {
406       TETRA,     // myVolumeNbNodes = 4
407       PYRAM,     // myVolumeNbNodes = 5
408       PENTA,     // myVolumeNbNodes = 6
409       UNKNOWN,   // myVolumeNbNodes = 7
410       HEXA       // myVolumeNbNodes = 8
411     };
412     return types[ myVolumeNbNodes - 4 ];
413   }
414
415   return UNKNOWN;
416 }
417
418 //=======================================================================
419 //function : getTetraVolume
420 //purpose  : 
421 //=======================================================================
422
423 static double getTetraVolume(const SMDS_MeshNode* n1,
424                              const SMDS_MeshNode* n2,
425                              const SMDS_MeshNode* n3,
426                              const SMDS_MeshNode* n4)
427 {
428   double X1 = n1->X();
429   double Y1 = n1->Y();
430   double Z1 = n1->Z();
431
432   double X2 = n2->X();
433   double Y2 = n2->Y();
434   double Z2 = n2->Z();
435
436   double X3 = n3->X();
437   double Y3 = n3->Y();
438   double Z3 = n3->Z();
439
440   double X4 = n4->X();
441   double Y4 = n4->Y();
442   double Z4 = n4->Z();
443
444   double Q1 = -(X1-X2)*(Y3*Z4-Y4*Z3);
445   double Q2 =  (X1-X3)*(Y2*Z4-Y4*Z2);
446   double R1 = -(X1-X4)*(Y2*Z3-Y3*Z2);
447   double R2 = -(X2-X3)*(Y1*Z4-Y4*Z1);
448   double S1 =  (X2-X4)*(Y1*Z3-Y3*Z1);
449   double S2 = -(X3-X4)*(Y1*Z2-Y2*Z1);
450
451   return (Q1+Q2+R1+R2+S1+S2)/6.0;
452 }
453
454 //=======================================================================
455 //function : GetSize
456 //purpose  : Return element volume
457 //=======================================================================
458
459 double SMDS_VolumeTool::GetSize() const
460 {
461   double V = 0.;
462   if ( !myVolume )
463     return 0.;
464
465   if ( myVolume->IsPoly() )
466   {
467     if ( !myPolyedre )
468       return 0.;
469
470     // split a polyhedron into tetrahedrons
471
472     SMDS_VolumeTool* me = const_cast< SMDS_VolumeTool* > ( this );
473     XYZ baryCenter;
474     me->GetBaryCenter(baryCenter.x, baryCenter.y, baryCenter.z);
475     SMDS_MeshNode bcNode ( baryCenter.x, baryCenter.y, baryCenter.z );
476
477     for ( int f = 0; f < NbFaces(); ++f )
478     {
479       bool externalFace = me->IsFaceExternal( f ); // it calls setFace()
480       for ( int n = 2; n < myFaceNbNodes; ++n )
481       {
482         double Vn = getTetraVolume( myFaceNodes[ 0 ],
483                                     myFaceNodes[ n-1 ],
484                                     myFaceNodes[ n ],
485                                     & bcNode );
486 ///         cout <<"++++   " << Vn << "   nodes " <<myFaceNodes[ 0 ]->GetID() << " " <<myFaceNodes[ n-1 ]->GetID() << " " <<myFaceNodes[ n ]->GetID() << "        < " << V << endl;
487         V += externalFace ? -Vn : Vn;
488       }
489     }
490   }
491   else 
492   {
493     const static int ind[] = {
494       0, 1, 3, 6, 11 };
495     const static int vtab[][4] = {
496       // tetrahedron
497       { 0, 1, 2, 3 },
498       // pyramid
499       { 0, 1, 3, 4 },
500       { 1, 2, 3, 4 },
501       // pentahedron
502       { 0, 1, 2, 3 },
503       { 1, 5, 3, 4 },
504       { 1, 5, 2, 3 },
505       // hexahedron
506       { 1, 4, 3, 0 },
507       { 4, 1, 6, 5 },
508       { 1, 3, 6, 2 },
509       { 4, 6, 3, 7 },
510       { 1, 4, 6, 3 }
511     };
512
513     int type = GetVolumeType();
514     int n1 = ind[type];
515     int n2 = ind[type+1];
516
517     for (int i = n1; i <  n2; i++)
518     {
519       V -= getTetraVolume( myVolumeNodes[ vtab[i][0] ],
520                            myVolumeNodes[ vtab[i][1] ],
521                            myVolumeNodes[ vtab[i][2] ],
522                            myVolumeNodes[ vtab[i][3] ]);
523     }
524   }
525   return V;
526 }
527
528 //=======================================================================
529 //function : GetBaryCenter
530 //purpose  : 
531 //=======================================================================
532
533 bool SMDS_VolumeTool::GetBaryCenter(double & X, double & Y, double & Z) const
534 {
535   X = Y = Z = 0.;
536   if ( !myVolume )
537     return false;
538
539   for ( int i = 0; i < myVolumeNbNodes; i++ ) {
540     X += myVolumeNodes[ i ]->X();
541     Y += myVolumeNodes[ i ]->Y();
542     Z += myVolumeNodes[ i ]->Z();
543   }
544   X /= myVolumeNbNodes;
545   Y /= myVolumeNbNodes;
546   Z /= myVolumeNbNodes;
547
548   return true;
549 }
550
551 //=======================================================================
552 //function : SetExternalNormal
553 //purpose  : Node order will be so that faces normals are external
554 //=======================================================================
555
556 void SMDS_VolumeTool::SetExternalNormal ()
557 {
558   myExternalFaces = true;
559   myCurFace = -1;
560 }
561
562 //=======================================================================
563 //function : NbFaceNodes
564 //purpose  : Return number of nodes in the array of face nodes
565 //=======================================================================
566
567 int SMDS_VolumeTool::NbFaceNodes( int faceIndex )
568 {
569     if ( !setFace( faceIndex ))
570       return 0;
571     return myFaceNbNodes;
572 }
573
574 //=======================================================================
575 //function : GetFaceNodes
576 //purpose  : Return pointer to the array of face nodes.
577 //           To comfort link iteration, the array
578 //           length == NbFaceNodes( faceIndex ) + 1 and
579 //           the last node == the first one.
580 //=======================================================================
581
582 const SMDS_MeshNode** SMDS_VolumeTool::GetFaceNodes( int faceIndex )
583 {
584   if ( !setFace( faceIndex ))
585     return 0;
586   return myFaceNodes;
587 }
588
589 //=======================================================================
590 //function : GetFaceNodesIndices
591 //purpose  : Return pointer to the array of face nodes indices
592 //           To comfort link iteration, the array
593 //           length == NbFaceNodes( faceIndex ) + 1 and
594 //           the last node index == the first one.
595 //=======================================================================
596
597 const int* SMDS_VolumeTool::GetFaceNodesIndices( int faceIndex )
598 {
599   if (myVolume->IsPoly()) {
600     MESSAGE("Warning: attempt to obtain FaceNodesIndices of polyhedral volume");
601     return NULL;
602   }
603   if ( !setFace( faceIndex ))
604     return 0;
605   return myFaceNodeIndices;
606 }
607
608 //=======================================================================
609 //function : GetFaceNodes
610 //purpose  : Return a set of face nodes.
611 //=======================================================================
612
613 bool SMDS_VolumeTool::GetFaceNodes (int                        faceIndex,
614                                     set<const SMDS_MeshNode*>& theFaceNodes )
615 {
616   if ( !setFace( faceIndex ))
617     return false;
618
619   theFaceNodes.clear();
620   int iNode, nbNode = myFaceNbNodes;
621   for ( iNode = 0; iNode < nbNode; iNode++ )
622     theFaceNodes.insert( myFaceNodes[ iNode ]);
623
624   return true;
625 }
626
627 //=======================================================================
628 //function : IsFaceExternal
629 //purpose  : Check normal orientation of a returned face
630 //=======================================================================
631
632 bool SMDS_VolumeTool::IsFaceExternal( int faceIndex )
633 {
634   if ( myExternalFaces || !myVolume )
635     return true;
636
637   if (myVolume->IsPoly()) {
638     XYZ aNormal, baryCenter, p0 (myPolyedre->GetFaceNode(faceIndex + 1, 1));
639     GetFaceNormal(faceIndex, aNormal.x, aNormal.y, aNormal.z);
640     GetBaryCenter(baryCenter.x, baryCenter.y, baryCenter.z);
641     XYZ insideVec (baryCenter - p0);
642     if (insideVec.Dot(aNormal) > 0)
643       return false;
644     return true;
645   }
646
647   switch ( myVolumeNbNodes ) {
648   case 4:
649   case 5:
650     // only the bottom of a reversed tetrahedron can be internal
651     return ( myVolForward || faceIndex != 0 );
652   case 6:
653     // in a forward pentahedron, the top is internal, in a reversed one - bottom
654     return ( myVolForward ? faceIndex != 1 : faceIndex != 0 );
655   case 8: {
656     // in a forward hexahedron, even face normal is external, odd - internal
657     bool odd = faceIndex % 2;
658     return ( myVolForward ? !odd : odd );
659   }
660   default:;
661   }
662   return false;
663 }
664
665 //=======================================================================
666 //function : GetFaceNormal
667 //purpose  : Return a normal to a face
668 //=======================================================================
669
670 bool SMDS_VolumeTool::GetFaceNormal (int faceIndex, double & X, double & Y, double & Z)
671 {
672   if ( !setFace( faceIndex ))
673     return false;
674
675   XYZ p1 ( myFaceNodes[0] );
676   XYZ p2 ( myFaceNodes[1] );
677   XYZ p3 ( myFaceNodes[2] );
678   XYZ aVec12( p2 - p1 );
679   XYZ aVec13( p3 - p1 );
680   XYZ cross = aVec12.Crossed( aVec13 );
681
682   if ( myFaceNbNodes == 4 ) {
683     XYZ p4 ( myFaceNodes[3] );
684     XYZ aVec14( p4 - p1 );
685     XYZ cross2 = aVec13.Crossed( aVec14 );
686     cross.x += cross2.x;
687     cross.y += cross2.y;
688     cross.z += cross2.z;    
689   }
690
691   double size = cross.Magnitude();
692   if ( size <= DBL_MIN )
693     return false;
694
695   X = cross.x / size;
696   Y = cross.y / size;
697   Z = cross.z / size;
698
699   return true;
700 }
701
702 //=======================================================================
703 //function : GetFaceArea
704 //purpose  : Return face area
705 //=======================================================================
706
707 double SMDS_VolumeTool::GetFaceArea( int faceIndex )
708 {
709   if (myVolume->IsPoly()) {
710     MESSAGE("Warning: attempt to obtain area of a face of polyhedral volume");
711     return 0;
712   }
713
714   if ( !setFace( faceIndex ))
715     return 0;
716
717   XYZ p1 ( myFaceNodes[0] );
718   XYZ p2 ( myFaceNodes[1] );
719   XYZ p3 ( myFaceNodes[2] );
720   XYZ aVec12( p2 - p1 );
721   XYZ aVec13( p3 - p1 );
722   double area = aVec12.Crossed( aVec13 ).Magnitude() * 0.5;
723
724   if ( myFaceNbNodes == 4 ) {
725     XYZ p4 ( myFaceNodes[3] );
726     XYZ aVec14( p4 - p1 );
727     area += aVec14.Crossed( aVec13 ).Magnitude() * 0.5;
728   }
729   return area;
730 }
731
732 //=======================================================================
733 //function : GetOppFaceIndex
734 //purpose  : Return index of the opposite face if it exists, else -1.
735 //=======================================================================
736
737 int SMDS_VolumeTool::GetOppFaceIndex( int faceIndex ) const
738 {
739   int ind = -1;
740   if (myVolume->IsPoly()) {
741     MESSAGE("Warning: attempt to obtain opposite face on polyhedral volume");
742     return ind;
743   }
744
745   if ( faceIndex >= 0 && faceIndex < NbFaces() ) {
746     switch ( myVolumeNbNodes ) {
747     case 6:
748       if ( faceIndex == 0 || faceIndex == 1 )
749         ind = 1 - faceIndex;
750         break;
751     case 8:
752       ind = faceIndex + ( faceIndex % 2 ? -1 : 1 );
753       break;
754     default:;
755     }
756   }
757   return ind;
758 }
759
760 //=======================================================================
761 //function : IsLinked
762 //purpose  : return true if theNode1 is linked with theNode2
763 //=======================================================================
764
765 bool SMDS_VolumeTool::IsLinked (const SMDS_MeshNode* theNode1,
766                                 const SMDS_MeshNode* theNode2) const
767 {
768   if ( !myVolume )
769     return false;
770
771   if (myVolume->IsPoly()) {
772     if (!myPolyedre) {
773       MESSAGE("Warning: bad volumic element");
774       return false;
775     }
776     bool isLinked = false;
777     int iface;
778     for (iface = 1; iface <= myNbFaces && !isLinked; iface++) {
779       int inode, nbFaceNodes = myPolyedre->NbFaceNodes(iface);
780
781       for (inode = 1; inode <= nbFaceNodes && !isLinked; inode++) {
782         const SMDS_MeshNode* curNode = myPolyedre->GetFaceNode(iface, inode);
783
784         if (curNode == theNode1 || curNode == theNode2) {
785           int inextnode = (inode == nbFaceNodes) ? 1 : inode + 1;
786           const SMDS_MeshNode* nextNode = myPolyedre->GetFaceNode(iface, inextnode);
787
788           if ((curNode == theNode1 && nextNode == theNode2) ||
789               (curNode == theNode2 && nextNode == theNode1)) {
790             isLinked = true;
791           }
792         }
793       }
794     }
795     return isLinked;
796   }
797
798   // find nodes indices
799   int i1 = -1, i2 = -1;
800   for ( int i = 0; i < myVolumeNbNodes; i++ ) {
801     if ( myVolumeNodes[ i ] == theNode1 )
802       i1 = i;
803     else if ( myVolumeNodes[ i ] == theNode2 )
804       i2 = i;
805   }
806   return IsLinked( i1, i2 );
807 }
808
809 //=======================================================================
810 //function : IsLinked
811 //purpose  : return true if the node with theNode1Index is linked
812 //           with the node with theNode2Index
813 //=======================================================================
814
815 bool SMDS_VolumeTool::IsLinked (const int theNode1Index,
816                                 const int theNode2Index) const
817 {
818   if (myVolume->IsPoly()) {
819     return IsLinked(myVolumeNodes[theNode1Index], myVolumeNodes[theNode2Index]);
820   }
821
822   int minInd = theNode1Index < theNode2Index ? theNode1Index : theNode2Index;
823   int maxInd = theNode1Index < theNode2Index ? theNode2Index : theNode1Index;
824
825   if ( minInd < 0 || maxInd > myVolumeNbNodes - 1 || maxInd == minInd )
826     return false;
827
828   switch ( myVolumeNbNodes ) {
829   case 4:
830     return true;
831   case 5:
832     if ( maxInd == 4 )
833       return true;
834     switch ( maxInd - minInd ) {
835     case 1:
836     case 3: return true;
837     default:;
838     }
839     break;
840   case 6:
841     switch ( maxInd - minInd ) {
842     case 1: return minInd != 2;
843     case 2: return minInd == 0 || minInd == 3;
844     case 3: return true;
845     default:;
846     }
847     break;
848   case 8:
849     switch ( maxInd - minInd ) {
850     case 1: return minInd != 3;
851     case 3: return minInd == 0 || minInd == 4;
852     case 4: return true;
853     default:;
854     }
855     break;
856   default:;
857   }
858   return false;
859 }
860
861 //=======================================================================
862 //function : GetNodeIndex
863 //purpose  : Return an index of theNode
864 //=======================================================================
865
866 int SMDS_VolumeTool::GetNodeIndex(const SMDS_MeshNode* theNode) const
867 {
868   if ( myVolume ) {
869     for ( int i = 0; i < myVolumeNbNodes; i++ ) {
870       if ( myVolumeNodes[ i ] == theNode )
871         return i;
872     }
873   }
874   return -1;
875 }
876
877 //=======================================================================
878 //function : IsFreeFace
879 //purpose  : check that only one volume is build on the face nodes
880 //=======================================================================
881
882 bool SMDS_VolumeTool::IsFreeFace( int faceIndex )
883 {
884   const int free = true;
885
886   if (!setFace( faceIndex ))
887     return !free;
888
889   const SMDS_MeshNode** nodes = GetFaceNodes( faceIndex );
890   int nbFaceNodes = myFaceNbNodes;
891
892   // evaluate nb of face nodes shared by other volume
893   int maxNbShared = -1;
894   typedef map< const SMDS_MeshElement*, int > TElemIntMap;
895   TElemIntMap volNbShared;
896   TElemIntMap::iterator vNbIt;
897   for ( int iNode = 0; iNode < nbFaceNodes; iNode++ )
898   {
899     const SMDS_MeshNode* n = nodes[ iNode ];
900     SMDS_ElemIteratorPtr eIt = n->GetInverseElementIterator();
901     while ( eIt->more() ) {
902       const SMDS_MeshElement* elem = eIt->next();
903       if ( elem != myVolume && elem->GetType() == SMDSAbs_Volume ) {
904         int nbShared = 1;
905         vNbIt = volNbShared.find( elem );
906         if ( vNbIt == volNbShared.end() )
907           volNbShared.insert ( TElemIntMap::value_type( elem, nbShared ));
908         else
909           nbShared = ++(*vNbIt).second;
910         if ( nbShared > maxNbShared )
911           maxNbShared = nbShared;
912       }
913     }
914   }
915   if ( maxNbShared < 3 )
916     return free; // is free
917
918   // find volumes laying on the opposite side of the face
919   // and sharing all nodes
920   XYZ intNormal; // internal normal
921   GetFaceNormal( faceIndex, intNormal.x, intNormal.y, intNormal.z );
922   if ( IsFaceExternal( faceIndex ))
923     intNormal = XYZ( -intNormal.x, -intNormal.y, -intNormal.z );
924   XYZ p0 ( nodes[0] ), baryCenter;
925   for ( vNbIt = volNbShared.begin(); vNbIt != volNbShared.end(); vNbIt++ )
926   {
927     int nbShared = (*vNbIt).second;
928     if ( nbShared >= 3 ) {
929       SMDS_VolumeTool volume( (*vNbIt).first );
930       volume.GetBaryCenter( baryCenter.x, baryCenter.y, baryCenter.z );
931       XYZ intNormal2( baryCenter - p0 );
932       if ( intNormal.Dot( intNormal2 ) < 0 )
933         continue; // opposite side
934     }
935     // remove a volume from volNbShared map
936     volNbShared.erase( vNbIt );
937   }
938   // here volNbShared contains only volumes laying on the
939   // opposite side of the face
940   if ( volNbShared.empty() )
941     return free; // is free
942
943   // check if the whole area of a face is shared
944   bool isShared[] = { false, false, false, false }; // 4 triangle parts of a quadrangle
945   for ( vNbIt = volNbShared.begin(); vNbIt != volNbShared.end(); vNbIt++ )
946   {
947     SMDS_VolumeTool volume( (*vNbIt).first );
948     bool prevLinkShared = false;
949     int nbSharedLinks = 0;
950     for ( int iNode = 0; iNode < nbFaceNodes; iNode++ )
951     {
952       bool linkShared = volume.IsLinked( nodes[ iNode ], nodes[ iNode + 1] );
953       if ( linkShared )
954         nbSharedLinks++;
955       if ( linkShared && prevLinkShared &&
956           volume.IsLinked( nodes[ iNode - 1 ], nodes[ iNode + 1] ))
957         isShared[ iNode ] = true;
958       prevLinkShared = linkShared;
959     }
960     if ( nbSharedLinks == nbFaceNodes )
961       return !free; // is not free
962     if ( nbFaceNodes == 4 ) {
963       // check traingle parts 1 & 3
964       if ( isShared[1] && isShared[3] )
965         return !free; // is not free
966       // check triangle parts 0 & 2;
967       // 0 part could not be checked in the loop; check it here
968       if ( isShared[2] && prevLinkShared &&
969           volume.IsLinked( nodes[ 0 ], nodes[ 1 ] ) &&
970           volume.IsLinked( nodes[ 1 ], nodes[ 3 ] ) )
971         return !free; // is not free
972     }
973   }
974   return free;
975 }
976
977 //=======================================================================
978 //function : GetFaceIndex
979 //purpose  : Return index of a face formed by theFaceNodes
980 //=======================================================================
981
982 int SMDS_VolumeTool::GetFaceIndex( const set<const SMDS_MeshNode*>& theFaceNodes )
983 {
984   for ( int iFace = 0; iFace < myNbFaces; iFace++ ) {
985     const SMDS_MeshNode** nodes = GetFaceNodes( iFace );
986     int nbFaceNodes = NbFaceNodes( iFace );
987     set<const SMDS_MeshNode*> nodeSet;
988     for ( int iNode = 0; iNode < nbFaceNodes; iNode++ )
989       nodeSet.insert( nodes[ iNode ] );
990     if ( theFaceNodes == nodeSet )
991       return iFace;
992   }
993   return -1;
994 }
995
996 //=======================================================================
997 //function : GetFaceIndex
998 //purpose  : Return index of a face formed by theFaceNodes
999 //=======================================================================
1000
1001 /*int SMDS_VolumeTool::GetFaceIndex( const set<int>& theFaceNodesIndices )
1002 {
1003   for ( int iFace = 0; iFace < myNbFaces; iFace++ ) {
1004     const int* nodes = GetFaceNodesIndices( iFace );
1005     int nbFaceNodes = NbFaceNodes( iFace );
1006     set<int> nodeSet;
1007     for ( int iNode = 0; iNode < nbFaceNodes; iNode++ )
1008       nodeSet.insert( nodes[ iNode ] );
1009     if ( theFaceNodesIndices == nodeSet )
1010       return iFace;
1011   }
1012   return -1;
1013 }*/
1014
1015 //=======================================================================
1016 //function : setFace
1017 //purpose  : 
1018 //=======================================================================
1019
1020 bool SMDS_VolumeTool::setFace( int faceIndex )
1021 {
1022   if ( !myVolume )
1023     return false;
1024
1025   if ( myCurFace == faceIndex )
1026     return true;
1027
1028   myCurFace = -1;
1029
1030   if ( faceIndex < 0 || faceIndex >= NbFaces() )
1031     return false;
1032
1033   if (myFaceNodes != NULL) {
1034     delete [] myFaceNodes;
1035     myFaceNodes = NULL;
1036   }
1037
1038   if (myVolume->IsPoly()) {
1039     if (!myPolyedre) {
1040       MESSAGE("Warning: bad volumic element");
1041       return false;
1042     }
1043
1044     // check orientation
1045     bool isGoodOri = true;
1046     if (myExternalFaces)
1047       isGoodOri = IsFaceExternal( faceIndex );
1048
1049     // set face nodes
1050     int iNode;
1051     myFaceNbNodes = myPolyedre->NbFaceNodes(faceIndex + 1);
1052     myFaceNodes = new const SMDS_MeshNode* [myFaceNbNodes + 1];
1053     if (isGoodOri) {
1054       for ( iNode = 0; iNode < myFaceNbNodes; iNode++ )
1055         myFaceNodes[ iNode ] = myPolyedre->GetFaceNode(faceIndex + 1, iNode + 1);
1056     } else {
1057       for ( iNode = 0; iNode < myFaceNbNodes; iNode++ )
1058         myFaceNodes[ iNode ] = myPolyedre->GetFaceNode(faceIndex + 1, myFaceNbNodes - iNode);
1059     }
1060     myFaceNodes[ myFaceNbNodes ] = myFaceNodes[ 0 ]; // last = first
1061
1062   } else {
1063     // choose face node indices
1064     switch ( myVolumeNbNodes ) {
1065     case 4:
1066       myFaceNbNodes = Tetra_nbN[ faceIndex ];
1067       if ( myExternalFaces )
1068         myFaceNodeIndices = myVolForward ? Tetra_F[ faceIndex ] : Tetra_RE[ faceIndex ];
1069       else
1070         myFaceNodeIndices = myVolForward ? Tetra_F[ faceIndex ] : Tetra_R[ faceIndex ];
1071       break;
1072     case 5:
1073       myFaceNbNodes = Pyramid_nbN[ faceIndex ];
1074       if ( myExternalFaces )
1075         myFaceNodeIndices = myVolForward ? Pyramid_F[ faceIndex ] : Pyramid_RE[ faceIndex ];
1076       else
1077         myFaceNodeIndices = myVolForward ? Pyramid_F[ faceIndex ] : Pyramid_R[ faceIndex ];
1078       break;
1079     case 6:
1080       myFaceNbNodes = Penta_nbN[ faceIndex ];
1081       if ( myExternalFaces )
1082         myFaceNodeIndices = myVolForward ? Penta_FE[ faceIndex ] : Penta_RE[ faceIndex ];
1083       else
1084         myFaceNodeIndices = myVolForward ? Penta_F[ faceIndex ] : Penta_R[ faceIndex ];
1085       break;
1086     case 8:
1087       myFaceNbNodes = Hexa_nbN[ faceIndex ];
1088       if ( myExternalFaces )
1089         myFaceNodeIndices = myVolForward ? Hexa_FE[ faceIndex ] : Hexa_RE[ faceIndex ];
1090       else
1091         myFaceNodeIndices = Hexa_F[ faceIndex ];
1092       break;
1093     default:
1094       return false;
1095     }
1096
1097     // set face nodes
1098     myFaceNodes = new const SMDS_MeshNode* [myFaceNbNodes + 1];
1099     for ( int iNode = 0; iNode < myFaceNbNodes; iNode++ )
1100       myFaceNodes[ iNode ] = myVolumeNodes[ myFaceNodeIndices[ iNode ]];
1101     myFaceNodes[ myFaceNbNodes ] = myFaceNodes[ 0 ];
1102   }
1103
1104   myCurFace = faceIndex;
1105
1106   return true;
1107 }
1108
1109 //=======================================================================
1110 //function : GetType
1111 //purpose  : return VolumeType by nb of nodes in a volume
1112 //=======================================================================
1113
1114 SMDS_VolumeTool::VolumeType SMDS_VolumeTool::GetType(int nbNodes)
1115 {
1116   switch ( nbNodes ) {
1117   case 4: return TETRA;
1118   case 5: return PYRAM;
1119   case 6: return PENTA;
1120   case 8: return HEXA;
1121   default:return UNKNOWN;
1122   }
1123 }
1124
1125 //=======================================================================
1126 //function : NbFaces
1127 //purpose  : return nb of faces by volume type
1128 //=======================================================================
1129
1130 int SMDS_VolumeTool::NbFaces( VolumeType type )
1131 {
1132   switch ( type ) {
1133   case TETRA: return 4;
1134   case PYRAM: return 5;
1135   case PENTA: return 5;
1136   case HEXA : return 6;
1137   default:    return 0;
1138   }
1139 }
1140
1141 //=======================================================================
1142 //function : GetFaceNodesIndices
1143 //purpose  : Return the array of face nodes indices
1144 //           To comfort link iteration, the array
1145 //           length == NbFaceNodes( faceIndex ) + 1 and
1146 //           the last node index == the first one.
1147 //=======================================================================
1148
1149 const int* SMDS_VolumeTool::GetFaceNodesIndices(VolumeType type,
1150                                                 int        faceIndex,
1151                                                 bool       external)
1152 {
1153   switch ( type ) {
1154   case TETRA: return Tetra_F[ faceIndex ];
1155   case PYRAM: return Pyramid_F[ faceIndex ];
1156   case PENTA: return external ? Penta_FE[ faceIndex ] : Penta_F[ faceIndex ];
1157   case HEXA:  return external ? Hexa_FE[ faceIndex ] : Hexa_F[ faceIndex ];
1158   default:;
1159   }
1160   return 0;
1161 }
1162
1163 //=======================================================================
1164 //function : NbFaceNodes
1165 //purpose  : Return number of nodes in the array of face nodes
1166 //=======================================================================
1167
1168 int SMDS_VolumeTool::NbFaceNodes(VolumeType type,
1169                                  int        faceIndex )
1170 {
1171   switch ( type ) {
1172   case TETRA: return Tetra_nbN[ faceIndex ];
1173   case PYRAM: return Pyramid_nbN[ faceIndex ];
1174   case PENTA: return Penta_nbN[ faceIndex ];
1175   case HEXA:  return Hexa_nbN[ faceIndex ];
1176   default:;
1177   }
1178   return 0;
1179 }
1180