Salome HOME
fix failures of non-regression tests
[modules/smesh.git] / src / StdMeshers / StdMeshers_Prism_3D.cxx
1 // Copyright (C) 2007-2012  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 // File      : StdMeshers_Prism_3D.cxx
24 // Module    : SMESH
25 // Created   : Fri Oct 20 11:37:07 2006
26 // Author    : Edward AGAPOV (eap)
27 //
28 #include "StdMeshers_Prism_3D.hxx"
29
30 #include "SMDS_EdgePosition.hxx"
31 #include "SMDS_VolumeOfNodes.hxx"
32 #include "SMDS_VolumeTool.hxx"
33 #include "SMESH_Comment.hxx"
34 #include "SMESH_Gen.hxx"
35 #include "SMESH_HypoFilter.hxx"
36 #include "SMESH_MesherHelper.hxx"
37 #include "StdMeshers_FaceSide.hxx"
38 #include "StdMeshers_ProjectionSource1D.hxx"
39 #include "StdMeshers_ProjectionSource2D.hxx"
40 #include "StdMeshers_ProjectionUtils.hxx"
41 #include "StdMeshers_Projection_1D.hxx"
42 #include "StdMeshers_Projection_1D2D.hxx"
43 #include "StdMeshers_Quadrangle_2D.hxx"
44
45 #include "utilities.h"
46
47 #include <BRepAdaptor_CompCurve.hxx>
48 #include <BRep_Tool.hxx>
49 #include <Bnd_B3d.hxx>
50 #include <Geom2dAdaptor_Curve.hxx>
51 #include <Geom2d_Line.hxx>
52 #include <Geom_Curve.hxx>
53 #include <TopExp.hxx>
54 #include <TopExp_Explorer.hxx>
55 #include <TopTools_ListIteratorOfListOfShape.hxx>
56 #include <TopTools_ListOfShape.hxx>
57 #include <TopTools_MapOfShape.hxx>
58 #include <TopTools_SequenceOfShape.hxx>
59 #include <TopoDS.hxx>
60 #include <gp_Ax2.hxx>
61 #include <gp_Ax3.hxx>
62
63 using namespace std;
64
65 #define RETURN_BAD_RESULT(msg) { MESSAGE(")-: Error: " << msg); return false; }
66 #define gpXYZ(n) gp_XYZ(n->X(),n->Y(),n->Z())
67 #define SHOWYXZ(msg, xyz) // {\
68 // gp_Pnt p (xyz); \
69 // cout << msg << " ("<< p.X() << "; " <<p.Y() << "; " <<p.Z() << ") " <<endl;\
70 // }
71
72 namespace TAssocTool = StdMeshers_ProjectionUtils;
73
74 typedef SMESH_Comment TCom;
75
76 enum { ID_BOT_FACE = SMESH_Block::ID_Fxy0,
77        ID_TOP_FACE = SMESH_Block::ID_Fxy1,
78        BOTTOM_EDGE = 0, TOP_EDGE, V0_EDGE, V1_EDGE, // edge IDs in face
79        NB_WALL_FACES = 4 }; //
80
81 namespace {
82
83   //=======================================================================
84   /*!
85    * \brief Quadrangle algorithm
86    */
87   struct TQuadrangleAlgo : public StdMeshers_Quadrangle_2D
88   {
89     TQuadrangleAlgo(int studyId, SMESH_Gen* gen)
90       : StdMeshers_Quadrangle_2D( gen->GetANewId(), studyId, gen)
91     {
92     }
93     static StdMeshers_Quadrangle_2D* instance( SMESH_Algo*         fatherAlgo,
94                                                SMESH_MesherHelper* helper=0)
95     {
96       static TQuadrangleAlgo* algo = new TQuadrangleAlgo( fatherAlgo->GetStudyId(),
97                                                           fatherAlgo->GetGen() );
98       if ( helper &&
99            algo->myProxyMesh &&
100            algo->myProxyMesh->GetMesh() != helper->GetMesh() )
101         algo->myProxyMesh.reset( new SMESH_ProxyMesh( *helper->GetMesh() ));
102
103       algo->myQuadStruct.reset();
104
105       if ( helper )
106         algo->_quadraticMesh = helper->GetIsQuadratic();
107
108       return algo;
109     }
110   };
111   //=======================================================================
112   /*!
113    * \brief Algorithm projecting 1D mesh
114    */
115   struct TProjction1dAlgo : public StdMeshers_Projection_1D
116   {
117     StdMeshers_ProjectionSource1D myHyp;
118
119     TProjction1dAlgo(int studyId, SMESH_Gen* gen)
120       : StdMeshers_Projection_1D( gen->GetANewId(), studyId, gen),
121         myHyp( gen->GetANewId(), studyId, gen)
122     {
123       StdMeshers_Projection_1D::_sourceHypo = & myHyp;
124     }
125     static TProjction1dAlgo* instance( SMESH_Algo* fatherAlgo )
126     {
127       static TProjction1dAlgo* algo = new TProjction1dAlgo( fatherAlgo->GetStudyId(),
128                                                             fatherAlgo->GetGen() );
129       return algo;
130     }
131   };
132   //=======================================================================
133   /*!
134    * \brief Algorithm projecting 2D mesh
135    */
136   struct TProjction2dAlgo : public StdMeshers_Projection_1D2D
137   {
138     StdMeshers_ProjectionSource2D myHyp;
139
140     TProjction2dAlgo(int studyId, SMESH_Gen* gen)
141       : StdMeshers_Projection_1D2D( gen->GetANewId(), studyId, gen),
142         myHyp( gen->GetANewId(), studyId, gen)
143     {
144       StdMeshers_Projection_2D::_sourceHypo = & myHyp;
145     }
146     static TProjction2dAlgo* instance( SMESH_Algo* fatherAlgo )
147     {
148       static TProjction2dAlgo* algo = new TProjction2dAlgo( fatherAlgo->GetStudyId(),
149                                                             fatherAlgo->GetGen() );
150       return algo;
151     }
152   };
153
154   //================================================================================
155   /*!
156    * \brief Make \a botE be the BOTTOM_SIDE of \a quad.
157    *        Return false if the BOTTOM_SIDE is composite
158    */
159   //================================================================================
160
161   bool setBottomEdge( const TopoDS_Edge&   botE,
162                       faceQuadStruct::Ptr& quad,
163                       const TopoDS_Shape&  face)
164   {
165     quad->side[ QUAD_TOP_SIDE  ]->Reverse();
166     quad->side[ QUAD_LEFT_SIDE ]->Reverse();
167     int edgeIndex = 0;
168     for ( size_t i = 0; i < quad->side.size(); ++i )
169     {
170       StdMeshers_FaceSide* quadSide = quad->side[i];
171       for ( int iE = 0; iE < quadSide->NbEdges(); ++iE )
172         if ( botE.IsSame( quadSide->Edge( iE )))
173         {
174           if ( quadSide->NbEdges() > 1 )
175             return false;
176           edgeIndex = i;
177           i = quad->side.size(); // to quit from the outer loop
178           break;
179         }
180     }
181     if ( edgeIndex != QUAD_BOTTOM_SIDE )
182       quad->shift( quad->side.size() - edgeIndex, /*keepUnitOri=*/false );
183
184     quad->face = TopoDS::Face( face );
185
186     return true;
187   }
188
189   //================================================================================
190   /*!
191    * \brief Return iterator pointing to node column for the given parameter
192    * \param columnsMap - node column map
193    * \param parameter - parameter
194    * \retval TParam2ColumnMap::iterator - result
195    *
196    * it returns closest left column
197    */
198   //================================================================================
199
200   TParam2ColumnIt getColumn( const TParam2ColumnMap* columnsMap,
201                              const double            parameter )
202   {
203     TParam2ColumnIt u_col = columnsMap->upper_bound( parameter );
204     if ( u_col != columnsMap->begin() )
205       --u_col;
206     return u_col; // return left column
207   }
208
209   //================================================================================
210   /*!
211    * \brief Return nodes around given parameter and a ratio
212    * \param column - node column
213    * \param param - parameter
214    * \param node1 - lower node
215    * \param node2 - upper node
216    * \retval double - ratio
217    */
218   //================================================================================
219
220   double getRAndNodes( const TNodeColumn*     column,
221                        const double           param,
222                        const SMDS_MeshNode* & node1,
223                        const SMDS_MeshNode* & node2)
224   {
225     if ( param >= 1.0 || column->size() == 1) {
226       node1 = node2 = column->back();
227       return 0;
228     }
229
230     int i = int( param * ( column->size() - 1 ));
231     double u0 = double( i )/ double( column->size() - 1 );
232     double r = ( param - u0 ) * ( column->size() - 1 );
233
234     node1 = (*column)[ i ];
235     node2 = (*column)[ i + 1];
236     return r;
237   }
238
239   //================================================================================
240   /*!
241    * \brief Compute boundary parameters of face parts
242     * \param nbParts - nb of parts to split columns into
243     * \param columnsMap - node columns of the face to split
244     * \param params - computed parameters
245    */
246   //================================================================================
247
248   void splitParams( const int               nbParts,
249                     const TParam2ColumnMap* columnsMap,
250                     vector< double > &      params)
251   {
252     params.clear();
253     params.reserve( nbParts + 1 );
254     TParam2ColumnIt last_par_col = --columnsMap->end();
255     double par = columnsMap->begin()->first; // 0.
256     double parLast = last_par_col->first;
257     params.push_back( par );
258     for ( int i = 0; i < nbParts - 1; ++ i )
259     {
260       double partSize = ( parLast - par ) / double ( nbParts - i );
261       TParam2ColumnIt par_col = getColumn( columnsMap, par + partSize );
262       if ( par_col->first == par ) {
263         ++par_col;
264         if ( par_col == last_par_col ) {
265           while ( i < nbParts - 1 )
266             params.push_back( par + partSize * i++ );
267           break;
268         }
269       }
270       par = par_col->first;
271       params.push_back( par );
272     }
273     params.push_back( parLast ); // 1.
274   }
275
276   //================================================================================
277   /*!
278    * \brief Return coordinate system for z-th layer of nodes
279    */
280   //================================================================================
281
282   gp_Ax2 getLayerCoordSys(const int                           z,
283                           const vector< const TNodeColumn* >& columns,
284                           int&                                xColumn)
285   {
286     // gravity center of a layer
287     gp_XYZ O(0,0,0);
288     int vertexCol = -1;
289     for ( int i = 0; i < columns.size(); ++i )
290     {
291       O += gpXYZ( (*columns[ i ])[ z ]);
292       if ( vertexCol < 0 &&
293            columns[ i ]->front()->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
294         vertexCol = i;
295     }
296     O /= columns.size();
297
298     // Z axis
299     gp_Vec Z(0,0,0);
300     int iPrev = columns.size()-1;
301     for ( int i = 0; i < columns.size(); ++i )
302     {
303       gp_Vec v1( O, gpXYZ( (*columns[ iPrev ])[ z ]));
304       gp_Vec v2( O, gpXYZ( (*columns[ i ]    )[ z ]));
305       Z += v1 ^ v2;
306       iPrev = i;
307     }
308
309     if ( vertexCol >= 0 )
310     {
311       O = gpXYZ( (*columns[ vertexCol ])[ z ]);
312     }
313     if ( xColumn < 0 || xColumn >= columns.size() )
314     {
315       // select a column for X dir
316       double maxDist = 0;
317       for ( int i = 0; i < columns.size(); ++i )
318       {
319         double dist = ( O - gpXYZ((*columns[ i ])[ z ])).SquareModulus();
320         if ( dist > maxDist )
321         {
322           xColumn = i;
323           maxDist = dist;
324         }
325       }
326     }
327
328     // X axis
329     gp_Vec X( O, gpXYZ( (*columns[ xColumn ])[ z ]));
330
331     return gp_Ax2( O, Z, X);
332   }
333
334   //================================================================================
335   /*!
336    * \brief Removes submeshes that are or can be meshed with regular grid from given list
337    *  \retval int - nb of removed submeshes
338    */
339   //================================================================================
340
341   int removeQuasiQuads(list< SMESH_subMesh* >&   notQuadSubMesh,
342                        SMESH_MesherHelper*       helper,
343                        StdMeshers_Quadrangle_2D* quadAlgo)
344   {
345     int nbRemoved = 0;
346     //SMESHDS_Mesh* mesh = notQuadSubMesh.front()->GetFather()->GetMeshDS();
347     list< SMESH_subMesh* >::iterator smIt = notQuadSubMesh.begin();
348     while ( smIt != notQuadSubMesh.end() )
349     {
350       SMESH_subMesh* faceSm = *smIt;
351       SMESHDS_SubMesh* faceSmDS = faceSm->GetSubMeshDS();
352       int nbQuads = faceSmDS ? faceSmDS->NbElements() : 0;
353       bool toRemove;
354       if ( nbQuads > 0 )
355         toRemove = helper->IsStructured( faceSm );
356       else
357         toRemove = quadAlgo->CheckNbEdges( *helper->GetMesh(),
358                                            faceSm->GetSubShape() );
359       nbRemoved += toRemove;
360       if ( toRemove )
361         smIt = notQuadSubMesh.erase( smIt );
362       else
363         ++smIt;
364     }
365
366     return nbRemoved;
367   }
368
369 } // namespace
370
371 //=======================================================================
372 //function : StdMeshers_Prism_3D
373 //purpose  : 
374 //=======================================================================
375
376 StdMeshers_Prism_3D::StdMeshers_Prism_3D(int hypId, int studyId, SMESH_Gen* gen)
377   :SMESH_3D_Algo(hypId, studyId, gen)
378 {
379   _name                    = "Prism_3D";
380   _shapeType               = (1 << TopAbs_SOLID); // 1 bit per shape type
381   _onlyUnaryInput          = false; // accept all SOLIDs at once
382   _requireDiscreteBoundary = false; // mesh FACEs and EDGEs by myself
383   _supportSubmeshes        = true;  // "source" FACE must be meshed by other algo
384   _neededLowerHyps[ 1 ]    = true;  // suppress warning on hiding a global 1D algo
385   _neededLowerHyps[ 2 ]    = true;  // suppress warning on hiding a global 2D algo
386
387   //myProjectTriangles       = false;
388   mySetErrorToSM           = true;  // to pass an error to a sub-mesh of a current solid or not
389 }
390
391 //================================================================================
392 /*!
393  * \brief Destructor
394  */
395 //================================================================================
396
397 StdMeshers_Prism_3D::~StdMeshers_Prism_3D()
398 {}
399
400 //=======================================================================
401 //function : CheckHypothesis
402 //purpose  : 
403 //=======================================================================
404
405 bool StdMeshers_Prism_3D::CheckHypothesis(SMESH_Mesh&                          aMesh,
406                                           const TopoDS_Shape&                  aShape,
407                                           SMESH_Hypothesis::Hypothesis_Status& aStatus)
408 {
409   // Check shape geometry
410 /*  PAL16229
411   aStatus = SMESH_Hypothesis::HYP_BAD_GEOMETRY;
412
413   // find not quadrangle faces
414   list< TopoDS_Shape > notQuadFaces;
415   int nbEdge, nbWire, nbFace = 0;
416   TopExp_Explorer exp( aShape, TopAbs_FACE );
417   for ( ; exp.More(); exp.Next() ) {
418     ++nbFace;
419     const TopoDS_Shape& face = exp.Current();
420     nbEdge = TAssocTool::Count( face, TopAbs_EDGE, 0 );
421     nbWire = TAssocTool::Count( face, TopAbs_WIRE, 0 );
422     if (  nbEdge!= 4 || nbWire!= 1 ) {
423       if ( !notQuadFaces.empty() ) {
424         if ( TAssocTool::Count( notQuadFaces.back(), TopAbs_EDGE, 0 ) != nbEdge ||
425              TAssocTool::Count( notQuadFaces.back(), TopAbs_WIRE, 0 ) != nbWire )
426           RETURN_BAD_RESULT("Different not quad faces");
427       }
428       notQuadFaces.push_back( face );
429     }
430   }
431   if ( !notQuadFaces.empty() )
432   {
433     if ( notQuadFaces.size() != 2 )
434       RETURN_BAD_RESULT("Bad nb not quad faces: " << notQuadFaces.size());
435
436     // check total nb faces
437     nbEdge = TAssocTool::Count( notQuadFaces.back(), TopAbs_EDGE, 0 );
438     if ( nbFace != nbEdge + 2 )
439       RETURN_BAD_RESULT("Bad nb of faces: " << nbFace << " but must be " << nbEdge + 2);
440   }
441 */
442   // no hypothesis
443   aStatus = SMESH_Hypothesis::HYP_OK;
444   return true;
445 }
446
447 //=======================================================================
448 //function : Compute
449 //purpose  : Compute mesh on a COMPOUND of SOLIDs
450 //=======================================================================
451
452 bool StdMeshers_Prism_3D::Compute(SMESH_Mesh& theMesh, const TopoDS_Shape& theShape)
453 {
454   SMESH_MesherHelper helper( theMesh );
455   myHelper = &helper;
456
457   int nbSolids = helper.Count( theShape, TopAbs_SOLID, /*skipSame=*/false );
458   if ( nbSolids < 1 )
459     return true;
460
461   TopTools_IndexedDataMapOfShapeListOfShape faceToSolids;
462   TopExp::MapShapesAndAncestors( theShape, TopAbs_FACE, TopAbs_SOLID, faceToSolids );
463
464   // look for meshed FACEs ("source" FACEs) that must be prism bottoms
465   list< TopoDS_Face > meshedFaces, notQuadMeshedFaces;//, notQuadFaces;
466   const bool meshHasQuads = ( theMesh.NbQuadrangles() > 0 );
467   for ( int iF = 1; iF < faceToSolids.Extent(); ++iF )
468   {
469     const TopoDS_Face& face = TopoDS::Face( faceToSolids.FindKey( iF ));
470     SMESH_subMesh*   faceSM = theMesh.GetSubMesh( face );
471     if ( !faceSM->IsEmpty() )
472     {
473       if ( !meshHasQuads ||
474            !helper.IsSameElemGeometry( faceSM->GetSubMeshDS(), SMDSGeom_QUADRANGLE ) ||
475            !helper.IsStructured( faceSM )
476            )
477         notQuadMeshedFaces.push_front( face );
478       else if ( myHelper->Count( face, TopAbs_EDGE, /*ignoreSame=*/false ) != 4 )
479         meshedFaces.push_front( face );
480       else
481         meshedFaces.push_back( face );
482     }
483   }
484   // notQuadMeshedFaces are of highest priority
485   meshedFaces.splice( meshedFaces.begin(), notQuadMeshedFaces );
486
487   Prism_3D::TPrismTopo prism;
488
489   if ( nbSolids == 1 )
490   {
491     if ( !meshedFaces.empty() )
492       prism.myBottom = meshedFaces.front();
493     return ( initPrism( prism, TopExp_Explorer( theShape, TopAbs_SOLID ).Current() ) &&
494              compute( prism ));
495   }
496
497   TopTools_MapOfShape meshedSolids;
498   list< Prism_3D::TPrismTopo > meshedPrism;
499   TopTools_ListIteratorOfListOfShape solidIt;
500
501   while ( meshedSolids.Extent() < nbSolids )
502   {
503     if ( _computeCanceled )
504       return toSM( error( SMESH_ComputeError::New(COMPERR_CANCELED)));
505
506     // compute prisms having avident computed source FACE
507     while ( !meshedFaces.empty() )
508     {
509       TopoDS_Face face = meshedFaces.front();
510       meshedFaces.pop_front();
511       TopTools_ListOfShape& solidList = faceToSolids.ChangeFromKey( face );
512       while ( !solidList.IsEmpty() )
513       {
514         TopoDS_Shape solid = solidList.First();
515         solidList.RemoveFirst();
516         if ( meshedSolids.Add( solid ))
517         {
518           prism.Clear();
519           prism.myBottom = face;
520           if ( !initPrism( prism, solid ) ||
521                !compute( prism ))
522             return false;
523
524           meshedFaces.push_front( prism.myTop );
525           meshedPrism.push_back( prism );
526         }
527       }
528     }
529     if ( meshedSolids.Extent() == nbSolids )
530       break;
531
532     // below in the loop we try to find source FACEs somehow
533
534     // project mesh from source FACEs of computed prisms to
535     // prisms sharing wall FACEs
536     list< Prism_3D::TPrismTopo >::iterator prismIt = meshedPrism.begin();
537     for ( ; prismIt != meshedPrism.end(); ++prismIt )
538     {
539       for ( size_t iW = 0; iW < prismIt->myWallQuads.size(); ++iW )
540       {
541         Prism_3D::TQuadList::iterator wQuad = prismIt->myWallQuads[iW].begin();
542         for ( ; wQuad != prismIt->myWallQuads[iW].end(); ++ wQuad )
543         {
544           const TopoDS_Face& wFace = (*wQuad)->face;
545           TopTools_ListOfShape& solidList = faceToSolids.ChangeFromKey( wFace );
546           solidIt.Initialize( solidList );
547           while ( solidIt.More() )
548           {
549             const TopoDS_Shape& solid = solidIt.Value();
550             if ( meshedSolids.Contains( solid )) {
551               solidList.Remove( solidIt );
552               continue; // already computed prism
553             }
554             // find a source FACE of the SOLID: it's a FACE sharing a bottom EDGE with wFace
555             const TopoDS_Edge& wEdge = (*wQuad)->side[ QUAD_TOP_SIDE ]->Edge(0);
556             PShapeIteratorPtr faceIt = myHelper->GetAncestors( wEdge, *myHelper->GetMesh(),
557                                                                TopAbs_FACE);
558             while ( const TopoDS_Shape* f = faceIt->next() )
559             {
560               const TopoDS_Face& candidateF = TopoDS::Face( *f );
561               prism.Clear();
562               prism.myBottom  = candidateF;
563               mySetErrorToSM = false;
564               if ( !myHelper->IsSubShape( candidateF, prismIt->myShape3D ) &&
565                    !myHelper->GetMesh()->GetSubMesh( candidateF )->IsMeshComputed() &&
566                    initPrism( prism, solid ) &&
567                    project2dMesh( prismIt->myBottom, candidateF))
568               {
569                 mySetErrorToSM = true;
570                 if ( !compute( prism ))
571                   return false;
572                 meshedFaces.push_front( prism.myTop );
573                 meshedFaces.push_front( prism.myBottom );
574                 meshedPrism.push_back( prism );
575                 meshedSolids.Add( solid );
576               }
577               InitComputeError();
578             }
579             mySetErrorToSM = true;
580             InitComputeError();
581             if ( meshedSolids.Contains( solid ))
582               solidList.Remove( solidIt );
583             else
584               solidIt.Next();
585           }
586         }
587       }
588       if ( !meshedFaces.empty() )
589         break; // to compute prisms with avident sources
590     }
591
592     // find FACEs with local 1D hyps, which has to be computed by now,
593     // or at least any computed FACEs
594     for ( int iF = 1; ( meshedFaces.empty() && iF < faceToSolids.Extent() ); ++iF )
595     {
596       const TopoDS_Face&               face = TopoDS::Face( faceToSolids.FindKey( iF ));
597       const TopTools_ListOfShape& solidList = faceToSolids.FindFromKey( face );
598       if ( solidList.IsEmpty() ) continue;
599       SMESH_subMesh*                 faceSM = theMesh.GetSubMesh( face );
600       if ( !faceSM->IsEmpty() )
601       {
602         meshedFaces.push_back( face ); // lower priority
603       }
604       else
605       {
606         bool allSubMeComputed = true;
607         SMESH_subMeshIteratorPtr smIt = faceSM->getDependsOnIterator(false,true);
608         while ( smIt->more() && allSubMeComputed )
609           allSubMeComputed = smIt->next()->IsMeshComputed();
610         if ( allSubMeComputed )
611         {
612           faceSM->ComputeStateEngine( SMESH_subMesh::COMPUTE );
613           if ( !faceSM->IsEmpty() )
614             meshedFaces.push_front( face ); // higher priority
615           else
616             faceSM->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
617         }
618       }
619     }
620
621
622     // TODO. there are other ways to find out the source FACE:
623     // propagation, topological similarity, ect.
624
625
626     if ( meshedFaces.empty() ) // set same error to 10 not-computed solids
627     {
628       SMESH_ComputeErrorPtr err = SMESH_ComputeError::New
629         ( COMPERR_BAD_INPUT_MESH, "No meshed source face found", this );
630
631       const int maxNbErrors = 10; // limit nb errors not to overload the Compute dialog
632       TopExp_Explorer solid( theShape, TopAbs_SOLID );
633       for ( int i = 0; ( i < maxNbErrors && solid.More() ); ++i, solid.Next() )
634         if ( !meshedSolids.Contains( solid.Current() ))
635         {
636           SMESH_subMesh* sm = theMesh.GetSubMesh( solid.Current() );
637           sm->GetComputeError() = err;
638         }
639       return false;
640     }
641   }
642   return true;
643 }
644
645 //================================================================================
646 /*!
647  * \brief Find wall faces by bottom edges
648  */
649 //================================================================================
650
651 bool StdMeshers_Prism_3D::getWallFaces( Prism_3D::TPrismTopo & thePrism,
652                                         const int              totalNbFaces)
653 {
654   thePrism.myWallQuads.clear();
655
656   SMESH_Mesh* mesh = myHelper->GetMesh();
657
658   StdMeshers_Quadrangle_2D* quadAlgo = TQuadrangleAlgo::instance( this, myHelper );
659
660   TopTools_MapOfShape faceMap;
661   TopTools_IndexedDataMapOfShapeListOfShape edgeToFaces;   
662   TopExp::MapShapesAndAncestors( thePrism.myShape3D,
663                                  TopAbs_EDGE, TopAbs_FACE, edgeToFaces );
664
665   // ------------------------------
666   // Get the 1st row of wall FACEs
667   // ------------------------------
668
669   list< TopoDS_Edge >::iterator edge = thePrism.myBottomEdges.begin();
670   std::list< int >::iterator     nbE = thePrism.myNbEdgesInWires.begin();
671   int iE = 0;
672   while ( edge != thePrism.myBottomEdges.end() )
673   {
674     ++iE;
675     if ( BRep_Tool::Degenerated( *edge ))
676     {
677       edge = thePrism.myBottomEdges.erase( edge );
678       --iE;
679       --(*nbE);
680     }
681     else
682     {
683       TopTools_ListIteratorOfListOfShape faceIt( edgeToFaces.FindFromKey( *edge ));
684       for ( ; faceIt.More(); faceIt.Next() )
685       {
686         const TopoDS_Face& face = TopoDS::Face( faceIt.Value() );
687         if ( !thePrism.myBottom.IsSame( face ))
688         {
689           Prism_3D::TQuadList quadList( 1, quadAlgo->CheckNbEdges( *mesh, face ));
690           if ( !quadList.back() )
691             return toSM( error(TCom("Side face #") << shapeID( face )
692                                << " not meshable with quadrangles"));
693           if ( ! setBottomEdge( *edge, quadList.back(), face ))
694             return toSM( error(TCom("Composite 'horizontal' edges are not supported")));
695           thePrism.myWallQuads.push_back( quadList );
696           faceMap.Add( face );
697           break;
698         }
699       }
700       ++edge;
701     }
702     if ( iE == *nbE )
703     {
704       iE = 0;
705       ++nbE;
706     }
707   }
708
709   // -------------------------
710   // Find the rest wall FACEs
711   // -------------------------
712
713   // Compose a vector of indixes of right neighbour FACE for each wall FACE
714   // that is not so evident in case of several WIREs in the bottom FACE
715   thePrism.myRightQuadIndex.clear();
716   for ( size_t i = 0; i < thePrism.myWallQuads.size(); ++i )
717     thePrism.myRightQuadIndex.push_back( i+1 );
718   list< int >::iterator nbEinW = thePrism.myNbEdgesInWires.begin();
719   for ( int iLeft = 0; nbEinW != thePrism.myNbEdgesInWires.end(); ++nbEinW )
720   {
721     thePrism.myRightQuadIndex[ iLeft + *nbEinW - 1 ] = iLeft; // 1st EDGE index of a current WIRE
722     iLeft += *nbEinW;
723   }
724
725   while ( totalNbFaces - faceMap.Extent() > 2 )
726   {
727     // find wall FACEs adjacent to each of wallQuads by the right side EDGE
728     int nbKnownFaces;
729     do {
730       nbKnownFaces = faceMap.Extent();
731       StdMeshers_FaceSide *rightSide, *topSide; // sides of the quad
732       for ( size_t i = 0; i < thePrism.myWallQuads.size(); ++i )
733       {
734         rightSide = thePrism.myWallQuads[i].back()->side[ QUAD_RIGHT_SIDE ];
735         for ( int iE = 0; iE < rightSide->NbEdges(); ++iE ) // rightSide can be composite
736         {
737           const TopoDS_Edge & rightE = rightSide->Edge( iE );
738           TopTools_ListIteratorOfListOfShape face( edgeToFaces.FindFromKey( rightE ));
739           for ( ; face.More(); face.Next() )
740             if ( faceMap.Add( face.Value() ))
741             {
742               // a new wall FACE encountered, store it in thePrism.myWallQuads
743               const int iRight = thePrism.myRightQuadIndex[i];
744               topSide = thePrism.myWallQuads[ iRight ].back()->side[ QUAD_TOP_SIDE ];
745               const TopoDS_Edge&   newBotE = topSide->Edge(0);
746               const TopoDS_Shape& newWallF = face.Value();
747               thePrism.myWallQuads[ iRight ].push_back( quadAlgo->CheckNbEdges( *mesh, newWallF ));
748               if ( !thePrism.myWallQuads[ iRight ].back() )
749                 return toSM( error(TCom("Side face #") << shapeID( newWallF ) <<
750                                    " not meshable with quadrangles"));
751               if ( ! setBottomEdge( newBotE, thePrism.myWallQuads[ iRight ].back(), newWallF ))
752                 return toSM( error(TCom("Composite 'horizontal' edges are not supported")));
753             }
754         }
755       }
756     } while ( nbKnownFaces != faceMap.Extent() );
757
758     // find wall FACEs adjacent to each of thePrism.myWallQuads by the top side EDGE
759     if ( totalNbFaces - faceMap.Extent() > 2 )
760     {
761       for ( size_t i = 0; i < thePrism.myWallQuads.size(); ++i )
762       {
763         StdMeshers_FaceSide* topSide = thePrism.myWallQuads[i].back()->side[ QUAD_TOP_SIDE ];
764         const TopoDS_Edge &     topE = topSide->Edge( 0 );
765         if ( topSide->NbEdges() > 1 )
766           return toSM( error(COMPERR_BAD_SHAPE, TCom("Side face #") <<
767                              shapeID( thePrism.myWallQuads[i].back()->face )
768                              << " has a composite top edge"));
769         TopTools_ListIteratorOfListOfShape faceIt( edgeToFaces.FindFromKey( topE ));
770         for ( ; faceIt.More(); faceIt.Next() )
771           if ( faceMap.Add( faceIt.Value() ))
772           {
773             // a new wall FACE encountered, store it in wallQuads
774             thePrism.myWallQuads[ i ].push_back( quadAlgo->CheckNbEdges( *mesh, faceIt.Value() ));
775             if ( !thePrism.myWallQuads[ i ].back() )
776               return toSM( error(TCom("Side face #") << shapeID( faceIt.Value() ) <<
777                                  " not meshable with quadrangles"));
778             if ( ! setBottomEdge( topE, thePrism.myWallQuads[ i ].back(), faceIt.Value() ))
779               return toSM( error(TCom("Composite 'horizontal' edges are not supported")));
780             if ( totalNbFaces - faceMap.Extent() == 2 )
781             {
782               i = thePrism.myWallQuads.size(); // to quit from the outer loop
783               break;
784             }
785           }
786       }
787     }
788   } // while ( totalNbFaces - faceMap.Extent() > 2 )
789
790   // ------------------
791   // Find the top FACE
792   // ------------------
793
794   if ( thePrism.myTop.IsNull() )
795   {
796     // now only top and bottom FACEs are not in the faceMap
797     faceMap.Add( thePrism.myBottom );
798     for ( TopExp_Explorer f( thePrism.myShape3D, TopAbs_FACE );f.More(); f.Next() )
799       if ( !faceMap.Contains( f.Current() )) {
800         thePrism.myTop = TopoDS::Face( f.Current() );
801         break;
802       }
803     if ( thePrism.myTop.IsNull() )
804       return toSM( error("Top face not found"));
805   }
806
807   // Check that the top FACE shares all the top EDGEs
808   for ( size_t i = 0; i < thePrism.myWallQuads.size(); ++i )
809   {
810     StdMeshers_FaceSide* topSide = thePrism.myWallQuads[i].back()->side[ QUAD_TOP_SIDE ];
811     const TopoDS_Edge &     topE = topSide->Edge( 0 );
812     if ( !myHelper->IsSubShape( topE, thePrism.myTop ))
813       return toSM( error( TCom("Wrong source face (#") << shapeID( thePrism.myBottom )));
814   }
815
816   return true;
817 }
818
819 //=======================================================================
820 //function : compute
821 //purpose  : Compute mesh on a SOLID
822 //=======================================================================
823
824 bool StdMeshers_Prism_3D::compute(const Prism_3D::TPrismTopo& thePrism)
825 {
826   myHelper->IsQuadraticSubMesh( thePrism.myShape3D );
827   if ( _computeCanceled )
828     return toSM( error( SMESH_ComputeError::New(COMPERR_CANCELED)));
829
830   // Make all side FACEs of thePrism meshed with quads
831   if ( !computeWalls( thePrism ))
832     return false;
833
834   // Analyse mesh and geometry to find block sub-shapes and submeshes
835   if ( !myBlock.Init( myHelper, thePrism ))
836     return toSM( error( myBlock.GetError()));
837
838   SMESHDS_Mesh* meshDS = myHelper->GetMeshDS();
839
840   int volumeID = meshDS->ShapeToIndex( thePrism.myShape3D );
841
842
843   // To compute coordinates of a node inside a block, it is necessary to know
844   // 1. normalized parameters of the node by which
845   // 2. coordinates of node projections on all block sub-shapes are computed
846
847   // So we fill projections on vertices at once as they are same for all nodes
848   myShapeXYZ.resize( myBlock.NbSubShapes() );
849   for ( int iV = SMESH_Block::ID_FirstV; iV < SMESH_Block::ID_FirstE; ++iV ) {
850     myBlock.VertexPoint( iV, myShapeXYZ[ iV ]);
851     SHOWYXZ("V point " <<iV << " ", myShapeXYZ[ iV ]);
852   }
853
854   // Projections on the top and bottom faces are taken from nodes existing
855   // on these faces; find correspondence between bottom and top nodes
856   myBotToColumnMap.clear();
857   if ( !assocOrProjBottom2Top() ) // it also fills myBotToColumnMap
858     return false;
859
860
861   // Create nodes inside the block
862
863   // try to use transformation (issue 0020680)
864   vector<gp_Trsf> trsf;
865   if ( myBlock.GetLayersTransformation( trsf, thePrism ))
866   {
867     // loop on nodes inside the bottom face
868     TNode2ColumnMap::iterator bot_column = myBotToColumnMap.begin();
869     for ( ; bot_column != myBotToColumnMap.end(); ++bot_column )
870     {
871       const Prism_3D::TNode& tBotNode = bot_column->first; // bottom TNode
872       if ( tBotNode.GetPositionType() != SMDS_TOP_FACE )
873         continue; // node is not inside face 
874
875       // column nodes; middle part of the column are zero pointers
876       TNodeColumn& column = bot_column->second;
877       TNodeColumn::iterator columnNodes = column.begin();
878       for ( int z = 0; columnNodes != column.end(); ++columnNodes, ++z)
879       {
880         const SMDS_MeshNode* & node = *columnNodes;
881         if ( node ) continue; // skip bottom or top node
882
883         gp_XYZ coords = tBotNode.GetCoords();
884         trsf[z-1].Transforms( coords );
885         node = meshDS->AddNode( coords.X(), coords.Y(), coords.Z() );
886         meshDS->SetNodeInVolume( node, volumeID );
887       }
888     } // loop on bottom nodes
889   }
890   else // use block approach
891   {
892     // loop on nodes inside the bottom face
893     Prism_3D::TNode prevBNode;
894     TNode2ColumnMap::iterator bot_column = myBotToColumnMap.begin();
895     for ( ; bot_column != myBotToColumnMap.end(); ++bot_column )
896     {
897       const Prism_3D::TNode& tBotNode = bot_column->first; // bottom TNode
898       if ( tBotNode.GetPositionType() != SMDS_TOP_FACE )
899         continue; // node is not inside face 
900
901       // column nodes; middle part of the column are zero pointers
902       TNodeColumn& column = bot_column->second;
903
904       // compute bottom node parameters
905       gp_XYZ paramHint(-1,-1,-1);
906       if ( prevBNode.IsNeighbor( tBotNode ))
907         paramHint = prevBNode.GetParams();
908       if ( !myBlock.ComputeParameters( tBotNode.GetCoords(), tBotNode.ChangeParams(),
909                                        ID_BOT_FACE, paramHint ))
910         return toSM( error(TCom("Can't compute normalized parameters for node ")
911                            << tBotNode.myNode->GetID() << " on the face #"
912                            << myBlock.SubMesh( ID_BOT_FACE )->GetId() ));
913       prevBNode = tBotNode;
914
915       myShapeXYZ[ ID_BOT_FACE ] = tBotNode.GetCoords();
916       gp_XYZ botParams          = tBotNode.GetParams();
917
918       // compute top node parameters
919       myShapeXYZ[ ID_TOP_FACE ] = gpXYZ( column.back() );
920       gp_XYZ topParams = botParams;
921       topParams.SetZ( 1 );
922       if ( column.size() > 2 ) {
923         gp_Pnt topCoords = myShapeXYZ[ ID_TOP_FACE ];
924         if ( !myBlock.ComputeParameters( topCoords, topParams, ID_TOP_FACE, topParams ))
925           return toSM( error(TCom("Can't compute normalized parameters ")
926                              << "for node " << column.back()->GetID()
927                              << " on the face #"<< column.back()->getshapeId() ));
928       }
929
930       // vertical loop
931       TNodeColumn::iterator columnNodes = column.begin();
932       for ( int z = 0; columnNodes != column.end(); ++columnNodes, ++z)
933       {
934         const SMDS_MeshNode* & node = *columnNodes;
935         if ( node ) continue; // skip bottom or top node
936
937         // params of a node to create
938         double rz = (double) z / (double) ( column.size() - 1 );
939         gp_XYZ params = botParams * ( 1 - rz ) + topParams * rz;
940
941         // set coords on all faces and nodes
942         const int nbSideFaces = 4;
943         int sideFaceIDs[nbSideFaces] = { SMESH_Block::ID_Fx0z,
944                                          SMESH_Block::ID_Fx1z,
945                                          SMESH_Block::ID_F0yz,
946                                          SMESH_Block::ID_F1yz };
947         for ( int iF = 0; iF < nbSideFaces; ++iF )
948           if ( !setFaceAndEdgesXYZ( sideFaceIDs[ iF ], params, z ))
949             return false;
950
951         // compute coords for a new node
952         gp_XYZ coords;
953         if ( !SMESH_Block::ShellPoint( params, myShapeXYZ, coords ))
954           return toSM( error("Can't compute coordinates by normalized parameters"));
955
956         SHOWYXZ("TOPFacePoint ",myShapeXYZ[ ID_TOP_FACE]);
957         SHOWYXZ("BOT Node "<< tBotNode.myNode->GetID(),gpXYZ(tBotNode.myNode));
958         SHOWYXZ("ShellPoint ",coords);
959
960         // create a node
961         node = meshDS->AddNode( coords.X(), coords.Y(), coords.Z() );
962         meshDS->SetNodeInVolume( node, volumeID );
963       }
964     } // loop on bottom nodes
965   }
966
967   // Create volumes
968
969   SMESHDS_SubMesh* smDS = myBlock.SubMeshDS( ID_BOT_FACE );
970   if ( !smDS ) return toSM( error(COMPERR_BAD_INPUT_MESH, "Null submesh"));
971
972   // loop on bottom mesh faces
973   SMDS_ElemIteratorPtr faceIt = smDS->GetElements();
974   while ( faceIt->more() )
975   {
976     const SMDS_MeshElement* face = faceIt->next();
977     if ( !face || face->GetType() != SMDSAbs_Face )
978       continue;
979
980     // find node columns for each node
981     int nbNodes = face->NbCornerNodes();
982     vector< const TNodeColumn* > columns( nbNodes );
983     for ( int i = 0; i < nbNodes; ++i )
984     {
985       const SMDS_MeshNode* n = face->GetNode( i );
986       if ( n->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE ) {
987         TNode2ColumnMap::iterator bot_column = myBotToColumnMap.find( n );
988         if ( bot_column == myBotToColumnMap.end() )
989           return toSM( error(TCom("No nodes found above node ") << n->GetID() ));
990         columns[ i ] = & bot_column->second;
991       }
992       else {
993         columns[ i ] = myBlock.GetNodeColumn( n );
994         if ( !columns[ i ] )
995           return toSM( error(TCom("No side nodes found above node ") << n->GetID() ));
996       }
997     }
998     // create prisms
999     AddPrisms( columns, myHelper );
1000
1001   } // loop on bottom mesh faces
1002
1003   // clear data
1004   myBotToColumnMap.clear();
1005   myBlock.Clear();
1006         
1007   return true;
1008 }
1009
1010 //=======================================================================
1011 //function : computeWalls
1012 //purpose  : Compute 2D mesh on walls FACEs of a prism
1013 //=======================================================================
1014
1015 bool StdMeshers_Prism_3D::computeWalls(const Prism_3D::TPrismTopo& thePrism)
1016 {
1017   SMESH_Mesh*     mesh = myHelper->GetMesh();
1018   SMESHDS_Mesh* meshDS = myHelper->GetMeshDS();
1019
1020   TProjction1dAlgo* projector1D = TProjction1dAlgo::instance( this );
1021   StdMeshers_Quadrangle_2D* quadAlgo = TQuadrangleAlgo::instance( this, myHelper );
1022
1023   SMESH_HypoFilter hyp1dFilter( SMESH_HypoFilter::IsAlgo(),/*not=*/true);
1024   hyp1dFilter.And( SMESH_HypoFilter::HasDim( 1 ));
1025   hyp1dFilter.And( SMESH_HypoFilter::IsMoreLocalThan( thePrism.myShape3D, *mesh ));
1026
1027   // Discretize equally 'vertical' EDGEs
1028   // -----------------------------------
1029   // find source FACE sides for projection: either already computed ones or
1030   // the 'most composite' ones
1031   multimap< int, int > wgt2quad;
1032   for ( size_t iW = 0; iW != thePrism.myWallQuads.size(); ++iW )
1033   {
1034     Prism_3D::TQuadList::const_iterator quad = thePrism.myWallQuads[iW].begin();
1035     int wgt = 0; // "weight"
1036     for ( ; quad != thePrism.myWallQuads[iW].end(); ++quad )
1037     {
1038       StdMeshers_FaceSide* lftSide = (*quad)->side[ QUAD_LEFT_SIDE ];
1039       for ( int i = 0; i < lftSide->NbEdges(); ++i )
1040       {
1041         ++wgt;
1042         const TopoDS_Edge& E = lftSide->Edge(i);
1043         if ( mesh->GetSubMesh( E )->IsMeshComputed() )
1044           wgt += 10;
1045         else if ( mesh->GetHypothesis( E, hyp1dFilter, true )) // local hypothesis!
1046           wgt += 100;
1047       }
1048     }
1049     wgt2quad.insert( make_pair( wgt, iW ));
1050
1051     // in quadratic mesh, pass ignoreMediumNodes to quad sides
1052     if ( myHelper->GetIsQuadratic() )
1053     {
1054       quad = thePrism.myWallQuads[iW].begin();
1055       for ( ; quad != thePrism.myWallQuads[iW].end(); ++quad )
1056         for ( int i = 0; i < NB_QUAD_SIDES; ++i )
1057           (*quad)->side[ i ]->SetIgnoreMediumNodes( true );
1058     }
1059   }
1060
1061   // Project 'vertical' EDGEs, from left to right
1062   multimap< int, int >::reverse_iterator w2q = wgt2quad.rbegin();
1063   for ( ; w2q != wgt2quad.rend(); ++w2q )
1064   {
1065     const int iW = w2q->second;
1066     const Prism_3D::TQuadList&         quads = thePrism.myWallQuads[ iW ];
1067     Prism_3D::TQuadList::const_iterator quad = quads.begin();
1068     for ( ; quad != quads.end(); ++quad )
1069     {
1070       StdMeshers_FaceSide* rgtSide = (*quad)->side[ QUAD_RIGHT_SIDE ]; // tgt
1071       StdMeshers_FaceSide* lftSide = (*quad)->side[ QUAD_LEFT_SIDE ];  // src
1072       bool swapLeftRight = ( lftSide->NbSegments( /*update=*/true ) == 0 &&
1073                              rgtSide->NbSegments( /*update=*/true )  > 0 );
1074       if ( swapLeftRight )
1075         std::swap( lftSide, rgtSide );
1076
1077       // assure that all the source (left) EDGEs are meshed
1078       int nbSrcSegments = 0;
1079       for ( int i = 0; i < lftSide->NbEdges(); ++i )
1080       {
1081         const TopoDS_Edge& srcE = lftSide->Edge(i);
1082         SMESH_subMesh*    srcSM = mesh->GetSubMesh( srcE );
1083         if ( !srcSM->IsMeshComputed() ) {
1084           srcSM->ComputeSubMeshStateEngine( SMESH_subMesh::COMPUTE );
1085           srcSM->ComputeStateEngine       ( SMESH_subMesh::COMPUTE );
1086           if ( !srcSM->IsMeshComputed() )
1087             return false;
1088         }
1089         nbSrcSegments += srcSM->GetSubMeshDS()->NbElements();
1090       }
1091       // check target EDGEs
1092       int nbTgtMeshed = 0, nbTgtSegments = 0;
1093       vector< bool > isTgtEdgeComputed( rgtSide->NbEdges() );
1094       for ( int i = 0; i < rgtSide->NbEdges(); ++i )
1095       {
1096         const TopoDS_Edge& tgtE = rgtSide->Edge(i);
1097         SMESH_subMesh*    tgtSM = mesh->GetSubMesh( tgtE );
1098         if (( isTgtEdgeComputed[ i ] = tgtSM->IsMeshComputed() )) {
1099           ++nbTgtMeshed;
1100           nbTgtSegments += tgtSM->GetSubMeshDS()->NbElements();
1101         }
1102       }
1103       if ( rgtSide->NbEdges() == nbTgtMeshed ) // all tgt EDGEs meshed
1104       {
1105         if ( nbTgtSegments != nbSrcSegments )
1106         {
1107           for ( int i = 0; i < lftSide->NbEdges(); ++i )
1108             addBadInputElements( meshDS->MeshElements( lftSide->Edge( i )));
1109           for ( int i = 0; i < rgtSide->NbEdges(); ++i )
1110             addBadInputElements( meshDS->MeshElements( rgtSide->Edge( i )));
1111           return toSM( error( TCom("Different nb of segment on logically vertical edges #")
1112                               << shapeID( lftSide->Edge(0) ) << " and #"
1113                               << shapeID( rgtSide->Edge(0) ) << ": "
1114                               << nbSrcSegments << " != " << nbTgtSegments ));
1115         }
1116         continue;
1117       }
1118       // Compute
1119       if ( nbTgtMeshed == 0 )
1120       {
1121         // compute nodes on target VERTEXes
1122         const UVPtStructVec&  srcNodeStr = lftSide->GetUVPtStruct();
1123         if ( srcNodeStr.size() == 0 )
1124           return toSM( error( TCom("Invalid node positions on edge #") <<
1125                               shapeID( lftSide->Edge(0) )));
1126         vector< SMDS_MeshNode* > newNodes( srcNodeStr.size() );
1127         for ( int is2ndV = 0; is2ndV < 2; ++is2ndV )
1128         {
1129           const TopoDS_Edge& E = rgtSide->Edge( is2ndV ? rgtSide->NbEdges()-1 : 0 );
1130           TopoDS_Vertex      v = myHelper->IthVertex( is2ndV, E );
1131           mesh->GetSubMesh( v )->ComputeStateEngine( SMESH_subMesh::COMPUTE );
1132           const SMDS_MeshNode* n = SMESH_Algo::VertexNode( v, meshDS );
1133           newNodes[ is2ndV ? 0 : newNodes.size()-1 ] = (SMDS_MeshNode*) n;
1134         }
1135
1136         // compute nodes on target EDGEs
1137         rgtSide->Reverse(); // direct it same as the lftSide
1138         myHelper->SetElementsOnShape( false );
1139         TopoDS_Edge tgtEdge;
1140         for ( size_t iN = 1; iN < srcNodeStr.size()-1; ++iN ) // add nodes
1141         {
1142           gp_Pnt       p = rgtSide->Value3d  ( srcNodeStr[ iN ].normParam );
1143           double       u = rgtSide->Parameter( srcNodeStr[ iN ].normParam, tgtEdge );
1144           newNodes[ iN ] = meshDS->AddNode( p.X(), p.Y(), p.Z() );
1145           meshDS->SetNodeOnEdge( newNodes[ iN ], tgtEdge, u );
1146         }
1147         for ( size_t iN = 1; iN < srcNodeStr.size(); ++iN ) // add segments
1148         {
1149           SMDS_MeshElement* newEdge = myHelper->AddEdge( newNodes[ iN-1 ], newNodes[ iN ] );
1150           std::pair<int, TopAbs_ShapeEnum> id2type = 
1151             myHelper->GetMediumPos( newNodes[ iN-1 ], newNodes[ iN ] );
1152           if ( id2type.second == TopAbs_EDGE )
1153           {
1154             meshDS->SetMeshElementOnShape( newEdge, id2type.first );
1155           }
1156           else // new nodes are on different EDGEs; put one of them on VERTEX
1157           {
1158             const int      edgeIndex = rgtSide->EdgeIndex( srcNodeStr[ iN-1 ].normParam );
1159             const double vertexParam = rgtSide->LastParameter( edgeIndex );
1160             const gp_Pnt           p = BRep_Tool::Pnt( rgtSide->LastVertex( edgeIndex ));
1161             const int         isPrev = ( Abs( srcNodeStr[ iN-1 ].normParam - vertexParam ) <
1162                                          Abs( srcNodeStr[ iN   ].normParam - vertexParam ));
1163             meshDS->SetMeshElementOnShape( newEdge, newNodes[ iN-(1-isPrev) ]->getshapeId() );
1164             meshDS->UnSetNodeOnShape( newNodes[ iN-isPrev ] );
1165             meshDS->SetNodeOnVertex ( newNodes[ iN-isPrev ], rgtSide->LastVertex( edgeIndex ));
1166             meshDS->MoveNode( newNodes[ iN-isPrev ], p.X(), p.Y(), p.Z() );
1167           }
1168         }
1169         myHelper->SetElementsOnShape( true );
1170         for ( int i = 0; i < rgtSide->NbEdges(); ++i ) // update state of sub-meshes
1171         {
1172           const TopoDS_Edge& E = rgtSide->Edge( i );
1173           SMESH_subMesh* tgtSM = mesh->GetSubMesh( E );
1174           tgtSM->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
1175         }
1176
1177         // to continue projection from the just computed side as a source
1178         if ( !swapLeftRight && rgtSide->NbEdges() > 1 && w2q->second == iW )
1179         {
1180           std::pair<int,int> wgt2quadKeyVal( w2q->first + 1, thePrism.myRightQuadIndex[ iW ]);
1181           wgt2quad.insert( wgt2quadKeyVal ); // it will be skipped by ++w2q
1182           wgt2quad.insert( wgt2quadKeyVal );
1183           w2q = wgt2quad.rbegin();
1184         }
1185       }
1186       else
1187       {
1188         // HOPE assigned hypotheses are OK, so that equal nb of segments will be generated
1189         //return toSM( error("Partial projection not implemented"));
1190       }
1191     } // loop on quads of a composite wall side
1192   } // loop on the ordered wall sides
1193
1194
1195
1196   for ( size_t iW = 0; iW != thePrism.myWallQuads.size(); ++iW )
1197   {
1198     Prism_3D::TQuadList::const_iterator quad = thePrism.myWallQuads[iW].begin();
1199     for ( ; quad != thePrism.myWallQuads[iW].end(); ++quad )
1200     {
1201       // Top EDGEs must be projections from the bottom ones
1202       // to compute stuctured quad mesh on wall FACEs
1203       // ---------------------------------------------------
1204       const TopoDS_Edge& botE = (*quad)->side[ QUAD_BOTTOM_SIDE ]->Edge(0);
1205       const TopoDS_Edge& topE = (*quad)->side[ QUAD_TOP_SIDE    ]->Edge(0);
1206
1207       projector1D->myHyp.SetSourceEdge( botE );
1208
1209       SMESH_subMesh* tgtEdgeSm = mesh->GetSubMesh( topE );
1210       if ( !tgtEdgeSm->IsMeshComputed() )
1211       {
1212         // compute nodes on VERTEXes
1213         tgtEdgeSm->ComputeSubMeshStateEngine( SMESH_subMesh::COMPUTE );
1214         // project segments
1215         projector1D->InitComputeError();
1216         bool ok = projector1D->Compute( *mesh, topE );
1217         if ( !ok )
1218         {
1219           SMESH_ComputeErrorPtr err = projector1D->GetComputeError();
1220           if ( err->IsOK() ) err->myName = COMPERR_ALGO_FAILED;
1221           tgtEdgeSm->GetComputeError() = err;
1222           return false;
1223         }
1224       }
1225       tgtEdgeSm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
1226
1227       // Compute quad mesh on wall FACEs
1228       // -------------------------------
1229       const TopoDS_Face& face = (*quad)->face;
1230       SMESH_subMesh* fSM = mesh->GetSubMesh( face );
1231       if ( ! fSM->IsMeshComputed() )
1232       {
1233         // make all EDGES meshed
1234         fSM->ComputeSubMeshStateEngine( SMESH_subMesh::COMPUTE );
1235         if ( !fSM->SubMeshesComputed() )
1236           return toSM( error( COMPERR_BAD_INPUT_MESH,
1237                               "Not all edges have valid algorithm and hypothesis"));
1238         // mesh the <face>
1239         quadAlgo->InitComputeError();
1240         bool ok = quadAlgo->Compute( *mesh, face );
1241         fSM->GetComputeError() = quadAlgo->GetComputeError();
1242         if ( !ok )
1243           return false;
1244         fSM->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
1245       }
1246       if ( myHelper->GetIsQuadratic() )
1247       {
1248         // fill myHelper with medium nodes built by quadAlgo
1249         SMDS_ElemIteratorPtr fIt = fSM->GetSubMeshDS()->GetElements();
1250         while ( fIt->more() )
1251           myHelper->AddTLinks( dynamic_cast<const SMDS_MeshFace*>( fIt->next() ));
1252       }
1253     }
1254   }
1255
1256   return true;
1257 }
1258
1259 //=======================================================================
1260 //function : Evaluate
1261 //purpose  : 
1262 //=======================================================================
1263
1264 bool StdMeshers_Prism_3D::Evaluate(SMESH_Mesh&         theMesh,
1265                                    const TopoDS_Shape& theShape,
1266                                    MapShapeNbElems&    aResMap)
1267 {
1268   if ( theShape.ShapeType() == TopAbs_COMPOUND )
1269   {
1270     bool ok = true;
1271     for ( TopoDS_Iterator it( theShape ); it.More(); it.Next() )
1272       ok &= Evaluate( theMesh, it.Value(), aResMap );
1273     return ok;
1274   }
1275   SMESH_MesherHelper helper( theMesh );
1276   myHelper = &helper;
1277   myHelper->SetSubShape( theShape );
1278
1279   // find face contains only triangles
1280   vector < SMESH_subMesh * >meshFaces;
1281   TopTools_SequenceOfShape aFaces;
1282   int NumBase = 0, i = 0, NbQFs = 0;
1283   for (TopExp_Explorer exp(theShape, TopAbs_FACE); exp.More(); exp.Next()) {
1284     i++;
1285     aFaces.Append(exp.Current());
1286     SMESH_subMesh *aSubMesh = theMesh.GetSubMesh(exp.Current());
1287     meshFaces.push_back(aSubMesh);
1288     MapShapeNbElemsItr anIt = aResMap.find(meshFaces[i-1]);
1289     if( anIt==aResMap.end() )
1290       return toSM( error( "Submesh can not be evaluated"));
1291
1292     std::vector<int> aVec = (*anIt).second;
1293     int nbtri = Max(aVec[SMDSEntity_Triangle],aVec[SMDSEntity_Quad_Triangle]);
1294     int nbqua = Max(aVec[SMDSEntity_Quadrangle],aVec[SMDSEntity_Quad_Quadrangle]);
1295     if( nbtri==0 && nbqua>0 ) {
1296       NbQFs++;
1297     }
1298     if( nbtri>0 ) {
1299       NumBase = i;
1300     }
1301   }
1302
1303   if(NbQFs<4) {
1304     std::vector<int> aResVec(SMDSEntity_Last);
1305     for(int i=SMDSEntity_Node; i<SMDSEntity_Last; i++) aResVec[i] = 0;
1306     SMESH_subMesh * sm = theMesh.GetSubMesh(theShape);
1307     aResMap.insert(std::make_pair(sm,aResVec));
1308     return toSM( error( "Submesh can not be evaluated" ));
1309   }
1310
1311   if(NumBase==0) NumBase = 1; // only quads => set 1 faces as base
1312
1313   // find number of 1d elems for base face
1314   int nb1d = 0;
1315   TopTools_MapOfShape Edges1;
1316   for (TopExp_Explorer exp(aFaces.Value(NumBase), TopAbs_EDGE); exp.More(); exp.Next()) {
1317     Edges1.Add(exp.Current());
1318     SMESH_subMesh *sm = theMesh.GetSubMesh(exp.Current());
1319     if( sm ) {
1320       MapShapeNbElemsItr anIt = aResMap.find(sm);
1321       if( anIt == aResMap.end() ) continue;
1322       std::vector<int> aVec = (*anIt).second;
1323       nb1d += Max(aVec[SMDSEntity_Edge],aVec[SMDSEntity_Quad_Edge]);
1324     }
1325   }
1326   // find face opposite to base face
1327   int OppNum = 0;
1328   for(i=1; i<=6; i++) {
1329     if(i==NumBase) continue;
1330     bool IsOpposite = true;
1331     for(TopExp_Explorer exp(aFaces.Value(i), TopAbs_EDGE); exp.More(); exp.Next()) {
1332       if( Edges1.Contains(exp.Current()) ) {
1333         IsOpposite = false;
1334         break;
1335       }
1336     }
1337     if(IsOpposite) {
1338       OppNum = i;
1339       break;
1340     }
1341   }
1342   // find number of 2d elems on side faces
1343   int nb2d = 0;
1344   for(i=1; i<=6; i++) {
1345     if( i==OppNum || i==NumBase ) continue;
1346     MapShapeNbElemsItr anIt = aResMap.find( meshFaces[i-1] );
1347     if( anIt == aResMap.end() ) continue;
1348     std::vector<int> aVec = (*anIt).second;
1349     nb2d += Max(aVec[SMDSEntity_Quadrangle],aVec[SMDSEntity_Quad_Quadrangle]);
1350   }
1351   
1352   MapShapeNbElemsItr anIt = aResMap.find( meshFaces[NumBase-1] );
1353   std::vector<int> aVec = (*anIt).second;
1354   bool IsQuadratic = (aVec[SMDSEntity_Quad_Triangle]>aVec[SMDSEntity_Triangle]) ||
1355                      (aVec[SMDSEntity_Quad_Quadrangle]>aVec[SMDSEntity_Quadrangle]);
1356   int nb2d_face0_3 = Max(aVec[SMDSEntity_Triangle],aVec[SMDSEntity_Quad_Triangle]);
1357   int nb2d_face0_4 = Max(aVec[SMDSEntity_Quadrangle],aVec[SMDSEntity_Quad_Quadrangle]);
1358   int nb0d_face0 = aVec[SMDSEntity_Node];
1359   int nb1d_face0_int = ( nb2d_face0_3*3 + nb2d_face0_4*4 - nb1d ) / 2;
1360
1361   std::vector<int> aResVec(SMDSEntity_Last);
1362   for(int i=SMDSEntity_Node; i<SMDSEntity_Last; i++) aResVec[i] = 0;
1363   if(IsQuadratic) {
1364     aResVec[SMDSEntity_Quad_Penta] = nb2d_face0_3 * ( nb2d/nb1d );
1365     aResVec[SMDSEntity_Quad_Hexa] = nb2d_face0_4 * ( nb2d/nb1d );
1366     aResVec[SMDSEntity_Node] = nb0d_face0 * ( 2*nb2d/nb1d - 1 ) - nb1d_face0_int * nb2d/nb1d;
1367   }
1368   else {
1369     aResVec[SMDSEntity_Node] = nb0d_face0 * ( nb2d/nb1d - 1 );
1370     aResVec[SMDSEntity_Penta] = nb2d_face0_3 * ( nb2d/nb1d );
1371     aResVec[SMDSEntity_Hexa] = nb2d_face0_4 * ( nb2d/nb1d );
1372   }
1373   SMESH_subMesh * sm = theMesh.GetSubMesh(theShape);
1374   aResMap.insert(std::make_pair(sm,aResVec));
1375
1376   return true;
1377 }
1378
1379 //================================================================================
1380 /*!
1381  * \brief Create prisms
1382  * \param columns - columns of nodes generated from nodes of a mesh face
1383  * \param helper - helper initialized by mesh and shape to add prisms to
1384  */
1385 //================================================================================
1386
1387 void StdMeshers_Prism_3D::AddPrisms( vector<const TNodeColumn*> & columns,
1388                                      SMESH_MesherHelper*          helper)
1389 {
1390   int nbNodes = columns.size();
1391   int nbZ     = columns[0]->size();
1392   if ( nbZ < 2 ) return;
1393
1394   // find out orientation
1395   bool isForward = true;
1396   SMDS_VolumeTool vTool;
1397   int z = 1;
1398   switch ( nbNodes ) {
1399   case 3: {
1400     SMDS_VolumeOfNodes tmpPenta ( (*columns[0])[z-1], // bottom
1401                                   (*columns[1])[z-1],
1402                                   (*columns[2])[z-1],
1403                                   (*columns[0])[z],   // top
1404                                   (*columns[1])[z],
1405                                   (*columns[2])[z] );
1406     vTool.Set( &tmpPenta );
1407     isForward  = vTool.IsForward();
1408     break;
1409   }
1410   case 4: {
1411     SMDS_VolumeOfNodes tmpHex( (*columns[0])[z-1], (*columns[1])[z-1], // bottom
1412                                (*columns[2])[z-1], (*columns[3])[z-1],
1413                                (*columns[0])[z],   (*columns[1])[z],   // top
1414                                (*columns[2])[z],   (*columns[3])[z] );
1415     vTool.Set( &tmpHex );
1416     isForward  = vTool.IsForward();
1417     break;
1418   }
1419   default:
1420     const int di = (nbNodes+1) / 3;
1421     SMDS_VolumeOfNodes tmpVol ( (*columns[0]   )[z-1],
1422                                 (*columns[di]  )[z-1],
1423                                 (*columns[2*di])[z-1],
1424                                 (*columns[0]   )[z],
1425                                 (*columns[di]  )[z],
1426                                 (*columns[2*di])[z] );
1427     vTool.Set( &tmpVol );
1428     isForward  = vTool.IsForward();
1429   }
1430
1431   // vertical loop on columns
1432
1433   helper->SetElementsOnShape( true );
1434
1435   switch ( nbNodes ) {
1436
1437   case 3: { // ---------- pentahedra
1438     const int i1 = isForward ? 1 : 2;
1439     const int i2 = isForward ? 2 : 1;
1440     for ( z = 1; z < nbZ; ++z )
1441       helper->AddVolume( (*columns[0 ])[z-1], // bottom
1442                          (*columns[i1])[z-1],
1443                          (*columns[i2])[z-1],
1444                          (*columns[0 ])[z],   // top
1445                          (*columns[i1])[z],
1446                          (*columns[i2])[z] );
1447     break;
1448   }
1449   case 4: { // ---------- hexahedra
1450     const int i1 = isForward ? 1 : 3;
1451     const int i3 = isForward ? 3 : 1;
1452     for ( z = 1; z < nbZ; ++z )
1453       helper->AddVolume( (*columns[0])[z-1], (*columns[i1])[z-1], // bottom
1454                          (*columns[2])[z-1], (*columns[i3])[z-1],
1455                          (*columns[0])[z],   (*columns[i1])[z],     // top
1456                          (*columns[2])[z],   (*columns[i3])[z] );
1457     break;
1458   }
1459   case 6: { // ---------- octahedra
1460     const int iBase1 = isForward ? -1 : 0;
1461     const int iBase2 = isForward ?  0 :-1;
1462     for ( z = 1; z < nbZ; ++z )
1463       helper->AddVolume( (*columns[0])[z+iBase1], (*columns[1])[z+iBase1], // bottom or top
1464                          (*columns[2])[z+iBase1], (*columns[3])[z+iBase1],
1465                          (*columns[4])[z+iBase1], (*columns[5])[z+iBase1],
1466                          (*columns[0])[z+iBase2], (*columns[1])[z+iBase2], // top or bottom
1467                          (*columns[2])[z+iBase2], (*columns[3])[z+iBase2],
1468                          (*columns[4])[z+iBase2], (*columns[5])[z+iBase2] );
1469     break;
1470   }
1471   default: // ---------- polyhedra
1472     vector<int> quantities( 2 + nbNodes, 4 );
1473     quantities[0] = quantities[1] = nbNodes;
1474     columns.resize( nbNodes + 1 );
1475     columns[ nbNodes ] = columns[ 0 ];
1476     const int i1 = isForward ? 1 : 3;
1477     const int i3 = isForward ? 3 : 1;
1478     const int iBase1 = isForward ? -1 : 0;
1479     const int iBase2 = isForward ?  0 :-1;
1480     vector<const SMDS_MeshNode*> nodes( 2*nbNodes + 4*nbNodes);
1481     for ( z = 1; z < nbZ; ++z )
1482     {
1483       for ( int i = 0; i < nbNodes; ++i ) {
1484         nodes[ i             ] = (*columns[ i ])[z+iBase1]; // bottom or top
1485         nodes[ 2*nbNodes-i-1 ] = (*columns[ i ])[z+iBase2]; // top or bottom
1486         // side
1487         int di = 2*nbNodes + 4*i;
1488         nodes[ di+0 ] = (*columns[i  ])[z  ];
1489         nodes[ di+i1] = (*columns[i+1])[z  ];
1490         nodes[ di+2 ] = (*columns[i+1])[z-1];
1491         nodes[ di+i3] = (*columns[i  ])[z-1];
1492       }
1493       helper->AddPolyhedralVolume( nodes, quantities );
1494     }
1495
1496   } // switch ( nbNodes )
1497 }
1498
1499 //================================================================================
1500 /*!
1501  * \brief Find correspondence between bottom and top nodes
1502  *  If elements on the bottom and top faces are topologically different,
1503  *  and projection is possible and allowed, perform the projection
1504  *  \retval bool - is a success or not
1505  */
1506 //================================================================================
1507
1508 bool StdMeshers_Prism_3D::assocOrProjBottom2Top()
1509 {
1510   SMESH_subMesh * botSM = myBlock.SubMesh( ID_BOT_FACE );
1511   SMESH_subMesh * topSM = myBlock.SubMesh( ID_TOP_FACE );
1512
1513   SMESHDS_SubMesh * botSMDS = botSM->GetSubMeshDS();
1514   SMESHDS_SubMesh * topSMDS = topSM->GetSubMeshDS();
1515
1516   if ( !botSMDS || botSMDS->NbElements() == 0 )
1517   {
1518     _gen->Compute( *myHelper->GetMesh(), botSM->GetSubShape() );
1519     botSMDS = botSM->GetSubMeshDS();
1520     if ( !botSMDS || botSMDS->NbElements() == 0 )
1521       return toSM( error(TCom("No elememts on face #") << botSM->GetId() ));
1522   }
1523
1524   bool needProject = !topSM->IsMeshComputed();
1525   if ( !needProject && 
1526        (botSMDS->NbElements() != topSMDS->NbElements() ||
1527         botSMDS->NbNodes()    != topSMDS->NbNodes()))
1528   {
1529     MESSAGE("nb elem bot " << botSMDS->NbElements() <<
1530             " top " << ( topSMDS ? topSMDS->NbElements() : 0 ));
1531     MESSAGE("nb node bot " << botSMDS->NbNodes() <<
1532             " top " << ( topSMDS ? topSMDS->NbNodes() : 0 ));
1533     return toSM( error(TCom("Mesh on faces #") << botSM->GetId()
1534                        <<" and #"<< topSM->GetId() << " seems different" ));
1535   }
1536
1537   if ( 0/*needProject && !myProjectTriangles*/ )
1538     return toSM( error(TCom("Mesh on faces #") << botSM->GetId()
1539                        <<" and #"<< topSM->GetId() << " seems different" ));
1540   ///RETURN_BAD_RESULT("Need to project but not allowed");
1541
1542   if ( needProject )
1543   {
1544     return projectBottomToTop();
1545   }
1546
1547   TopoDS_Face botFace = TopoDS::Face( myBlock.Shape( ID_BOT_FACE ));
1548   TopoDS_Face topFace = TopoDS::Face( myBlock.Shape( ID_TOP_FACE ));
1549   // associate top and bottom faces
1550   TAssocTool::TShapeShapeMap shape2ShapeMap;
1551   if ( !TAssocTool::FindSubShapeAssociation( botFace, myBlock.Mesh(),
1552                                              topFace, myBlock.Mesh(),
1553                                              shape2ShapeMap) )
1554     return toSM( error(TCom("Topology of faces #") << botSM->GetId()
1555                        <<" and #"<< topSM->GetId() << " seems different" ));
1556
1557   // Find matching nodes of top and bottom faces
1558   TNodeNodeMap n2nMap;
1559   if ( ! TAssocTool::FindMatchingNodesOnFaces( botFace, myBlock.Mesh(),
1560                                                topFace, myBlock.Mesh(),
1561                                                shape2ShapeMap, n2nMap ))
1562     return toSM( error(TCom("Mesh on faces #") << botSM->GetId()
1563                        <<" and #"<< topSM->GetId() << " seems different" ));
1564
1565   // Fill myBotToColumnMap
1566
1567   int zSize = myBlock.VerticalSize();
1568   //TNode prevTNode;
1569   TNodeNodeMap::iterator bN_tN = n2nMap.begin();
1570   for ( ; bN_tN != n2nMap.end(); ++bN_tN )
1571   {
1572     const SMDS_MeshNode* botNode = bN_tN->first;
1573     const SMDS_MeshNode* topNode = bN_tN->second;
1574     if ( botNode->GetPosition()->GetTypeOfPosition() != SMDS_TOP_FACE )
1575       continue; // wall columns are contained in myBlock
1576     // create node column
1577     Prism_3D::TNode bN( botNode );
1578     TNode2ColumnMap::iterator bN_col = 
1579       myBotToColumnMap.insert( make_pair ( bN, TNodeColumn() )).first;
1580     TNodeColumn & column = bN_col->second;
1581     column.resize( zSize );
1582     column.front() = botNode;
1583     column.back()  = topNode;
1584   }
1585   return true;
1586 }
1587
1588 //================================================================================
1589 /*!
1590  * \brief Remove quadrangles from the top face and
1591  * create triangles there by projection from the bottom
1592  * \retval bool - a success or not
1593  */
1594 //================================================================================
1595
1596 bool StdMeshers_Prism_3D::projectBottomToTop()
1597 {
1598   SMESHDS_Mesh*  meshDS = myBlock.MeshDS();
1599   SMESH_subMesh * botSM = myBlock.SubMesh( ID_BOT_FACE );
1600   SMESH_subMesh * topSM = myBlock.SubMesh( ID_TOP_FACE );
1601
1602   SMESHDS_SubMesh * botSMDS = botSM->GetSubMeshDS();
1603   SMESHDS_SubMesh * topSMDS = topSM->GetSubMeshDS();
1604
1605   if ( topSMDS && topSMDS->NbElements() > 0 )
1606     topSM->ComputeStateEngine( SMESH_subMesh::CLEAN );
1607
1608   const TopoDS_Shape& botFace = myBlock.Shape( ID_BOT_FACE ); // oriented within the 3D SHAPE
1609   const TopoDS_Shape& topFace = myBlock.Shape( ID_TOP_FACE);
1610   int topFaceID = meshDS->ShapeToIndex( topFace );
1611
1612   // Fill myBotToColumnMap
1613
1614   int zSize = myBlock.VerticalSize();
1615   Prism_3D::TNode prevTNode;
1616   SMDS_NodeIteratorPtr nIt = botSMDS->GetNodes();
1617   while ( nIt->more() )
1618   {
1619     const SMDS_MeshNode* botNode = nIt->next();
1620     if ( botNode->GetPosition()->GetTypeOfPosition() != SMDS_TOP_FACE )
1621       continue; // strange
1622     // compute bottom node params
1623     Prism_3D::TNode bN( botNode );
1624     gp_XYZ paramHint(-1,-1,-1);
1625     if ( prevTNode.IsNeighbor( bN ))
1626       paramHint = prevTNode.GetParams();
1627     if ( !myBlock.ComputeParameters( bN.GetCoords(), bN.ChangeParams(),
1628                                      ID_BOT_FACE, paramHint ))
1629       return toSM( error(TCom("Can't compute normalized parameters for node ")
1630                          << botNode->GetID() << " on the face #"<< botSM->GetId() ));
1631     prevTNode = bN;
1632     // compute top node coords
1633     gp_XYZ topXYZ; gp_XY topUV;
1634     if ( !myBlock.FacePoint( ID_TOP_FACE, bN.GetParams(), topXYZ ) ||
1635          !myBlock.FaceUV   ( ID_TOP_FACE, bN.GetParams(), topUV ))
1636       return toSM( error(TCom("Can't compute coordinates "
1637                               "by normalized parameters on the face #")<< topSM->GetId() ));
1638     SMDS_MeshNode * topNode = meshDS->AddNode( topXYZ.X(),topXYZ.Y(),topXYZ.Z() );
1639     meshDS->SetNodeOnFace( topNode, topFaceID, topUV.X(), topUV.Y() );
1640     // create node column
1641     TNode2ColumnMap::iterator bN_col = 
1642       myBotToColumnMap.insert( make_pair ( bN, TNodeColumn() )).first;
1643     TNodeColumn & column = bN_col->second;
1644     column.resize( zSize );
1645     column.front() = botNode;
1646     column.back()  = topNode;
1647   }
1648
1649   // Create top faces
1650
1651   const bool oldSetElemsOnShape = myHelper->SetElementsOnShape( false );
1652
1653   // care of orientation;
1654   // if the bottom faces is orienetd OK then top faces must be reversed
1655   bool reverseTop = true;
1656   if ( myHelper->NbAncestors( botFace, *myBlock.Mesh(), TopAbs_SOLID ) > 1 )
1657     reverseTop = ! myHelper->IsReversedSubMesh( TopoDS::Face( botFace ));
1658   int iFrw, iRev, *iPtr = &( reverseTop ? iRev : iFrw );
1659
1660   // loop on bottom mesh faces
1661   SMDS_ElemIteratorPtr faceIt = botSMDS->GetElements();
1662   vector< const SMDS_MeshNode* > nodes;
1663   while ( faceIt->more() )
1664   {
1665     const SMDS_MeshElement* face = faceIt->next();
1666     if ( !face || face->GetType() != SMDSAbs_Face )
1667       continue;
1668
1669     // find top node in columns for each bottom node
1670     int nbNodes = face->NbCornerNodes();
1671     nodes.resize( nbNodes );
1672     for ( iFrw = 0, iRev = nbNodes-1; iFrw < nbNodes; ++iFrw, --iRev )
1673     {
1674       const SMDS_MeshNode* n = face->GetNode( *iPtr );
1675       if ( n->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE ) {
1676         TNode2ColumnMap::iterator bot_column = myBotToColumnMap.find( n );
1677         if ( bot_column == myBotToColumnMap.end() )
1678           return toSM( error(TCom("No nodes found above node ") << n->GetID() ));
1679         nodes[ iFrw ] = bot_column->second.back();
1680       }
1681       else {
1682         const TNodeColumn* column = myBlock.GetNodeColumn( n );
1683         if ( !column )
1684           return toSM( error(TCom("No side nodes found above node ") << n->GetID() ));
1685         nodes[ iFrw ] = column->back();
1686       }
1687     }
1688     SMDS_MeshElement* newFace = 0;
1689     switch ( nbNodes ) {
1690
1691     case 3: {
1692       newFace = myHelper->AddFace(nodes[0], nodes[1], nodes[2]);
1693       break;
1694       }
1695     case 4: {
1696       newFace = myHelper->AddFace( nodes[0], nodes[1], nodes[2], nodes[3] );
1697       break;
1698       }
1699     default:
1700       newFace = meshDS->AddPolygonalFace( nodes );
1701     }
1702     if ( newFace )
1703       meshDS->SetMeshElementOnShape( newFace, topFaceID );
1704   }
1705
1706   myHelper->SetElementsOnShape( oldSetElemsOnShape );  
1707
1708   return true;
1709 }
1710
1711 //=======================================================================
1712 //function : project2dMesh
1713 //purpose  : Project mesh faces from a source FACE of one prism (theSrcFace)
1714 //           to a source FACE of another prism (theTgtFace)
1715 //=======================================================================
1716
1717 bool StdMeshers_Prism_3D::project2dMesh(const TopoDS_Face& theSrcFace,
1718                                         const TopoDS_Face& theTgtFace)
1719 {
1720   TProjction2dAlgo* projector2D = TProjction2dAlgo::instance( this );
1721   projector2D->myHyp.SetSourceFace( theSrcFace );
1722   bool ok = projector2D->Compute( *myHelper->GetMesh(), theTgtFace );
1723
1724   SMESH_subMesh* tgtSM = myHelper->GetMesh()->GetSubMesh( theTgtFace );
1725   tgtSM->ComputeStateEngine       ( SMESH_subMesh::CHECK_COMPUTE_STATE );
1726   tgtSM->ComputeSubMeshStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
1727
1728   return ok;
1729 }
1730
1731 //================================================================================
1732 /*!
1733  * \brief Set projection coordinates of a node to a face and it's sub-shapes
1734  * \param faceID - the face given by in-block ID
1735  * \param params - node normalized parameters
1736  * \retval bool - is a success
1737  */
1738 //================================================================================
1739
1740 bool StdMeshers_Prism_3D::setFaceAndEdgesXYZ( const int faceID, const gp_XYZ& params, int z )
1741 {
1742   // find base and top edges of the face
1743   enum { BASE = 0, TOP, LEFT, RIGHT };
1744   vector< int > edgeVec; // 0-base, 1-top
1745   SMESH_Block::GetFaceEdgesIDs( faceID, edgeVec );
1746
1747   myBlock.EdgePoint( edgeVec[ BASE ], params, myShapeXYZ[ edgeVec[ BASE ]]);
1748   myBlock.EdgePoint( edgeVec[ TOP  ], params, myShapeXYZ[ edgeVec[ TOP ]]);
1749
1750   SHOWYXZ("\nparams ", params);
1751   SHOWYXZ("TOP is " <<edgeVec[ TOP ], myShapeXYZ[ edgeVec[ TOP]]);
1752   SHOWYXZ("BASE is "<<edgeVec[ BASE], myShapeXYZ[ edgeVec[ BASE]]);
1753
1754   if ( faceID == SMESH_Block::ID_Fx0z || faceID == SMESH_Block::ID_Fx1z )
1755   {
1756     myBlock.EdgePoint( edgeVec[ LEFT ], params, myShapeXYZ[ edgeVec[ LEFT ]]);
1757     myBlock.EdgePoint( edgeVec[ RIGHT ], params, myShapeXYZ[ edgeVec[ RIGHT ]]);
1758
1759     SHOWYXZ("VER "<<edgeVec[ LEFT], myShapeXYZ[ edgeVec[ LEFT]]);
1760     SHOWYXZ("VER "<<edgeVec[ RIGHT], myShapeXYZ[ edgeVec[ RIGHT]]);
1761   }
1762   myBlock.FacePoint( faceID, params, myShapeXYZ[ faceID ]);
1763   SHOWYXZ("FacePoint "<<faceID, myShapeXYZ[ faceID]);
1764
1765   return true;
1766 }
1767
1768 //=======================================================================
1769 //function : toSM
1770 //purpose  : If (!isOK), sets the error to a sub-mesh of a current SOLID
1771 //=======================================================================
1772
1773 bool StdMeshers_Prism_3D::toSM( bool isOK )
1774 {
1775   if ( mySetErrorToSM &&
1776        !isOK &&
1777        myHelper &&
1778        !myHelper->GetSubShape().IsNull() &&
1779        myHelper->GetSubShape().ShapeType() == TopAbs_SOLID)
1780   {
1781     SMESH_subMesh* sm = myHelper->GetMesh()->GetSubMesh( myHelper->GetSubShape() );
1782     sm->GetComputeError() = this->GetComputeError();
1783     // clear error in order not to return it twice
1784     _error = COMPERR_OK;
1785     _comment.clear();
1786   }
1787   return isOK;
1788 }
1789
1790 //=======================================================================
1791 //function : shapeID
1792 //purpose  : Return index of a shape
1793 //=======================================================================
1794
1795 int StdMeshers_Prism_3D::shapeID( const TopoDS_Shape& S )
1796 {
1797   if ( S.IsNull() ) return 0;
1798   if ( !myHelper  ) return -3;
1799   return myHelper->GetMeshDS()->ShapeToIndex( S );
1800 }
1801
1802 namespace Prism_3D
1803 {
1804   //================================================================================
1805   /*!
1806    * \brief Return true if this node and other one belong to one face
1807    */
1808   //================================================================================
1809
1810   bool Prism_3D::TNode::IsNeighbor( const Prism_3D::TNode& other ) const
1811   {
1812     if ( !other.myNode || !myNode ) return false;
1813
1814     SMDS_ElemIteratorPtr fIt = other.myNode->GetInverseElementIterator(SMDSAbs_Face);
1815     while ( fIt->more() )
1816       if ( fIt->next()->GetNodeIndex( myNode ) >= 0 )
1817         return true;
1818     return false;
1819   }
1820
1821   //================================================================================
1822   /*!
1823    * \brief Prism initialization
1824    */
1825   //================================================================================
1826
1827   void TPrismTopo::Clear()
1828   {
1829     myShape3D.Nullify();
1830     myTop.Nullify();
1831     myBottom.Nullify();
1832     myWallQuads.clear();
1833     myBottomEdges.clear();
1834     myNbEdgesInWires.clear();
1835     myWallQuads.clear();
1836   }
1837
1838 } // namespace Prism_3D
1839
1840 //================================================================================
1841 /*!
1842  * \brief Constructor. Initialization is needed
1843  */
1844 //================================================================================
1845
1846 StdMeshers_PrismAsBlock::StdMeshers_PrismAsBlock()
1847 {
1848   mySide = 0;
1849 }
1850
1851 StdMeshers_PrismAsBlock::~StdMeshers_PrismAsBlock()
1852 {
1853   Clear();
1854 }
1855 void StdMeshers_PrismAsBlock::Clear()
1856 {
1857   myHelper = 0;
1858   myShapeIDMap.Clear();
1859   myError.reset();
1860
1861   if ( mySide ) {
1862     delete mySide; mySide = 0;
1863   }
1864   myParam2ColumnMaps.clear();
1865   myShapeIndex2ColumnMap.clear();
1866 }
1867
1868 //=======================================================================
1869 //function : initPrism
1870 //purpose  : Analyse shape geometry and mesh.
1871 //           If there are triangles on one of faces, it becomes 'bottom'.
1872 //           thePrism.myBottom can be already set up.
1873 //=======================================================================
1874
1875 bool StdMeshers_Prism_3D::initPrism(Prism_3D::TPrismTopo& thePrism,
1876                                     const TopoDS_Shape&   shape3D)
1877 {
1878   myHelper->SetSubShape( shape3D );
1879
1880   SMESH_subMesh* mainSubMesh = myHelper->GetMesh()->GetSubMeshContaining( shape3D );
1881   if ( !mainSubMesh ) return toSM( error(COMPERR_BAD_INPUT_MESH,"Null submesh of shape3D"));
1882
1883   // detect not-quad FACE sub-meshes of the 3D SHAPE
1884   list< SMESH_subMesh* > notQuadGeomSubMesh;
1885   list< SMESH_subMesh* > notQuadElemSubMesh;
1886   int nbFaces = 0;
1887   //
1888   SMESH_subMesh* anyFaceSM = 0;
1889   SMESH_subMeshIteratorPtr smIt = mainSubMesh->getDependsOnIterator(false,true);
1890   while ( smIt->more() )
1891   {
1892     SMESH_subMesh* sm = smIt->next();
1893     const TopoDS_Shape& face = sm->GetSubShape();
1894     if      ( face.ShapeType() > TopAbs_FACE ) break;
1895     else if ( face.ShapeType() < TopAbs_FACE ) continue;
1896     nbFaces++;
1897     anyFaceSM = sm;
1898
1899     // is quadrangle FACE?
1900     list< TopoDS_Edge > orderedEdges;
1901     list< int >         nbEdgesInWires;
1902     int nbWires = SMESH_Block::GetOrderedEdges( TopoDS::Face( face ), orderedEdges,
1903                                                 nbEdgesInWires );
1904     if ( nbWires != 1 || nbEdgesInWires.front() != 4 )
1905       notQuadGeomSubMesh.push_back( sm );
1906
1907     // look for not quadrangle mesh elements
1908     if ( SMESHDS_SubMesh* smDS = sm->GetSubMeshDS() )
1909       if ( !myHelper->IsSameElemGeometry( smDS, SMDSGeom_QUADRANGLE ))
1910         notQuadElemSubMesh.push_back( sm );
1911   }
1912
1913   int nbNotQuadMeshed = notQuadElemSubMesh.size();
1914   int       nbNotQuad = notQuadGeomSubMesh.size();
1915   bool     hasNotQuad = ( nbNotQuad || nbNotQuadMeshed );
1916
1917   // detect bad cases
1918   if ( nbNotQuadMeshed > 2 )
1919   {
1920     return toSM( error(COMPERR_BAD_INPUT_MESH,
1921                        TCom("More than 2 faces with not quadrangle elements: ")
1922                        <<nbNotQuadMeshed));
1923   }
1924   if ( nbNotQuad > 2 || !thePrism.myBottom.IsNull() )
1925   {
1926     // Issue 0020843 - one of side FACEs is quasi-quadrilateral (not 4 EDGEs).
1927     // Remove from notQuadGeomSubMesh faces meshed with regular grid
1928     int nbQuasiQuads = removeQuasiQuads( notQuadGeomSubMesh, myHelper,
1929                                          TQuadrangleAlgo::instance(this,myHelper) );
1930     nbNotQuad -= nbQuasiQuads;
1931     if ( nbNotQuad > 2 )
1932       return toSM( error(COMPERR_BAD_SHAPE,
1933                          TCom("More than 2 not quadrilateral faces: ") <<nbNotQuad));
1934     hasNotQuad = ( nbNotQuad || nbNotQuadMeshed );
1935   }
1936
1937   // Analyse mesh and topology of FACEs: choose the bottom sub-mesh.
1938   // If there are not quadrangle FACEs, they are top and bottom ones.
1939   // Not quadrangle FACEs must be only on top and bottom.
1940
1941   SMESH_subMesh * botSM = 0;
1942   SMESH_subMesh * topSM = 0;
1943
1944   if ( hasNotQuad ) // can chose a bottom FACE
1945   {
1946     if ( nbNotQuadMeshed > 0 ) botSM = notQuadElemSubMesh.front();
1947     else                       botSM = notQuadGeomSubMesh.front();
1948     if ( nbNotQuadMeshed > 1 ) topSM = notQuadElemSubMesh.back();
1949     else if ( nbNotQuad  > 1 ) topSM = notQuadGeomSubMesh.back();
1950
1951     if ( topSM == botSM ) {
1952       if ( nbNotQuadMeshed > 1 ) topSM = notQuadElemSubMesh.front();
1953       else                       topSM = notQuadGeomSubMesh.front();
1954     }
1955
1956     // detect mesh triangles on wall FACEs
1957     if ( nbNotQuad == 2 && nbNotQuadMeshed > 0 ) {
1958       bool ok = false;
1959       if ( nbNotQuadMeshed == 1 )
1960         ok = ( find( notQuadGeomSubMesh.begin(),
1961                      notQuadGeomSubMesh.end(), botSM ) != notQuadGeomSubMesh.end() );
1962       else
1963         ok = ( notQuadGeomSubMesh == notQuadElemSubMesh );
1964       if ( !ok )
1965         return toSM( error(COMPERR_BAD_INPUT_MESH,
1966                            "Side face meshed with not quadrangle elements"));
1967     }
1968   }
1969
1970   thePrism.myNotQuadOnTop = ( nbNotQuadMeshed > 1 );
1971
1972   // use thePrism.myBottom
1973   if ( !thePrism.myBottom.IsNull() )
1974   {
1975     if ( botSM ) {
1976       if ( ! botSM->GetSubShape().IsSame( thePrism.myBottom )) {
1977         std::swap( botSM, topSM );
1978         if ( ! botSM->GetSubShape().IsSame( thePrism.myBottom ))
1979           return toSM( error( COMPERR_BAD_INPUT_MESH,
1980                               "Incompatible non-structured sub-meshes"));
1981       }
1982     }
1983     else {
1984       botSM = myHelper->GetMesh()->GetSubMesh( thePrism.myBottom );
1985     }
1986   }
1987   else if ( !botSM ) // find a proper bottom
1988   {
1989     // composite walls or not prism shape
1990     for ( TopExp_Explorer f( shape3D, TopAbs_FACE ); f.More(); f.Next() )
1991     {
1992       int minNbFaces = 2 + myHelper->Count( f.Current(), TopAbs_EDGE, false);
1993       if ( nbFaces >= minNbFaces)
1994       {
1995         thePrism.Clear();
1996         thePrism.myBottom = TopoDS::Face( f.Current() );
1997         if ( initPrism( thePrism, shape3D ))
1998           return true;
1999       }
2000       return toSM( error( COMPERR_BAD_SHAPE ));
2001     }
2002   }
2003
2004   // find vertex 000 - the one with smallest coordinates (for easy DEBUG :-)
2005   TopoDS_Vertex V000;
2006   double minVal = DBL_MAX, minX, val;
2007   for ( TopExp_Explorer exp( botSM->GetSubShape(), TopAbs_VERTEX );
2008         exp.More(); exp.Next() )
2009   {
2010     const TopoDS_Vertex& v = TopoDS::Vertex( exp.Current() );
2011     gp_Pnt P = BRep_Tool::Pnt( v );
2012     val = P.X() + P.Y() + P.Z();
2013     if ( val < minVal || ( val == minVal && P.X() < minX )) {
2014       V000 = v;
2015       minVal = val;
2016       minX = P.X();
2017     }
2018   }
2019
2020   thePrism.myShape3D = shape3D;
2021   if ( thePrism.myBottom.IsNull() )
2022     thePrism.myBottom  = TopoDS::Face( botSM->GetSubShape() );
2023   thePrism.myBottom.Orientation( myHelper->GetSubShapeOri( shape3D,
2024                                                            thePrism.myBottom ));
2025   // Get ordered bottom edges
2026   TopoDS_Face reverseBottom = // to have order of top EDGEs as in the top FACE
2027     TopoDS::Face( thePrism.myBottom.Reversed() );
2028   SMESH_Block::GetOrderedEdges( reverseBottom,
2029                                 thePrism.myBottomEdges,
2030                                 thePrism.myNbEdgesInWires, V000 );
2031
2032   // Get Wall faces corresponding to the ordered bottom edges and the top FACE
2033   if ( !getWallFaces( thePrism, nbFaces ))
2034     return false; //toSM( error(COMPERR_BAD_SHAPE, "Can't find side faces"));
2035
2036   if ( topSM )
2037   {
2038     if ( !thePrism.myTop.IsSame( topSM->GetSubShape() ))
2039       return toSM( error
2040                    (notQuadGeomSubMesh.empty() ? COMPERR_BAD_INPUT_MESH : COMPERR_BAD_SHAPE,
2041                     "Non-quadrilateral faces are not opposite"));
2042
2043     // check that the found top and bottom FACEs are opposite
2044     list< TopoDS_Edge >::iterator edge = thePrism.myBottomEdges.begin();
2045     for ( ; edge != thePrism.myBottomEdges.end(); ++edge )
2046       if ( myHelper->IsSubShape( *edge, thePrism.myTop ))
2047         return toSM( error
2048                      (notQuadGeomSubMesh.empty() ? COMPERR_BAD_INPUT_MESH : COMPERR_BAD_SHAPE,
2049                       "Non-quadrilateral faces are not opposite"));
2050   }
2051
2052   return true;
2053 }
2054
2055 //================================================================================
2056 /*!
2057  * \brief Initialization.
2058  * \param helper - helper loaded with mesh and 3D shape
2059  * \param thePrism - a prosm data
2060  * \retval bool - false if a mesh or a shape are KO
2061  */
2062 //================================================================================
2063
2064 bool StdMeshers_PrismAsBlock::Init(SMESH_MesherHelper*         helper,
2065                                    const Prism_3D::TPrismTopo& thePrism)
2066 {
2067   if ( mySide ) {
2068     delete mySide; mySide = 0;
2069   }
2070   vector< TSideFace* >         sideFaces( NB_WALL_FACES, 0 );
2071   vector< pair< double, double> > params( NB_WALL_FACES );
2072   mySide = new TSideFace( sideFaces, params );
2073
2074   myHelper = helper;
2075   SMESHDS_Mesh* meshDS = myHelper->GetMeshDS();
2076
2077   SMESH_Block::init();
2078   myShapeIDMap.Clear();
2079   myShapeIndex2ColumnMap.clear();
2080   
2081   int wallFaceIds[ NB_WALL_FACES ] = { // to walk around a block
2082     SMESH_Block::ID_Fx0z, SMESH_Block::ID_F1yz,
2083     SMESH_Block::ID_Fx1z, SMESH_Block::ID_F0yz
2084   };
2085
2086   myError = SMESH_ComputeError::New();
2087
2088   myNotQuadOnTop = thePrism.myNotQuadOnTop;
2089
2090   // Find columns of wall nodes and calculate edges' lengths
2091   // --------------------------------------------------------
2092
2093   myParam2ColumnMaps.clear();
2094   myParam2ColumnMaps.resize( thePrism.myBottomEdges.size() ); // total nb edges
2095
2096   size_t iE, nbEdges = thePrism.myNbEdgesInWires.front(); // nb outer edges
2097   vector< double >    edgeLength( nbEdges );
2098   multimap< double, int > len2edgeMap;
2099
2100   list< TopoDS_Edge >::const_iterator edgeIt = thePrism.myBottomEdges.begin();
2101   for ( iE = 0; iE < nbEdges; ++iE, ++edgeIt )
2102   {
2103     TParam2ColumnMap & faceColumns = myParam2ColumnMaps[ iE ];
2104
2105     Prism_3D::TQuadList::const_iterator quad = thePrism.myWallQuads[ iE ].begin();
2106     for ( ; quad != thePrism.myWallQuads[ iE ].end(); ++quad )
2107     {
2108       const TopoDS_Edge& quadBot = (*quad)->side[ QUAD_BOTTOM_SIDE ]->Edge( 0 );
2109       if ( !myHelper->LoadNodeColumns( faceColumns, (*quad)->face, quadBot, meshDS ))
2110         return error(COMPERR_BAD_INPUT_MESH, TCom("Can't find regular quadrangle mesh ")
2111                      << "on a side face #" << MeshDS()->ShapeToIndex( (*quad)->face ));
2112     }
2113     SHOWYXZ("\np1 F " <<iE, gpXYZ(faceColumns.begin()->second.front() ));
2114     SHOWYXZ("p2 F "   <<iE, gpXYZ(faceColumns.rbegin()->second.front() ));
2115     SHOWYXZ("V First "<<iE, BRep_Tool::Pnt( TopExp::FirstVertex(*edgeIt,true )));
2116
2117     edgeLength[ iE ] = SMESH_Algo::EdgeLength( *edgeIt );
2118
2119     if ( nbEdges < NB_WALL_FACES ) // fill map used to split faces
2120     {
2121       SMESHDS_SubMesh* smDS = meshDS->MeshElements( *edgeIt);
2122       if ( !smDS )
2123         return error(COMPERR_BAD_INPUT_MESH, TCom("Null submesh on the edge #")
2124                      << MeshDS()->ShapeToIndex( *edgeIt ));
2125       len2edgeMap.insert( make_pair( edgeLength[ iE ], iE ));
2126     }
2127   }
2128   // Load columns of internal edges (forming holes)
2129   // and fill map ShapeIndex to TParam2ColumnMap for them
2130   for ( ; edgeIt != thePrism.myBottomEdges.end() ; ++edgeIt, ++iE )
2131   {
2132     TParam2ColumnMap & faceColumns = myParam2ColumnMaps[ iE ];
2133
2134     Prism_3D::TQuadList::const_iterator quad = thePrism.myWallQuads[ iE ].begin();
2135     for ( ; quad != thePrism.myWallQuads[ iE ].end(); ++quad )
2136     {
2137       const TopoDS_Edge& quadBot = (*quad)->side[ QUAD_BOTTOM_SIDE ]->Edge( 0 );
2138       if ( !myHelper->LoadNodeColumns( faceColumns, (*quad)->face, quadBot, meshDS ))
2139         return error(COMPERR_BAD_INPUT_MESH, TCom("Can't find regular quadrangle mesh ")
2140                      << "on a side face #" << MeshDS()->ShapeToIndex( (*quad)->face ));
2141     }
2142     // edge columns
2143     int id = MeshDS()->ShapeToIndex( *edgeIt );
2144     bool isForward = true; // meaningless for intenal wires
2145     myShapeIndex2ColumnMap[ id ] = make_pair( & faceColumns, isForward );
2146     // columns for vertices
2147     // 1
2148     const SMDS_MeshNode* n0 = faceColumns.begin()->second.front();
2149     id = n0->getshapeId();
2150     myShapeIndex2ColumnMap[ id ] = make_pair( & faceColumns, isForward );
2151     // 2
2152     const SMDS_MeshNode* n1 = faceColumns.rbegin()->second.front();
2153     id = n1->getshapeId();
2154     myShapeIndex2ColumnMap[ id ] = make_pair( & faceColumns, isForward );
2155
2156     // SHOWYXZ("\np1 F " <<iE, gpXYZ(faceColumns.begin()->second.front() ));
2157     // SHOWYXZ("p2 F "   <<iE, gpXYZ(faceColumns.rbegin()->second.front() ));
2158     // SHOWYXZ("V First "<<iE, BRep_Tool::Pnt( TopExp::FirstVertex(*edgeIt,true )));
2159   }
2160
2161   // Create 4 wall faces of a block
2162   // -------------------------------
2163
2164   if ( nbEdges <= NB_WALL_FACES ) // ************* Split faces if necessary
2165   {
2166     map< int, int > iE2nbSplit;
2167     if ( nbEdges != NB_WALL_FACES ) // define how to split
2168     {
2169       if ( len2edgeMap.size() != nbEdges )
2170         RETURN_BAD_RESULT("Uniqueness of edge lengths not assured");
2171       map< double, int >::reverse_iterator maxLen_i = len2edgeMap.rbegin();
2172       map< double, int >::reverse_iterator midLen_i = ++len2edgeMap.rbegin();
2173       double maxLen = maxLen_i->first;
2174       double midLen = ( len2edgeMap.size() == 1 ) ? 0 : midLen_i->first;
2175       switch ( nbEdges ) {
2176       case 1: // 0-th edge is split into 4 parts
2177         iE2nbSplit.insert( make_pair( 0, 4 )); break;
2178       case 2: // either the longest edge is split into 3 parts, or both edges into halves
2179         if ( maxLen / 3 > midLen / 2 ) {
2180           iE2nbSplit.insert( make_pair( maxLen_i->second, 3 ));
2181         }
2182         else {
2183           iE2nbSplit.insert( make_pair( maxLen_i->second, 2 ));
2184           iE2nbSplit.insert( make_pair( midLen_i->second, 2 ));
2185         }
2186         break;
2187       case 3:
2188         // split longest into halves
2189         iE2nbSplit.insert( make_pair( maxLen_i->second, 2 ));
2190       }
2191     }
2192     // Create TSideFace's
2193     int iSide = 0;
2194     list< TopoDS_Edge >::const_iterator botE = thePrism.myBottomEdges.begin();
2195     for ( iE = 0; iE < nbEdges; ++iE, ++botE )
2196     {
2197       TFaceQuadStructPtr quad = thePrism.myWallQuads[ iE ].front();
2198       // split?
2199       map< int, int >::iterator i_nb = iE2nbSplit.find( iE );
2200       if ( i_nb != iE2nbSplit.end() ) {
2201         // split!
2202         int nbSplit = i_nb->second;
2203         vector< double > params;
2204         splitParams( nbSplit, &myParam2ColumnMaps[ iE ], params );
2205         const bool isForward =
2206           StdMeshers_PrismAsBlock::IsForwardEdge( myHelper->GetMeshDS(),
2207                                                   myParam2ColumnMaps[iE],
2208                                                   *botE, SMESH_Block::ID_Fx0z );
2209         for ( int i = 0; i < nbSplit; ++i ) {
2210           double f = ( isForward ? params[ i ]   : params[ nbSplit - i-1 ]);
2211           double l = ( isForward ? params[ i+1 ] : params[ nbSplit - i ]);
2212           TSideFace* comp = new TSideFace( myHelper, wallFaceIds[ iSide ],
2213                                            thePrism.myWallQuads[ iE ], *botE,
2214                                            &myParam2ColumnMaps[ iE ], f, l );
2215           mySide->SetComponent( iSide++, comp );
2216         }
2217       }
2218       else {
2219         TSideFace* comp = new TSideFace( myHelper, wallFaceIds[ iSide ],
2220                                          thePrism.myWallQuads[ iE ], *botE,
2221                                          &myParam2ColumnMaps[ iE ]);
2222         mySide->SetComponent( iSide++, comp );
2223       }
2224     }
2225   }
2226   else { // **************************** Unite faces
2227
2228     // unite first faces
2229     int nbExraFaces = nbEdges - 3;
2230     int iSide = 0, iE;
2231     double u0 = 0, sumLen = 0;
2232     for ( iE = 0; iE < nbExraFaces; ++iE )
2233       sumLen += edgeLength[ iE ];
2234
2235     vector< TSideFace* >        components( nbExraFaces );
2236     vector< pair< double, double> > params( nbExraFaces );
2237     list< TopoDS_Edge >::const_iterator botE = thePrism.myBottomEdges.begin();
2238     for ( iE = 0; iE < nbExraFaces; ++iE, ++botE )
2239     {
2240       components[ iE ] = new TSideFace( myHelper, wallFaceIds[ iSide ],
2241                                         thePrism.myWallQuads[ iE ], *botE,
2242                                         &myParam2ColumnMaps[ iE ]);
2243       double u1 = u0 + edgeLength[ iE ] / sumLen;
2244       params[ iE ] = make_pair( u0 , u1 );
2245       u0 = u1;
2246     }
2247     mySide->SetComponent( iSide++, new TSideFace( components, params ));
2248
2249     // fill the rest faces
2250     for ( ; iE < nbEdges; ++iE, ++botE )
2251     {
2252       TSideFace* comp = new TSideFace( myHelper, wallFaceIds[ iSide ],
2253                                        thePrism.myWallQuads[ iE ], *botE,
2254                                        &myParam2ColumnMaps[ iE ]);
2255       mySide->SetComponent( iSide++, comp );
2256     }
2257   }
2258
2259
2260   // Fill geometry fields of SMESH_Block
2261   // ------------------------------------
2262
2263   vector< int > botEdgeIdVec;
2264   SMESH_Block::GetFaceEdgesIDs( ID_BOT_FACE, botEdgeIdVec );
2265
2266   bool isForward[NB_WALL_FACES] = { true, true, true, true };
2267   Adaptor2d_Curve2d* botPcurves[NB_WALL_FACES];
2268   Adaptor2d_Curve2d* topPcurves[NB_WALL_FACES];
2269
2270   for ( int iF = 0; iF < NB_WALL_FACES; ++iF )
2271   {
2272     TSideFace * sideFace = mySide->GetComponent( iF );
2273     if ( !sideFace )
2274       RETURN_BAD_RESULT("NULL TSideFace");
2275     int fID = sideFace->FaceID(); // in-block ID
2276
2277     // fill myShapeIDMap
2278     if ( sideFace->InsertSubShapes( myShapeIDMap ) != 8 &&
2279          !sideFace->IsComplex())
2280       MESSAGE( ": Warning : InsertSubShapes() < 8 on side " << iF );
2281
2282     // side faces geometry
2283     Adaptor2d_Curve2d* pcurves[NB_WALL_FACES];
2284     if ( !sideFace->GetPCurves( pcurves ))
2285       RETURN_BAD_RESULT("TSideFace::GetPCurves() failed");
2286
2287     SMESH_Block::TFace& tFace = myFace[ fID - ID_FirstF ];
2288     tFace.Set( fID, sideFace->Surface(), pcurves, isForward );
2289
2290     SHOWYXZ( endl<<"F "<< iF << " id " << fID << " FRW " << sideFace->IsForward(), sideFace->Value(0,0));
2291     // edges 3D geometry
2292     vector< int > edgeIdVec;
2293     SMESH_Block::GetFaceEdgesIDs( fID, edgeIdVec );
2294     for ( int isMax = 0; isMax < 2; ++isMax ) {
2295       {
2296         int eID = edgeIdVec[ isMax ];
2297         SMESH_Block::TEdge& tEdge = myEdge[ eID - ID_FirstE ];
2298         tEdge.Set( eID, sideFace->HorizCurve(isMax), true);
2299         SHOWYXZ(eID<<" HOR"<<isMax<<"(0)", sideFace->HorizCurve(isMax)->Value(0));
2300         SHOWYXZ(eID<<" HOR"<<isMax<<"(1)", sideFace->HorizCurve(isMax)->Value(1));
2301       }
2302       {
2303         int eID = edgeIdVec[ isMax+2 ];
2304         SMESH_Block::TEdge& tEdge = myEdge[ eID - ID_FirstE  ];
2305         tEdge.Set( eID, sideFace->VertiCurve(isMax), true);
2306         SHOWYXZ(eID<<" VER"<<isMax<<"(0)", sideFace->VertiCurve(isMax)->Value(0));
2307         SHOWYXZ(eID<<" VER"<<isMax<<"(1)", sideFace->VertiCurve(isMax)->Value(1));
2308
2309         // corner points
2310         vector< int > vertexIdVec;
2311         SMESH_Block::GetEdgeVertexIDs( eID, vertexIdVec );
2312         myPnt[ vertexIdVec[0] - ID_FirstV ] = tEdge.GetCurve()->Value(0).XYZ();
2313         myPnt[ vertexIdVec[1] - ID_FirstV ] = tEdge.GetCurve()->Value(1).XYZ();
2314       }
2315     }
2316     // pcurves on horizontal faces
2317     for ( iE = 0; iE < NB_WALL_FACES; ++iE ) {
2318       if ( edgeIdVec[ BOTTOM_EDGE ] == botEdgeIdVec[ iE ] ) {
2319         botPcurves[ iE ] = sideFace->HorizPCurve( false, thePrism.myBottom );
2320         topPcurves[ iE ] = sideFace->HorizPCurve( true,  thePrism.myTop );
2321         break;
2322       }
2323     }
2324     //sideFace->dumpNodes( 4 ); // debug
2325   }
2326   // horizontal faces geometry
2327   {
2328     SMESH_Block::TFace& tFace = myFace[ ID_BOT_FACE - ID_FirstF ];
2329     tFace.Set( ID_BOT_FACE, new BRepAdaptor_Surface( thePrism.myBottom ), botPcurves, isForward );
2330     SMESH_Block::Insert( thePrism.myBottom, ID_BOT_FACE, myShapeIDMap );
2331   }
2332   {
2333     SMESH_Block::TFace& tFace = myFace[ ID_TOP_FACE - ID_FirstF ];
2334     tFace.Set( ID_TOP_FACE, new BRepAdaptor_Surface( thePrism.myTop ), topPcurves, isForward );
2335     SMESH_Block::Insert( thePrism.myTop, ID_TOP_FACE, myShapeIDMap );
2336   }
2337
2338   // Fill map ShapeIndex to TParam2ColumnMap
2339   // ----------------------------------------
2340
2341   list< TSideFace* > fList;
2342   list< TSideFace* >::iterator fListIt;
2343   fList.push_back( mySide );
2344   for ( fListIt = fList.begin(); fListIt != fList.end(); ++fListIt)
2345   {
2346     int nb = (*fListIt)->NbComponents();
2347     for ( int i = 0; i < nb; ++i ) {
2348       if ( TSideFace* comp = (*fListIt)->GetComponent( i ))
2349         fList.push_back( comp );
2350     }
2351     if ( TParam2ColumnMap* cols = (*fListIt)->GetColumns()) {
2352       // columns for a base edge
2353       int id = MeshDS()->ShapeToIndex( (*fListIt)->BaseEdge() );
2354       bool isForward = (*fListIt)->IsForward();
2355       myShapeIndex2ColumnMap[ id ] = make_pair( cols, isForward );
2356
2357       // columns for vertices
2358       const SMDS_MeshNode* n0 = cols->begin()->second.front();
2359       id = n0->getshapeId();
2360       myShapeIndex2ColumnMap[ id ] = make_pair( cols, isForward );
2361
2362       const SMDS_MeshNode* n1 = cols->rbegin()->second.front();
2363       id = n1->getshapeId();
2364       myShapeIndex2ColumnMap[ id ] = make_pair( cols, !isForward );
2365     }
2366   }
2367
2368 //   gp_XYZ testPar(0.25, 0.25, 0), testCoord;
2369 //   if ( !FacePoint( ID_BOT_FACE, testPar, testCoord ))
2370 //     RETURN_BAD_RESULT("TEST FacePoint() FAILED");
2371 //   SHOWYXZ("IN TEST PARAM" , testPar);
2372 //   SHOWYXZ("OUT TEST CORD" , testCoord);
2373 //   if ( !ComputeParameters( testCoord, testPar , ID_BOT_FACE))
2374 //     RETURN_BAD_RESULT("TEST ComputeParameters() FAILED");
2375 //   SHOWYXZ("OUT TEST PARAM" , testPar);
2376
2377   return true;
2378 }
2379
2380 //================================================================================
2381 /*!
2382  * \brief Return pointer to column of nodes
2383  * \param node - bottom node from which the returned column goes up
2384  * \retval const TNodeColumn* - the found column
2385  */
2386 //================================================================================
2387
2388 const TNodeColumn* StdMeshers_PrismAsBlock::GetNodeColumn(const SMDS_MeshNode* node) const
2389 {
2390   int sID = node->getshapeId();
2391
2392   map<int, pair< TParam2ColumnMap*, bool > >::const_iterator col_frw =
2393     myShapeIndex2ColumnMap.find( sID );
2394   if ( col_frw != myShapeIndex2ColumnMap.end() ) {
2395     const TParam2ColumnMap* cols = col_frw->second.first;
2396     TParam2ColumnIt u_col = cols->begin();
2397     for ( ; u_col != cols->end(); ++u_col )
2398       if ( u_col->second[ 0 ] == node )
2399         return & u_col->second;
2400   }
2401   return 0;
2402 }
2403
2404 //=======================================================================
2405 //function : GetLayersTransformation
2406 //purpose  : Return transformations to get coordinates of nodes of each layer
2407 //           by nodes of the bottom. Layer is a set of nodes at a certain step
2408 //           from bottom to top.
2409 //=======================================================================
2410
2411 bool StdMeshers_PrismAsBlock::GetLayersTransformation(vector<gp_Trsf> &           trsf,
2412                                                       const Prism_3D::TPrismTopo& prism) const
2413 {
2414   const int zSize = VerticalSize();
2415   if ( zSize < 3 ) return true;
2416   trsf.resize( zSize - 2 );
2417
2418   // Select some node columns by which we will define coordinate system of layers
2419
2420   vector< const TNodeColumn* > columns;
2421   {
2422     bool isReverse;
2423     list< TopoDS_Edge >::const_iterator edgeIt = prism.myBottomEdges.begin();
2424     for ( int iE = 0; iE < prism.myNbEdgesInWires.front(); ++iE, ++edgeIt )
2425     {
2426       if ( BRep_Tool::Degenerated( *edgeIt )) continue;
2427       const TParam2ColumnMap* u2colMap =
2428         GetParam2ColumnMap( MeshDS()->ShapeToIndex( *edgeIt ), isReverse );
2429       if ( !u2colMap ) return false;
2430       double f = u2colMap->begin()->first, l = u2colMap->rbegin()->first;
2431       //isReverse = ( edgeIt->Orientation() == TopAbs_REVERSED );
2432       //if ( isReverse ) swap ( f, l ); -- u2colMap takes orientation into account
2433       const int nbCol = 5;
2434       for ( int i = 0; i < nbCol; ++i )
2435       {
2436         double u = f + i/double(nbCol) * ( l - f );
2437         const TNodeColumn* col = & getColumn( u2colMap, u )->second;
2438         if ( columns.empty() || col != columns.back() )
2439           columns.push_back( col );
2440       }
2441     }
2442   }
2443
2444   // Find tolerance to check transformations
2445
2446   double tol2;
2447   {
2448     Bnd_B3d bndBox;
2449     for ( int i = 0; i < columns.size(); ++i )
2450       bndBox.Add( gpXYZ( columns[i]->front() ));
2451     tol2 = bndBox.SquareExtent() * 1e-5;
2452   }
2453
2454   // Compute transformations
2455
2456   int xCol = -1;
2457   gp_Trsf fromCsZ, toCs0;
2458   gp_Ax3 cs0 = getLayerCoordSys(0, columns, xCol );
2459   //double dist0 = cs0.Location().Distance( gpXYZ( (*columns[0])[0]));
2460   toCs0.SetTransformation( cs0 );
2461   for ( int z = 1; z < zSize-1; ++z )
2462   {
2463     gp_Ax3 csZ = getLayerCoordSys(z, columns, xCol );
2464     //double distZ = csZ.Location().Distance( gpXYZ( (*columns[0])[z]));
2465     fromCsZ.SetTransformation( csZ );
2466     fromCsZ.Invert();
2467     gp_Trsf& t = trsf[ z-1 ];
2468     t = fromCsZ * toCs0;
2469     //t.SetScaleFactor( distZ/dist0 ); - it does not work properly, wrong base point
2470
2471     // check a transformation
2472     for ( int i = 0; i < columns.size(); ++i )
2473     {
2474       gp_Pnt p0 = gpXYZ( (*columns[i])[0] );
2475       gp_Pnt pz = gpXYZ( (*columns[i])[z] );
2476       t.Transforms( p0.ChangeCoord() );
2477       if ( p0.SquareDistance( pz ) > tol2 )
2478         return false;
2479     }
2480   }
2481   return true;
2482 }
2483
2484 //================================================================================
2485 /*!
2486  * \brief Check curve orientation of a bootom edge
2487   * \param meshDS - mesh DS
2488   * \param columnsMap - node columns map of side face
2489   * \param bottomEdge - the bootom edge
2490   * \param sideFaceID - side face in-block ID
2491   * \retval bool - true if orientation coinside with in-block forward orientation
2492  */
2493 //================================================================================
2494
2495 bool StdMeshers_PrismAsBlock::IsForwardEdge(SMESHDS_Mesh*           meshDS,
2496                                             const TParam2ColumnMap& columnsMap,
2497                                             const TopoDS_Edge &     bottomEdge,
2498                                             const int               sideFaceID)
2499 {
2500   bool isForward = false;
2501   if ( SMESH_MesherHelper::IsClosedEdge( bottomEdge ))
2502   {
2503     isForward = ( bottomEdge.Orientation() == TopAbs_FORWARD );
2504   }
2505   else
2506   {
2507     const TNodeColumn&     firstCol = columnsMap.begin()->second;
2508     const SMDS_MeshNode* bottomNode = firstCol[0];
2509     TopoDS_Shape firstVertex = SMESH_MesherHelper::GetSubShapeByNode( bottomNode, meshDS );
2510     isForward = ( firstVertex.IsSame( TopExp::FirstVertex( bottomEdge, true )));
2511   }
2512   // on 2 of 4 sides first vertex is end
2513   if ( sideFaceID == ID_Fx1z || sideFaceID == ID_F0yz )
2514     isForward = !isForward;
2515   return isForward;
2516 }
2517
2518 //================================================================================
2519 /*!
2520  * \brief Constructor
2521   * \param faceID - in-block ID
2522   * \param face - geom FACE
2523   * \param baseEdge - EDGE proreply oriented in the bottom EDGE !!!
2524   * \param columnsMap - map of node columns
2525   * \param first - first normalized param
2526   * \param last - last normalized param
2527  */
2528 //================================================================================
2529
2530 StdMeshers_PrismAsBlock::TSideFace::TSideFace(SMESH_MesherHelper*        helper,
2531                                               const int                  faceID,
2532                                               const Prism_3D::TQuadList& quadList,
2533                                               const TopoDS_Edge&         baseEdge,
2534                                               TParam2ColumnMap*          columnsMap,
2535                                               const double               first,
2536                                               const double               last):
2537   myID( faceID ),
2538   myParamToColumnMap( columnsMap ),
2539   myHelper( helper )
2540 {
2541   myParams.resize( 1 );
2542   myParams[ 0 ] = make_pair( first, last );
2543   mySurface     = PSurface( new BRepAdaptor_Surface( quadList.front()->face ));
2544   myBaseEdge    = baseEdge;
2545   myIsForward   = StdMeshers_PrismAsBlock::IsForwardEdge( myHelper->GetMeshDS(),
2546                                                           *myParamToColumnMap,
2547                                                           myBaseEdge, myID );
2548   if ( quadList.size() > 1 ) // side is vertically composite
2549   {
2550     // fill myShapeID2Surf map to enable finding a right surface by any sub-shape ID
2551
2552     SMESHDS_Mesh* meshDS = myHelper->GetMeshDS();
2553
2554     TopTools_IndexedDataMapOfShapeListOfShape subToFaces;
2555     Prism_3D::TQuadList::const_iterator quad = quadList.begin();
2556     for ( ; quad != quadList.end(); ++quad )
2557     {
2558       const TopoDS_Face& face = (*quad)->face;
2559       TopExp::MapShapesAndAncestors( face, TopAbs_VERTEX, TopAbs_FACE, subToFaces );
2560       TopExp::MapShapesAndAncestors( face, TopAbs_EDGE,   TopAbs_FACE, subToFaces );
2561       myShapeID2Surf.insert( make_pair( meshDS->ShapeToIndex( face ),
2562                                         PSurface( new BRepAdaptor_Surface( face ))));
2563     }
2564     for ( int i = 1; i <= subToFaces.Extent(); ++i )
2565     {
2566       const TopoDS_Shape&     sub = subToFaces.FindKey( i );
2567       TopTools_ListOfShape& faces = subToFaces( i );
2568       int subID  = meshDS->ShapeToIndex( sub );
2569       int faceID = meshDS->ShapeToIndex( faces.First() );
2570       myShapeID2Surf.insert ( make_pair( subID, myShapeID2Surf[ faceID ]));
2571     }
2572   }
2573 }
2574
2575 //================================================================================
2576 /*!
2577  * \brief Constructor of complex side face
2578  */
2579 //================================================================================
2580
2581 StdMeshers_PrismAsBlock::TSideFace::
2582 TSideFace(const vector< TSideFace* >&             components,
2583           const vector< pair< double, double> > & params)
2584   :myID( components[0] ? components[0]->myID : 0 ),
2585    myParamToColumnMap( 0 ),
2586    myParams( params ),
2587    myIsForward( true ),
2588    myComponents( components ),
2589    myHelper( components[0] ? components[0]->myHelper : 0 )
2590 {}
2591 //================================================================================
2592 /*!
2593  * \brief Copy constructor
2594   * \param other - other side
2595  */
2596 //================================================================================
2597
2598 StdMeshers_PrismAsBlock::TSideFace::TSideFace( const TSideFace& other )
2599 {
2600   myID               = other.myID;
2601   mySurface          = other.mySurface;
2602   myBaseEdge         = other.myBaseEdge;
2603   myParams           = other.myParams;
2604   myIsForward        = other.myIsForward;
2605   myHelper           = other.myHelper;
2606   myParamToColumnMap = other.myParamToColumnMap;
2607
2608   myComponents.resize( other.myComponents.size());
2609   for (int i = 0 ; i < myComponents.size(); ++i )
2610     myComponents[ i ] = new TSideFace( *other.myComponents[ i ]);
2611 }
2612
2613 //================================================================================
2614 /*!
2615  * \brief Deletes myComponents
2616  */
2617 //================================================================================
2618
2619 StdMeshers_PrismAsBlock::TSideFace::~TSideFace()
2620 {
2621   for (int i = 0 ; i < myComponents.size(); ++i )
2622     if ( myComponents[ i ] )
2623       delete myComponents[ i ];
2624 }
2625
2626 //================================================================================
2627 /*!
2628  * \brief Return geometry of the vertical curve
2629   * \param isMax - true means curve located closer to (1,1,1) block point
2630   * \retval Adaptor3d_Curve* - curve adaptor
2631  */
2632 //================================================================================
2633
2634 Adaptor3d_Curve* StdMeshers_PrismAsBlock::TSideFace::VertiCurve(const bool isMax) const
2635 {
2636   if ( !myComponents.empty() ) {
2637     if ( isMax )
2638       return myComponents.back()->VertiCurve(isMax);
2639     else
2640       return myComponents.front()->VertiCurve(isMax);
2641   }
2642   double f = myParams[0].first, l = myParams[0].second;
2643   if ( !myIsForward ) std::swap( f, l );
2644   return new TVerticalEdgeAdaptor( myParamToColumnMap, isMax ? l : f );
2645 }
2646
2647 //================================================================================
2648 /*!
2649  * \brief Return geometry of the top or bottom curve
2650   * \param isTop - 
2651   * \retval Adaptor3d_Curve* - 
2652  */
2653 //================================================================================
2654
2655 Adaptor3d_Curve* StdMeshers_PrismAsBlock::TSideFace::HorizCurve(const bool isTop) const
2656 {
2657   return new THorizontalEdgeAdaptor( this, isTop );
2658 }
2659
2660 //================================================================================
2661 /*!
2662  * \brief Return pcurves
2663   * \param pcurv - array of 4 pcurves
2664   * \retval bool - is a success
2665  */
2666 //================================================================================
2667
2668 bool StdMeshers_PrismAsBlock::TSideFace::GetPCurves(Adaptor2d_Curve2d* pcurv[4]) const
2669 {
2670   int iEdge[ 4 ] = { BOTTOM_EDGE, TOP_EDGE, V0_EDGE, V1_EDGE };
2671
2672   for ( int i = 0 ; i < 4 ; ++i ) {
2673     Handle(Geom2d_Line) line;
2674     switch ( iEdge[ i ] ) {
2675     case TOP_EDGE:
2676       line = new Geom2d_Line( gp_Pnt2d( 0, 1 ), gp::DX2d() ); break;
2677     case BOTTOM_EDGE:
2678       line = new Geom2d_Line( gp::Origin2d(), gp::DX2d() ); break;
2679     case V0_EDGE:
2680       line = new Geom2d_Line( gp::Origin2d(), gp::DY2d() ); break;
2681     case V1_EDGE:
2682       line = new Geom2d_Line( gp_Pnt2d( 1, 0 ), gp::DY2d() ); break;
2683     }
2684     pcurv[ i ] = new Geom2dAdaptor_Curve( line, 0, 1 );
2685   }
2686   return true;
2687 }
2688
2689 //================================================================================
2690 /*!
2691  * \brief Returns geometry of pcurve on a horizontal face
2692   * \param isTop - is top or bottom face
2693   * \param horFace - a horizontal face
2694   * \retval Adaptor2d_Curve2d* - curve adaptor
2695  */
2696 //================================================================================
2697
2698 Adaptor2d_Curve2d*
2699 StdMeshers_PrismAsBlock::TSideFace::HorizPCurve(const bool         isTop,
2700                                                 const TopoDS_Face& horFace) const
2701 {
2702   return new TPCurveOnHorFaceAdaptor( this, isTop, horFace );
2703 }
2704
2705 //================================================================================
2706 /*!
2707  * \brief Return a component corresponding to parameter
2708   * \param U - parameter along a horizontal size
2709   * \param localU - parameter along a horizontal size of a component
2710   * \retval TSideFace* - found component
2711  */
2712 //================================================================================
2713
2714 StdMeshers_PrismAsBlock::TSideFace*
2715 StdMeshers_PrismAsBlock::TSideFace::GetComponent(const double U,double & localU) const
2716 {
2717   localU = U;
2718   if ( myComponents.empty() )
2719     return const_cast<TSideFace*>( this );
2720
2721   int i;
2722   for ( i = 0; i < myComponents.size(); ++i )
2723     if ( U < myParams[ i ].second )
2724       break;
2725   if ( i >= myComponents.size() )
2726     i = myComponents.size() - 1;
2727
2728   double f = myParams[ i ].first, l = myParams[ i ].second;
2729   localU = ( U - f ) / ( l - f );
2730   return myComponents[ i ];
2731 }
2732
2733 //================================================================================
2734 /*!
2735  * \brief Find node columns for a parameter
2736   * \param U - parameter along a horizontal edge
2737   * \param col1 - the 1st found column
2738   * \param col2 - the 2nd found column
2739   * \retval r - normalized position of U between the found columns
2740  */
2741 //================================================================================
2742
2743 double StdMeshers_PrismAsBlock::TSideFace::GetColumns(const double      U,
2744                                                       TParam2ColumnIt & col1,
2745                                                       TParam2ColumnIt & col2) const
2746 {
2747   double u = U, r = 0;
2748   if ( !myComponents.empty() ) {
2749     TSideFace * comp = GetComponent(U,u);
2750     return comp->GetColumns( u, col1, col2 );
2751   }
2752
2753   if ( !myIsForward )
2754     u = 1 - u;
2755   double f = myParams[0].first, l = myParams[0].second;
2756   u = f + u * ( l - f );
2757
2758   col1 = col2 = getColumn( myParamToColumnMap, u );
2759   if ( ++col2 == myParamToColumnMap->end() ) {
2760     --col2;
2761     r = 0.5;
2762   }
2763   else {
2764     double uf = col1->first;
2765     double ul = col2->first;
2766     r = ( u - uf ) / ( ul - uf );
2767   }
2768   return r;
2769 }
2770
2771 //================================================================================
2772 /*!
2773  * \brief Return coordinates by normalized params
2774   * \param U - horizontal param
2775   * \param V - vertical param
2776   * \retval gp_Pnt - result point
2777  */
2778 //================================================================================
2779
2780 gp_Pnt StdMeshers_PrismAsBlock::TSideFace::Value(const Standard_Real U,
2781                                                  const Standard_Real V) const
2782 {
2783   if ( !myComponents.empty() ) {
2784     double u;
2785     TSideFace * comp = GetComponent(U,u);
2786     return comp->Value( u, V );
2787   }
2788
2789   TParam2ColumnIt u_col1, u_col2;
2790   double vR, hR = GetColumns( U, u_col1, u_col2 );
2791
2792   const SMDS_MeshNode* nn[4];
2793
2794   // BEGIN issue 0020680: Bad cell created by Radial prism in center of torus
2795   // Workaround for a wrongly located point returned by mySurface.Value() for
2796   // UV located near boundary of BSpline surface.
2797   // To bypass the problem, we take point from 3D curve of EDGE.
2798   // It solves pb of the bloc_fiss_new.py
2799   const double tol = 1e-3;
2800   if ( V < tol || V+tol >= 1. )
2801   {
2802     nn[0] = V < tol ? u_col1->second.front() : u_col1->second.back();
2803     nn[2] = V < tol ? u_col2->second.front() : u_col2->second.back();
2804     TopoDS_Edge edge;
2805     if ( V < tol )
2806     {
2807       edge = myBaseEdge;
2808     }
2809     else
2810     {
2811       TopoDS_Shape s = myHelper->GetSubShapeByNode( nn[0], myHelper->GetMeshDS() );
2812       if ( s.ShapeType() != TopAbs_EDGE )
2813         s = myHelper->GetSubShapeByNode( nn[2], myHelper->GetMeshDS() );
2814       if ( s.ShapeType() == TopAbs_EDGE )
2815         edge = TopoDS::Edge( s );
2816     }
2817     if ( !edge.IsNull() )
2818     {
2819       double u1 = myHelper->GetNodeU( edge, nn[0] );
2820       double u3 = myHelper->GetNodeU( edge, nn[2] );
2821       double u = u1 * ( 1 - hR ) + u3 * hR;
2822       TopLoc_Location loc; double f,l;
2823       Handle(Geom_Curve) curve = BRep_Tool::Curve( edge,loc,f,l );
2824       return curve->Value( u ).Transformed( loc );
2825     }
2826   }
2827   // END issue 0020680: Bad cell created by Radial prism in center of torus
2828
2829   vR = getRAndNodes( & u_col1->second, V, nn[0], nn[1] );
2830   vR = getRAndNodes( & u_col2->second, V, nn[2], nn[3] );
2831
2832   if ( !myShapeID2Surf.empty() ) // side is vertically composite
2833   {
2834     // find a FACE on which the 4 nodes lie
2835     TSideFace* me = (TSideFace*) this;
2836     int notFaceID1 = 0, notFaceID2 = 0;
2837     for ( int i = 0; i < 4; ++i )
2838       if ( nn[i]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE ) // node on FACE
2839       {
2840         me->mySurface = me->myShapeID2Surf[ nn[i]->getshapeId() ];
2841         notFaceID2 = 0;
2842         break;
2843       }
2844       else if ( notFaceID1 == 0 ) // node on EDGE or VERTEX
2845       {
2846         me->mySurface  = me->myShapeID2Surf[ nn[i]->getshapeId() ];
2847         notFaceID1 = nn[i]->getshapeId();
2848       }
2849       else if ( notFaceID1 != nn[i]->getshapeId() ) // node on other EDGE or VERTEX
2850       {
2851         if ( mySurface != me->myShapeID2Surf[ nn[i]->getshapeId() ])
2852           notFaceID2 = nn[i]->getshapeId();
2853       }
2854     if ( notFaceID2 ) // no nodes of FACE and nodes are on different FACEs
2855     {
2856       SMESHDS_Mesh* meshDS = myHelper->GetMeshDS();
2857       TopoDS_Shape face = myHelper->GetCommonAncestor( meshDS->IndexToShape( notFaceID1 ),
2858                                                        meshDS->IndexToShape( notFaceID2 ),
2859                                                        *myHelper->GetMesh(),
2860                                                        TopAbs_FACE );
2861       if ( face.IsNull() ) 
2862         throw SALOME_Exception("StdMeshers_PrismAsBlock::TSideFace::Value() face.IsNull()");
2863       int faceID = meshDS->ShapeToIndex( face );
2864       me->mySurface = me->myShapeID2Surf[ faceID ];
2865       if ( !mySurface )
2866         throw SALOME_Exception("StdMeshers_PrismAsBlock::TSideFace::Value() !mySurface");
2867     }
2868   }
2869   
2870   gp_XY uv1 = myHelper->GetNodeUV( mySurface->Face(), nn[0], nn[2]);
2871   gp_XY uv2 = myHelper->GetNodeUV( mySurface->Face(), nn[1], nn[3]);
2872   gp_XY uv12 = uv1 * ( 1 - vR ) + uv2 * vR;
2873
2874   gp_XY uv3 = myHelper->GetNodeUV( mySurface->Face(), nn[2], nn[0]);
2875   gp_XY uv4 = myHelper->GetNodeUV( mySurface->Face(), nn[3], nn[1]);
2876   gp_XY uv34 = uv3 * ( 1 - vR ) + uv4 * vR;
2877
2878   gp_XY uv = uv12 * ( 1 - hR ) + uv34 * hR;
2879
2880   gp_Pnt p = mySurface->Value( uv.X(), uv.Y() );
2881   return p;
2882 }
2883
2884
2885 //================================================================================
2886 /*!
2887  * \brief Return boundary edge
2888   * \param edge - edge index
2889   * \retval TopoDS_Edge - found edge
2890  */
2891 //================================================================================
2892
2893 TopoDS_Edge StdMeshers_PrismAsBlock::TSideFace::GetEdge(const int iEdge) const
2894 {
2895   if ( !myComponents.empty() ) {
2896     switch ( iEdge ) {
2897     case V0_EDGE : return myComponents.front()->GetEdge( iEdge );
2898     case V1_EDGE : return myComponents.back() ->GetEdge( iEdge );
2899     default: return TopoDS_Edge();
2900     }
2901   }
2902   TopoDS_Shape edge;
2903   const SMDS_MeshNode* node = 0;
2904   SMESHDS_Mesh * meshDS = myHelper->GetMesh()->GetMeshDS();
2905   TNodeColumn* column;
2906
2907   switch ( iEdge ) {
2908   case TOP_EDGE:
2909   case BOTTOM_EDGE:
2910     column = & (( ++myParamToColumnMap->begin())->second );
2911     node = ( iEdge == TOP_EDGE ) ? column->back() : column->front();
2912     edge = myHelper->GetSubShapeByNode ( node, meshDS );
2913     if ( edge.ShapeType() == TopAbs_VERTEX ) {
2914       column = & ( myParamToColumnMap->begin()->second );
2915       node = ( iEdge == TOP_EDGE ) ? column->back() : column->front();
2916     }
2917     break;
2918   case V0_EDGE:
2919   case V1_EDGE: {
2920     bool back = ( iEdge == V1_EDGE );
2921     if ( !myIsForward ) back = !back;
2922     if ( back )
2923       column = & ( myParamToColumnMap->rbegin()->second );
2924     else
2925       column = & ( myParamToColumnMap->begin()->second );
2926     if ( column->size() > 0 )
2927       edge = myHelper->GetSubShapeByNode( (*column)[ 1 ], meshDS );
2928     if ( edge.IsNull() || edge.ShapeType() == TopAbs_VERTEX )
2929       node = column->front();
2930     break;
2931   }
2932   default:;
2933   }
2934   if ( !edge.IsNull() && edge.ShapeType() == TopAbs_EDGE )
2935     return TopoDS::Edge( edge );
2936
2937   // find edge by 2 vertices
2938   TopoDS_Shape V1 = edge;
2939   TopoDS_Shape V2 = myHelper->GetSubShapeByNode( node, meshDS );
2940   if ( !V2.IsNull() && V2.ShapeType() == TopAbs_VERTEX && !V2.IsSame( V1 ))
2941   {
2942     TopoDS_Shape ancestor = myHelper->GetCommonAncestor( V1, V2, *myHelper->GetMesh(), TopAbs_EDGE);
2943     if ( !ancestor.IsNull() )
2944       return TopoDS::Edge( ancestor );
2945   }
2946   return TopoDS_Edge();
2947 }
2948
2949 //================================================================================
2950 /*!
2951  * \brief Fill block sub-shapes
2952   * \param shapeMap - map to fill in
2953   * \retval int - nb inserted sub-shapes
2954  */
2955 //================================================================================
2956
2957 int StdMeshers_PrismAsBlock::TSideFace::InsertSubShapes(TBlockShapes& shapeMap) const
2958 {
2959   int nbInserted = 0;
2960
2961   // Insert edges
2962   vector< int > edgeIdVec;
2963   SMESH_Block::GetFaceEdgesIDs( myID, edgeIdVec );
2964
2965   for ( int i = BOTTOM_EDGE; i <=V1_EDGE ; ++i ) {
2966     TopoDS_Edge e = GetEdge( i );
2967     if ( !e.IsNull() ) {
2968       nbInserted += SMESH_Block::Insert( e, edgeIdVec[ i ], shapeMap);
2969     }
2970   }
2971
2972   // Insert corner vertices
2973
2974   TParam2ColumnIt col1, col2 ;
2975   vector< int > vertIdVec;
2976
2977   // from V0 column
2978   SMESH_Block::GetEdgeVertexIDs( edgeIdVec[ V0_EDGE ], vertIdVec);
2979   GetColumns(0, col1, col2 );
2980   const SMDS_MeshNode* node0 = col1->second.front();
2981   const SMDS_MeshNode* node1 = col1->second.back();
2982   TopoDS_Shape v0 = myHelper->GetSubShapeByNode( node0, myHelper->GetMeshDS());
2983   TopoDS_Shape v1 = myHelper->GetSubShapeByNode( node1, myHelper->GetMeshDS());
2984   if ( v0.ShapeType() == TopAbs_VERTEX ) {
2985     nbInserted += SMESH_Block::Insert( v0, vertIdVec[ 0 ], shapeMap);
2986   }
2987   if ( v1.ShapeType() == TopAbs_VERTEX ) {
2988     nbInserted += SMESH_Block::Insert( v1, vertIdVec[ 1 ], shapeMap);
2989   }
2990   
2991   // from V1 column
2992   SMESH_Block::GetEdgeVertexIDs( edgeIdVec[ V1_EDGE ], vertIdVec);
2993   GetColumns(1, col1, col2 );
2994   node0 = col2->second.front();
2995   node1 = col2->second.back();
2996   v0 = myHelper->GetSubShapeByNode( node0, myHelper->GetMeshDS());
2997   v1 = myHelper->GetSubShapeByNode( node1, myHelper->GetMeshDS());
2998   if ( v0.ShapeType() == TopAbs_VERTEX ) {
2999     nbInserted += SMESH_Block::Insert( v0, vertIdVec[ 0 ], shapeMap);
3000   }
3001   if ( v1.ShapeType() == TopAbs_VERTEX ) {
3002     nbInserted += SMESH_Block::Insert( v1, vertIdVec[ 1 ], shapeMap);
3003   }
3004
3005 //   TopoDS_Vertex V0, V1, Vcom;
3006 //   TopExp::Vertices( myBaseEdge, V0, V1, true );
3007 //   if ( !myIsForward ) std::swap( V0, V1 );
3008
3009 //   // bottom vertex IDs
3010 //   SMESH_Block::GetEdgeVertexIDs( edgeIdVec[ _u0 ], vertIdVec);
3011 //   SMESH_Block::Insert( V0, vertIdVec[ 0 ], shapeMap);
3012 //   SMESH_Block::Insert( V1, vertIdVec[ 1 ], shapeMap);
3013
3014 //   TopoDS_Edge sideEdge = GetEdge( V0_EDGE );
3015 //   if ( sideEdge.IsNull() || !TopExp::CommonVertex( botEdge, sideEdge, Vcom ))
3016 //     return false;
3017
3018 //   // insert one side edge
3019 //   int edgeID;
3020 //   if ( Vcom.IsSame( V0 )) edgeID = edgeIdVec[ _v0 ];
3021 //   else                    edgeID = edgeIdVec[ _v1 ];
3022 //   SMESH_Block::Insert( sideEdge, edgeID, shapeMap);
3023
3024 //   // top vertex of the side edge
3025 //   SMESH_Block::GetEdgeVertexIDs( edgeID, vertIdVec);
3026 //   TopoDS_Vertex Vtop = TopExp::FirstVertex( sideEdge );
3027 //   if ( Vcom.IsSame( Vtop ))
3028 //     Vtop = TopExp::LastVertex( sideEdge );
3029 //   SMESH_Block::Insert( Vtop, vertIdVec[ 1 ], shapeMap);
3030
3031 //   // other side edge
3032 //   sideEdge = GetEdge( V1_EDGE );
3033 //   if ( sideEdge.IsNull() )
3034 //     return false;
3035 //   if ( edgeID = edgeIdVec[ _v1 ]) edgeID = edgeIdVec[ _v0 ];
3036 //   else                            edgeID = edgeIdVec[ _v1 ];
3037 //   SMESH_Block::Insert( sideEdge, edgeID, shapeMap);
3038   
3039 //   // top edge
3040 //   TopoDS_Edge topEdge = GetEdge( TOP_EDGE );
3041 //   SMESH_Block::Insert( topEdge, edgeIdVec[ _u1 ], shapeMap);
3042
3043 //   // top vertex of the other side edge
3044 //   if ( !TopExp::CommonVertex( topEdge, sideEdge, Vcom ))
3045 //     return false;
3046 //   SMESH_Block::GetEdgeVertexIDs( edgeID, vertIdVec );
3047 //   SMESH_Block::Insert( Vcom, vertIdVec[ 1 ], shapeMap);
3048
3049   return nbInserted;
3050 }
3051
3052 //================================================================================
3053 /*!
3054  * \brief Dump ids of nodes of sides
3055  */
3056 //================================================================================
3057
3058 void StdMeshers_PrismAsBlock::TSideFace::dumpNodes(int nbNodes) const
3059 {
3060 #ifdef _DEBUG_
3061   cout << endl << "NODES OF FACE "; SMESH_Block::DumpShapeID( myID, cout ) << endl;
3062   THorizontalEdgeAdaptor* hSize0 = (THorizontalEdgeAdaptor*) HorizCurve(0);
3063   cout << "Horiz side 0: "; hSize0->dumpNodes(nbNodes); cout << endl;
3064   THorizontalEdgeAdaptor* hSize1 = (THorizontalEdgeAdaptor*) HorizCurve(1);
3065   cout << "Horiz side 1: "; hSize1->dumpNodes(nbNodes); cout << endl;
3066   TVerticalEdgeAdaptor* vSide0 = (TVerticalEdgeAdaptor*) VertiCurve(0);
3067   cout << "Verti side 0: "; vSide0->dumpNodes(nbNodes); cout << endl;
3068   TVerticalEdgeAdaptor* vSide1 = (TVerticalEdgeAdaptor*) VertiCurve(1);
3069   cout << "Verti side 1: "; vSide1->dumpNodes(nbNodes); cout << endl;
3070   delete hSize0; delete hSize1; delete vSide0; delete vSide1;
3071 #endif
3072 }
3073
3074 //================================================================================
3075 /*!
3076  * \brief Creates TVerticalEdgeAdaptor 
3077   * \param columnsMap - node column map
3078   * \param parameter - normalized parameter
3079  */
3080 //================================================================================
3081
3082 StdMeshers_PrismAsBlock::TVerticalEdgeAdaptor::
3083 TVerticalEdgeAdaptor( const TParam2ColumnMap* columnsMap, const double parameter)
3084 {
3085   myNodeColumn = & getColumn( columnsMap, parameter )->second;
3086 }
3087
3088 //================================================================================
3089 /*!
3090  * \brief Return coordinates for the given normalized parameter
3091   * \param U - normalized parameter
3092   * \retval gp_Pnt - coordinates
3093  */
3094 //================================================================================
3095
3096 gp_Pnt StdMeshers_PrismAsBlock::TVerticalEdgeAdaptor::Value(const Standard_Real U) const
3097 {
3098   const SMDS_MeshNode* n1;
3099   const SMDS_MeshNode* n2;
3100   double r = getRAndNodes( myNodeColumn, U, n1, n2 );
3101   return gpXYZ(n1) * ( 1 - r ) + gpXYZ(n2) * r;
3102 }
3103
3104 //================================================================================
3105 /*!
3106  * \brief Dump ids of nodes
3107  */
3108 //================================================================================
3109
3110 void StdMeshers_PrismAsBlock::TVerticalEdgeAdaptor::dumpNodes(int nbNodes) const
3111 {
3112 #ifdef _DEBUG_
3113   for ( int i = 0; i < nbNodes && i < myNodeColumn->size(); ++i )
3114     cout << (*myNodeColumn)[i]->GetID() << " ";
3115   if ( nbNodes < myNodeColumn->size() )
3116     cout << myNodeColumn->back()->GetID();
3117 #endif
3118 }
3119
3120 //================================================================================
3121 /*!
3122  * \brief Return coordinates for the given normalized parameter
3123   * \param U - normalized parameter
3124   * \retval gp_Pnt - coordinates
3125  */
3126 //================================================================================
3127
3128 gp_Pnt StdMeshers_PrismAsBlock::THorizontalEdgeAdaptor::Value(const Standard_Real U) const
3129 {
3130   return mySide->TSideFace::Value( U, myV );
3131 }
3132
3133 //================================================================================
3134 /*!
3135  * \brief Dump ids of <nbNodes> first nodes and the last one
3136  */
3137 //================================================================================
3138
3139 void StdMeshers_PrismAsBlock::THorizontalEdgeAdaptor::dumpNodes(int nbNodes) const
3140 {
3141 #ifdef _DEBUG_
3142   // Not bedugged code. Last node is sometimes incorrect
3143   const TSideFace* side = mySide;
3144   double u = 0;
3145   if ( mySide->IsComplex() )
3146     side = mySide->GetComponent(0,u);
3147
3148   TParam2ColumnIt col, col2;
3149   TParam2ColumnMap* u2cols = side->GetColumns();
3150   side->GetColumns( u , col, col2 );
3151   
3152   int j, i = myV ? mySide->ColumnHeight()-1 : 0;
3153
3154   const SMDS_MeshNode* n = 0;
3155   const SMDS_MeshNode* lastN
3156     = side->IsForward() ? u2cols->rbegin()->second[ i ] : u2cols->begin()->second[ i ];
3157   for ( j = 0; j < nbNodes && n != lastN; ++j )
3158   {
3159     n = col->second[ i ];
3160     cout << n->GetID() << " ";
3161     if ( side->IsForward() )
3162       ++col;
3163     else
3164       --col;
3165   }
3166
3167   // last node
3168   u = 1;
3169   if ( mySide->IsComplex() )
3170     side = mySide->GetComponent(1,u);
3171
3172   side->GetColumns( u , col, col2 );
3173   if ( n != col->second[ i ] )
3174     cout << col->second[ i ]->GetID();
3175 #endif
3176 }
3177 //================================================================================
3178 /*!
3179  * \brief Return UV on pcurve for the given normalized parameter
3180   * \param U - normalized parameter
3181   * \retval gp_Pnt - coordinates
3182  */
3183 //================================================================================
3184
3185 gp_Pnt2d StdMeshers_PrismAsBlock::TPCurveOnHorFaceAdaptor::Value(const Standard_Real U) const
3186 {
3187   TParam2ColumnIt u_col1, u_col2;
3188   double r = mySide->GetColumns( U, u_col1, u_col2 );
3189   gp_XY uv1 = mySide->GetNodeUV( myFace, u_col1->second[ myZ ]);
3190   gp_XY uv2 = mySide->GetNodeUV( myFace, u_col2->second[ myZ ]);
3191   return uv1 * ( 1 - r ) + uv2 * r;
3192 }