Salome HOME
3b8a61bbe665a8084d1c489ebc4b8b2933230394
[modules/smesh.git] / src / SMESH / SMESH_Quadrangle_2D.cxx
1 //  SMESH SMESH : implementaion of SMESH idl descriptions
2 //
3 //  Copyright (C) 2003  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.opencascade.org/SALOME/ or email : webmaster.salome@opencascade.org 
21 //
22 //
23 //
24 //  File   : SMESH_Quadrangle_2D.cxx
25 //  Author : Paul RASCLE, EDF
26 //  Module : SMESH
27 //  $Header$
28
29 using namespace std;
30 #include "SMESH_Quadrangle_2D.hxx"
31 #include "SMESH_Gen.hxx"
32 #include "SMESH_Mesh.hxx"
33
34 #include "SMDS_MeshElement.hxx"
35 #include "SMDS_MeshNode.hxx"
36 #include "SMDS_EdgePosition.hxx"
37 #include "SMDS_FacePosition.hxx"
38
39 #include <BRep_Tool.hxx>
40 #include <BRepTools.hxx>
41 #include <BRepTools_WireExplorer.hxx>
42 #include <Geom_Surface.hxx>
43 #include <Geom_Curve.hxx>
44 #include <Geom2d_Curve.hxx>
45 #include <Handle_Geom2d_Curve.hxx>
46 #include <Handle_Geom_Curve.hxx>
47 #include <gp_Pnt2d.hxx>
48 #include <TColStd_ListIteratorOfListOfInteger.hxx>
49
50 #include "utilities.h"
51
52 //=============================================================================
53 /*!
54  *  
55  */
56 //=============================================================================
57
58 SMESH_Quadrangle_2D::SMESH_Quadrangle_2D(int hypId,
59         int studyId, SMESH_Gen * gen):SMESH_2D_Algo(hypId, studyId, gen)
60 {
61         MESSAGE("SMESH_Quadrangle_2D::SMESH_Quadrangle_2D");
62         _name = "Quadrangle_2D";
63         //  _shapeType = TopAbs_FACE;
64         _shapeType = (1 << TopAbs_FACE);
65 }
66
67 //=============================================================================
68 /*!
69  *  
70  */
71 //=============================================================================
72
73 SMESH_Quadrangle_2D::~SMESH_Quadrangle_2D()
74 {
75         MESSAGE("SMESH_Quadrangle_2D::~SMESH_Quadrangle_2D");
76 }
77
78 //=============================================================================
79 /*!
80  *  
81  */
82 //=============================================================================
83
84 bool SMESH_Quadrangle_2D::CheckHypothesis(SMESH_Mesh & aMesh,
85         const TopoDS_Shape & aShape)
86 {
87         //MESSAGE("SMESH_Quadrangle_2D::CheckHypothesis");
88
89         bool isOk = true;
90
91         // nothing to check
92
93         return isOk;
94 }
95
96 //=============================================================================
97 /*!
98  *  
99  */
100 //=============================================================================
101
102 bool SMESH_Quadrangle_2D::Compute(SMESH_Mesh & aMesh,
103         const TopoDS_Shape & aShape)throw(SALOME_Exception)
104 {
105         //MESSAGE("SMESH_Quadrangle_2D::Compute");
106         SMESHDS_Mesh * meshDS = aMesh.GetMeshDS();
107         SMESH_subMesh *theSubMesh = aMesh.GetSubMesh(aShape);
108
109         FaceQuadStruct *quad = CheckAnd2Dcompute(aMesh, aShape);
110         if (!quad)
111                 return false;
112
113         // --- compute 3D values on points, store points & quadrangles
114
115         int nbdown = quad->nbPts[0];
116         int nbright = quad->nbPts[1];
117         int nbVertices = nbdown * nbright;
118         int nbQuad = (nbdown - 1) * (nbright - 1);
119         //SCRUTE(nbVertices);
120         //SCRUTE(nbQuad);
121
122         //   const TopoDS_Face& FF = TopoDS::Face(aShape);
123         //   bool faceIsForward = (FF.Orientation() == TopAbs_FORWARD);
124         //   TopoDS_Face F = TopoDS::Face(FF.Oriented(TopAbs_FORWARD));
125         const TopoDS_Face & F = TopoDS::Face(aShape);
126         bool faceIsForward = (F.Orientation() == TopAbs_FORWARD);
127         Handle(Geom_Surface) S = BRep_Tool::Surface(F);
128
129         for (int i = 1; i < nbdown - 1; i++)
130                 for (int j = 1; j < nbright - 1; j++)   // internal points
131                 {
132                         int ij = j * nbdown + i;
133                         double u = quad->uv_grid[ij].u;
134                         double v = quad->uv_grid[ij].v;
135                         gp_Pnt P = S->Value(u, v);
136                         SMDS_MeshNode * node = meshDS->AddNode(P.X(), P.Y(), P.Z());
137                         int nodeId = node->GetID();
138                         meshDS->SetNodeOnFace(node, F);
139                         quad->uv_grid[ij].nodeId = nodeId;
140 //  Handle (SMDS_FacePosition) fpos
141 //    = new SMDS_FacePosition(theSubMesh->GetId(),i,j); // easier than u,v
142 //  node->SetPosition(fpos);
143                         SMDS_FacePosition* fpos
144                                 = dynamic_cast<SMDS_FacePosition*>(node->GetPosition());
145                         fpos->SetUParameter(i);
146                         fpos->SetVParameter(j);
147                 }
148
149         //   bool isQuadForward = ( faceIsForward == quad->isEdgeForward[0]);
150         for (int i = 0; i < nbdown - 1; i++)
151                 for (int j = 0; j < nbright - 1; j++)   // faces
152                 {
153                         int a = quad->uv_grid[j * nbdown + i].nodeId;
154                         int b = quad->uv_grid[j * nbdown + i + 1].nodeId;
155                         int c = quad->uv_grid[(j + 1) * nbdown + i + 1].nodeId;
156                         int d = quad->uv_grid[(j + 1) * nbdown + i].nodeId;
157                         int faceId;
158                         //  if (isQuadForward) faceId = meshDS->AddFace(a,b,c,d);
159                         //  else faceId = meshDS->AddFace(a,d,c,b);
160                         SMDS_MeshFace * face = meshDS->AddFace(a, b, c, d);
161                         meshDS->SetMeshElementOnShape(face, F);
162                 }
163
164         QuadDelete(quad);
165         bool isOk = true;
166         return isOk;
167 }
168
169 //=============================================================================
170 /*!
171  *  
172  */
173 //=============================================================================
174
175 FaceQuadStruct *SMESH_Quadrangle_2D::CheckAnd2Dcompute(SMESH_Mesh & aMesh,
176         const TopoDS_Shape & aShape)throw(SALOME_Exception)
177 {
178         //MESSAGE("SMESH_Quadrangle_2D::ComputeWithoutStore");
179
180         SMESH_subMesh *theSubMesh = aMesh.GetSubMesh(aShape);
181
182         //   const TopoDS_Face& FF = TopoDS::Face(aShape);
183         //   bool faceIsForward = (FF.Orientation() == TopAbs_FORWARD);
184         //   TopoDS_Face F = TopoDS::Face(FF.Oriented(TopAbs_FORWARD));
185         const TopoDS_Face & F = TopoDS::Face(aShape);
186         bool faceIsForward = (F.Orientation() == TopAbs_FORWARD);
187
188         // verify 1 wire only, with 4 edges, same number of points on opposite edges
189
190         if (NumberOfWires(F) != 1)
191         {
192                 MESSAGE("only 1 wire by face (quadrangles)");
193                 return 0;
194                 //throw SALOME_Exception(LOCALIZED("only 1 wire by face (quadrangles)"));
195         }
196         //   const TopoDS_Wire WW = BRepTools::OuterWire(F);
197         //   TopoDS_Wire W = TopoDS::Wire(WW.Oriented(TopAbs_FORWARD));
198         const TopoDS_Wire & W = BRepTools::OuterWire(F);
199         BRepTools_WireExplorer wexp(W, F);
200
201         FaceQuadStruct *quad = new FaceQuadStruct;
202         for (int i = 0; i < 4; i++)
203                 quad->uv_edges[i] = 0;
204         quad->uv_grid = 0;
205
206         int nbEdges = 0;
207         for (wexp.Init(W, F); wexp.More(); wexp.Next())
208         {
209                 //       const TopoDS_Edge& EE = wexp.Current();
210                 //       TopoDS_Edge E = TopoDS::Edge(EE.Oriented(TopAbs_FORWARD));
211                 const TopoDS_Edge & E = wexp.Current();
212                 int nb = aMesh.GetSubMesh(E)->GetSubMeshDS()->NbNodes();
213                 if (nbEdges < 4)
214                 {
215                         quad->edge[nbEdges] = E;
216                         quad->nbPts[nbEdges] = nb + 2;  // internal points + 2 extrema
217                 }
218                 nbEdges++;
219         }
220
221         if (nbEdges != 4)
222         {
223                 MESSAGE("face must have 4 edges /quadrangles");
224                 QuadDelete(quad);
225                 return 0;
226                 //throw SALOME_Exception(LOCALIZED("face must have 4 edges /quadrangles"));
227         }
228
229         if (quad->nbPts[0] != quad->nbPts[2])
230         {
231                 MESSAGE("different point number-opposed edge");
232                 QuadDelete(quad);
233                 return 0;
234                 //throw SALOME_Exception(LOCALIZED("different point number-opposed edge"));
235         }
236
237         if (quad->nbPts[1] != quad->nbPts[3])
238         {
239                 MESSAGE("different point number-opposed edge");
240                 QuadDelete(quad);
241                 return 0;
242                 //throw SALOME_Exception(LOCALIZED("different point number-opposed edge"));
243         }
244
245         // set normalized grid on unit square in parametric domain
246
247         SetNormalizedGrid(aMesh, F, quad);
248
249         return quad;
250 }
251
252 //=============================================================================
253 /*!
254  *  
255  */
256 //=============================================================================
257
258 void SMESH_Quadrangle_2D::QuadDelete(FaceQuadStruct * quad)
259 {
260         //MESSAGE("SMESH_Quadrangle_2D::QuadDelete");
261         if (quad)
262         {
263                 for (int i = 0; i < 4; i++)
264                 {
265                         if (quad->uv_edges[i])
266                                 delete[]quad->uv_edges[i];
267                         quad->edge[i].Nullify();
268                 }
269                 if (quad->uv_grid)
270                         delete[]quad->uv_grid;
271                 delete quad;
272         }
273 }
274
275 //=============================================================================
276 /*!
277  *  
278  */
279 //=============================================================================
280
281 void SMESH_Quadrangle_2D::SetNormalizedGrid(SMESH_Mesh & aMesh,
282         const TopoDS_Shape & aShape, FaceQuadStruct * quad) throw(SALOME_Exception)
283 {
284         // Algorithme décrit dans "Génération automatique de maillages"
285         // P.L. GEORGE, MASSON, § 6.4.1 p. 84-85
286         // traitement dans le domaine paramétrique 2d u,v
287         // transport - projection sur le carré unité
288
289         const TopoDS_Face & F = TopoDS::Face(aShape);
290
291         // 1 --- find orientation of the 4 edges, by test on extrema
292
293         //      max             min                    0     x1     1
294         //     |<----north-2-------^                a3 -------------> a2
295         //     |                   |                   ^1          1^
296         //    west-3            east-1 =right          |            |
297         //     |                   |         ==>       |            |
298         //  y0 |                   | y1                |            |
299         //     |                   |                   |0          0|
300         //     v----south-0-------->                a0 -------------> a1
301         //      min             max                    0     x0     1
302         //             =down
303         //
304
305         Handle(Geom2d_Curve) c2d[4];
306         gp_Pnt2d pf[4];
307         gp_Pnt2d pl[4];
308         for (int i = 0; i < 4; i++)
309         {
310                 c2d[i] = BRep_Tool::CurveOnSurface(quad->edge[i],
311                         F, quad->first[i], quad->last[i]);
312                 pf[i] = c2d[i]->Value(quad->first[i]);
313                 pl[i] = c2d[i]->Value(quad->last[i]);
314                 quad->isEdgeForward[i] = false;
315         }
316
317         double eps2d = 1.e-3;           // *** utiliser plutot TopExp::CommonVertex, puis
318         // distances si piece fausse
319         int i = 0;
320         if ((pf[1].Distance(pl[0]) < eps2d) || (pl[1].Distance(pl[0]) < eps2d))
321         {
322                 quad->isEdgeForward[0] = true;
323         }
324         else
325         {
326                 double tmp = quad->first[0];
327                 quad->first[0] = quad->last[0];
328                 quad->last[0] = tmp;
329                 pf[0] = c2d[0]->Value(quad->first[0]);
330                 pl[0] = c2d[0]->Value(quad->last[0]);
331         }
332         for (int i = 1; i < 4; i++)
333         {
334                 quad->isEdgeForward[i] = (pf[i].Distance(pl[i - 1]) < eps2d);
335                 if (!quad->isEdgeForward[i])
336                 {
337                         double tmp = quad->first[i];
338                         quad->first[i] = quad->last[i];
339                         quad->last[i] = tmp;
340                         pf[i] = c2d[i]->Value(quad->first[i]);
341                         pl[i] = c2d[i]->Value(quad->last[i]);
342                         //SCRUTE(pf[i].Distance(pl[i-1]));
343                         ASSERT(pf[i].Distance(pl[i - 1]) < eps2d);
344                 }
345         }
346         //SCRUTE(pf[0].Distance(pl[3]));
347         ASSERT(pf[0].Distance(pl[3]) < eps2d);
348
349 //   for (int i=0; i<4; i++)
350 //     {
351 //       SCRUTE(quad->isEdgeForward[i]);
352 //       MESSAGE(" -first "<<i<<" "<<pf[i].X()<<" "<<pf[i].Y());
353 //       MESSAGE(" -last  "<<i<<" "<<pl[i].X()<<" "<<pl[i].Y());
354 //     }
355
356         // 2 --- load 2d edge points (u,v) with orientation and value on unit square
357
358         for (int i = 0; i < 2; i++)
359         {
360                 quad->uv_edges[i] = LoadEdgePoints(aMesh, F,
361                         quad->edge[i], quad->first[i], quad->last[i]);
362
363                 //                         quad->isEdgeForward[i]);
364         }
365         for (int i = 2; i < 4; i++)
366         {
367                 quad->uv_edges[i] = LoadEdgePoints(aMesh, F,
368                         quad->edge[i], quad->last[i], quad->first[i]);
369
370                 //                         !quad->isEdgeForward[i]);
371         }
372
373         // 3 --- 2D normalized values on unit square [0..1][0..1]
374
375         int nbdown = quad->nbPts[0];
376         int nbright = quad->nbPts[1];
377         quad->uv_grid = new UVPtStruct[nbright * nbdown];
378
379         UVPtStruct *uv_grid = quad->uv_grid;
380         UVPtStruct *uv_e0 = quad->uv_edges[0];
381         UVPtStruct *uv_e1 = quad->uv_edges[1];
382         UVPtStruct *uv_e2 = quad->uv_edges[2];
383         UVPtStruct *uv_e3 = quad->uv_edges[3];
384         gp_Pnt2d a0 = pf[0];
385         gp_Pnt2d a1 = pf[1];
386         gp_Pnt2d a2 = pf[2];
387         gp_Pnt2d a3 = pf[3];
388
389         // nodes Id on edges
390
391         int j = 0;
392         for (int i = 0; i < nbdown; i++)
393         {
394                 int ij = j * nbdown + i;
395                 uv_grid[ij].nodeId = uv_e0[i].nodeId;
396         }
397         i = nbdown - 1;
398         for (int j = 0; j < nbright; j++)
399         {
400                 int ij = j * nbdown + i;
401                 uv_grid[ij].nodeId = uv_e1[j].nodeId;
402         }
403         j = nbright - 1;
404         for (int i = 0; i < nbdown; i++)
405         {
406                 int ij = j * nbdown + i;
407                 uv_grid[ij].nodeId = uv_e2[i].nodeId;
408         }
409         i = 0;
410         for (int j = 0; j < nbright; j++)
411         {
412                 int ij = j * nbdown + i;
413                 uv_grid[ij].nodeId = uv_e3[j].nodeId;
414         }
415
416         // normalized 2d values on grid
417
418         for (int i = 0; i < nbdown; i++)
419                 for (int j = 0; j < nbright; j++)
420                 {
421                         int ij = j * nbdown + i;
422                         // --- droite i cste : x = x0 + y(x1-x0)
423                         double x0 = uv_e0[i].normParam; // bas - sud
424                         double x1 = uv_e2[i].normParam; // haut - nord
425                         // --- droite j cste : y = y0 + x(y1-y0)
426                         double y0 = uv_e3[j].normParam; // gauche-ouest
427                         double y1 = uv_e1[j].normParam; // droite - est
428                         // --- intersection : x=x0+(y0+x(y1-y0))(x1-x0)
429                         double x = (x0 + y0 * (x1 - x0)) / (1 - (y1 - y0) * (x1 - x0));
430                         double y = y0 + x * (y1 - y0);
431                         uv_grid[ij].x = x;
432                         uv_grid[ij].y = y;
433                         //MESSAGE("-xy-01 "<<x0<<" "<<x1<<" "<<y0<<" "<<y1);
434                         //MESSAGE("-xy-norm "<<i<<" "<<j<<" "<<x<<" "<<y);
435                 }
436
437         // 4 --- projection on 2d domain (u,v)
438
439         for (int i = 0; i < nbdown; i++)
440                 for (int j = 0; j < nbright; j++)
441                 {
442                         int ij = j * nbdown + i;
443                         double x = uv_grid[ij].x;
444                         double y = uv_grid[ij].y;
445                         double param_0 = uv_e0[0].param + x * (uv_e0[nbdown - 1].param - uv_e0[0].param);       // sud
446                         double param_2 = uv_e2[0].param + x * (uv_e2[nbdown - 1].param - uv_e2[0].param);       // nord
447                         double param_1 = uv_e1[0].param + y * (uv_e1[nbright - 1].param - uv_e1[0].param);      // est
448                         double param_3 = uv_e3[0].param + y * (uv_e3[nbright - 1].param - uv_e3[0].param);      // ouest
449
450                         //MESSAGE("params "<<param_0<<" "<<param_1<<" "<<param_2<<" "<<param_3);
451                         gp_Pnt2d p0 = c2d[0]->Value(param_0);
452                         gp_Pnt2d p1 = c2d[1]->Value(param_1);
453                         gp_Pnt2d p2 = c2d[2]->Value(param_2);
454                         gp_Pnt2d p3 = c2d[3]->Value(param_3);
455
456                         double u =
457                                 (1 - y) * p0.X() + x * p1.X() + y * p2.X() + (1 - x) * p3.X();
458                         double v =
459                                 (1 - y) * p0.Y() + x * p1.Y() + y * p2.Y() + (1 - x) * p3.Y();
460
461                         u -= (1 - x) * (1 - y) * a0.X() + x * (1 - y) * a1.X() +
462                                 x * y * a2.X() + (1 - x) * y * a3.X();
463                         v -= (1 - x) * (1 - y) * a0.Y() + x * (1 - y) * a1.Y() +
464                                 x * y * a2.Y() + (1 - x) * y * a3.Y();
465
466                         uv_grid[ij].u = u;
467                         uv_grid[ij].v = v;
468
469                         //MESSAGE("-uv- "<<i<<" "<<j<<" "<<uv_grid[ij].u<<" "<<uv_grid[ij].v);
470                 }
471 }
472
473 //=============================================================================
474 /*!
475  *  
476  */
477 //=============================================================================
478
479 UVPtStruct *SMESH_Quadrangle_2D::LoadEdgePoints(SMESH_Mesh & aMesh,
480         const TopoDS_Face & F, const TopoDS_Edge & E, double first, double last)
481   //                        bool isForward)
482 {
483         //MESSAGE("SMESH_Quadrangle_2D::LoadEdgePoints");
484
485         SMDS_Mesh * meshDS = aMesh.GetMeshDS();
486
487         // --- IDNodes of first and last Vertex
488
489         TopoDS_Vertex VFirst, VLast;
490         TopExp::Vertices(E, VFirst, VLast);     // corresponds to f and l
491
492         ASSERT(!VFirst.IsNull());
493         SMESH_subMesh *firstSubMesh = aMesh.GetSubMesh(VFirst);
494         const vector<int>& lidf
495                 = firstSubMesh->GetSubMeshDS()->GetIDNodes();
496         int idFirst = lidf[0];
497         //SCRUTE(idFirst);
498
499         ASSERT(!VLast.IsNull());
500         SMESH_subMesh *lastSubMesh = aMesh.GetSubMesh(VLast);
501         const vector<int> & lidl
502                 = lastSubMesh->GetSubMeshDS()->GetIDNodes();
503         int idLast = lidl[0];
504         //SCRUTE(idLast);
505
506         // --- edge internal IDNodes (relies on good order storage, not checked)
507
508         int nbPoints = aMesh.GetSubMesh(E)->GetSubMeshDS()->NbNodes();
509         //SCRUTE(nbPoints);
510         UVPtStruct *uvslf = new UVPtStruct[nbPoints + 2];
511
512         double f, l;
513         Handle(Geom2d_Curve) C2d = BRep_Tool::CurveOnSurface(E, F, f, l);
514
515         const vector<int> & indElt
516                 = aMesh.GetSubMesh(E)->GetSubMeshDS()->GetIDNodes();
517
518         //SCRUTE(nbPoints);
519         //SCRUTE(indElt.Extent());
520         ASSERT(nbPoints == indElt.size());
521
522         map<double, int> params;
523         for (int ite=0; ite<indElt.size(); ite++)
524         {
525                 int nodeId = indElt[ite];
526                 const SMDS_MeshNode * node = meshDS->FindNode(nodeId);
527                 const SMDS_EdgePosition* epos
528                         = static_cast<const SMDS_EdgePosition*>(node->GetPosition());
529                 double param = epos->GetUParameter();
530                 params[param] = nodeId;
531         }
532
533         bool isForward = (((l - f) * (last - first)) > 0);
534         double paramin = 0;
535         double paramax = 0;
536         if (isForward)
537         {
538                 paramin = f;
539                 paramax = l;
540                 gp_Pnt2d p = C2d->Value(f);     // first point = Vertex Forward
541                 uvslf[0].x = p.X();
542                 uvslf[0].y = p.Y();
543                 uvslf[0].param = f;
544                 uvslf[0].nodeId = idFirst;
545                 //MESSAGE("__ f "<<f<<" "<<uvslf[0].x <<" "<<uvslf[0].y);
546                 map < double, int >::iterator itp = params.begin();
547                 for (int i = 1; i <= nbPoints; i++)     // nbPoints internal
548                 {
549                         double param = (*itp).first;
550                         int nodeId = (*itp).second;
551                         gp_Pnt2d p = C2d->Value(param);
552                         uvslf[i].x = p.X();
553                         uvslf[i].y = p.Y();
554                         uvslf[i].param = param;
555                         uvslf[i].nodeId = nodeId;
556                         //MESSAGE("__ "<<i<<" "<<param<<" "<<uvslf[i].x <<" "<<uvslf[i].y);
557                         itp++;
558                 }
559                 p = C2d->Value(l);              // last point = Vertex Reversed
560                 uvslf[nbPoints + 1].x = p.X();
561                 uvslf[nbPoints + 1].y = p.Y();
562                 uvslf[nbPoints + 1].param = l;
563                 uvslf[nbPoints + 1].nodeId = idLast;
564                 //MESSAGE("__ l "<<l<<" "<<uvslf[nbPoints+1].x <<" "<<uvslf[nbPoints+1].y);
565         }
566         else
567         {
568                 paramin = l;
569                 paramax = f;
570                 gp_Pnt2d p = C2d->Value(l);     // first point = Vertex Reversed
571                 uvslf[0].x = p.X();
572                 uvslf[0].y = p.Y();
573                 uvslf[0].param = l;
574                 uvslf[0].nodeId = idLast;
575                 //MESSAGE("__ l "<<l<<" "<<uvslf[0].x <<" "<<uvslf[0].y);
576                 map < double, int >::reverse_iterator itp = params.rbegin();
577                 for (int j = nbPoints; j >= 1; j--)     // nbPoints internal
578                 {
579                         double param = (*itp).first;
580                         int nodeId = (*itp).second;
581                         int i = nbPoints + 1 - j;
582                         gp_Pnt2d p = C2d->Value(param);
583                         uvslf[i].x = p.X();
584                         uvslf[i].y = p.Y();
585                         uvslf[i].param = param;
586                         uvslf[i].nodeId = nodeId;
587                         //MESSAGE("__ "<<i<<" "<<param<<" "<<uvslf[i].x <<" "<<uvslf[i].y);
588                         itp++;
589                 }
590                 p = C2d->Value(f);              // last point = Vertex Forward
591                 uvslf[nbPoints + 1].x = p.X();
592                 uvslf[nbPoints + 1].y = p.Y();
593                 uvslf[nbPoints + 1].param = f;
594                 uvslf[nbPoints + 1].nodeId = idFirst;
595                 //MESSAGE("__ f "<<f<<" "<<uvslf[nbPoints+1].x <<" "<<uvslf[nbPoints+1].y);
596         }
597
598         ASSERT(paramin != paramax);
599         for (int i = 0; i < nbPoints + 2; i++)
600         {
601                 uvslf[i].normParam = (uvslf[i].param - paramin) / (paramax - paramin);
602                 //SCRUTE(uvslf[i].normParam);
603         }
604
605         return uvslf;
606 }
607
608 //=============================================================================
609 /*!
610  *  
611  */
612 //=============================================================================
613
614 ostream & SMESH_Quadrangle_2D::SaveTo(ostream & save)
615 {
616         return save << this;
617 }
618
619 //=============================================================================
620 /*!
621  *  
622  */
623 //=============================================================================
624
625 istream & SMESH_Quadrangle_2D::LoadFrom(istream & load)
626 {
627         return load >> (*this);
628 }
629
630 //=============================================================================
631 /*!
632  *  
633  */
634 //=============================================================================
635
636 ostream & operator <<(ostream & save, SMESH_Quadrangle_2D & hyp)
637 {
638         return save;
639 }
640
641 //=============================================================================
642 /*!
643  *  
644  */
645 //=============================================================================
646
647 istream & operator >>(istream & load, SMESH_Quadrangle_2D & hyp)
648 {
649         return load;
650 }