1 // Copyright (C) 2007-2016 CEA/DEN, EDF R&D, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
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.
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.
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
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 // File : SMDS_VolumeTool.cxx
24 // Created : Tue Jul 13 12:22:13 2004
25 // Author : Edward AGAPOV (eap)
28 #pragma warning(disable:4786)
31 #include "SMDS_VolumeTool.hxx"
33 #include "SMDS_MeshElement.hxx"
34 #include "SMDS_MeshNode.hxx"
35 #include "SMDS_VtkVolume.hxx"
36 #include "SMDS_Mesh.hxx"
38 #include "utilities.h"
50 // ======================================================
51 // Node indices in faces depending on volume orientation
52 // making most faces normals external
53 // ======================================================
54 // For all elements, 0-th face is bottom based on the first nodes.
55 // For prismatic elements (tetra,hexa,prisms), 1-th face is a top one.
56 // For all elements, side faces follow order of bottom nodes
57 // ======================================================
65 // N0 +---|---+ N1 TETRAHEDRON
73 static int Tetra_F [4][4] = { // FORWARD == EXTERNAL
74 { 0, 1, 2, 0 }, // All faces have external normals
78 static int Tetra_RE [4][4] = { // REVERSED -> FORWARD (EXTERNAL)
79 { 0, 2, 1, 0 }, // All faces have external normals
83 static int Tetra_nbN [] = { 3, 3, 3, 3 };
88 static int Pyramid_F [5][5] = { // FORWARD == EXTERNAL
89 { 0, 1, 2, 3, 0 }, // All faces have external normals
95 static int Pyramid_RE [5][5] = { // REVERSED -> FORWARD (EXTERNAL)
96 { 0, 3, 2, 1, 0 }, // All faces but a bottom have external normals
101 static int Pyramid_nbN [] = { 4, 3, 3, 3, 3 };
112 // | / \ | PENTAHEDRON
118 static int Penta_F [5][5] = { // FORWARD
119 { 0, 1, 2, 0, 0 }, // All faces have external normals
120 { 3, 5, 4, 3, 3 }, // 0 is bottom, 1 is top face
124 static int Penta_RE [5][5] = { // REVERSED -> EXTERNAL
130 static int Penta_nbN [] = { 3, 3, 4, 4, 4 };
137 // N4+----------+N7 |
138 // | | | | HEXAHEDRON
139 // | N1+------|---+N2
145 static int Hexa_F [6][5] = { // FORWARD
147 { 4, 7, 6, 5, 4 }, // all face normals are external
152 static int Hexa_RE [6][5] = { // REVERSED -> EXTERNAL
154 { 4, 5, 6, 7, 4 }, // all face normals are external
159 static int Hexa_nbN [] = { 4, 4, 4, 4, 4, 4 };
160 static int Hexa_oppF[] = { 1, 0, 4, 5, 2, 3 }; // oppopsite facet indices
179 static int HexPrism_F [8][7] = { // FORWARD
180 { 0, 1, 2, 3, 4, 5, 0 },
181 { 6,11,10, 9, 8, 7, 6 },
182 { 0, 6, 7, 1, 0, 0, 0 },
183 { 1, 7, 8, 2, 1, 1, 1 },
184 { 2, 8, 9, 3, 2, 2, 2 },
185 { 3, 9,10, 4, 3, 3, 3 },
186 { 4,10,11, 5, 4, 4, 4 },
187 { 5,11, 6, 0, 5, 5, 5 }};
188 static int HexPrism_RE [8][7] = { // REVERSED -> EXTERNAL
189 { 0, 5, 4, 3, 2, 1, 0 },
190 { 6,11,10, 9, 8, 7, 6 },
191 { 0, 6, 7, 1, 0, 0, 0 },
192 { 1, 7, 8, 2, 1, 1, 1 },
193 { 2, 8, 9, 3, 2, 2, 2 },
194 { 3, 9,10, 4, 3, 3, 3 },
195 { 4,10,11, 5, 4, 4, 4 },
196 { 5,11, 6, 0, 5, 5, 5 }};
197 static int HexPrism_nbN [] = { 6, 6, 4, 4, 4, 4, 4, 4 };
206 // N0 +---|---+ N1 TETRAHEDRON
214 static int QuadTetra_F [4][7] = { // FORWARD
215 { 0, 4, 1, 5, 2, 6, 0 }, // All faces have external normals
216 { 0, 7, 3, 8, 1, 4, 0 },
217 { 1, 8, 3, 9, 2, 5, 1 },
218 { 0, 6, 2, 9, 3, 7, 0 }};
219 static int QuadTetra_RE [4][7] = { // REVERSED -> FORWARD (EXTERNAL)
220 { 0, 6, 2, 5, 1, 4, 0 }, // All faces have external normals
221 { 0, 4, 1, 8, 3, 7, 0 },
222 { 1, 5, 2, 9, 3, 8, 1 },
223 { 0, 7, 3, 9, 2, 6, 0 }};
224 static int QuadTetra_nbN [] = { 6, 6, 6, 6 };
234 // | | 9 - middle point for (0,4) etc.
247 static int QuadPyram_F [5][9] = { // FORWARD
248 { 0, 5, 1, 6, 2, 7, 3, 8, 0 }, // All faces have external normals
249 { 0, 9, 4, 10,1, 5, 0, 4, 4 },
250 { 1, 10,4, 11,2, 6, 1, 4, 4 },
251 { 2, 11,4, 12,3, 7, 2, 4, 4 },
252 { 3, 12,4, 9, 0, 8, 3, 4, 4 }};
253 static int QuadPyram_RE [5][9] = { // REVERSED -> FORWARD (EXTERNAL)
254 { 0, 8, 3, 7, 2, 6, 1, 5, 0 }, // All faces but a bottom have external normals
255 { 0, 5, 1, 10,4, 9, 0, 4, 4 },
256 { 1, 6, 2, 11,4, 10,1, 4, 4 },
257 { 2, 7, 3, 12,4, 11,2, 4, 4 },
258 { 3, 8, 0, 9, 4, 12,3, 4, 4 }};
259 static int QuadPyram_nbN [] = { 8, 6, 6, 6, 6 };
283 static int QuadPenta_F [5][9] = { // FORWARD
284 { 0, 6, 1, 7, 2, 8, 0, 0, 0 },
285 { 3, 11,5, 10,4, 9, 3, 3, 3 },
286 { 0, 12,3, 9, 4, 13,1, 6, 0 },
287 { 1, 13,4, 10,5, 14,2, 7, 1 },
288 { 0, 8, 2, 14,5, 11,3, 12,0 }};
289 static int QuadPenta_RE [5][9] = { // REVERSED -> EXTERNAL
290 { 0, 8, 2, 7, 1, 6, 0, 0, 0 },
291 { 3, 9, 4, 10,5, 11,3, 3, 3 },
292 { 0, 6, 1, 13,4, 9, 3, 12,0 },
293 { 1, 7, 2, 14,5, 10,4, 13,1 },
294 { 0, 12,3, 11,5, 14,2, 8, 0 }};
295 static int QuadPenta_nbN [] = { 6, 6, 8, 8, 8 };
299 // N5+-----+-----+N6 +-----+-----+
301 // 12+ | 14+ | + | +25 + |
303 // N4+-----+-----+N7 | QUADRATIC +-----+-----+ | Central nodes
304 // | | 15 | | HEXAHEDRON | | | | of tri-quadratic
305 // | | | | | | | | HEXAHEDRON
306 // | 17+ | +18 | + 22+ | +
308 // | | | | | + | 26+ | + |
310 // 16+ | +19 | + | +24 + |
313 // | N1+-----+-|---+N2 | +-----+-|---+
315 // | +8 | +10 | + 20+ | +
317 // N0+-----+-----+N3 +-----+-----+
320 static int QuadHexa_F [6][9] = { // FORWARD
321 { 0, 8, 1, 9, 2, 10,3, 11,0 }, // all face normals are external,
322 { 4, 15,7, 14,6, 13,5, 12,4 },
323 { 0, 16,4, 12,5, 17,1, 8, 0 },
324 { 1, 17,5, 13,6, 18,2, 9, 1 },
325 { 3, 10,2, 18,6, 14,7, 19,3 },
326 { 0, 11,3, 19,7, 15,4, 16,0 }};
327 static int QuadHexa_RE [6][9] = { // REVERSED -> EXTERNAL
328 { 0, 11,3, 10,2, 9, 1, 8, 0 }, // all face normals are external
329 { 4, 12,5, 13,6, 14,7, 15,4 },
330 { 0, 8, 1, 17,5, 12,4, 16,0 },
331 { 1, 9, 2, 18,6, 13,5, 17,1 },
332 { 3, 19,7, 14,6, 18,2, 10,3 },
333 { 0, 16,4, 15,7, 19,3, 11,0 }};
334 static int QuadHexa_nbN [] = { 8, 8, 8, 8, 8, 8 };
336 static int TriQuadHexa_F [6][9] = { // FORWARD
337 { 0, 8, 1, 9, 2, 10,3, 11, 20 }, // all face normals are external
338 { 4, 15,7, 14,6, 13,5, 12, 25 },
339 { 0, 16,4, 12,5, 17,1, 8, 21 },
340 { 1, 17,5, 13,6, 18,2, 9, 22 },
341 { 3, 10,2, 18,6, 14,7, 19, 23 },
342 { 0, 11,3, 19,7, 15,4, 16, 24 }};
343 static int TriQuadHexa_RE [6][9] = { // REVERSED -> EXTERNAL
344 { 0, 11,3, 10,2, 9, 1, 8, 20 }, // opposite faces are neighbouring,
345 { 4, 12,5, 13,6, 14,7, 15, 25 }, // all face normals are external
346 { 0, 8, 1, 17,5, 12,4, 16, 21 },
347 { 1, 9, 2, 18,6, 13,5, 17, 22 },
348 { 3, 19,7, 14,6, 18,2, 10, 23 },
349 { 0, 16,4, 15,7, 19,3, 11, 24 }};
350 static int TriQuadHexa_nbN [] = { 9, 9, 9, 9, 9, 9 };
353 // ========================================================
354 // to perform some calculations without linkage to CASCADE
355 // ========================================================
360 XYZ() { x = 0; y = 0; z = 0; }
361 XYZ( double X, double Y, double Z ) { x = X; y = Y; z = Z; }
362 XYZ( const XYZ& other ) { x = other.x; y = other.y; z = other.z; }
363 XYZ( const SMDS_MeshNode* n ) { x = n->X(); y = n->Y(); z = n->Z(); }
364 inline XYZ operator-( const XYZ& other );
365 inline XYZ operator+( const XYZ& other );
366 inline XYZ Crossed( const XYZ& other );
367 inline double Dot( const XYZ& other );
368 inline double Magnitude();
369 inline double SquareMagnitude();
371 inline XYZ XYZ::operator-( const XYZ& Right ) {
372 return XYZ(x - Right.x, y - Right.y, z - Right.z);
374 inline XYZ XYZ::operator+( const XYZ& Right ) {
375 return XYZ(x + Right.x, y + Right.y, z + Right.z);
377 inline XYZ XYZ::Crossed( const XYZ& Right ) {
378 return XYZ (y * Right.z - z * Right.y,
379 z * Right.x - x * Right.z,
380 x * Right.y - y * Right.x);
382 inline double XYZ::Dot( const XYZ& Other ) {
383 return(x * Other.x + y * Other.y + z * Other.z);
385 inline double XYZ::Magnitude() {
386 return sqrt (x * x + y * y + z * z);
388 inline double XYZ::SquareMagnitude() {
389 return (x * x + y * y + z * z);
392 //================================================================================
394 * \brief Return linear type corresponding to a quadratic one
396 //================================================================================
398 SMDS_VolumeTool::VolumeType quadToLinear(SMDS_VolumeTool::VolumeType quadType)
400 SMDS_VolumeTool::VolumeType linType = SMDS_VolumeTool::VolumeType( int(quadType)-4 );
401 const int nbCornersByQuad = SMDS_VolumeTool::NbCornerNodes( quadType );
402 if ( SMDS_VolumeTool::NbCornerNodes( linType ) == nbCornersByQuad )
406 for ( ; iLin < SMDS_VolumeTool::NB_VOLUME_TYPES; ++iLin )
407 if ( SMDS_VolumeTool::NbCornerNodes( SMDS_VolumeTool::VolumeType( iLin )) == nbCornersByQuad)
408 return SMDS_VolumeTool::VolumeType( iLin );
410 return SMDS_VolumeTool::UNKNOWN;
415 //================================================================================
417 * \brief Saver/restorer of a SMDS_VolumeTool::myCurFace
419 //================================================================================
421 struct SMDS_VolumeTool::SaveFacet
423 SMDS_VolumeTool::Facet mySaved;
424 SMDS_VolumeTool::Facet& myToRestore;
425 SaveFacet( SMDS_VolumeTool::Facet& facet ): myToRestore( facet )
428 mySaved.myNodes.swap( facet.myNodes );
432 if ( myToRestore.myIndex != mySaved.myIndex )
433 myToRestore = mySaved;
434 myToRestore.myNodes.swap( mySaved.myNodes );
438 //=======================================================================
439 //function : SMDS_VolumeTool
441 //=======================================================================
443 SMDS_VolumeTool::SMDS_VolumeTool ()
448 //=======================================================================
449 //function : SMDS_VolumeTool
451 //=======================================================================
453 SMDS_VolumeTool::SMDS_VolumeTool (const SMDS_MeshElement* theVolume,
454 const bool ignoreCentralNodes)
456 Set( theVolume, ignoreCentralNodes );
459 //=======================================================================
460 //function : SMDS_VolumeTool
462 //=======================================================================
464 SMDS_VolumeTool::~SMDS_VolumeTool()
466 myCurFace.myNodeIndices = NULL;
469 //=======================================================================
470 //function : SetVolume
471 //purpose : Set volume to iterate on
472 //=======================================================================
474 bool SMDS_VolumeTool::Set (const SMDS_MeshElement* theVolume,
475 const bool ignoreCentralNodes)
480 myIgnoreCentralNodes = ignoreCentralNodes;
484 myVolumeNodes.clear();
485 myPolyIndices.clear();
486 myPolyQuantities.clear();
487 myPolyFacetOri.clear();
490 myExternalFaces = false;
492 myAllFacesNodeIndices_F = 0;
493 myAllFacesNodeIndices_RE = 0;
494 myAllFacesNbNodes = 0;
496 myCurFace.myIndex = -1;
497 myCurFace.myNodeIndices = NULL;
498 myCurFace.myNodes.clear();
501 if ( !theVolume || theVolume->GetType() != SMDSAbs_Volume )
504 myVolume = theVolume;
505 myNbFaces = theVolume->NbFaces();
506 if ( myVolume->IsPoly() )
508 myPolyedre = dynamic_cast<const SMDS_VtkVolume*>( myVolume );
509 myPolyFacetOri.resize( myNbFaces, 0 );
514 myVolumeNodes.resize( myVolume->NbNodes() );
515 SMDS_ElemIteratorPtr nodeIt = myVolume->nodesIterator();
516 while ( nodeIt->more() )
517 myVolumeNodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
521 return ( myVolume = 0 );
525 // define volume orientation
527 if ( GetFaceNormal( 0, botNormal.x, botNormal.y, botNormal.z ))
529 const SMDS_MeshNode* botNode = myVolumeNodes[ 0 ];
530 int topNodeIndex = myVolume->NbCornerNodes() - 1;
531 while ( !IsLinked( 0, topNodeIndex, /*ignoreMediumNodes=*/true )) --topNodeIndex;
532 const SMDS_MeshNode* topNode = myVolumeNodes[ topNodeIndex ];
533 XYZ upDir (topNode->X() - botNode->X(),
534 topNode->Y() - botNode->Y(),
535 topNode->Z() - botNode->Z() );
536 myVolForward = ( botNormal.Dot( upDir ) < 0 );
539 myCurFace.myIndex = -1; // previous setFace(0) didn't take myVolForward into account
544 //=======================================================================
546 //purpose : Inverse volume
547 //=======================================================================
549 #define SWAP_NODES(nodes,i1,i2) \
551 const SMDS_MeshNode* tmp = nodes[ i1 ]; \
552 nodes[ i1 ] = nodes[ i2 ]; \
555 void SMDS_VolumeTool::Inverse ()
557 if ( !myVolume ) return;
559 if (myVolume->IsPoly()) {
560 MESSAGE("Warning: attempt to inverse polyhedral volume");
564 myVolForward = !myVolForward;
565 myCurFace.myIndex = -1;
567 // inverse top and bottom faces
568 switch ( myVolumeNodes.size() ) {
570 SWAP_NODES( myVolumeNodes, 1, 2 );
573 SWAP_NODES( myVolumeNodes, 1, 3 );
576 SWAP_NODES( myVolumeNodes, 1, 2 );
577 SWAP_NODES( myVolumeNodes, 4, 5 );
580 SWAP_NODES( myVolumeNodes, 1, 3 );
581 SWAP_NODES( myVolumeNodes, 5, 7 );
584 SWAP_NODES( myVolumeNodes, 1, 5 );
585 SWAP_NODES( myVolumeNodes, 2, 4 );
586 SWAP_NODES( myVolumeNodes, 7, 11 );
587 SWAP_NODES( myVolumeNodes, 8, 10 );
591 SWAP_NODES( myVolumeNodes, 1, 2 );
592 SWAP_NODES( myVolumeNodes, 4, 6 );
593 SWAP_NODES( myVolumeNodes, 8, 9 );
596 SWAP_NODES( myVolumeNodes, 1, 3 );
597 SWAP_NODES( myVolumeNodes, 5, 8 );
598 SWAP_NODES( myVolumeNodes, 6, 7 );
599 SWAP_NODES( myVolumeNodes, 10, 12 );
602 SWAP_NODES( myVolumeNodes, 1, 2 );
603 SWAP_NODES( myVolumeNodes, 4, 5 );
604 SWAP_NODES( myVolumeNodes, 6, 8 );
605 SWAP_NODES( myVolumeNodes, 9, 11 );
606 SWAP_NODES( myVolumeNodes, 13, 14 );
609 SWAP_NODES( myVolumeNodes, 1, 3 );
610 SWAP_NODES( myVolumeNodes, 5, 7 );
611 SWAP_NODES( myVolumeNodes, 8, 11 );
612 SWAP_NODES( myVolumeNodes, 9, 10 );
613 SWAP_NODES( myVolumeNodes, 12, 15 );
614 SWAP_NODES( myVolumeNodes, 13, 14 );
615 SWAP_NODES( myVolumeNodes, 17, 19 );
618 SWAP_NODES( myVolumeNodes, 1, 3 );
619 SWAP_NODES( myVolumeNodes, 5, 7 );
620 SWAP_NODES( myVolumeNodes, 8, 11 );
621 SWAP_NODES( myVolumeNodes, 9, 10 );
622 SWAP_NODES( myVolumeNodes, 12, 15 );
623 SWAP_NODES( myVolumeNodes, 13, 14 );
624 SWAP_NODES( myVolumeNodes, 17, 19 );
625 SWAP_NODES( myVolumeNodes, 21, 24 );
626 SWAP_NODES( myVolumeNodes, 22, 23 );
632 //=======================================================================
633 //function : GetVolumeType
635 //=======================================================================
637 SMDS_VolumeTool::VolumeType SMDS_VolumeTool::GetVolumeType() const
642 switch( myVolumeNodes.size() ) {
643 case 4: return TETRA;
644 case 5: return PYRAM;
645 case 6: return PENTA;
647 case 12: return HEX_PRISM;
648 case 10: return QUAD_TETRA;
649 case 13: return QUAD_PYRAM;
650 case 15: return QUAD_PENTA;
651 case 20: return QUAD_HEXA;
652 case 27: return QUAD_HEXA;
659 //=======================================================================
660 //function : getTetraVolume
662 //=======================================================================
664 static double getTetraVolume(const SMDS_MeshNode* n1,
665 const SMDS_MeshNode* n2,
666 const SMDS_MeshNode* n3,
667 const SMDS_MeshNode* n4)
669 double p1[3], p2[3], p3[3], p4[3];
675 double Q1 = -(p1[ 0 ]-p2[ 0 ])*(p3[ 1 ]*p4[ 2 ]-p4[ 1 ]*p3[ 2 ]);
676 double Q2 = (p1[ 0 ]-p3[ 0 ])*(p2[ 1 ]*p4[ 2 ]-p4[ 1 ]*p2[ 2 ]);
677 double R1 = -(p1[ 0 ]-p4[ 0 ])*(p2[ 1 ]*p3[ 2 ]-p3[ 1 ]*p2[ 2 ]);
678 double R2 = -(p2[ 0 ]-p3[ 0 ])*(p1[ 1 ]*p4[ 2 ]-p4[ 1 ]*p1[ 2 ]);
679 double S1 = (p2[ 0 ]-p4[ 0 ])*(p1[ 1 ]*p3[ 2 ]-p3[ 1 ]*p1[ 2 ]);
680 double S2 = -(p3[ 0 ]-p4[ 0 ])*(p1[ 1 ]*p2[ 2 ]-p2[ 1 ]*p1[ 2 ]);
682 return (Q1+Q2+R1+R2+S1+S2)/6.0;
685 //=======================================================================
687 //purpose : Return element volume
688 //=======================================================================
690 double SMDS_VolumeTool::GetSize() const
696 if ( myVolume->IsPoly() )
701 // split a polyhedron into tetrahedrons
703 SaveFacet savedFacet( myCurFace );
704 SMDS_VolumeTool* me = const_cast< SMDS_VolumeTool* > ( this );
705 for ( int f = 0; f < NbFaces(); ++f )
708 XYZ area (0,0,0), p1( myCurFace.myNodes[0] );
709 for ( int n = 0; n < myCurFace.myNbNodes; ++n )
711 XYZ p2( myCurFace.myNodes[ n+1 ]);
712 area = area + p1.Crossed( p2 );
721 const static int ind[] = {
722 0, 1, 3, 6, 11, 23, 31, 44, 58, 78 };
723 const static int vtab[][4] = { // decomposition into tetra in the order of enum VolumeType
756 // quadratic tetrahedron
781 // quadratic pentahedron
798 // quadratic hexahedron
823 int type = GetVolumeType();
825 int n2 = ind[type+1];
827 for (int i = n1; i < n2; i++) {
828 V -= getTetraVolume( myVolumeNodes[ vtab[i][0] ],
829 myVolumeNodes[ vtab[i][1] ],
830 myVolumeNodes[ vtab[i][2] ],
831 myVolumeNodes[ vtab[i][3] ]);
837 //=======================================================================
838 //function : GetBaryCenter
840 //=======================================================================
842 bool SMDS_VolumeTool::GetBaryCenter(double & X, double & Y, double & Z) const
848 for ( size_t i = 0; i < myVolumeNodes.size(); i++ ) {
849 X += myVolumeNodes[ i ]->X();
850 Y += myVolumeNodes[ i ]->Y();
851 Z += myVolumeNodes[ i ]->Z();
853 X /= myVolumeNodes.size();
854 Y /= myVolumeNodes.size();
855 Z /= myVolumeNodes.size();
860 //================================================================================
862 * \brief Classify a point
863 * \param tol - thickness of faces
865 //================================================================================
867 bool SMDS_VolumeTool::IsOut(double X, double Y, double Z, double tol) const
869 // LIMITATION: for convex volumes only
871 for ( int iF = 0; iF < myNbFaces; ++iF )
874 if ( !GetFaceNormal( iF, faceNormal.x, faceNormal.y, faceNormal.z ))
876 if ( !IsFaceExternal( iF ))
877 faceNormal = XYZ() - faceNormal; // reverse
879 XYZ face2p( p - XYZ( myCurFace.myNodes[0] ));
880 if ( face2p.Dot( faceNormal ) > tol )
886 //=======================================================================
887 //function : SetExternalNormal
888 //purpose : Node order will be so that faces normals are external
889 //=======================================================================
891 void SMDS_VolumeTool::SetExternalNormal ()
893 myExternalFaces = true;
894 myCurFace.myIndex = -1;
897 //=======================================================================
898 //function : NbFaceNodes
899 //purpose : Return number of nodes in the array of face nodes
900 //=======================================================================
902 int SMDS_VolumeTool::NbFaceNodes( int faceIndex ) const
904 if ( !setFace( faceIndex ))
906 return myCurFace.myNbNodes;
909 //=======================================================================
910 //function : GetFaceNodes
911 //purpose : Return pointer to the array of face nodes.
912 // To comfort link iteration, the array
913 // length == NbFaceNodes( faceIndex ) + 1 and
914 // the last node == the first one.
915 //=======================================================================
917 const SMDS_MeshNode** SMDS_VolumeTool::GetFaceNodes( int faceIndex ) const
919 if ( !setFace( faceIndex ))
921 return &myCurFace.myNodes[0];
924 //=======================================================================
925 //function : GetFaceNodesIndices
926 //purpose : Return pointer to the array of face nodes indices
927 // To comfort link iteration, the array
928 // length == NbFaceNodes( faceIndex ) + 1 and
929 // the last node index == the first one.
930 //=======================================================================
932 const int* SMDS_VolumeTool::GetFaceNodesIndices( int faceIndex ) const
934 if ( !setFace( faceIndex ))
937 return myCurFace.myNodeIndices;
940 //=======================================================================
941 //function : GetFaceNodes
942 //purpose : Return a set of face nodes.
943 //=======================================================================
945 bool SMDS_VolumeTool::GetFaceNodes (int faceIndex,
946 set<const SMDS_MeshNode*>& theFaceNodes ) const
948 if ( !setFace( faceIndex ))
951 theFaceNodes.clear();
952 theFaceNodes.insert( myCurFace.myNodes.begin(), myCurFace.myNodes.end() );
959 struct NLink : public std::pair<int,int>
962 NLink(const SMDS_MeshNode* n1=0, const SMDS_MeshNode* n2=0, int ori=1 )
966 if (( myOri = ( n1->GetID() < n2->GetID() )))
969 second = n2->GetID();
975 second = n1->GetID();
981 myOri = first = second = 0;
984 //int Node1() const { return myOri == -1 ? second : first; }
986 //bool IsSameOri( const std::pair<int,int>& link ) const { return link.first == Node1(); }
990 //=======================================================================
991 //function : IsFaceExternal
992 //purpose : Check normal orientation of a given face
993 //=======================================================================
995 bool SMDS_VolumeTool::IsFaceExternal( int faceIndex ) const
997 if ( myExternalFaces || !myVolume )
1000 if ( !myPolyedre ) // all classical volumes have external facet normals
1003 SMDS_VolumeTool* me = const_cast< SMDS_VolumeTool* >( this );
1005 if ( myPolyFacetOri[ faceIndex ])
1006 return myPolyFacetOri[ faceIndex ] > 0;
1008 int ori = 0; // -1-in, +1-out, 0-undef
1009 double minProj, maxProj;
1010 if ( projectNodesToNormal( faceIndex, minProj, maxProj ))
1012 // all nodes are on the same side of the facet
1013 ori = ( minProj < 0 ? +1 : -1 );
1014 me->myPolyFacetOri[ faceIndex ] = ori;
1016 if ( !myFwdLinks.empty() ) // concave polyhedron; collect oriented links
1017 for ( int i = 0; i < myCurFace.myNbNodes; ++i )
1019 NLink link( myCurFace.myNodes[i], myCurFace.myNodes[i+1], ori );
1020 me->myFwdLinks.insert( make_pair( link, link.myOri ));
1025 SaveFacet savedFacet( myCurFace );
1027 // concave polyhedron
1029 if ( myFwdLinks.empty() ) // get links of the least ambiguously oriented facet
1031 for ( size_t i = 0; i < myPolyFacetOri.size() && !ori; ++i )
1032 ori = myPolyFacetOri[ i ];
1034 if ( !ori ) // none facet is oriented yet
1036 // find the least ambiguously oriented facet
1037 int faceMostConvex = -1;
1038 std::map< double, int > convexity2face;
1039 for ( size_t iF = 0; iF < myPolyFacetOri.size() && faceMostConvex < 0; ++iF )
1041 if ( projectNodesToNormal( iF, minProj, maxProj ))
1043 // all nodes are on the same side of the facet
1044 me->myPolyFacetOri[ iF ] = ( minProj < 0 ? +1 : -1 );
1045 faceMostConvex = iF;
1049 ori = ( -minProj < maxProj ? -1 : +1 );
1050 double convexity = std::min( -minProj, maxProj ) / std::max( -minProj, maxProj );
1051 convexity2face.insert( make_pair( convexity, iF * ori ));
1054 if ( faceMostConvex < 0 ) // none facet has nodes on the same side
1056 // use the least ambiguous facet
1057 faceMostConvex = convexity2face.begin()->second;
1058 ori = ( faceMostConvex < 0 ? -1 : +1 );
1059 faceMostConvex = std::abs( faceMostConvex );
1060 me->myPolyFacetOri[ faceMostConvex ] = ori;
1063 // collect links of the oriented facets in myFwdLinks
1064 for ( size_t iF = 0; iF < myPolyFacetOri.size(); ++iF )
1066 ori = myPolyFacetOri[ iF ];
1067 if ( !ori ) continue;
1069 for ( int i = 0; i < myCurFace.myNbNodes; ++i )
1071 NLink link( myCurFace.myNodes[i], myCurFace.myNodes[i+1], ori );
1072 me->myFwdLinks.insert( make_pair( link, link.myOri ));
1077 // compare orientation of links of the facet with myFwdLinks
1079 setFace( faceIndex );
1080 vector< NLink > links( myCurFace.myNbNodes ), links2;
1081 for ( int i = 0; i < myCurFace.myNbNodes && !ori; ++i )
1083 NLink link( myCurFace.myNodes[i], myCurFace.myNodes[i+1] );
1084 std::map<Link, int>::const_iterator l2o = myFwdLinks.find( link );
1085 if ( l2o != myFwdLinks.end() )
1086 ori = link.myOri * l2o->second * -1;
1089 while ( !ori ) // the facet has no common links with already oriented facets
1091 // orient and collect links of other non-oriented facets
1092 for ( size_t iF = 0; iF < myPolyFacetOri.size(); ++iF )
1094 if ( myPolyFacetOri[ iF ] ) continue; // already oriented
1098 for ( int i = 0; i < myCurFace.myNbNodes && !ori; ++i )
1100 NLink link( myCurFace.myNodes[i], myCurFace.myNodes[i+1] );
1101 std::map<Link, int>::const_iterator l2o = myFwdLinks.find( link );
1102 if ( l2o != myFwdLinks.end() )
1103 ori = link.myOri * l2o->second * -1;
1104 links2.push_back( link );
1106 if ( ori ) // one more facet oriented
1108 me->myPolyFacetOri[ iF ] = ori;
1109 for ( size_t i = 0; i < links2.size(); ++i )
1110 me->myFwdLinks.insert( make_pair( links2[i], links2[i].myOri * ori ));
1115 return false; // error in algorithm: infinite loop
1117 // try to orient the facet again
1119 for ( size_t i = 0; i < links.size() && !ori; ++i )
1121 std::map<Link, int>::const_iterator l2o = myFwdLinks.find( links[i] );
1122 if ( l2o != myFwdLinks.end() )
1123 ori = links[i].myOri * l2o->second * -1;
1125 me->myPolyFacetOri[ faceIndex ] = ori;
1131 //=======================================================================
1132 //function : projectNodesToNormal
1133 //purpose : compute min and max projections of all nodes to normal of a facet.
1134 //=======================================================================
1136 bool SMDS_VolumeTool::projectNodesToNormal( int faceIndex,
1138 double& maxProj ) const
1140 minProj = std::numeric_limits<double>::max();
1141 maxProj = std::numeric_limits<double>::min();
1144 if ( !GetFaceNormal( faceIndex, normal.x, normal.y, normal.z ))
1146 XYZ p0 ( myCurFace.myNodes[0] );
1147 for ( size_t i = 0; i < myVolumeNodes.size(); ++i )
1149 if ( std::find( myCurFace.myNodes.begin() + 1,
1150 myCurFace.myNodes.end(),
1151 myVolumeNodes[ i ] ) != myCurFace.myNodes.end() )
1152 continue; // node of the faceIndex-th facet
1154 double proj = normal.Dot( XYZ( myVolumeNodes[ i ]) - p0 );
1155 if ( proj < minProj ) minProj = proj;
1156 if ( proj > maxProj ) maxProj = proj;
1158 const double tol = 1e-7;
1161 bool diffSize = ( minProj * maxProj < 0 );
1164 // minProj = -minProj;
1166 // else if ( minProj < 0 )
1168 // minProj = -minProj;
1169 // maxProj = -maxProj;
1172 return !diffSize; // ? 0 : (minProj >= 0);
1175 //=======================================================================
1176 //function : GetFaceNormal
1177 //purpose : Return a normal to a face
1178 //=======================================================================
1180 bool SMDS_VolumeTool::GetFaceNormal (int faceIndex, double & X, double & Y, double & Z) const
1182 if ( !setFace( faceIndex ))
1185 const int iQuad = ( !myPolyedre && myCurFace.myNbNodes > 6 ) ? 2 : 1;
1186 XYZ p1 ( myCurFace.myNodes[0*iQuad] );
1187 XYZ p2 ( myCurFace.myNodes[1*iQuad] );
1188 XYZ p3 ( myCurFace.myNodes[2*iQuad] );
1189 XYZ aVec12( p2 - p1 );
1190 XYZ aVec13( p3 - p1 );
1191 XYZ cross = aVec12.Crossed( aVec13 );
1193 if ( myCurFace.myNbNodes >3*iQuad ) {
1194 XYZ p4 ( myCurFace.myNodes[3*iQuad] );
1195 XYZ aVec14( p4 - p1 );
1196 XYZ cross2 = aVec13.Crossed( aVec14 );
1197 cross = cross + cross2;
1200 double size = cross.Magnitude();
1201 if ( size <= numeric_limits<double>::min() )
1211 //================================================================================
1213 * \brief Return barycenter of a face
1215 //================================================================================
1217 bool SMDS_VolumeTool::GetFaceBaryCenter (int faceIndex, double & X, double & Y, double & Z) const
1219 if ( !setFace( faceIndex ))
1223 for ( int i = 0; i < myCurFace.myNbNodes; ++i )
1225 X += myCurFace.myNodes[i]->X() / myCurFace.myNbNodes;
1226 Y += myCurFace.myNodes[i]->Y() / myCurFace.myNbNodes;
1227 Z += myCurFace.myNodes[i]->Z() / myCurFace.myNbNodes;
1232 //=======================================================================
1233 //function : GetFaceArea
1234 //purpose : Return face area
1235 //=======================================================================
1237 double SMDS_VolumeTool::GetFaceArea( int faceIndex ) const
1240 if ( !setFace( faceIndex ))
1243 XYZ p1 ( myCurFace.myNodes[0] );
1244 XYZ p2 ( myCurFace.myNodes[1] );
1245 XYZ p3 ( myCurFace.myNodes[2] );
1246 XYZ aVec12( p2 - p1 );
1247 XYZ aVec13( p3 - p1 );
1248 area += aVec12.Crossed( aVec13 ).Magnitude();
1250 if (myVolume->IsPoly())
1252 for ( int i = 3; i < myCurFace.myNbNodes; ++i )
1254 XYZ pI ( myCurFace.myNodes[i] );
1255 XYZ aVecI( pI - p1 );
1256 area += aVec13.Crossed( aVecI ).Magnitude();
1262 if ( myCurFace.myNbNodes == 4 ) {
1263 XYZ p4 ( myCurFace.myNodes[3] );
1264 XYZ aVec14( p4 - p1 );
1265 area += aVec14.Crossed( aVec13 ).Magnitude();
1271 //================================================================================
1273 * \brief Return index of the node located at face center of a quadratic element like HEX27
1275 //================================================================================
1277 int SMDS_VolumeTool::GetCenterNodeIndex( int faceIndex ) const
1279 if ( myAllFacesNbNodes && myVolumeNodes.size() == 27 ) // classic element with 27 nodes
1281 switch ( faceIndex ) {
1285 return faceIndex + 19;
1291 //=======================================================================
1292 //function : GetOppFaceIndex
1293 //purpose : Return index of the opposite face if it exists, else -1.
1294 //=======================================================================
1296 int SMDS_VolumeTool::GetOppFaceIndex( int faceIndex ) const
1300 MESSAGE("Warning: attempt to obtain opposite face on polyhedral volume");
1304 const int nbHoriFaces = 2;
1306 if ( faceIndex >= 0 && faceIndex < NbFaces() ) {
1307 switch ( myVolumeNodes.size() ) {
1310 if ( faceIndex == 0 || faceIndex == 1 )
1311 ind = 1 - faceIndex;
1315 if ( faceIndex <= 1 ) // top or bottom
1316 ind = 1 - faceIndex;
1318 const int nbSideFaces = myAllFacesNbNodes[0];
1319 ind = ( faceIndex - nbHoriFaces + nbSideFaces/2 ) % nbSideFaces + nbHoriFaces;
1324 ind = GetOppFaceIndexOfHex( faceIndex );
1332 //=======================================================================
1333 //function : GetOppFaceIndexOfHex
1334 //purpose : Return index of the opposite face of the hexahedron
1335 //=======================================================================
1337 int SMDS_VolumeTool::GetOppFaceIndexOfHex( int faceIndex )
1339 return Hexa_oppF[ faceIndex ];
1342 //=======================================================================
1343 //function : IsLinked
1344 //purpose : return true if theNode1 is linked with theNode2
1345 // If theIgnoreMediumNodes then corner nodes of quadratic cell are considered linked as well
1346 //=======================================================================
1348 bool SMDS_VolumeTool::IsLinked (const SMDS_MeshNode* theNode1,
1349 const SMDS_MeshNode* theNode2,
1350 const bool theIgnoreMediumNodes) const
1355 if (myVolume->IsPoly()) {
1357 MESSAGE("Warning: bad volumic element");
1360 if ( !myAllFacesNbNodes ) {
1361 SMDS_VolumeTool* me = const_cast< SMDS_VolumeTool* >( this );
1362 me->myPolyQuantities = myPolyedre->GetQuantities();
1363 myAllFacesNbNodes = &myPolyQuantities[0];
1365 int from, to = 0, d1 = 1, d2 = 2;
1366 if ( myPolyedre->IsQuadratic() ) {
1367 if ( theIgnoreMediumNodes ) {
1373 vector<const SMDS_MeshNode*>::const_iterator i;
1374 for (int iface = 0; iface < myNbFaces; iface++)
1377 to += myPolyQuantities[iface];
1378 i = std::find( myVolumeNodes.begin() + from, myVolumeNodes.begin() + to, theNode1 );
1379 if ( i != myVolumeNodes.end() )
1381 if (( theNode2 == *( i-d1 ) ||
1382 theNode2 == *( i+d1 )))
1385 (( theNode2 == *( i-d2 ) ||
1386 theNode2 == *( i+d2 ))))
1393 // find nodes indices
1394 int i1 = -1, i2 = -1, nbFound = 0;
1395 for ( size_t i = 0; i < myVolumeNodes.size() && nbFound < 2; i++ )
1397 if ( myVolumeNodes[ i ] == theNode1 )
1399 else if ( myVolumeNodes[ i ] == theNode2 )
1402 return IsLinked( i1, i2 );
1405 //=======================================================================
1406 //function : IsLinked
1407 //purpose : return true if the node with theNode1Index is linked
1408 // with the node with theNode2Index
1409 // If theIgnoreMediumNodes then corner nodes of quadratic cell are considered linked as well
1410 //=======================================================================
1412 bool SMDS_VolumeTool::IsLinked (const int theNode1Index,
1413 const int theNode2Index,
1414 bool theIgnoreMediumNodes) const
1416 if ( myVolume->IsPoly() ) {
1417 return IsLinked(myVolumeNodes[theNode1Index], myVolumeNodes[theNode2Index]);
1420 int minInd = min( theNode1Index, theNode2Index );
1421 int maxInd = max( theNode1Index, theNode2Index );
1423 if ( minInd < 0 || maxInd > (int)myVolumeNodes.size() - 1 || maxInd == minInd )
1426 VolumeType type = GetVolumeType();
1427 if ( myVolume->IsQuadratic() )
1429 int firstMediumInd = myVolume->NbCornerNodes();
1430 if ( minInd >= firstMediumInd )
1431 return false; // both nodes are medium - not linked
1432 if ( maxInd < firstMediumInd ) // both nodes are corners
1434 if ( theIgnoreMediumNodes )
1435 type = quadToLinear(type); // to check linkage of corner nodes only
1437 return false; // corner nodes are not linked directly in a quadratic cell
1445 switch ( maxInd - minInd ) {
1446 case 1: return minInd != 3;
1447 case 3: return minInd == 0 || minInd == 4;
1448 case 4: return true;
1455 switch ( maxInd - minInd ) {
1457 case 3: return true;
1462 switch ( maxInd - minInd ) {
1463 case 1: return minInd != 2;
1464 case 2: return minInd == 0 || minInd == 3;
1465 case 3: return true;
1472 case 0: if( maxInd==4 || maxInd==6 || maxInd==7 ) return true;
1473 case 1: if( maxInd==4 || maxInd==5 || maxInd==8 ) return true;
1474 case 2: if( maxInd==5 || maxInd==6 || maxInd==9 ) return true;
1475 case 3: if( maxInd==7 || maxInd==8 || maxInd==9 ) return true;
1483 case 0: if( maxInd==8 || maxInd==11 || maxInd==16 ) return true;
1484 case 1: if( maxInd==8 || maxInd==9 || maxInd==17 ) return true;
1485 case 2: if( maxInd==9 || maxInd==10 || maxInd==18 ) return true;
1486 case 3: if( maxInd==10 || maxInd==11 || maxInd==19 ) return true;
1487 case 4: if( maxInd==12 || maxInd==15 || maxInd==16 ) return true;
1488 case 5: if( maxInd==12 || maxInd==13 || maxInd==17 ) return true;
1489 case 6: if( maxInd==13 || maxInd==14 || maxInd==18 ) return true;
1490 case 7: if( maxInd==14 || maxInd==15 || maxInd==19 ) return true;
1498 case 0: if( maxInd==5 || maxInd==8 || maxInd==9 ) return true;
1499 case 1: if( maxInd==5 || maxInd==6 || maxInd==10 ) return true;
1500 case 2: if( maxInd==6 || maxInd==7 || maxInd==11 ) return true;
1501 case 3: if( maxInd==7 || maxInd==8 || maxInd==12 ) return true;
1502 case 4: if( maxInd==9 || maxInd==10 || maxInd==11 || maxInd==12 ) return true;
1510 case 0: if( maxInd==6 || maxInd==8 || maxInd==12 ) return true;
1511 case 1: if( maxInd==6 || maxInd==7 || maxInd==13 ) return true;
1512 case 2: if( maxInd==7 || maxInd==8 || maxInd==14 ) return true;
1513 case 3: if( maxInd==9 || maxInd==11 || maxInd==12 ) return true;
1514 case 4: if( maxInd==9 || maxInd==10 || maxInd==13 ) return true;
1515 case 5: if( maxInd==10 || maxInd==11 || maxInd==14 ) return true;
1522 const int diff = maxInd-minInd;
1523 if ( diff > 6 ) return false;// not linked top and bottom
1524 if ( diff == 6 ) return true; // linked top and bottom
1525 return diff == 1 || diff == 7;
1532 //=======================================================================
1533 //function : GetNodeIndex
1534 //purpose : Return an index of theNode
1535 //=======================================================================
1537 int SMDS_VolumeTool::GetNodeIndex(const SMDS_MeshNode* theNode) const
1540 for ( size_t i = 0; i < myVolumeNodes.size(); i++ ) {
1541 if ( myVolumeNodes[ i ] == theNode )
1548 //================================================================================
1550 * \brief Fill vector with boundary faces existing in the mesh
1551 * \param faces - vector of found nodes
1552 * \retval int - nb of found faces
1554 //================================================================================
1556 int SMDS_VolumeTool::GetAllExistingFaces(vector<const SMDS_MeshElement*> & faces) const
1559 SaveFacet savedFacet( myCurFace );
1561 for ( int iF = 0; iF < NbFaces(); ++iF ) {
1563 if ( const SMDS_MeshElement* face = SMDS_Mesh::FindFace( myCurFace.myNodes ))
1564 faces.push_back( face );
1567 for ( int iF = 0; iF < NbFaces(); ++iF ) {
1568 const SMDS_MeshFace* face = 0;
1569 const SMDS_MeshNode** nodes = GetFaceNodes( iF );
1570 switch ( NbFaceNodes( iF )) {
1572 face = SMDS_Mesh::FindFace( nodes[0], nodes[1], nodes[2] ); break;
1574 face = SMDS_Mesh::FindFace( nodes[0], nodes[1], nodes[2], nodes[3] ); break;
1576 face = SMDS_Mesh::FindFace( nodes[0], nodes[1], nodes[2],
1577 nodes[3], nodes[4], nodes[5]); break;
1579 face = SMDS_Mesh::FindFace( nodes[0], nodes[1], nodes[2], nodes[3],
1580 nodes[4], nodes[5], nodes[6], nodes[7]); break;
1583 faces.push_back( face );
1585 return faces.size();
1589 //================================================================================
1591 * \brief Fill vector with boundary edges existing in the mesh
1592 * \param edges - vector of found edges
1593 * \retval int - nb of found faces
1595 //================================================================================
1597 int SMDS_VolumeTool::GetAllExistingEdges(vector<const SMDS_MeshElement*> & edges) const
1600 edges.reserve( myVolumeNodes.size() * 2 );
1601 for ( size_t i = 0; i < myVolumeNodes.size()-1; ++i ) {
1602 for ( size_t j = i + 1; j < myVolumeNodes.size(); ++j ) {
1603 if ( IsLinked( i, j )) {
1604 const SMDS_MeshElement* edge =
1605 SMDS_Mesh::FindEdge( myVolumeNodes[i], myVolumeNodes[j] );
1607 edges.push_back( edge );
1611 return edges.size();
1614 //================================================================================
1616 * \brief Return minimal square distance between connected corner nodes
1618 //================================================================================
1620 double SMDS_VolumeTool::MinLinearSize2() const
1622 double minSize = 1e+100;
1623 int iQ = myVolume->IsQuadratic() ? 2 : 1;
1625 SaveFacet savedFacet( myCurFace );
1627 // it seems that compute distance twice is faster than organization of a sole computing
1628 myCurFace.myIndex = -1;
1629 for ( int iF = 0; iF < myNbFaces; ++iF )
1632 for ( int iN = 0; iN < myCurFace.myNbNodes; iN += iQ )
1634 XYZ n1( myCurFace.myNodes[ iN ]);
1635 XYZ n2( myCurFace.myNodes[(iN + iQ) % myCurFace.myNbNodes]);
1636 minSize = std::min( minSize, (n1 - n2).SquareMagnitude());
1643 //================================================================================
1645 * \brief Return maximal square distance between connected corner nodes
1647 //================================================================================
1649 double SMDS_VolumeTool::MaxLinearSize2() const
1651 double maxSize = -1e+100;
1652 int iQ = myVolume->IsQuadratic() ? 2 : 1;
1654 SaveFacet savedFacet( myCurFace );
1656 // it seems that compute distance twice is faster than organization of a sole computing
1657 myCurFace.myIndex = -1;
1658 for ( int iF = 0; iF < myNbFaces; ++iF )
1661 for ( int iN = 0; iN < myCurFace.myNbNodes; iN += iQ )
1663 XYZ n1( myCurFace.myNodes[ iN ]);
1664 XYZ n2( myCurFace.myNodes[(iN + iQ) % myCurFace.myNbNodes]);
1665 maxSize = std::max( maxSize, (n1 - n2).SquareMagnitude());
1672 //================================================================================
1674 * \brief fast check that only one volume is build on the face nodes
1675 * This check is valid for conformal meshes only
1677 //================================================================================
1679 bool SMDS_VolumeTool::IsFreeFace( int faceIndex, const SMDS_MeshElement** otherVol/*=0*/ ) const
1681 const bool isFree = true;
1683 if ( !setFace( faceIndex ))
1686 const SMDS_MeshNode** nodes = GetFaceNodes( faceIndex );
1688 const int di = myVolume->IsQuadratic() ? 2 : 1;
1689 const int nbN = ( myCurFace.myNbNodes/di <= 4 && !IsPoly()) ? 3 : myCurFace.myNbNodes/di; // nb nodes to check
1691 SMDS_ElemIteratorPtr eIt = nodes[0]->GetInverseElementIterator( SMDSAbs_Volume );
1692 while ( eIt->more() )
1694 const SMDS_MeshElement* vol = eIt->next();
1695 if ( vol == myVolume )
1698 for ( iN = 1; iN < nbN; ++iN )
1699 if ( vol->GetNodeIndex( nodes[ iN*di ]) < 0 )
1701 if ( iN == nbN ) // nbN nodes are shared with vol
1703 // if ( vol->IsPoly() || vol->NbFaces() > 6 ) // vol is polyhed or hex prism
1705 // int nb = myCurFace.myNbNodes;
1706 // if ( myVolume->GetEntityType() != vol->GetEntityType() )
1707 // nb -= ( GetCenterNodeIndex(0) > 0 );
1708 // set<const SMDS_MeshNode*> faceNodes( nodes, nodes + nb );
1709 // if ( SMDS_VolumeTool( vol ).GetFaceIndex( faceNodes ) < 0 )
1712 if ( otherVol ) *otherVol = vol;
1716 if ( otherVol ) *otherVol = 0;
1720 //================================================================================
1722 * \brief Thorough check that only one volume is build on the face nodes
1724 //================================================================================
1726 bool SMDS_VolumeTool::IsFreeFaceAdv( int faceIndex, const SMDS_MeshElement** otherVol/*=0*/ ) const
1728 const bool isFree = true;
1730 if (!setFace( faceIndex ))
1733 const SMDS_MeshNode** nodes = GetFaceNodes( faceIndex );
1734 const int nbFaceNodes = myCurFace.myNbNodes;
1736 // evaluate nb of face nodes shared by other volumes
1737 int maxNbShared = -1;
1738 typedef map< const SMDS_MeshElement*, int > TElemIntMap;
1739 TElemIntMap volNbShared;
1740 TElemIntMap::iterator vNbIt;
1741 for ( int iNode = 0; iNode < nbFaceNodes; iNode++ ) {
1742 const SMDS_MeshNode* n = nodes[ iNode ];
1743 SMDS_ElemIteratorPtr eIt = n->GetInverseElementIterator( SMDSAbs_Volume );
1744 while ( eIt->more() ) {
1745 const SMDS_MeshElement* elem = eIt->next();
1746 if ( elem != myVolume ) {
1747 vNbIt = volNbShared.insert( make_pair( elem, 0 )).first;
1749 if ( vNbIt->second > maxNbShared )
1750 maxNbShared = vNbIt->second;
1754 if ( maxNbShared < 3 )
1755 return isFree; // is free
1757 // find volumes laying on the opposite side of the face
1758 // and sharing all nodes
1759 XYZ intNormal; // internal normal
1760 GetFaceNormal( faceIndex, intNormal.x, intNormal.y, intNormal.z );
1761 if ( IsFaceExternal( faceIndex ))
1762 intNormal = XYZ( -intNormal.x, -intNormal.y, -intNormal.z );
1763 XYZ p0 ( nodes[0] ), baryCenter;
1764 for ( vNbIt = volNbShared.begin(); vNbIt != volNbShared.end(); ) {
1765 const int& nbShared = (*vNbIt).second;
1766 if ( nbShared >= 3 ) {
1767 SMDS_VolumeTool volume( (*vNbIt).first );
1768 volume.GetBaryCenter( baryCenter.x, baryCenter.y, baryCenter.z );
1769 XYZ intNormal2( baryCenter - p0 );
1770 if ( intNormal.Dot( intNormal2 ) < 0 ) {
1772 if ( nbShared >= nbFaceNodes )
1774 // a volume shares the whole facet
1775 if ( otherVol ) *otherVol = vNbIt->first;
1782 // remove a volume from volNbShared map
1783 volNbShared.erase( vNbIt++ );
1786 // here volNbShared contains only volumes laying on the opposite side of
1787 // the face and sharing 3 or more but not all face nodes with myVolume
1788 if ( volNbShared.size() < 2 ) {
1789 return isFree; // is free
1792 // check if the whole area of a face is shared
1793 for ( int iNode = 0; iNode < nbFaceNodes; iNode++ )
1795 const SMDS_MeshNode* n = nodes[ iNode ];
1796 // check if n is shared by one of volumes of volNbShared
1797 bool isShared = false;
1798 SMDS_ElemIteratorPtr eIt = n->GetInverseElementIterator( SMDSAbs_Volume );
1799 while ( eIt->more() && !isShared )
1800 isShared = volNbShared.count( eIt->next() );
1804 if ( otherVol ) *otherVol = volNbShared.begin()->first;
1807 // if ( !myVolume->IsPoly() )
1809 // bool isShared[] = { false, false, false, false }; // 4 triangle parts of a quadrangle
1810 // for ( vNbIt = volNbShared.begin(); vNbIt != volNbShared.end(); vNbIt++ ) {
1811 // SMDS_VolumeTool volume( (*vNbIt).first );
1812 // bool prevLinkShared = false;
1813 // int nbSharedLinks = 0;
1814 // for ( int iNode = 0; iNode < nbFaceNodes; iNode++ ) {
1815 // bool linkShared = volume.IsLinked( nodes[ iNode ], nodes[ iNode + 1] );
1816 // if ( linkShared )
1818 // if ( linkShared && prevLinkShared &&
1819 // volume.IsLinked( nodes[ iNode - 1 ], nodes[ iNode + 1] ))
1820 // isShared[ iNode ] = true;
1821 // prevLinkShared = linkShared;
1823 // if ( nbSharedLinks == nbFaceNodes )
1824 // return !free; // is not free
1825 // if ( nbFaceNodes == 4 ) {
1826 // // check traingle parts 1 & 3
1827 // if ( isShared[1] && isShared[3] )
1828 // return !free; // is not free
1829 // // check triangle parts 0 & 2;
1830 // // 0 part could not be checked in the loop; check it here
1831 // if ( isShared[2] && prevLinkShared &&
1832 // volume.IsLinked( nodes[ 0 ], nodes[ 1 ] ) &&
1833 // volume.IsLinked( nodes[ 1 ], nodes[ 3 ] ) )
1834 // return !free; // is not free
1841 //=======================================================================
1842 //function : GetFaceIndex
1843 //purpose : Return index of a face formed by theFaceNodes
1844 //=======================================================================
1846 int SMDS_VolumeTool::GetFaceIndex( const set<const SMDS_MeshNode*>& theFaceNodes,
1847 const int theFaceIndexHint ) const
1849 if ( theFaceIndexHint >= 0 )
1851 int nbNodes = NbFaceNodes( theFaceIndexHint );
1852 if ( nbNodes == (int) theFaceNodes.size() )
1854 const SMDS_MeshNode** nodes = GetFaceNodes( theFaceIndexHint );
1856 if ( theFaceNodes.count( nodes[ nbNodes-1 ]))
1861 return theFaceIndexHint;
1864 for ( int iFace = 0; iFace < myNbFaces; iFace++ )
1866 if ( iFace == theFaceIndexHint )
1868 int nbNodes = NbFaceNodes( iFace );
1869 if ( nbNodes == (int) theFaceNodes.size() )
1871 const SMDS_MeshNode** nodes = GetFaceNodes( iFace );
1873 if ( theFaceNodes.count( nodes[ nbNodes-1 ]))
1884 //=======================================================================
1885 //function : GetFaceIndex
1886 //purpose : Return index of a face formed by theFaceNodes
1887 //=======================================================================
1889 /*int SMDS_VolumeTool::GetFaceIndex( const set<int>& theFaceNodesIndices )
1891 for ( int iFace = 0; iFace < myNbFaces; iFace++ ) {
1892 const int* nodes = GetFaceNodesIndices( iFace );
1893 int nbFaceNodes = NbFaceNodes( iFace );
1895 for ( int iNode = 0; iNode < nbFaceNodes; iNode++ )
1896 nodeSet.insert( nodes[ iNode ] );
1897 if ( theFaceNodesIndices == nodeSet )
1903 //=======================================================================
1904 //function : setFace
1906 //=======================================================================
1908 bool SMDS_VolumeTool::setFace( int faceIndex ) const
1913 if ( myCurFace.myIndex == faceIndex )
1916 myCurFace.myIndex = -1;
1918 if ( faceIndex < 0 || faceIndex >= NbFaces() )
1921 if (myVolume->IsPoly())
1924 MESSAGE("Warning: bad volumic element");
1929 SMDS_VolumeTool* me = const_cast< SMDS_VolumeTool* >( this );
1930 if ( !myAllFacesNbNodes ) {
1931 me->myPolyQuantities = myPolyedre->GetQuantities();
1932 myAllFacesNbNodes = &myPolyQuantities[0];
1934 myCurFace.myNbNodes = myAllFacesNbNodes[ faceIndex ];
1935 myCurFace.myNodes.resize( myCurFace.myNbNodes + 1 );
1936 me->myPolyIndices.resize( myCurFace.myNbNodes + 1 );
1937 myCurFace.myNodeIndices = & me->myPolyIndices[0];
1938 int shift = std::accumulate( myAllFacesNbNodes, myAllFacesNbNodes+faceIndex, 0 );
1939 for ( int iNode = 0; iNode < myCurFace.myNbNodes; iNode++ )
1941 myCurFace.myNodes [ iNode ] = myVolumeNodes[ shift + iNode ];
1942 myCurFace.myNodeIndices[ iNode ] = shift + iNode;
1944 myCurFace.myNodes [ myCurFace.myNbNodes ] = myCurFace.myNodes[ 0 ]; // last = first
1945 myCurFace.myNodeIndices[ myCurFace.myNbNodes ] = myCurFace.myNodeIndices[ 0 ];
1947 // check orientation
1948 if (myExternalFaces)
1950 myCurFace.myIndex = faceIndex; // avoid infinite recursion in IsFaceExternal()
1951 myExternalFaces = false; // force normal computation by IsFaceExternal()
1952 if ( !IsFaceExternal( faceIndex ))
1953 std::reverse( myCurFace.myNodes.begin(), myCurFace.myNodes.end() );
1954 myExternalFaces = true;
1959 if ( !myAllFacesNodeIndices_F )
1961 // choose data for an element type
1962 switch ( myVolumeNodes.size() ) {
1964 myAllFacesNodeIndices_F = &Tetra_F [0][0];
1965 //myAllFacesNodeIndices_FE = &Tetra_F [0][0];
1966 myAllFacesNodeIndices_RE = &Tetra_RE[0][0];
1967 myAllFacesNbNodes = Tetra_nbN;
1968 myMaxFaceNbNodes = sizeof(Tetra_F[0])/sizeof(Tetra_F[0][0]);
1971 myAllFacesNodeIndices_F = &Pyramid_F [0][0];
1972 //myAllFacesNodeIndices_FE = &Pyramid_F [0][0];
1973 myAllFacesNodeIndices_RE = &Pyramid_RE[0][0];
1974 myAllFacesNbNodes = Pyramid_nbN;
1975 myMaxFaceNbNodes = sizeof(Pyramid_F[0])/sizeof(Pyramid_F[0][0]);
1978 myAllFacesNodeIndices_F = &Penta_F [0][0];
1979 //myAllFacesNodeIndices_FE = &Penta_FE[0][0];
1980 myAllFacesNodeIndices_RE = &Penta_RE[0][0];
1981 myAllFacesNbNodes = Penta_nbN;
1982 myMaxFaceNbNodes = sizeof(Penta_F[0])/sizeof(Penta_F[0][0]);
1985 myAllFacesNodeIndices_F = &Hexa_F [0][0];
1986 ///myAllFacesNodeIndices_FE = &Hexa_FE[0][0];
1987 myAllFacesNodeIndices_RE = &Hexa_RE[0][0];
1988 myAllFacesNbNodes = Hexa_nbN;
1989 myMaxFaceNbNodes = sizeof(Hexa_F[0])/sizeof(Hexa_F[0][0]);
1992 myAllFacesNodeIndices_F = &QuadTetra_F [0][0];
1993 //myAllFacesNodeIndices_FE = &QuadTetra_F [0][0];
1994 myAllFacesNodeIndices_RE = &QuadTetra_RE[0][0];
1995 myAllFacesNbNodes = QuadTetra_nbN;
1996 myMaxFaceNbNodes = sizeof(QuadTetra_F[0])/sizeof(QuadTetra_F[0][0]);
1999 myAllFacesNodeIndices_F = &QuadPyram_F [0][0];
2000 //myAllFacesNodeIndices_FE = &QuadPyram_F [0][0];
2001 myAllFacesNodeIndices_RE = &QuadPyram_RE[0][0];
2002 myAllFacesNbNodes = QuadPyram_nbN;
2003 myMaxFaceNbNodes = sizeof(QuadPyram_F[0])/sizeof(QuadPyram_F[0][0]);
2006 myAllFacesNodeIndices_F = &QuadPenta_F [0][0];
2007 //myAllFacesNodeIndices_FE = &QuadPenta_FE[0][0];
2008 myAllFacesNodeIndices_RE = &QuadPenta_RE[0][0];
2009 myAllFacesNbNodes = QuadPenta_nbN;
2010 myMaxFaceNbNodes = sizeof(QuadPenta_F[0])/sizeof(QuadPenta_F[0][0]);
2014 myAllFacesNodeIndices_F = &QuadHexa_F [0][0];
2015 //myAllFacesNodeIndices_FE = &QuadHexa_FE[0][0];
2016 myAllFacesNodeIndices_RE = &QuadHexa_RE[0][0];
2017 myAllFacesNbNodes = QuadHexa_nbN;
2018 myMaxFaceNbNodes = sizeof(QuadHexa_F[0])/sizeof(QuadHexa_F[0][0]);
2019 if ( !myIgnoreCentralNodes && myVolumeNodes.size() == 27 )
2021 myAllFacesNodeIndices_F = &TriQuadHexa_F [0][0];
2022 //myAllFacesNodeIndices_FE = &TriQuadHexa_FE[0][0];
2023 myAllFacesNodeIndices_RE = &TriQuadHexa_RE[0][0];
2024 myAllFacesNbNodes = TriQuadHexa_nbN;
2025 myMaxFaceNbNodes = sizeof(TriQuadHexa_F[0])/sizeof(TriQuadHexa_F[0][0]);
2029 myAllFacesNodeIndices_F = &HexPrism_F [0][0];
2030 //myAllFacesNodeIndices_FE = &HexPrism_FE[0][0];
2031 myAllFacesNodeIndices_RE = &HexPrism_RE[0][0];
2032 myAllFacesNbNodes = HexPrism_nbN;
2033 myMaxFaceNbNodes = sizeof(HexPrism_F[0])/sizeof(HexPrism_F[0][0]);
2039 myCurFace.myNbNodes = myAllFacesNbNodes[ faceIndex ];
2040 // if ( myExternalFaces )
2041 // myCurFace.myNodeIndices = (int*)( myVolForward ? myAllFacesNodeIndices_FE + faceIndex*myMaxFaceNbNodes : myAllFacesNodeIndices_RE + faceIndex*myMaxFaceNbNodes );
2043 // myCurFace.myNodeIndices = (int*)( myAllFacesNodeIndices_F + faceIndex*myMaxFaceNbNodes );
2044 myCurFace.myNodeIndices = (int*)( myVolForward ? myAllFacesNodeIndices_F + faceIndex*myMaxFaceNbNodes : myAllFacesNodeIndices_RE + faceIndex*myMaxFaceNbNodes );
2047 myCurFace.myNodes.resize( myCurFace.myNbNodes + 1 );
2048 for ( int iNode = 0; iNode < myCurFace.myNbNodes; iNode++ )
2049 myCurFace.myNodes[ iNode ] = myVolumeNodes[ myCurFace.myNodeIndices[ iNode ]];
2050 myCurFace.myNodes[ myCurFace.myNbNodes ] = myCurFace.myNodes[ 0 ];
2053 myCurFace.myIndex = faceIndex;
2058 //=======================================================================
2059 //function : GetType
2060 //purpose : return VolumeType by nb of nodes in a volume
2061 //=======================================================================
2063 SMDS_VolumeTool::VolumeType SMDS_VolumeTool::GetType(int nbNodes)
2065 switch ( nbNodes ) {
2066 case 4: return TETRA;
2067 case 5: return PYRAM;
2068 case 6: return PENTA;
2069 case 8: return HEXA;
2070 case 10: return QUAD_TETRA;
2071 case 13: return QUAD_PYRAM;
2072 case 15: return QUAD_PENTA;
2074 case 27: return QUAD_HEXA;
2075 case 12: return HEX_PRISM;
2076 default:return UNKNOWN;
2080 //=======================================================================
2081 //function : NbFaces
2082 //purpose : return nb of faces by volume type
2083 //=======================================================================
2085 int SMDS_VolumeTool::NbFaces( VolumeType type )
2089 case QUAD_TETRA: return 4;
2091 case QUAD_PYRAM: return 5;
2093 case QUAD_PENTA: return 5;
2095 case QUAD_HEXA : return 6;
2096 case HEX_PRISM : return 8;
2101 //================================================================================
2103 * \brief Useful to know nb of corner nodes of a quadratic volume
2104 * \param type - volume type
2105 * \retval int - nb of corner nodes
2107 //================================================================================
2109 int SMDS_VolumeTool::NbCornerNodes(VolumeType type)
2113 case QUAD_TETRA: return 4;
2115 case QUAD_PYRAM: return 5;
2117 case QUAD_PENTA: return 6;
2119 case QUAD_HEXA : return 8;
2120 case HEX_PRISM : return 12;
2127 //=======================================================================
2128 //function : GetFaceNodesIndices
2129 //purpose : Return the array of face nodes indices
2130 // To comfort link iteration, the array
2131 // length == NbFaceNodes( faceIndex ) + 1 and
2132 // the last node index == the first one.
2133 //=======================================================================
2135 const int* SMDS_VolumeTool::GetFaceNodesIndices(VolumeType type,
2140 case TETRA: return Tetra_F[ faceIndex ];
2141 case PYRAM: return Pyramid_F[ faceIndex ];
2142 case PENTA: return external ? Penta_F[ faceIndex ] : Penta_F[ faceIndex ];
2143 case HEXA: return external ? Hexa_F[ faceIndex ] : Hexa_F[ faceIndex ];
2144 case QUAD_TETRA: return QuadTetra_F[ faceIndex ];
2145 case QUAD_PYRAM: return QuadPyram_F[ faceIndex ];
2146 case QUAD_PENTA: return external ? QuadPenta_F[ faceIndex ] : QuadPenta_F[ faceIndex ];
2147 // what about SMDSEntity_TriQuad_Hexa?
2148 case QUAD_HEXA: return external ? QuadHexa_F[ faceIndex ] : QuadHexa_F[ faceIndex ];
2149 case HEX_PRISM: return external ? HexPrism_F[ faceIndex ] : HexPrism_F[ faceIndex ];
2155 //=======================================================================
2156 //function : NbFaceNodes
2157 //purpose : Return number of nodes in the array of face nodes
2158 //=======================================================================
2160 int SMDS_VolumeTool::NbFaceNodes(VolumeType type,
2164 case TETRA: return Tetra_nbN[ faceIndex ];
2165 case PYRAM: return Pyramid_nbN[ faceIndex ];
2166 case PENTA: return Penta_nbN[ faceIndex ];
2167 case HEXA: return Hexa_nbN[ faceIndex ];
2168 case QUAD_TETRA: return QuadTetra_nbN[ faceIndex ];
2169 case QUAD_PYRAM: return QuadPyram_nbN[ faceIndex ];
2170 case QUAD_PENTA: return QuadPenta_nbN[ faceIndex ];
2171 // what about SMDSEntity_TriQuad_Hexa?
2172 case QUAD_HEXA: return QuadHexa_nbN[ faceIndex ];
2173 case HEX_PRISM: return HexPrism_nbN[ faceIndex ];
2179 //=======================================================================
2180 //function : Element
2181 //purpose : return element
2182 //=======================================================================
2184 const SMDS_MeshVolume* SMDS_VolumeTool::Element() const
2186 return static_cast<const SMDS_MeshVolume*>( myVolume );
2189 //=======================================================================
2191 //purpose : return element ID
2192 //=======================================================================
2194 int SMDS_VolumeTool::ID() const
2196 return myVolume ? myVolume->GetID() : 0;