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