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