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