+namespace VISCOUS_3D
+{
+ struct ShrinkFace;
+ //--------------------------------------------------------------------------------
+ /*!
+ * \brief Pair of periodic FACEs
+ */
+ struct PeriodicFaces
+ {
+ typedef StdMeshers_ProjectionUtils::TrsfFinder3D Trsf;
+
+ ShrinkFace* _shriFace[2];
+ TNodeNodeMap _nnMap;
+ Trsf _trsf;
+
+ PeriodicFaces( ShrinkFace* sf1, ShrinkFace* sf2 ): _shriFace{ sf1, sf2 } {}
+ bool IncludeShrunk( const TopoDS_Face& face, const TopTools_MapOfShape& shrunkFaces ) const;
+ bool MoveNodes( const TopoDS_Face& tgtFace );
+ };
+
+ //--------------------------------------------------------------------------------
+ /*!
+ * \brief Shrink FACE data used to find periodic FACEs
+ */
+ struct ShrinkFace
+ {
+ // ................................................................................
+ struct BndPart //!< part of FACE boundary, either shrink or no-shrink
+ {
+ bool _isShrink, _isReverse;
+ int _nbSegments;
+ AverageHyp* _hyp;
+ std::vector< SMESH_NodeXYZ > _nodes;
+ TopAbs_ShapeEnum _vertSWOLType[2]; // shrink part includes VERTEXes
+ AverageHyp* _vertHyp[2];
+
+ BndPart():
+ _isShrink(0), _isReverse(0), _nbSegments(0), _hyp(0),
+ _vertSWOLType{ TopAbs_WIRE, TopAbs_WIRE }, _vertHyp{ 0, 0 }
+ {}
+
+ bool operator==( const BndPart& other ) const
+ {
+ return ( _isShrink == other._isShrink &&
+ _nbSegments == other._nbSegments &&
+ _nodes.size() == other._nodes.size() &&
+ vertSWOLType1() == other.vertSWOLType1() &&
+ vertSWOLType2() == other.vertSWOLType2() &&
+ (( !_isShrink ) ||
+ ( *_hyp == *other._hyp &&
+ vertHyp1() == other.vertHyp1() &&
+ vertHyp2() == other.vertHyp2() ))
+ );
+ }
+ bool CanAppend( const BndPart& other )
+ {
+ return ( _isShrink == other._isShrink &&
+ (( !_isShrink ) ||
+ ( *_hyp == *other._hyp &&
+ *_hyp == vertHyp2() &&
+ vertHyp2() == other.vertHyp1() ))
+ );
+ }
+ void Append( const BndPart& other )
+ {
+ _nbSegments += other._nbSegments;
+ bool hasCommonNode = ( _nodes.back()->GetID() == other._nodes.front()->GetID() );
+ _nodes.insert( _nodes.end(), other._nodes.begin() + hasCommonNode, other._nodes.end() );
+ _vertSWOLType[1] = other._vertSWOLType[1];
+ if ( _isShrink )
+ _vertHyp[1] = other._vertHyp[1];
+ }
+ const SMDS_MeshNode* Node(size_t i) const
+ {
+ return _nodes[ _isReverse ? ( _nodes.size() - 1 - i ) : i ]._node;
+ }
+ void Reverse() { _isReverse = !_isReverse; }
+ const TopAbs_ShapeEnum& vertSWOLType1() const { return _vertSWOLType[ _isReverse ]; }
+ const TopAbs_ShapeEnum& vertSWOLType2() const { return _vertSWOLType[ !_isReverse ]; }
+ const AverageHyp& vertHyp1() const { return *(_vertHyp[ _isReverse ]); }
+ const AverageHyp& vertHyp2() const { return *(_vertHyp[ !_isReverse ]); }
+ };
+ // ................................................................................
+
+ SMESH_subMesh* _subMesh;
+ _SolidData* _data1;
+ _SolidData* _data2;
+ //bool _isPeriodic;
+
+ std::list< BndPart > _boundary;
+ int _boundarySize, _nbBoundaryParts;
+
+ void Init( SMESH_subMesh* sm, _SolidData* sd1, _SolidData* sd2 )
+ {
+ _subMesh = sm; _data1 = sd1; _data2 = sd2; //_isPeriodic = false;
+ }
+ bool IsSame( const TopoDS_Face& face ) const
+ {
+ return _subMesh->GetSubShape().IsSame( face );
+ }
+ bool IsShrunk( const TopTools_MapOfShape& shrunkFaces ) const
+ {
+ return shrunkFaces.Contains( _subMesh->GetSubShape() );
+ }
+
+ //================================================================================
+ /*!
+ * Check if meshes on two FACEs are equal
+ */
+ bool IsPeriodic( ShrinkFace& other, PeriodicFaces& periodic )
+ {
+ if ( !IsSameNbElements( other ))
+ return false;
+
+ this->SetBoundary();
+ other.SetBoundary();
+ if ( this->_boundarySize != other._boundarySize ||
+ this->_nbBoundaryParts != other._nbBoundaryParts )
+ return false;
+
+ for ( int isReverse = 0; isReverse < 2; ++isReverse )
+ {
+ if ( isReverse )
+ Reverse( _boundary );
+
+ // check boundaries
+ bool equalBoundary = false;
+ for ( int iP = 0; iP < _nbBoundaryParts && !equalBoundary; ++iP )
+ {
+ if ( ! ( equalBoundary = ( this->_boundary == other._boundary )))
+ // set first part at end
+ _boundary.splice( _boundary.end(), _boundary, _boundary.begin() );
+ }
+ if ( !equalBoundary )
+ continue;
+
+ // check connectivity
+ std::set<const SMDS_MeshElement*> elemsThis, elemsOther;
+ this->GetElements( elemsThis );
+ other.GetElements( elemsOther );
+ SMESH_MeshEditor::Sew_Error err =
+ SMESH_MeshEditor::FindMatchingNodes( elemsThis, elemsOther,
+ this->_boundary.front().Node(0),
+ other._boundary.front().Node(0),
+ this->_boundary.front().Node(1),
+ other._boundary.front().Node(1),
+ periodic._nnMap );
+ if ( err != SMESH_MeshEditor::SEW_OK )
+ continue;
+
+ // check node positions
+ std::vector< gp_XYZ > srcPnts, tgtPnts;
+ this->GetBoundaryPoints( srcPnts );
+ other.GetBoundaryPoints( tgtPnts );
+ if ( !periodic._trsf.Solve( srcPnts, tgtPnts )) {
+ continue;
+ }
+ double tol = std::numeric_limits<double>::max();
+ for ( size_t i = 1; i < srcPnts.size(); ++i ) {
+ tol = Min( tol, ( srcPnts[i-1] - srcPnts[i] ).SquareModulus() );
+ }
+ tol = 0.01 * Sqrt( tol );
+ bool nodeCoincide = true;
+ TNodeNodeMap::iterator n2n = periodic._nnMap.begin();
+ for ( ; n2n != periodic._nnMap.end() && nodeCoincide; ++n2n )
+ {
+ SMESH_NodeXYZ nSrc = n2n->first;
+ SMESH_NodeXYZ nTgt = n2n->second;
+ gp_XYZ pTgt = periodic._trsf.Transform( nSrc );
+ nodeCoincide = (( pTgt - nTgt ).SquareModulus() < tol );
+ }
+ if ( nodeCoincide )
+ return true;
+ }
+ return false;
+ }
+
+ bool IsSameNbElements( ShrinkFace& other ) // check number of mesh faces
+ {
+ SMESHDS_SubMesh* sm1 = this->_subMesh->GetSubMeshDS();
+ SMESHDS_SubMesh* sm2 = other._subMesh->GetSubMeshDS();
+ return ( sm1->NbElements() == sm2->NbElements() &&
+ sm1->NbNodes() == sm2->NbNodes() );
+ }
+
+ void Reverse( std::list< BndPart >& boundary )
+ {
+ boundary.reverse();
+ for ( std::list< BndPart >::iterator part = boundary.begin(); part != boundary.end(); ++part )
+ part->Reverse();
+ }
+
+ void SetBoundary()
+ {
+ if ( !_boundary.empty() )
+ return;
+
+ TopoDS_Face F = TopoDS::Face( _subMesh->GetSubShape() );
+ if ( F.Orientation() >= TopAbs_INTERNAL ) F.Orientation( TopAbs_FORWARD );
+ std::list< TopoDS_Edge > edges;
+ std::list< int > nbEdgesInWire;
+ /*int nbWires =*/ SMESH_Block::GetOrderedEdges (F, edges, nbEdgesInWire);
+
+ // std::list< TopoDS_Edge >::iterator edgesEnd = edges.end();
+ // if ( nbWires > 1 ) {
+ // edgesEnd = edges.begin();
+ // std::advance( edgesEnd, nbEdgesInWire.front() );
+ // }
+ StdMeshers_FaceSide fSide( F, edges, _subMesh->GetFather(),
+ /*fwd=*/true, /*skipMedium=*/true );
+ _boundarySize = fSide.NbSegments();
+
+ //TopoDS_Vertex vv[2];
+ //std::list< TopoDS_Edge >::iterator edgeIt = edges.begin();
+ for ( int iE = 0; iE < nbEdgesInWire.front(); ++iE )
+ {
+ BndPart bndPart;
+ _EdgesOnShape* eos = _data1->GetShapeEdges( fSide.EdgeID( iE ));
+
+ bndPart._isShrink = ( eos->SWOLType() == TopAbs_FACE );
+ if ( bndPart._isShrink )
+ if (( _data1->_noShrinkShapes.count( eos->_shapeID )) ||
+ ( _data2 && _data2->_noShrinkShapes.count( eos->_shapeID )))
+ bndPart._isShrink = false;
+
+ if ( bndPart._isShrink )
+ {
+ bndPart._hyp = & eos->_hyp;
+ _EdgesOnShape* eov[2] = { _data1->GetShapeEdges( fSide.FirstVertex( iE )),
+ _data1->GetShapeEdges( fSide.LastVertex ( iE )) };
+ for ( int iV = 0; iV < 2; ++iV )
+ {
+ bndPart._vertHyp [iV] = & eov[iV]->_hyp;
+ bndPart._vertSWOLType[iV] = eov[iV]->SWOLType();
+ if ( _data1->_noShrinkShapes.count( eov[iV]->_shapeID ))
+ bndPart._vertSWOLType[iV] = TopAbs_SHAPE;
+ if ( _data2 && bndPart._vertSWOLType[iV] != TopAbs_SHAPE )
+ {
+ eov[iV] = _data2->GetShapeEdges( iV ? fSide.LastVertex(iE) : fSide.FirstVertex(iE ));
+ if ( _data2->_noShrinkShapes.count( eov[iV]->_shapeID ))
+ bndPart._vertSWOLType[iV] = TopAbs_SHAPE;
+ else if ( eov[iV]->SWOLType() > bndPart._vertSWOLType[iV] )
+ bndPart._vertSWOLType[iV] = eov[iV]->SWOLType();
+ }
+ }
+ }
+ std::vector<const SMDS_MeshNode*> nodes = fSide.GetOrderedNodes( iE );
+ bndPart._nodes.assign( nodes.begin(), nodes.end() );
+ bndPart._nbSegments = bndPart._nodes.size() - 1;
+
+ if ( _boundary.empty() || ! _boundary.back().CanAppend( bndPart ))
+ _boundary.push_back( bndPart );
+ else
+ _boundary.back().Append( bndPart );
+ }
+
+ _nbBoundaryParts = _boundary.size();
+ if ( _nbBoundaryParts > 1 && _boundary.front()._isShrink == _boundary.back()._isShrink )
+ {
+ _boundary.back().Append( _boundary.front() );
+ _boundary.pop_front();
+ --_nbBoundaryParts;
+ }
+ }
+
+ void GetElements( std::set<const SMDS_MeshElement*>& theElems)
+ {
+ if ( SMESHDS_SubMesh* sm = _subMesh->GetSubMeshDS() )
+ for ( SMDS_ElemIteratorPtr fIt = sm->GetElements(); fIt->more(); )
+ theElems.insert( theElems.end(), fIt->next() );
+
+ return ;
+ }
+
+ void GetBoundaryPoints( std::vector< gp_XYZ >& points )
+ {
+ points.reserve( _boundarySize );
+ size_t nb = _boundary.rbegin()->_nodes.size();
+ int lastID = _boundary.rbegin()->Node( nb - 1 )->GetID();
+ std::list< BndPart >::const_iterator part = _boundary.begin();
+ for ( ; part != _boundary.end(); ++part )
+ {
+ size_t nb = part->_nodes.size();
+ size_t iF = 0;
+ size_t iR = nb - 1;
+ size_t* i = part->_isReverse ? &iR : &iF;
+ if ( part->_nodes[ *i ]->GetID() == lastID )
+ ++iF, --iR;
+ for ( ; iF < nb; ++iF, --iR )
+ points.push_back( part->_nodes[ *i ]);
+ --iF, ++iR;
+ lastID = part->_nodes[ *i ]->GetID();
+ }
+ }
+ }; // struct ShrinkFace
+
+ //--------------------------------------------------------------------------------
+ /*!
+ * \brief Periodic FACEs
+ */
+ struct Periodicity
+ {
+ std::vector< ShrinkFace > _shrinkFaces;
+ std::vector< PeriodicFaces > _periodicFaces;
+
+ PeriodicFaces* GetPeriodic( const TopoDS_Face& face, const TopTools_MapOfShape& shrunkFaces )
+ {
+ for ( size_t i = 0; i < _periodicFaces.size(); ++i )
+ if ( _periodicFaces[ i ].IncludeShrunk( face, shrunkFaces ))
+ return & _periodicFaces[ i ];
+ return 0;
+ }
+ };
+
+ //================================================================================
+ /*!
+ * Check if a pair includes the given FACE and the other FACE is already shrunk
+ */
+ bool PeriodicFaces::IncludeShrunk( const TopoDS_Face& face,
+ const TopTools_MapOfShape& shrunkFaces ) const
+ {
+ return (( _shriFace[0]->IsSame( face ) && _shriFace[1]->IsShrunk( shrunkFaces )) ||
+ ( _shriFace[1]->IsSame( face ) && _shriFace[0]->IsShrunk( shrunkFaces )));
+ }
+
+ //================================================================================
+ /*!
+ * Make equal meshes on periodic faces by moving corresponding nodes
+ */
+ bool PeriodicFaces::MoveNodes( const TopoDS_Face& tgtFace )
+ {
+ int iTgt = _shriFace[1]->IsSame( tgtFace );
+ int iSrc = 1 - iTgt;
+
+ _SolidData* dataSrc = _shriFace[iSrc]->_data1;
+ _SolidData* dataTgt = _shriFace[iTgt]->_data1;
+
+ Trsf * trsf = & _trsf, trsfInverse;
+ if ( iSrc != 0 )
+ {
+ trsfInverse = _trsf;
+ trsfInverse.Invert();
+ trsf = &trsfInverse;
+ }
+ SMESHDS_Mesh* meshDS = dataSrc->GetHelper().GetMeshDS();
+
+ TNode2Edge::iterator n2e;
+ TNodeNodeMap::iterator n2n = _nnMap.begin();
+ for ( ; n2n != _nnMap.end(); ++n2n )
+ {
+ const SMDS_MeshNode* const* nn = & n2n->first;
+ const SMDS_MeshNode* nSrc = nn[ iSrc ];
+ const SMDS_MeshNode* nTgt = nn[ iTgt ];
+
+ if (( nSrc->GetPosition()->GetDim() == 2 ) ||
+ (( n2e = dataSrc->_n2eMap.find( nSrc )) == dataSrc->_n2eMap.end() ))
+ {
+ SMESH_NodeXYZ pSrc = nSrc;
+ gp_XYZ pTgt = trsf->Transform( pSrc );
+ meshDS->MoveNode( nTgt, pTgt.X(), pTgt.Y(), pTgt.Z() );
+ }
+ else
+ {
+ _LayerEdge* leSrc = n2e->second;
+ n2e = dataTgt->_n2eMap.find( nTgt );
+ if ( n2e == dataTgt->_n2eMap.end() )
+ break;
+ _LayerEdge* leTgt = n2e->second;
+ if ( leSrc->_nodes.size() != leTgt->_nodes.size() )
+ break;
+ for ( size_t iN = 1; iN < leSrc->_nodes.size(); ++iN )
+ {
+ SMESH_NodeXYZ pSrc = leSrc->_nodes[ iN ];
+ gp_XYZ pTgt = trsf->Transform( pSrc );
+ meshDS->MoveNode( leTgt->_nodes[ iN ], pTgt.X(), pTgt.Y(), pTgt.Z() );
+ }
+ }
+ }
+ bool done = ( n2n == _nnMap.end() );
+ // cout << "MMMMMMMOOOOOOOOOOVVVVVVVVVVVEEEEEEEE "
+ // << _shriFace[iSrc]->_subMesh->GetId() << " -> "
+ // << _shriFace[iTgt]->_subMesh->GetId() << " -- "
+ // << ( done ? "DONE" : "FAIL") << endl;
+
+ return done;
+ }
+} // namespace VISCOUS_3D; Periodicity part
+
+
+//================================================================================
+/*!
+ * \brief Find FACEs to shrink, that are equally meshed before shrink (i.e. periodic)
+ * and should remain equal after shrink
+ */
+//================================================================================
+
+void _ViscousBuilder::findPeriodicFaces()
+{
+ // make map of (ids of FACEs to shrink mesh on) to (list of _SolidData containing
+ // _LayerEdge's inflated along FACE or EDGE)
+ std::map< TGeomID, std::list< _SolidData* > > id2sdMap;
+ for ( size_t i = 0 ; i < _sdVec.size(); ++i )
+ {
+ _SolidData& data = _sdVec[i];
+ std::map< TGeomID, TopoDS_Shape >::iterator s2s = data._shrinkShape2Shape.begin();
+ for (; s2s != data._shrinkShape2Shape.end(); ++s2s )
+ if ( s2s->second.ShapeType() == TopAbs_FACE )
+ id2sdMap[ getMeshDS()->ShapeToIndex( s2s->second )].push_back( &data );
+ }
+
+ _periodicity.reset( new Periodicity );
+ _periodicity->_shrinkFaces.resize( id2sdMap.size() );
+
+ std::map< TGeomID, std::list< _SolidData* > >::iterator id2sdIt = id2sdMap.begin();
+ for ( size_t i = 0; i < id2sdMap.size(); ++i, ++id2sdIt )
+ {
+ _SolidData* sd1 = id2sdIt->second.front();
+ _SolidData* sd2 = id2sdIt->second.back();
+ _periodicity->_shrinkFaces[ i ].Init( _mesh->GetSubMeshContaining( id2sdIt->first ), sd1, sd2 );
+ }
+
+ for ( size_t i1 = 0; i1 < _periodicity->_shrinkFaces.size(); ++i1 )
+ for ( size_t i2 = i1 + 1; i2 < _periodicity->_shrinkFaces.size(); ++i2 )
+ {
+ PeriodicFaces pf( & _periodicity->_shrinkFaces[ i1 ],
+ & _periodicity->_shrinkFaces[ i2 ]);
+ if ( pf._shriFace[0]->IsPeriodic( *pf._shriFace[1], pf ))
+ {
+ _periodicity->_periodicFaces.push_back( pf );
+ }
+ }
+ return;
+}
+