Salome HOME
Mantis issue 0020422: EDF 1070 GEOM: Partition anomaly - REGRESSION from 4.1.x.
[modules/smesh.git] / src / StdMeshers / StdMeshers_CompositeSegment_1D.cxx
1 //  Copyright (C) 2007-2008  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 //  Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 //  CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 //  This library is free software; you can redistribute it and/or
7 //  modify it under the terms of the GNU Lesser General Public
8 //  License as published by the Free Software Foundation; either
9 //  version 2.1 of the License.
10 //
11 //  This library is distributed in the hope that it will be useful,
12 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 //  Lesser General Public License for more details.
15 //
16 //  You should have received a copy of the GNU Lesser General Public
17 //  License along with this library; if not, write to the Free Software
18 //  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 //  See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22 //  SMESH SMESH : implementaion of SMESH idl descriptions
23 //  File   : StdMeshers_Regular_1D.cxx
24 //           Moved here from SMESH_Regular_1D.cxx
25 //  Author : Paul RASCLE, EDF
26 //  Module : SMESH
27 //  $Header$
28 //
29 #include "StdMeshers_CompositeSegment_1D.hxx"
30 #include "StdMeshers_FaceSide.hxx"
31 #include "StdMeshers_AutomaticLength.hxx"
32
33 #include "SMESH_Gen.hxx"
34 #include "SMESH_Mesh.hxx"
35 #include "SMESH_HypoFilter.hxx"
36 #include "SMESH_subMesh.hxx"
37 #include "SMESH_subMeshEventListener.hxx"
38 #include "SMESH_Comment.hxx"
39
40 #include "SMDS_MeshElement.hxx"
41 #include "SMDS_MeshNode.hxx"
42
43 #include "utilities.h"
44
45 #include <BRepAdaptor_CompCurve.hxx>
46 #include <BRep_Builder.hxx>
47 #include <GCPnts_AbscissaPoint.hxx>
48 #include <TopExp.hxx>
49 #include <TopExp_Explorer.hxx>
50 #include <TopTools_ListIteratorOfListOfShape.hxx>
51 #include <TopTools_MapOfShape.hxx>
52 #include <TopoDS.hxx>
53 #include <TopoDS_Edge.hxx>
54 #include <TopoDS_Vertex.hxx>
55 #include <TopoDS_Wire.hxx>
56 #include <gp_Pnt.hxx>
57
58 #include <Standard_ErrorHandler.hxx>
59 #include <Standard_Failure.hxx>
60
61 typedef SMESH_Comment TComm;
62
63 using namespace std;
64
65
66 namespace {
67
68   //================================================================================
69   /*!
70    * \brief Search for an edge conjunct to the given one by the vertex
71    *        Return NULL if more than 2 edges share the vertex or edges
72    *        continuity is less than C1
73    */
74   //================================================================================
75
76   TopoDS_Edge nextC1Edge(const TopoDS_Edge&  edge,
77                          SMESH_Mesh &        aMesh,
78                          const bool          forward)
79   {
80     TopoDS_Edge eNext;
81     TopTools_MapOfShape edgeCounter;
82     edgeCounter.Add( edge );
83     TopoDS_Vertex v;
84     if (edge.Orientation() <= TopAbs_REVERSED) // FORWARD || REVERSED
85       v = forward ? TopExp::LastVertex(edge,true) : TopExp::FirstVertex(edge,true);
86     else {
87       TopoDS_Iterator vIt (edge);
88       if (forward)
89         for (; vIt.More(); vIt.Next())
90           v = TopoDS::Vertex(vIt.Value()); // last
91       else if (vIt.More())
92         v = TopoDS::Vertex(vIt.Value()); // first
93     }
94     TopTools_ListIteratorOfListOfShape ancestIt = aMesh.GetAncestors( v );
95     for ( ; ancestIt.More(); ancestIt.Next() )
96     {
97       const TopoDS_Shape & ancestor = ancestIt.Value();
98       if ( ancestor.ShapeType() == TopAbs_EDGE && edgeCounter.Add( ancestor ))
99         eNext = TopoDS::Edge( ancestor );
100     }
101     if ( edgeCounter.Extent() < 3 && !eNext.IsNull() ) {
102       if ( SMESH_Algo::IsContinuous( edge, eNext )) {
103         // care of orientation
104         TopoDS_Vertex vn;
105         if (eNext.Orientation() <= TopAbs_REVERSED) // FORWARD || REVERSED
106           vn = forward ? TopExp::FirstVertex(eNext,true) : TopExp::LastVertex(eNext,true);
107         else {
108           TopoDS_Iterator vIt (eNext);
109           if (forward)
110             vn = TopoDS::Vertex(vIt.Value()); // first
111           else if (vIt.More())
112             for (; vIt.More(); vIt.Next())
113               vn = TopoDS::Vertex(vIt.Value()); // last
114         }
115
116         bool reverse = (!v.IsSame(vn));
117         if ( reverse )
118           eNext.Reverse();
119         return eNext;
120       }
121     }
122     return TopoDS_Edge();
123   }
124
125   //================================================================================
126   /*!
127    * \brief Update submeshes state for all edges and internal vertices,
128    * make them look computed even if none edge or node is set on them
129    */
130   //================================================================================
131
132   void careOfSubMeshes( StdMeshers_FaceSide& side, EventListener* eListener)
133   {
134     if ( side.NbEdges() < 2)
135       return;
136     for ( int iE = 0; iE < side.NbEdges(); ++iE )
137     {
138       // set listener and its data
139       EventListenerData * listenerData = new EventListenerData(true);
140       const TopoDS_Edge& edge = side.Edge( iE );
141       SMESH_subMesh * sm = side.GetMesh()->GetSubMesh( edge );
142       sm->SetEventListener( eListener, listenerData, sm );
143       // add edge submesh to the data
144       sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
145       if ( sm->GetComputeState() != SMESH_subMesh::COMPUTE_OK ) {
146         sm->SetIsAlwaysComputed( true );
147         listenerData->mySubMeshes.push_back( sm );
148       }
149       // add internal vertex submesh to the data
150       if ( iE )
151       {
152         TopoDS_Vertex V = side.FirstVertex( iE );
153         sm = side.GetMesh()->GetSubMesh( V );
154         sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
155         if ( sm->GetComputeState() != SMESH_subMesh::COMPUTE_OK )
156           sm->SetIsAlwaysComputed( true );
157         listenerData->mySubMeshes.push_back( sm );
158       }
159     }
160   }
161
162   //================================================================================
163   /*!
164    * \brief Class used to restore nodes on internal vertices of a complex side
165    *  when StdMeshers_CompositeSegment_1D algorithm is removed
166    */
167   //================================================================================
168
169   struct VertexNodesRestoringListener : public SMESH_subMeshEventListener
170   {
171     VertexNodesRestoringListener():SMESH_subMeshEventListener(0) // won't be deleted by submesh
172     {}
173   /*!
174    * \brief Restore nodes on internal vertices of a complex side
175    * \param event - algo_event or compute_event itself (of SMESH_subMesh)
176    * \param eventType - ALGO_EVENT or COMPUTE_EVENT (of SMESH_subMesh)
177    * \param subMesh - the submesh where the event occures
178    * \param data - listener data stored in the subMesh
179    * \param hyp - hypothesis, if eventType is algo_event
180    */
181     void ProcessEvent(const int          event,
182                       const int          eventType,
183                       SMESH_subMesh*     subMesh,
184                       EventListenerData* data,
185                       const SMESH_Hypothesis*  /*hyp*/)
186     {
187       bool hypRemoved = ( eventType == SMESH_subMesh::ALGO_EVENT &&
188                           subMesh->GetAlgoState() != SMESH_subMesh::HYP_OK );
189       if ( hypRemoved && data )
190       {
191         list<SMESH_subMesh*>::iterator smIt = data->mySubMeshes.begin();
192         for ( ; smIt != data->mySubMeshes.end(); ++smIt )
193         {
194           if ( SMESH_subMesh* sm = *smIt ) {
195             sm->SetIsAlwaysComputed( false );
196             sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
197           }
198         }
199       }
200       // at study restoration:
201       // check if edge submesh must have _alwaysComputed flag
202       else if ( event     == SMESH_subMesh::SUBMESH_RESTORED &&
203                 eventType == SMESH_subMesh::COMPUTE_EVENT )
204       {
205         if ( !subMesh->GetEventListenerData( this )) { // not yet checked
206           SMESHDS_Mesh * meshDS = subMesh->GetFather()->GetMeshDS();
207           if ( meshDS->NbNodes() > 0 ) {
208             // check if there are nodes on all vertices
209             bool hasNodesOnVerext = true;
210             SMESH_subMeshIteratorPtr smIt = subMesh->getDependsOnIterator(false,false);
211             while ( hasNodesOnVerext && smIt->more() ) {
212               SMESH_subMesh* sm = smIt->next();
213               hasNodesOnVerext = ( sm->GetSubMeshDS() && sm->GetSubMeshDS()->NbNodes() );
214             }
215             if ( !hasNodesOnVerext ) {
216               // check if an edge is a part of a complex side
217               TopoDS_Face face;
218               TopoDS_Edge edge = TopoDS::Edge( subMesh->GetSubShape() );
219               auto_ptr< StdMeshers_FaceSide > side
220                 ( StdMeshers_CompositeSegment_1D::GetFaceSide(*subMesh->GetFather(),
221                                                               edge, face, false ));
222               if ( side->NbEdges() > 1 && side->NbSegments() )
223                 careOfSubMeshes( *side, this );
224             }
225           }
226         }
227       }
228     }
229   }; // struct VertexNodesRestoringListener
230 }
231
232 //=============================================================================
233 /*!
234  *  
235  */
236 //=============================================================================
237
238 StdMeshers_CompositeSegment_1D::StdMeshers_CompositeSegment_1D(int         hypId,
239                                                                int         studyId,
240                                                                SMESH_Gen * gen)
241   :StdMeshers_Regular_1D(hypId, studyId, gen)
242 {
243   MESSAGE("StdMeshers_CompositeSegment_1D::StdMeshers_CompositeSegment_1D");
244   _name = "CompositeSegment_1D";
245   _EventListener = new VertexNodesRestoringListener();
246 }
247
248 //=============================================================================
249 /*!
250  *  
251  */
252 //=============================================================================
253
254 StdMeshers_CompositeSegment_1D::~StdMeshers_CompositeSegment_1D()
255 {
256   delete _EventListener;
257 }
258
259 //=============================================================================
260 /*!
261  * \brief Sets event listener to submeshes if necessary
262  * \param subMesh - submesh where algo is set
263  *
264  * This method is called when a submesh gets HYP_OK algo_state.
265  * After being set, event listener is notified on each event of a submesh.
266  */
267 //=============================================================================
268
269 void StdMeshers_CompositeSegment_1D::SetEventListener(SMESH_subMesh* subMesh)
270 {
271   // issue 0020279. Set "_alwaysComputed" flag to the submeshes of internal
272   // vertices of composite edge in order to avoid creation of vertices on
273   // them for the sake of stability.
274
275   // check if "_alwaysComputed" is not yet set
276   bool isAlwaysComputed = false;
277   SMESH_subMeshIteratorPtr smIt = subMesh->getDependsOnIterator(false,false);
278   while ( !isAlwaysComputed && smIt->more() )
279     isAlwaysComputed = smIt->next()->IsAlwaysComputed();
280
281   if ( !isAlwaysComputed )
282   {
283     // check if an edge is a part of a complex side
284     TopoDS_Face face;
285     TopoDS_Edge edge = TopoDS::Edge( subMesh->GetSubShape() );
286     auto_ptr< StdMeshers_FaceSide > side
287       ( StdMeshers_CompositeSegment_1D::GetFaceSide(*subMesh->GetFather(),edge, face, false ));
288     if ( side->NbEdges() > 1 ) { // complex
289
290       // set _alwaysComputed to vertices
291       for ( int iE = 1; iE < side->NbEdges(); ++iE )
292       {
293         TopoDS_Vertex V = side->FirstVertex( iE );
294         SMESH_subMesh* sm = side->GetMesh()->GetSubMesh( V );
295         sm->SetIsAlwaysComputed( true );
296       }
297     }
298   }
299   // set listener that will remove _alwaysComputed from submeshes at algorithm change
300   subMesh->SetEventListener( _EventListener, 0, subMesh);
301   StdMeshers_Regular_1D::SetEventListener( subMesh );
302 }
303
304 //=============================================================================
305 /*!
306  * \brief Return a face side the edge belongs to
307  */
308 //=============================================================================
309
310 StdMeshers_FaceSide *
311 StdMeshers_CompositeSegment_1D::GetFaceSide(SMESH_Mesh&        aMesh,
312                                             const TopoDS_Edge& anEdge,
313                                             const TopoDS_Face& aFace,
314                                             const bool         ignoreMeshed)
315 {
316   list< TopoDS_Edge > edges;
317   edges.push_back( anEdge );
318
319   list <const SMESHDS_Hypothesis *> hypList;
320   SMESH_Algo* theAlgo = aMesh.GetGen()->GetAlgo( aMesh, anEdge );
321   if ( theAlgo ) hypList = theAlgo->GetUsedHypothesis(aMesh, anEdge, false);
322   for ( int forward = 0; forward < 2; ++forward )
323   {
324     TopoDS_Edge eNext = nextC1Edge( anEdge, aMesh, forward );
325     while ( !eNext.IsNull() ) {
326       if ( ignoreMeshed ) {
327         // eNext must not have computed mesh
328         if ( SMESHDS_SubMesh* sm = aMesh.GetMeshDS()->MeshElements(eNext) )
329           if ( sm->NbNodes() || sm->NbElements() )
330             break;
331       }
332       // eNext must have same hypotheses
333       SMESH_Algo* algo = aMesh.GetGen()->GetAlgo( aMesh, eNext );
334       if ( !algo ||
335            string(theAlgo->GetName()) != algo->GetName() ||
336            hypList != algo->GetUsedHypothesis(aMesh, eNext, false))
337         break;
338       if ( forward )
339         edges.push_back( eNext );
340       else
341         edges.push_front( eNext );
342       eNext = nextC1Edge( eNext, aMesh, forward );
343     }
344   }
345   return new StdMeshers_FaceSide( aFace, edges, &aMesh, true, false );
346 }
347
348 //=============================================================================
349 /*!
350  *  
351  */
352 //=============================================================================
353
354 bool StdMeshers_CompositeSegment_1D::Compute(SMESH_Mesh &         aMesh,
355                                              const TopoDS_Shape & aShape)
356 {
357   TopoDS_Edge edge = TopoDS::Edge( aShape );
358   SMESHDS_Mesh * meshDS = aMesh.GetMeshDS();
359
360   // Get edges to be discretized as a whole
361   TopoDS_Face nullFace;
362   auto_ptr< StdMeshers_FaceSide > side( GetFaceSide(aMesh, edge, nullFace, true ));
363   //side->dump("IN COMPOSITE SEG");
364
365   if ( side->NbEdges() < 2 )
366     return StdMeshers_Regular_1D::Compute( aMesh, aShape );
367
368   // update segment lenght computed by StdMeshers_AutomaticLength
369   const list <const SMESHDS_Hypothesis * > & hyps = GetUsedHypothesis(aMesh, aShape);
370   if ( !hyps.empty() ) {
371     StdMeshers_AutomaticLength * autoLenHyp = const_cast<StdMeshers_AutomaticLength *>
372       (dynamic_cast <const StdMeshers_AutomaticLength * >(hyps.front()));
373     if ( autoLenHyp )
374       _value[ BEG_LENGTH_IND ]= autoLenHyp->GetLength( &aMesh, side->Length() );
375   }
376
377   // Compute node parameters
378   auto_ptr< BRepAdaptor_CompCurve > C3d ( side->GetCurve3d() );
379   double f = C3d->FirstParameter(), l = C3d->LastParameter();
380   list< double > params;
381   if ( !computeInternalParameters ( aMesh, *C3d, side->Length(), f, l, params, false ))
382     return false;
383
384   // Redistribute parameters near ends
385   TopoDS_Vertex VFirst = side->FirstVertex();
386   TopoDS_Vertex VLast  = side->LastVertex();
387   redistributeNearVertices( aMesh, *C3d, side->Length(), params, VFirst, VLast );
388
389   params.push_front(f);
390   params.push_back(l);
391   int nbNodes = params.size();
392
393   // Create mesh
394
395   const SMDS_MeshNode * nFirst = SMESH_Algo::VertexNode( VFirst, meshDS );
396   const SMDS_MeshNode * nLast  = SMESH_Algo::VertexNode( VLast, meshDS );
397   if (!nFirst)
398     return error(COMPERR_BAD_INPUT_MESH, TComm("No node on vertex ")
399                  <<meshDS->ShapeToIndex(VFirst));
400   if (!nLast)
401     return error(COMPERR_BAD_INPUT_MESH, TComm("No node on vertex ")
402                  <<meshDS->ShapeToIndex(VLast));
403
404   vector<const SMDS_MeshNode*> nodes( nbNodes, (const SMDS_MeshNode*)0 );
405   nodes.front() = nFirst;
406   nodes.back()  = nLast;
407
408   // create internal nodes
409   list< double >::iterator parIt = params.begin();
410   double prevPar = *parIt;
411   Standard_Real u;
412   for ( int iN = 0; parIt != params.end(); ++iN, ++parIt)
413   {
414     if ( !nodes[ iN ] ) {
415       gp_Pnt p = C3d->Value( *parIt );
416       SMDS_MeshNode* n = meshDS->AddNode( p.X(), p.Y(), p.Z());
417       C3d->Edge( *parIt, edge, u );
418       meshDS->SetNodeOnEdge( n, edge, u );
419 //       cout << "new NODE: par="<<*parIt<<" ePar="<<u<<" e="<<edge.TShape().operator->()
420 //            << " " << n << endl;
421       nodes[ iN ] = n;
422     }
423     // create edges
424     if ( iN ) {
425       double mPar = ( prevPar + *parIt )/2;
426       if ( _quadraticMesh ) {
427         // create medium node
428         double segLen = GCPnts_AbscissaPoint::Length(*C3d, prevPar, *parIt);
429         GCPnts_AbscissaPoint ruler( *C3d, segLen/2., prevPar );
430         if ( ruler.IsDone() )
431           mPar = ruler.Parameter();
432         gp_Pnt p = C3d->Value( mPar );
433         SMDS_MeshNode* n = meshDS->AddNode( p.X(), p.Y(), p.Z());
434         //cout << "new NODE "<< n << endl;
435         meshDS->SetNodeOnEdge( n, edge, u );
436         SMDS_MeshEdge * seg = meshDS->AddEdge(nodes[ iN-1 ], nodes[ iN ], n);
437         meshDS->SetMeshElementOnShape(seg, edge);
438       }
439       else {
440         C3d->Edge( mPar, edge, u );
441         SMDS_MeshEdge * seg = meshDS->AddEdge(nodes[ iN-1 ], nodes[ iN ]);
442         meshDS->SetMeshElementOnShape(seg, edge);
443       }
444     }
445     prevPar = *parIt;
446   }
447
448   // remove nodes on internal vertices
449   for ( int iE = 1; iE < side->NbEdges(); ++iE )
450   {
451     TopoDS_Vertex V = side->FirstVertex( iE );
452     while ( const SMDS_MeshNode * n = SMESH_Algo::VertexNode( V, meshDS ))
453       meshDS->RemoveNode( n );
454   }
455
456   // Update submeshes state for all edges and internal vertices,
457   // make them look computed even if none edge or node is set on them
458   careOfSubMeshes( *side, _EventListener );
459
460   return true;
461 }