1 // Copyright (C) 2007-2020 CEA/DEN, EDF R&D, OPEN CASCADE
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 // Lesser General Public License for more details.
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
20 // File : StdMeshers_RadialQuadrangle_1D2D.cxx
23 #include "StdMeshers_RadialQuadrangle_1D2D.hxx"
25 #include "StdMeshers_NumberOfLayers.hxx"
26 #include "StdMeshers_LayerDistribution.hxx"
27 #include "StdMeshers_Regular_1D.hxx"
28 #include "StdMeshers_NumberOfSegments.hxx"
29 #include "StdMeshers_FaceSide.hxx"
31 #include "SMDS_MeshNode.hxx"
32 #include "SMESHDS_Mesh.hxx"
33 #include "SMESHDS_SubMesh.hxx"
34 #include "SMESH_Gen.hxx"
35 #include "SMESH_HypoFilter.hxx"
36 #include "SMESH_Mesh.hxx"
37 #include "SMESH_MesherHelper.hxx"
38 #include "SMESH_subMesh.hxx"
39 #include "SMESH_subMeshEventListener.hxx"
40 #include "SMESH_Block.hxx"
42 #include "utilities.h"
44 #include <BRepAdaptor_CompCurve.hxx>
45 #include <BRepAdaptor_Curve.hxx>
46 #include <BRepBuilderAPI_MakeEdge.hxx>
47 #include <BRep_Builder.hxx>
48 #include <BRep_Tool.hxx>
49 #include <GCPnts_AbscissaPoint.hxx>
50 #include <Geom2d_Line.hxx>
51 #include <Geom2d_TrimmedCurve.hxx>
52 #include <GeomAPI_ProjectPointOnSurf.hxx>
53 #include <Geom_Circle.hxx>
54 #include <Geom_Line.hxx>
55 #include <Geom_TrimmedCurve.hxx>
56 #include <ShapeFix_Edge.hxx>
57 #include <TColgp_SequenceOfPnt.hxx>
58 #include <TColgp_SequenceOfPnt2d.hxx>
60 #include <TopExp_Explorer.hxx>
61 #include <TopTools_ListIteratorOfListOfShape.hxx>
67 #define RETURN_BAD_RESULT(msg) { MESSAGE(")-: Error: " << msg); return false; }
68 #define gpXYZ(n) gp_XYZ(n->X(),n->Y(),n->Z())
71 //=======================================================================
72 //function : StdMeshers_RadialQuadrangle_1D2D
74 //=======================================================================
76 StdMeshers_RadialQuadrangle_1D2D::StdMeshers_RadialQuadrangle_1D2D(int hypId,
78 :StdMeshers_Quadrangle_2D( hypId, gen )
80 _name = "RadialQuadrangle_1D2D";
81 _shapeType = (1 << TopAbs_FACE); // 1 bit per shape type
83 _compatibleHypothesis.push_back("LayerDistribution2D");
84 _compatibleHypothesis.push_back("NumberOfLayers2D");
85 _requireDiscreteBoundary = false;
86 _supportSubmeshes = true;
87 _neededLowerHyps[ 1 ] = true; // suppress warning on hiding a global 1D algo
90 myDistributionHypo = 0;
94 //================================================================================
98 //================================================================================
100 StdMeshers_RadialQuadrangle_1D2D::~StdMeshers_RadialQuadrangle_1D2D()
104 //=======================================================================
105 //function : CheckHypothesis
107 //=======================================================================
109 bool StdMeshers_RadialQuadrangle_1D2D::CheckHypothesis
111 const TopoDS_Shape& aShape,
112 SMESH_Hypothesis::Hypothesis_Status& aStatus)
116 myDistributionHypo = 0;
118 list <const SMESHDS_Hypothesis * >::const_iterator itl;
120 const list <const SMESHDS_Hypothesis * >&hyps = GetUsedHypothesis(aMesh, aShape);
121 if ( hyps.size() == 0 ) {
122 aStatus = SMESH_Hypothesis::HYP_OK;
123 return true; // can work with no hypothesis
126 if ( hyps.size() > 1 ) {
127 aStatus = SMESH_Hypothesis::HYP_ALREADY_EXIST;
131 const SMESHDS_Hypothesis *theHyp = hyps.front();
133 string hypName = theHyp->GetName();
135 if (hypName == "NumberOfLayers2D") {
136 myNbLayerHypo = static_cast<const StdMeshers_NumberOfLayers *>(theHyp);
137 aStatus = SMESH_Hypothesis::HYP_OK;
140 if (hypName == "LayerDistribution2D") {
141 myDistributionHypo = static_cast<const StdMeshers_LayerDistribution *>(theHyp);
142 aStatus = SMESH_Hypothesis::HYP_OK;
145 aStatus = SMESH_Hypothesis::HYP_INCOMPATIBLE;
151 // ------------------------------------------------------------------------------
153 * \brief Listener used to mark edges meshed by StdMeshers_RadialQuadrangle_1D2D
155 class TEdgeMarker : public SMESH_subMeshEventListener
157 TEdgeMarker(): SMESH_subMeshEventListener(/*isDeletable=*/false,
158 "StdMeshers_RadialQuadrangle_1D2D::TEdgeMarker") {}
160 //!< Return static listener
161 static SMESH_subMeshEventListener* getListener()
163 static TEdgeMarker theEdgeMarker;
164 return &theEdgeMarker;
166 //! Clear edge sumbesh if something happens on face
167 void ProcessEvent(const int /*event*/,
169 SMESH_subMesh* /*faceSubMesh*/,
170 EventListenerData* edgesHolder,
171 const SMESH_Hypothesis* /*hyp*/)
173 if ( edgesHolder && eventType == SMESH_subMesh::ALGO_EVENT)
175 std::list<SMESH_subMesh*>::iterator smIt = edgesHolder->mySubMeshes.begin();
176 for ( ; smIt != edgesHolder->mySubMeshes.end(); ++smIt )
178 SMESH_subMesh* edgeSM = *smIt;
179 edgeSM->ComputeStateEngine( SMESH_subMesh::CLEAN );
183 //! Store edge SMESH_subMesh'es computed by the algo
184 static void markEdge( const TopoDS_Edge& edge, SMESH_subMesh* faceSM )
186 if ( SMESH_subMesh* edgeSM = faceSM->GetFather()->GetSubMeshContaining( edge ))
188 EventListenerData* edgesHolder = faceSM->GetEventListenerData( getListener() );
191 std::list<SMESH_subMesh*>::iterator smIt = std::find( edgesHolder->mySubMeshes.begin(),
192 edgesHolder->mySubMeshes.end(),
194 if ( smIt == edgesHolder->mySubMeshes.end() )
195 edgesHolder->mySubMeshes.push_back( edgeSM );
199 edgesHolder = SMESH_subMeshEventListenerData::MakeData( edgeSM );
200 faceSM->SetEventListener( TEdgeMarker::getListener(), edgesHolder, faceSM );
206 //================================================================================
208 * \brief Return sides of the face connected in the order: aCircEdge, aLinEdge1, aLinEdge2
209 * \retval int - nb of sides
211 //================================================================================
213 int analyseFace(const TopoDS_Shape& aShape,
215 StdMeshers_FaceSidePtr& aCircSide,
216 StdMeshers_FaceSidePtr& aLinSide1,
217 StdMeshers_FaceSidePtr& aLinSide2,
218 SMESH_MesherHelper* helper)
220 const TopoDS_Face& face = TopoDS::Face( aShape );
221 aCircSide.reset(); aLinSide1.reset(); aLinSide2.reset();
223 list< TopoDS_Edge > edges;
224 list< int > nbEdgesInWire;
225 int nbWire = SMESH_Block::GetOrderedEdges ( face, edges, nbEdgesInWire );
226 if ( nbWire > 2 || nbEdgesInWire.front() < 1 ) return 0;
228 // remove degenerated EDGEs
229 TopTools_MapOfShape degenVV;
230 list<TopoDS_Edge>::iterator edge = edges.begin();
231 while ( edge != edges.end() )
232 if ( SMESH_Algo::isDegenerated( *edge ))
234 degenVV.Add( SMESH_MesherHelper::IthVertex( 0, *edge ));
235 degenVV.Add( SMESH_MesherHelper::IthVertex( 1, *edge ));
236 edge = edges.erase( edge );
242 int nbEdges = edges.size();
244 // find VERTEXes between continues EDGEs
245 TopTools_MapOfShape contVV;
248 TopoDS_Edge ePrev = edges.back();
249 for ( edge = edges.begin(); edge != edges.end(); ++edge )
251 if ( SMESH_Algo::IsContinuous( ePrev, *edge ))
252 contVV.Add( SMESH_MesherHelper::IthVertex( 0, *edge ));
256 // make edges start from a non-continues VERTEX
257 if ( 1 < contVV.Extent() && contVV.Extent() < nbEdges )
259 while ( contVV.Contains( SMESH_MesherHelper::IthVertex( 0, edges.front() )))
260 edges.splice( edges.end(), edges, edges.begin() );
265 while ( !edges.empty() )
267 list< TopoDS_Edge > sideEdges;
268 sideEdges.splice( sideEdges.end(), edges, edges.begin() );
269 while ( !edges.empty() &&
270 contVV.Contains( SMESH_MesherHelper::IthVertex( 0, edges.front() )))
271 sideEdges.splice( sideEdges.end(), edges, edges.begin() );
273 StdMeshers_FaceSidePtr side;
275 side = StdMeshers_FaceSide::New( face, sideEdges, aMesh,
276 /*isFwd=*/true, /*skipMedium=*/ true, helper );
277 sides.push_back( side );
280 if ( !aMesh ) // call from IsApplicable()
283 if ( sides.size() > 3 )
286 if ( nbWire == 2 && (( sides.size() != 2 ) ||
287 ( sides[0]->IsClosed() && sides[1]->IsClosed() ) ||
288 ( !sides[0]->IsClosed() && !sides[1]->IsClosed() )))
291 // detect an elliptic side
293 if ( sides.size() == 1 )
295 aCircSide = sides[0];
299 // sort sides by deviation from a straight line
300 multimap< double, int > deviation2sideInd;
301 const double nbSamples = 7;
302 for ( size_t iS = 0; iS < sides.size(); ++iS )
304 gp_Pnt pf = BRep_Tool::Pnt( sides[iS]->FirstVertex() );
305 gp_Pnt pl = BRep_Tool::Pnt( sides[iS]->LastVertex() );
307 double v1Len = v1.Magnitude();
308 if ( v1Len < std::numeric_limits< double >::min() )
310 deviation2sideInd.insert( make_pair( sides[iS]->Length(), iS )); // the side seems closed
314 for ( int i = 0; i < nbSamples; ++i )
316 gp_Pnt pi( sides[iS]->Value3d(( i + 1 ) / nbSamples ));
318 double h = 0.5 * v1.Crossed( vi ).Magnitude() / v1Len;
319 devia = Max( devia, h );
321 deviation2sideInd.insert( make_pair( devia, iS ));
323 double maxDevi = deviation2sideInd.rbegin()->first;
324 if ( maxDevi < 1e-7 && sides.size() == 3 )
326 // a triangle FACE; use a side with the most outstanding length as an elliptic one
327 deviation2sideInd.clear();
328 multimap< double, int > len2sideInd;
329 for ( size_t iS = 0; iS < sides.size(); ++iS )
330 len2sideInd.insert( make_pair( sides[iS]->Length(), iS ));
332 multimap< double, int >::iterator l2i = len2sideInd.begin();
333 double len0 = l2i->first;
334 double len1 = (++l2i)->first;
335 double len2 = (++l2i)->first;
336 if ( len1 - len0 > len2 - len1 )
337 deviation2sideInd.insert( std::make_pair( 0., len2sideInd.begin()->second ));
339 deviation2sideInd.insert( std::make_pair( 0., len2sideInd.rbegin()->second ));
342 double minDevi = deviation2sideInd.begin()->first;
343 int iMinCurv = deviation2sideInd.begin()->second;
344 if ( sides.size() == 3 && degenVV.Size() == 1 &&
345 minDevi / sides[ iMinCurv ]->Length() > 1e-3 )
347 // a triangle with curved sides and a degenerated EDGE (IPAL54585);
348 // use a side opposite to the degenerated EDGE as an elliptic one
349 for ( size_t iS = 0; iS < sides.size(); ++iS )
350 if ( degenVV.Contains( sides[ iS ]->FirstVertex() ))
352 deviation2sideInd.clear();
353 deviation2sideInd.insert( std::make_pair( 0.,( iS + 1 ) % sides.size() ));
358 int iCirc = deviation2sideInd.rbegin()->second;
359 aCircSide = sides[ iCirc ];
360 aLinSide1 = sides[( iCirc + 1 ) % sides.size() ];
361 if ( sides.size() > 2 )
363 aLinSide2 = sides[( iCirc + 2 ) % sides.size() ];
364 aLinSide2->Reverse(); // to be "parallel" to aLinSide1
367 if (( nbWire == 2 && aLinSide1 ) &&
368 ( aLinSide1->Edge(0).Orientation() == TopAbs_INTERNAL ) &&
369 ( aCircSide->IsClosed() ))
371 // assure that aCircSide starts at aLinSide1
372 TopoDS_Vertex v0 = aLinSide1->FirstVertex();
373 TopoDS_Vertex v1 = aLinSide1->LastVertex();
374 if ( ! aCircSide->FirstVertex().IsSame( v0 ) &&
375 ! aCircSide->FirstVertex().IsSame( v1 ))
378 for ( ; iE < aCircSide->NbEdges(); ++iE )
379 if ( aCircSide->FirstVertex(iE).IsSame( v0 ) ||
380 aCircSide->FirstVertex(iE).IsSame( v1 ))
382 if ( iE == aCircSide->NbEdges() )
386 for ( int i = 0; i < aCircSide->NbEdges(); ++i, ++iE )
387 edges.push_back( aCircSide->Edge( iE % aCircSide->NbEdges() ));
389 aCircSide = StdMeshers_FaceSide::New( face, edges, aMesh,
390 /*isFwd=*/true, /*skipMedium=*/ true, helper );
397 //================================================================================
399 * \brief Checks if the common vertex between LinSide's lies inside the circle
401 * \return bool - false if there are 3 EDGEs and the corner is outside
403 //================================================================================
405 bool isCornerInsideCircle(const StdMeshers_FaceSidePtr& /*CircSide*/,
406 const StdMeshers_FaceSidePtr& /*LinSide1*/,
407 const StdMeshers_FaceSidePtr& /*LinSide2*/)
409 // if ( CircSide && LinSide1 && LinSide2 )
411 // Handle(Geom_Circle) aCirc = Handle(Geom_Circle)::DownCast( getCurve( CircSide ));
412 // TopoDS_Vertex aCommonV;
413 // if ( !aCirc.IsNull() &&
414 // TopExp::CommonVertex( LinSide1, LinSide2, aCommonV ))
416 // gp_Pnt aCommonP = BRep_Tool::Pnt( aCommonV );
417 // gp_Pnt aCenter = aCirc->Location();
418 // double dist = aCenter.Distance( aCommonP );
419 // return dist < 0.1 * aCirc->Radius();
425 //================================================================================
427 * \brief Create an EDGE connecting the ellipse center with the most distant point
429 * \param [in] circSide - the elliptic side
430 * \param [in] face - the FACE
431 * \param [out] circNode - a node on circSide most distant from the center
432 * \return TopoDS_Edge - the create EDGE
434 //================================================================================
436 TopoDS_Edge makeEdgeToCenter( StdMeshers_FaceSidePtr& circSide,
437 const TopoDS_Face& face,
438 const SMDS_MeshNode*& circNode)
440 // find the center and a point most distant from it
442 double maxDist = 0, normPar = 0;
444 for ( int i = 0; i < 32; ++i )
446 double u = 0.5 * i / 32.;
447 gp_Pnt2d p1 = circSide->Value2d( u );
448 gp_Pnt2d p2 = circSide->Value2d( u + 0.5 );
449 double dist = p1.SquareDistance( p2 );
450 if ( dist > maxDist )
458 gp_XY center = 0.5 * ( uv1 + uv2 );
459 double len = 0.5 * Sqrt( maxDist );
460 bool isCirc = ( Abs( len - circSide->Value2d( 0 ).Distance( center )) < 1e-3 * len );
462 // find a node closest to the most distant point
465 const UVPtStructVec& circNodes = circSide->GetUVPtStruct();
468 double minDist = 1e100;
469 for ( size_t i = 0; i <= circNodes.size(); ++i )
471 double dist = Abs( circNodes[i].normParam - normPar );
472 if ( dist < minDist )
479 circNode = circNodes[iDist].node;
480 uv1 = circNodes[iDist].UV();
481 len = ( uv1 - center ).Modulus();
483 // make the EDGE between the most distant point and the center
485 Handle(Geom2d_Line) line = new Geom2d_Line( uv1, gp_Dir2d( center - uv1 ) );
486 Handle(Geom2d_Curve) pcu = new Geom2d_TrimmedCurve( line, 0, len );
488 Handle(Geom_Surface) surface = BRep_Tool::Surface( face );
489 TopoDS_Edge edge = BRepBuilderAPI_MakeEdge( pcu, surface, 0, len );
491 BRep_Builder().UpdateEdge( edge, pcu, face, 1e-7 );
492 ShapeFix_Edge().FixAddCurve3d( edge );
495 // assure that circSide starts at circNode
496 if ( iDist != 0 && iDist != circNodes.size()-1 )
498 // create a new circSide
499 UVPtStructVec nodesNew;
500 nodesNew.reserve( circNodes.size() );
501 nodesNew.insert( nodesNew.end(), circNodes.begin() + iDist, circNodes.end() );
502 nodesNew.insert( nodesNew.end(), circNodes.begin() + 1, circNodes.begin() + iDist + 1 );
503 circSide = StdMeshers_FaceSide::New( nodesNew );
509 //================================================================================
511 * \brief Set nodes existing on a linSide to UVPtStructVec and create missing nodes
512 * corresponding to layerPositions
514 //================================================================================
516 void makeMissingMesh( StdMeshers_FaceSidePtr& linSide,
517 UVPtStructVec& nodes,
518 const vector< double >& layerPositions,
519 SMESH_MesherHelper* helper )
521 // tolerance to compare normParam
523 for ( size_t i = 1; i < layerPositions.size(); ++i )
524 tol = Min( tol, layerPositions[i] - layerPositions[i-1] );
527 // merge existing nodes with layerPositions into UVPtStructVec
528 // ------------------------------------------------------------
530 const UVPtStructVec& exiNodes = linSide->GetUVPtStruct();
532 nodes.reserve( layerPositions.size() + exiNodes.size() );
533 vector< double >::const_iterator pos = layerPositions.begin(), posEnd = layerPositions.end();
534 for ( size_t i = 0; i < exiNodes.size(); ++i )
536 switch ( exiNodes[i].node->GetPosition()->GetTypeOfPosition() )
538 case SMDS_TOP_VERTEX:
540 // allocate UVPtStruct's for non-existing nodes
541 while ( pos != posEnd && *pos < exiNodes[i].normParam - tol )
544 uvPS.normParam = *pos++;
545 nodes.push_back( uvPS );
547 // save existing node on a VERTEX
548 nodes.push_back( exiNodes[i] );
553 // save existing nodes on an EDGE
554 while ( i < exiNodes.size() && exiNodes[i].node->GetPosition()->GetDim() == 1 )
556 nodes.push_back( exiNodes[i++] );
558 // save existing node on a VERTEX
559 if ( i < exiNodes.size() && exiNodes[i].node->GetPosition()->GetDim() == 0 )
561 nodes.push_back( exiNodes[i] );
568 // skip layer positions covered by saved nodes
569 while ( pos != posEnd && *pos < nodes.back().normParam + tol )
574 // allocate UVPtStruct's for the rest non-existing nodes
575 while ( pos != posEnd )
578 uvPS.normParam = *pos++;
579 nodes.push_back( uvPS );
582 // create missing nodes
583 // ---------------------
585 SMESHDS_Mesh * meshDS = helper->GetMeshDS();
586 const TopoDS_Face& F = TopoDS::Face( helper->GetSubShape() );
587 Handle(ShapeAnalysis_Surface) surface = helper->GetSurface( F );
589 helper->SetElementsOnShape( false ); // we create nodes on EDGEs, not on the FACE
591 for ( size_t i = 0; i < nodes.size(); ++i )
593 if ( nodes[ i ].node ) continue;
595 gp_Pnt2d uv = linSide->Value2d( nodes[i].normParam );
596 gp_Pnt xyz = surface->Value( uv.X(), uv.Y() );
598 nodes[ i ].SetUV( uv.XY() );
599 nodes[ i ].node = helper->AddNode( xyz.X(), xyz.Y(), xyz.Z() );
602 // set nodes on VERTEXes
603 for ( int iE = 0; iE < linSide->NbEdges(); ++iE )
605 TopoDS_Vertex v = linSide->LastVertex( iE );
606 if ( SMESH_Algo::VertexNode( v, meshDS ))
609 double normPar = linSide->LastParameter( iE );
611 while ( nodes[ i ].normParam < normPar )
613 if (( nodes[ i ].normParam - normPar ) > ( normPar - nodes[ i-1 ].normParam ))
615 meshDS->SetNodeOnVertex( nodes[ i ].node, v );
618 // set nodes on EDGEs
620 for ( size_t i = 0; i < nodes.size(); ++i )
622 if ( nodes[ i ].node->getshapeId() > 0 ) continue;
624 double u = linSide->Parameter( nodes[i].normParam, edgeID );
625 meshDS->SetNodeOnEdge( nodes[ i ].node, edgeID, u );
629 for ( size_t i = 1; i < nodes.size(); ++i )
631 if ( meshDS->FindEdge( nodes[i].node, nodes[i-1].node )) continue;
633 const SMDS_MeshElement* seg = helper->AddEdge( nodes[i].node, nodes[i-1].node );
635 double normParam = 0.5 * ( nodes[i].normParam + nodes[i-1].normParam );
636 edgeID = linSide->EdgeID( linSide->EdgeIndex( normParam ));
637 meshDS->SetMeshElementOnShape( seg, edgeID );
640 helper->SetElementsOnShape( true );
642 } // end makeMissingMesh()
644 //================================================================================
645 //================================================================================
647 * \brief Class computing layers distribution using data of
648 * StdMeshers_LayerDistribution hypothesis
650 //================================================================================
651 //================================================================================
653 class TNodeDistributor: public StdMeshers_Regular_1D
655 list <const SMESHDS_Hypothesis *> myUsedHyps;
657 // -----------------------------------------------------------------------------
658 static TNodeDistributor* GetDistributor(SMESH_Mesh& aMesh)
660 const int myID = -1001;
661 TNodeDistributor* myHyp = dynamic_cast<TNodeDistributor*>( aMesh.GetHypothesis( myID ));
663 myHyp = new TNodeDistributor( myID, aMesh.GetGen() );
666 // -----------------------------------------------------------------------------
667 //! Computes distribution of nodes on a straight line ending at pIn and pOut
668 bool Compute( vector< double > & positions,
669 const TopoDS_Edge& edge,
670 Adaptor3d_Curve& curve,
674 const SMESH_Hypothesis* hyp1d)
676 if ( !hyp1d ) return error( "Invalid LayerDistribution hypothesis");
679 myUsedHyps.push_back( hyp1d );
681 SMESH_Hypothesis::Hypothesis_Status aStatus;
682 if ( !StdMeshers_Regular_1D::CheckHypothesis( mesh, edge, aStatus ))
683 return error( "StdMeshers_Regular_1D::CheckHypothesis() failed "
684 "with LayerDistribution hypothesis");
686 double len = GCPnts_AbscissaPoint::Length( curve, f, l );
688 list< double > params;
689 if ( !StdMeshers_Regular_1D::computeInternalParameters( mesh, curve, len, f, l, params, false ))
690 return error("StdMeshers_Regular_1D failed to compute layers distribution");
692 params.push_front( f );
693 params.push_back ( l );
695 positions.reserve( params.size() );
696 for (list<double>::iterator itU = params.begin(); itU != params.end(); itU++)
697 positions.push_back(( *itU - f ) / ( l - f ));
700 // -----------------------------------------------------------------------------
701 //! Make mesh on an edge using assigned 1d hyp or default nb of segments
702 bool ComputeCircularEdge( SMESH_Mesh& aMesh,
703 const StdMeshers_FaceSidePtr& aSide )
706 for ( int i = 0; i < aSide->NbEdges(); ++i )
708 const TopoDS_Edge& edge = aSide->Edge( i );
709 _gen->Compute( aMesh, edge );
710 SMESH_subMesh *sm = aMesh.GetSubMesh( edge );
711 if ( sm->GetComputeState() != SMESH_subMesh::COMPUTE_OK)
713 // find any 1d hyp assigned (there can be a hyp w/o algo)
714 myUsedHyps = SMESH_Algo::GetUsedHypothesis( aMesh, edge, /*ignoreAux=*/true );
715 Hypothesis_Status aStatus;
716 if ( !StdMeshers_Regular_1D::CheckHypothesis( aMesh, edge, aStatus ))
718 // no valid 1d hyp assigned, use default nb of segments
719 _hypType = NB_SEGMENTS;
720 _ivalue[ DISTR_TYPE_IND ] = StdMeshers_NumberOfSegments::DT_Regular;
721 _ivalue[ NB_SEGMENTS_IND ] = _gen->GetDefaultNbSegments();
723 ok &= StdMeshers_Regular_1D::Compute( aMesh, edge );
728 // -----------------------------------------------------------------------------
729 //! Make mesh on an edge using assigned 1d hyp or default nb of segments
730 bool EvaluateCircularEdge(SMESH_Mesh& aMesh,
731 const StdMeshers_FaceSidePtr aSide,
732 MapShapeNbElems& aResMap)
735 for ( int i = 0; i < aSide->NbEdges(); ++i )
737 const TopoDS_Edge& anEdge = aSide->Edge( i );
738 _gen->Evaluate( aMesh, anEdge, aResMap );
739 if ( aResMap.count( aMesh.GetSubMesh( anEdge )))
742 // find any 1d hyp assigned
743 myUsedHyps = SMESH_Algo::GetUsedHypothesis(aMesh, anEdge, /*ignoreAux=*/true);
744 Hypothesis_Status aStatus;
745 if ( !StdMeshers_Regular_1D::CheckHypothesis( aMesh, anEdge, aStatus ))
747 // no valid 1d hyp assigned, use default nb of segments
748 _hypType = NB_SEGMENTS;
749 _ivalue[ DISTR_TYPE_IND ] = StdMeshers_NumberOfSegments::DT_Regular;
750 _ivalue[ NB_SEGMENTS_IND ] = _gen->GetDefaultNbSegments();
752 ok &= StdMeshers_Regular_1D::Evaluate( aMesh, anEdge, aResMap );
757 // -----------------------------------------------------------------------------
758 TNodeDistributor( int hypId, SMESH_Gen* gen)
759 : StdMeshers_Regular_1D( hypId, gen)
762 // -----------------------------------------------------------------------------
763 virtual const list <const SMESHDS_Hypothesis *> &
764 GetUsedHypothesis(SMESH_Mesh &, const TopoDS_Shape &, const bool)
768 // -----------------------------------------------------------------------------
772 //=======================================================================
774 * \brief Allow algo to do something after persistent restoration
775 * \param subMesh - restored submesh
777 * call TEdgeMarker::markEdge()
779 //=======================================================================
781 void StdMeshers_RadialQuadrangle_1D2D::SubmeshRestored(SMESH_subMesh* faceSubMesh)
783 if ( !faceSubMesh->IsEmpty() )
784 SetEventListener( faceSubMesh );
787 //=======================================================================
789 * \brief Sets event listener to a submesh
790 * \param subMesh - submesh where algo is set
792 * This method is called when a submesh gets HYP_OK algo_state.
794 //=======================================================================
796 void StdMeshers_RadialQuadrangle_1D2D::SetEventListener(SMESH_subMesh* faceSubMesh)
798 for ( TopExp_Explorer e( faceSubMesh->GetSubShape(), TopAbs_EDGE ); e.More(); e.Next() )
800 TEdgeMarker::markEdge( TopoDS::Edge( e.Current() ), faceSubMesh );
804 //=======================================================================
807 //=======================================================================
809 bool StdMeshers_RadialQuadrangle_1D2D::Compute(SMESH_Mesh& aMesh,
810 const TopoDS_Shape& aShape)
812 SMESH_MesherHelper helper( aMesh );
813 StdMeshers_Quadrangle_2D::myHelper = & helper;
814 StdMeshers_Quadrangle_2D::myNeedSmooth = false;
815 StdMeshers_Quadrangle_2D::myCheckOri = false;
816 StdMeshers_Quadrangle_2D::myQuadList.clear();
818 myHelper->SetSubShape( aShape );
819 myHelper->SetElementsOnShape( true );
821 StdMeshers_FaceSidePtr circSide, linSide1, linSide2;
822 int nbSides = analyseFace( aShape, &aMesh, circSide, linSide1, linSide2, myHelper );
823 if( nbSides > 3 || nbSides < 1 )
824 return error("The face must be a full ellipse or a part of ellipse (i.e. the number "
825 "of edges is less or equal to 3 and one of them is an ellipse curve)");
827 // get not yet computed EDGEs
828 list< TopoDS_Edge > emptyEdges;
829 for ( TopExp_Explorer e( aShape, TopAbs_EDGE ); e.More(); e.Next() )
831 if ( aMesh.GetSubMesh( e.Current() )->IsEmpty() )
832 emptyEdges.push_back( TopoDS::Edge( e.Current() ));
835 TNodeDistributor* algo1d = TNodeDistributor::GetDistributor(aMesh);
837 if ( !algo1d->ComputeCircularEdge( aMesh, circSide ))
838 return error( algo1d->GetComputeError() );
841 TopoDS_Face F = TopoDS::Face(aShape);
842 Handle(Geom_Surface) S = BRep_Tool::Surface(F);
844 myHelper->IsQuadraticSubMesh( aShape );
846 vector< double > layerPositions; // [0,1]
848 const SMDS_MeshNode* centerNode = 0;
849 gp_Pnt2d centerUV(0,0);
851 // ------------------------------------------------------------------------------------------
852 if ( nbSides == 1 ) // full ellipse
854 const SMDS_MeshNode* circNode;
855 TopoDS_Edge linEdge = makeEdgeToCenter( circSide, F, circNode );
857 StdMeshers_FaceSidePtr tmpSide =
858 StdMeshers_FaceSide::New( F, linEdge, &aMesh, /*isFrw=*/true, /*skipMedium=*/true, myHelper );
860 if ( !computeLayerPositions( tmpSide, layerPositions ))
863 UVPtStructVec nodes( layerPositions.size() );
864 nodes[0].node = circNode;
865 for ( size_t i = 0; i < layerPositions.size(); ++i )
867 gp_Pnt2d uv = tmpSide->Value2d( layerPositions[i] );
868 gp_Pnt xyz = S->Value( uv.X(), uv.Y() );
870 nodes[ i ].SetUV( uv.XY() );
871 nodes[ i ].normParam = layerPositions[i];
873 nodes[ i ].node = myHelper->AddNode( xyz.X(), xyz.Y(), xyz.Z(), 0, uv.X(), uv.Y() );
876 linSide1 = StdMeshers_FaceSide::New( nodes );
877 linSide2 = StdMeshers_FaceSide::New( nodes );
879 centerNode = nodes.back().node;
880 centerUV = nodes.back().UV();
882 // ------------------------------------------------------------------------------------------
883 else if ( nbSides == 2 && linSide1->Edge(0).Orientation() == TopAbs_INTERNAL )
885 // full ellipse with an internal radial side
887 // eliminate INTERNAL orientation
888 list< TopoDS_Edge > edges;
889 for ( int iE = 0; iE < linSide1->NbEdges(); ++iE )
891 edges.push_back( linSide1->Edge( iE ));
892 edges.back().Orientation( TopAbs_FORWARD );
895 // orient the internal side
896 bool isVIn0Shared = false;
897 TopoDS_Vertex vIn0 = myHelper->IthVertex( 0, edges.front() );
898 for ( int iE = 0; iE < circSide->NbEdges() && !isVIn0Shared; ++iE )
899 isVIn0Shared = vIn0.IsSame( circSide->FirstVertex( iE ));
901 linSide1 = StdMeshers_FaceSide::New( F, edges, &aMesh,
902 /*isFrw=*/isVIn0Shared, /*skipMedium=*/true, myHelper );
905 if ( !computeLayerPositions( linSide1, layerPositions, &nbMeshedEdges ))
908 // merge existing nodes with new nodes at layerPositions into a UVPtStructVec
910 if ( nbMeshedEdges != linSide1->NbEdges() )
911 makeMissingMesh( linSide1, nodes, layerPositions, myHelper );
913 nodes = linSide1->GetUVPtStruct();
915 linSide1 = StdMeshers_FaceSide::New( nodes );
916 linSide2 = StdMeshers_FaceSide::New( nodes );
918 centerNode = nodes.back().node;
919 centerUV = nodes.back().UV();
921 // ------------------------------------------------------------------------------------------
922 else if ( nbSides == 2 )
924 // find positions of layers for the first half of linSide1
926 if ( !computeLayerPositions( linSide1, layerPositions, &nbMeshedEdges, /*useHalf=*/true ))
929 // make positions for the whole linSide1
930 for ( size_t i = 0; i < layerPositions.size(); ++i )
932 layerPositions[i] *= 0.5;
934 layerPositions.reserve( layerPositions.size() * 2 );
935 for ( int nb = layerPositions.size()-1; nb > 0; --nb )
936 layerPositions.push_back( layerPositions.back() + layerPositions[nb] - layerPositions[nb-1] );
938 // merge existing nodes with new nodes at layerPositions into a UVPtStructVec
940 if ( nbMeshedEdges != linSide1->NbEdges() )
941 makeMissingMesh( linSide1, nodes, layerPositions, myHelper );
943 nodes = linSide1->GetUVPtStruct();
945 // find a central node
947 while ( nodes[ i ].normParam < 0.5 ) ++i;
948 if (( nodes[ i ].normParam - 0.5 ) > ( 0.5 - nodes[ i-1 ].normParam )) --i;
950 // distribute nodes between two linear sides
951 UVPtStructVec nodes2( nodes.rbegin(), nodes.rbegin() + nodes.size() - i );
952 nodes.resize( i + 1 );
954 linSide1 = StdMeshers_FaceSide::New( nodes );
955 linSide2 = StdMeshers_FaceSide::New( nodes2 );
957 centerNode = nodes.back().node;
958 centerUV = nodes.back().UV();
960 // ------------------------------------------------------------------------------------------
963 // one curve must be a part of ellipse and 2 other curves must be segments of line
965 int nbMeshedEdges1, nbMeshedEdges2;
966 vector< double > layerPositions2;
967 bool ok1 = computeLayerPositions( linSide1, layerPositions, &nbMeshedEdges1 );
968 bool ok2 = computeLayerPositions( linSide2, layerPositions2, &nbMeshedEdges2 );
972 bool linSide1Computed = ( nbMeshedEdges1 == linSide1->NbEdges() );
973 bool linSide2Computed = ( nbMeshedEdges2 == linSide2->NbEdges() );
977 if ( linSide1Computed && !linSide2Computed )
979 // use layer positions of linSide1 to mesh linSide2
980 makeMissingMesh( linSide2, nodes, layerPositions, myHelper );
981 linSide2 = StdMeshers_FaceSide::New( nodes );
983 else if ( linSide2Computed && !linSide1Computed )
985 // use layer positions of linSide2 to mesh linSide1
986 makeMissingMesh( linSide1, nodes, layerPositions2, myHelper );
987 linSide1 = StdMeshers_FaceSide::New( nodes );
989 else if ( !linSide2Computed && !linSide1Computed )
991 // use layer positions of a longer side to mesh the shorter side
992 vector< double >& lp =
993 ( linSide1->Length() > linSide2->Length() ) ? layerPositions : layerPositions2;
995 makeMissingMesh( linSide1, nodes, lp, myHelper );
996 linSide1 = StdMeshers_FaceSide::New( nodes );
997 makeMissingMesh( linSide2, nodes, lp, myHelper );
998 linSide2 = StdMeshers_FaceSide::New( nodes );
1001 const UVPtStructVec& nodes2 = linSide2->GetUVPtStruct();
1002 centerNode = nodes2.back().node;
1003 centerUV = nodes2.back().UV();
1006 list< TopoDS_Edge >::iterator ee = emptyEdges.begin();
1007 for ( ; ee != emptyEdges.end(); ++ee )
1008 TEdgeMarker::markEdge( *ee, aMesh.GetSubMesh( F ));
1010 circSide->GetUVPtStruct(); // let sides take into account just computed nodes
1011 linSide1->GetUVPtStruct();
1012 linSide2->GetUVPtStruct();
1014 FaceQuadStruct::Ptr quad( new FaceQuadStruct );
1015 quad->side.resize( 4 );
1016 quad->side[0] = circSide;
1017 quad->side[1] = linSide1;
1018 quad->side[2] = StdMeshers_FaceSide::New( circSide.get(), centerNode, ¢erUV );
1019 quad->side[3] = linSide2;
1022 myQuadList.push_back( quad );
1024 // create quadrangles
1026 if ( linSide1->NbPoints() == linSide2->NbPoints() )
1027 ok = StdMeshers_Quadrangle_2D::computeQuadDominant( aMesh, F, quad );
1029 ok = StdMeshers_Quadrangle_2D::computeTriangles( aMesh, F, quad );
1031 if ( helper.HasDegeneratedEdges() )
1033 StdMeshers_Quadrangle_2D::myNeedSmooth = true;
1034 StdMeshers_Quadrangle_2D::smooth( quad );
1037 StdMeshers_Quadrangle_2D::myHelper = 0;
1042 //================================================================================
1044 * \brief Compute nodes on the radial edge
1045 * \retval int - nb of segments on the linSide
1047 //================================================================================
1049 int StdMeshers_RadialQuadrangle_1D2D::computeLayerPositions(StdMeshers_FaceSidePtr linSide,
1050 vector< double > & positions,
1054 // First, try to compute positions of layers
1058 SMESH_Mesh * mesh = myHelper->GetMesh();
1060 const SMESH_Hypothesis* hyp1D = myDistributionHypo ? myDistributionHypo->GetLayerDistribution() : 0;
1061 int nbLayers = myNbLayerHypo ? myNbLayerHypo->GetNumberOfLayers() : 0;
1063 if ( !hyp1D && !nbLayers )
1065 // No own algo hypotheses assigned, so first try to find any 1D hypothesis.
1066 // find a hyp usable by TNodeDistributor
1067 TopoDS_Shape edge = linSide->Edge(0);
1068 const SMESH_HypoFilter* hypKind =
1069 TNodeDistributor::GetDistributor(*mesh)->GetCompatibleHypoFilter(/*ignoreAux=*/true);
1070 hyp1D = mesh->GetHypothesis( edge, *hypKind, /*fromAncestors=*/true);
1072 if ( hyp1D ) // try to compute with hyp1D
1074 BRepAdaptor_CompCurve* curve = linSide->GetCurve3d();
1075 SMESHUtils::Deleter< BRepAdaptor_CompCurve > delCurve( curve );
1076 double f = curve->FirstParameter();
1077 double l = curve->LastParameter();
1080 l = 0.5 * ( f + l ); // first part of linSide is used
1082 if ( !TNodeDistributor::GetDistributor(*mesh)->Compute( positions, linSide->Edge(0),
1083 *curve, f, l, *mesh, hyp1D ))
1085 if ( myDistributionHypo ) { // bad hyp assigned
1086 return error( TNodeDistributor::GetDistributor(*mesh)->GetComputeError() );
1089 // bad hyp found, its Ok, lets try with default nb of segments
1094 if ( positions.empty() ) // try to use nb of layers
1097 nbLayers = _gen->GetDefaultNbSegments();
1101 positions.resize( nbLayers + 1 );
1102 for ( int z = 0; z < nbLayers; ++z )
1103 positions[ z ] = double( z )/ double( nbLayers );
1104 positions.back() = 1;
1108 // Second, check presence of a mesh built by other algo on linSide
1110 int nbEdgesComputed = 0;
1111 for ( int i = 0; i < linSide->NbEdges(); ++i )
1113 nbEdgesComputed += ( !mesh->GetSubMesh( linSide->Edge(i))->IsEmpty() );
1116 if ( nbEdgesComputed == linSide->NbEdges() )
1118 const UVPtStructVec& points = linSide->GetUVPtStruct();
1119 if ( points.size() >= 2 )
1121 positions.resize( points.size() );
1122 for ( size_t i = 0; i < points.size(); ++i )
1123 positions[ i ] = points[i].normParam;
1127 if ( nbMeshedEdges ) *nbMeshedEdges = nbEdgesComputed;
1129 return positions.size();
1133 //=======================================================================
1134 //function : Evaluate
1136 //=======================================================================
1138 bool StdMeshers_RadialQuadrangle_1D2D::Evaluate(SMESH_Mesh& aMesh,
1139 const TopoDS_Shape& aShape,
1140 MapShapeNbElems& aResMap)
1142 if( aShape.ShapeType() != TopAbs_FACE ) {
1145 SMESH_subMesh * sm = aMesh.GetSubMesh(aShape);
1146 if( aResMap.count(sm) )
1149 vector<int>& aResVec =
1150 aResMap.insert( make_pair(sm, vector<int>(SMDSEntity_Last,0))).first->second;
1152 myHelper = new SMESH_MesherHelper( aMesh );
1153 myHelper->SetSubShape( aShape );
1154 SMESHUtils::Deleter<SMESH_MesherHelper> helperDeleter( myHelper );
1156 TNodeDistributor* algo1d = TNodeDistributor::GetDistributor(aMesh);
1158 StdMeshers_FaceSidePtr circSide, linSide1, linSide2;
1159 int nbSides = analyseFace( aShape, &aMesh, circSide, linSide1, linSide2, myHelper );
1160 if( nbSides > 3 || nbSides < 1 )
1163 if ( algo1d->EvaluateCircularEdge( aMesh, circSide, aResMap ))
1166 vector< double > layerPositions; // [0,1]
1168 // ------------------------------------------------------------------------------------------
1171 const TopoDS_Face& F = TopoDS::Face( aShape );
1173 const SMDS_MeshNode* circNode;
1174 TopoDS_Edge linEdge = makeEdgeToCenter( circSide, F, circNode );
1176 StdMeshers_FaceSidePtr tmpSide =
1177 StdMeshers_FaceSide::New( F, linEdge, &aMesh, /*isFrw=*/true, /*skipMedium=*/true, myHelper );
1179 if ( !computeLayerPositions( tmpSide, layerPositions ))
1182 // ------------------------------------------------------------------------------------------
1183 else if ( nbSides == 2 && linSide1->Edge(0).Orientation() == TopAbs_INTERNAL )
1185 if ( !computeLayerPositions( linSide1, layerPositions ))
1188 // ------------------------------------------------------------------------------------------
1189 else if ( nbSides == 2 )
1191 // find positions of layers for the first half of linSide1
1192 if ( !computeLayerPositions( linSide1, layerPositions, 0, /*useHalf=*/true ))
1195 // ------------------------------------------------------------------------------------------
1196 else // nbSides == 3
1198 if ( !computeLayerPositions(( linSide1->Length() > linSide2->Length() ) ? linSide1 : linSide2,
1203 bool isQuadratic = false;
1204 for ( TopExp_Explorer edge( aShape, TopAbs_EDGE ); edge.More() && !isQuadratic ; edge.Next() )
1206 sm = aMesh.GetSubMesh( edge.Current() );
1207 vector<int>& nbElems = aResMap[ sm ];
1208 if ( SMDSEntity_Quad_Edge < (int) nbElems.size() )
1209 isQuadratic = nbElems[ SMDSEntity_Quad_Edge ];
1212 int nbCircSegments = 0;
1213 for ( int iE = 0; iE < circSide->NbEdges(); ++iE )
1215 sm = aMesh.GetSubMesh( circSide->Edge( iE ));
1216 vector<int>& nbElems = aResMap[ sm ];
1217 if ( SMDSEntity_Quad_Edge < (int) nbElems.size() )
1218 nbCircSegments += ( nbElems[ SMDSEntity_Edge ] + nbElems[ SMDSEntity_Quad_Edge ]);
1221 int nbQuads = nbCircSegments * ( layerPositions.size() - 1 );
1222 int nbTria = nbCircSegments;
1223 int nbNodes = ( nbCircSegments - 1 ) * ( layerPositions.size() - 2 );
1226 nbNodes += (( nbCircSegments - 1 ) * ( layerPositions.size() - 1 ) + // radial
1227 ( nbCircSegments ) * ( layerPositions.size() - 2 )); // circular
1228 aResVec[SMDSEntity_Quad_Triangle ] = nbTria;
1229 aResVec[SMDSEntity_Quad_Quadrangle] = nbQuads;
1233 aResVec[SMDSEntity_Triangle ] = nbTria;
1234 aResVec[SMDSEntity_Quadrangle] = nbQuads;
1236 aResVec[SMDSEntity_Node] = nbNodes;
1240 // evaluation for linSides
1241 vector<int> aResVec(SMDSEntity_Last, 0);
1242 if ( isQuadratic ) {
1243 aResVec[SMDSEntity_Node ] = 2 * ( layerPositions.size() - 1 ) + 1;
1244 aResVec[SMDSEntity_Quad_Edge] = layerPositions.size() - 1;
1247 aResVec[SMDSEntity_Node] = layerPositions.size() - 2;
1248 aResVec[SMDSEntity_Edge] = layerPositions.size() - 1;
1250 sm = aMesh.GetSubMesh( linSide1->Edge(0) );
1251 aResMap[sm] = aResVec;
1254 sm = aMesh.GetSubMesh( linSide2->Edge(0) );
1255 aResMap[sm] = aResVec;
1262 //================================================================================
1264 * \brief Return true if the algorithm can compute mesh on this shape
1266 //================================================================================
1268 bool StdMeshers_RadialQuadrangle_1D2D::IsApplicable( const TopoDS_Shape & aShape, bool toCheckAll )
1270 int nbFoundFaces = 0;
1271 for (TopExp_Explorer exp( aShape, TopAbs_FACE ); exp.More(); exp.Next(), ++nbFoundFaces )
1273 StdMeshers_FaceSidePtr circSide, linSide1, linSide2;
1274 int nbSides = analyseFace( exp.Current(), NULL, circSide, linSide1, linSide2, NULL );
1275 bool ok = ( 0 < nbSides && nbSides <= 3 &&
1276 isCornerInsideCircle( circSide, linSide1, linSide2 ));
1277 if( toCheckAll && !ok ) return false;
1278 if( !toCheckAll && ok ) return true;
1280 if( toCheckAll && nbFoundFaces != 0 ) return true;