Salome HOME
HAVE_NETGEN (#define) to remove an unresolved symbol when building without netgen
[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                         meshDS->SetNodeOnFace(node, F);
138                         quad->uv_grid[ij].node = node;
139 //  Handle (SMDS_FacePosition) fpos
140 //    = new SMDS_FacePosition(theSubMesh->GetId(),i,j); // easier than u,v
141 //  node->SetPosition(fpos);
142                         SMDS_FacePosition* fpos
143                                 = dynamic_cast<SMDS_FacePosition*>(node->GetPosition());
144                         fpos->SetUParameter(i);
145                         fpos->SetVParameter(j);
146                 }
147
148         //   bool isQuadForward = ( faceIsForward == quad->isEdgeForward[0]);
149         for (int i = 0; i < nbdown - 1; i++)
150                 for (int j = 0; j < nbright - 1; j++)   // faces
151                 {
152                         const SMDS_MeshNode *a, *b, *c, *d;
153                         a = quad->uv_grid[j * nbdown + i].node;
154                         b = quad->uv_grid[j * nbdown + i + 1].node;
155                         c = quad->uv_grid[(j + 1) * nbdown + i + 1].node;
156                         d = quad->uv_grid[(j + 1) * nbdown + i].node;
157                         //  if (isQuadForward) faceId = meshDS->AddFace(a,b,c,d);
158                         //  else faceId = meshDS->AddFace(a,d,c,b);
159                         SMDS_MeshFace * face = meshDS->AddFace(a, b, c, d);
160                         meshDS->SetMeshElementOnShape(face, F);
161                 }
162
163         QuadDelete(quad);
164         bool isOk = true;
165         return isOk;
166 }
167
168 //=============================================================================
169 /*!
170  *  
171  */
172 //=============================================================================
173
174 FaceQuadStruct *SMESH_Quadrangle_2D::CheckAnd2Dcompute(SMESH_Mesh & aMesh,
175         const TopoDS_Shape & aShape)throw(SALOME_Exception)
176 {
177         //MESSAGE("SMESH_Quadrangle_2D::ComputeWithoutStore");
178
179         SMESH_subMesh *theSubMesh = aMesh.GetSubMesh(aShape);
180
181         //   const TopoDS_Face& FF = TopoDS::Face(aShape);
182         //   bool faceIsForward = (FF.Orientation() == TopAbs_FORWARD);
183         //   TopoDS_Face F = TopoDS::Face(FF.Oriented(TopAbs_FORWARD));
184         const TopoDS_Face & F = TopoDS::Face(aShape);
185         bool faceIsForward = (F.Orientation() == TopAbs_FORWARD);
186
187         // verify 1 wire only, with 4 edges, same number of points on opposite edges
188
189         if (NumberOfWires(F) != 1)
190         {
191                 MESSAGE("only 1 wire by face (quadrangles)");
192                 return 0;
193                 //throw SALOME_Exception(LOCALIZED("only 1 wire by face (quadrangles)"));
194         }
195         //   const TopoDS_Wire WW = BRepTools::OuterWire(F);
196         //   TopoDS_Wire W = TopoDS::Wire(WW.Oriented(TopAbs_FORWARD));
197         const TopoDS_Wire & W = BRepTools::OuterWire(F);
198         BRepTools_WireExplorer wexp(W, F);
199
200         FaceQuadStruct *quad = new FaceQuadStruct;
201         for (int i = 0; i < 4; i++)
202                 quad->uv_edges[i] = 0;
203         quad->uv_grid = 0;
204
205         int nbEdges = 0;
206         for (wexp.Init(W, F); wexp.More(); wexp.Next())
207         {
208                 //       const TopoDS_Edge& EE = wexp.Current();
209                 //       TopoDS_Edge E = TopoDS::Edge(EE.Oriented(TopAbs_FORWARD));
210                 const TopoDS_Edge & E = wexp.Current();
211                 int nb = aMesh.GetSubMesh(E)->GetSubMeshDS()->NbNodes();
212                 if (nbEdges < 4)
213                 {
214                         quad->edge[nbEdges] = E;
215                         quad->nbPts[nbEdges] = nb + 2;  // internal points + 2 extrema
216                 }
217                 nbEdges++;
218         }
219
220         if (nbEdges != 4)
221         {
222                 MESSAGE("face must have 4 edges /quadrangles");
223                 QuadDelete(quad);
224                 return 0;
225                 //throw SALOME_Exception(LOCALIZED("face must have 4 edges /quadrangles"));
226         }
227
228         if (quad->nbPts[0] != quad->nbPts[2])
229         {
230                 MESSAGE("different point number-opposed edge");
231                 QuadDelete(quad);
232                 return 0;
233                 //throw SALOME_Exception(LOCALIZED("different point number-opposed edge"));
234         }
235
236         if (quad->nbPts[1] != quad->nbPts[3])
237         {
238                 MESSAGE("different point number-opposed edge");
239                 QuadDelete(quad);
240                 return 0;
241                 //throw SALOME_Exception(LOCALIZED("different point number-opposed edge"));
242         }
243
244         // set normalized grid on unit square in parametric domain
245
246         SetNormalizedGrid(aMesh, F, quad);
247
248         return quad;
249 }
250
251 //=============================================================================
252 /*!
253  *  
254  */
255 //=============================================================================
256
257 void SMESH_Quadrangle_2D::QuadDelete(FaceQuadStruct * quad)
258 {
259         //MESSAGE("SMESH_Quadrangle_2D::QuadDelete");
260         if (quad)
261         {
262                 for (int i = 0; i < 4; i++)
263                 {
264                         if (quad->uv_edges[i])
265                                 delete[]quad->uv_edges[i];
266                         quad->edge[i].Nullify();
267                 }
268                 if (quad->uv_grid)
269                         delete[]quad->uv_grid;
270                 delete quad;
271         }
272 }
273
274 //=============================================================================
275 /*!
276  *  
277  */
278 //=============================================================================
279
280 void SMESH_Quadrangle_2D::SetNormalizedGrid(SMESH_Mesh & aMesh,
281         const TopoDS_Shape & aShape, FaceQuadStruct * quad) throw(SALOME_Exception)
282 {
283         // Algorithme décrit dans "Génération automatique de maillages"
284         // P.L. GEORGE, MASSON, Â§ 6.4.1 p. 84-85
285         // traitement dans le domaine paramétrique 2d u,v
286         // transport - projection sur le carré unité
287
288         const TopoDS_Face & F = TopoDS::Face(aShape);
289
290         // 1 --- find orientation of the 4 edges, by test on extrema
291
292         //      max             min                    0     x1     1
293         //     |<----north-2-------^                a3 -------------> a2
294         //     |                   |                   ^1          1^
295         //    west-3            east-1 =right          |            |
296         //     |                   |         ==>       |            |
297         //  y0 |                   | y1                |            |
298         //     |                   |                   |0          0|
299         //     v----south-0-------->                a0 -------------> a1
300         //      min             max                    0     x0     1
301         //             =down
302         //
303
304         Handle(Geom2d_Curve) c2d[4];
305         gp_Pnt2d pf[4];
306         gp_Pnt2d pl[4];
307         for (int i = 0; i < 4; i++)
308         {
309                 c2d[i] = BRep_Tool::CurveOnSurface(quad->edge[i],
310                         F, quad->first[i], quad->last[i]);
311                 pf[i] = c2d[i]->Value(quad->first[i]);
312                 pl[i] = c2d[i]->Value(quad->last[i]);
313                 quad->isEdgeForward[i] = false;
314         }
315
316         double eps2d = 1.e-3;           // *** utiliser plutot TopExp::CommonVertex, puis
317         // distances si piece fausse
318         int i = 0;
319         if ((pf[1].Distance(pl[0]) < eps2d) || (pl[1].Distance(pl[0]) < eps2d))
320         {
321                 quad->isEdgeForward[0] = true;
322         }
323         else
324         {
325                 double tmp = quad->first[0];
326                 quad->first[0] = quad->last[0];
327                 quad->last[0] = tmp;
328                 pf[0] = c2d[0]->Value(quad->first[0]);
329                 pl[0] = c2d[0]->Value(quad->last[0]);
330         }
331         for (int i = 1; i < 4; i++)
332         {
333                 quad->isEdgeForward[i] = (pf[i].Distance(pl[i - 1]) < eps2d);
334                 if (!quad->isEdgeForward[i])
335                 {
336                         double tmp = quad->first[i];
337                         quad->first[i] = quad->last[i];
338                         quad->last[i] = tmp;
339                         pf[i] = c2d[i]->Value(quad->first[i]);
340                         pl[i] = c2d[i]->Value(quad->last[i]);
341                         //SCRUTE(pf[i].Distance(pl[i-1]));
342                         ASSERT(pf[i].Distance(pl[i - 1]) < eps2d);
343                 }
344         }
345         //SCRUTE(pf[0].Distance(pl[3]));
346         ASSERT(pf[0].Distance(pl[3]) < eps2d);
347
348 //   for (int i=0; i<4; i++)
349 //     {
350 //       SCRUTE(quad->isEdgeForward[i]);
351 //       MESSAGE(" -first "<<i<<" "<<pf[i].X()<<" "<<pf[i].Y());
352 //       MESSAGE(" -last  "<<i<<" "<<pl[i].X()<<" "<<pl[i].Y());
353 //     }
354
355         // 2 --- load 2d edge points (u,v) with orientation and value on unit square
356
357         for (int i = 0; i < 2; i++)
358         {
359                 quad->uv_edges[i] = LoadEdgePoints(aMesh, F,
360                         quad->edge[i], quad->first[i], quad->last[i]);
361
362                 //                         quad->isEdgeForward[i]);
363         }
364         for (int i = 2; i < 4; i++)
365         {
366                 quad->uv_edges[i] = LoadEdgePoints(aMesh, F,
367                         quad->edge[i], quad->last[i], quad->first[i]);
368
369                 //                         !quad->isEdgeForward[i]);
370         }
371
372         // 3 --- 2D normalized values on unit square [0..1][0..1]
373
374         int nbdown = quad->nbPts[0];
375         int nbright = quad->nbPts[1];
376         quad->uv_grid = new UVPtStruct[nbright * nbdown];
377
378         UVPtStruct *uv_grid = quad->uv_grid;
379         UVPtStruct *uv_e0 = quad->uv_edges[0];
380         UVPtStruct *uv_e1 = quad->uv_edges[1];
381         UVPtStruct *uv_e2 = quad->uv_edges[2];
382         UVPtStruct *uv_e3 = quad->uv_edges[3];
383         gp_Pnt2d a0 = pf[0];
384         gp_Pnt2d a1 = pf[1];
385         gp_Pnt2d a2 = pf[2];
386         gp_Pnt2d a3 = pf[3];
387
388         // nodes Id on edges
389
390         int j = 0;
391         for (int i = 0; i < nbdown; i++)
392         {
393                 int ij = j * nbdown + i;
394                 uv_grid[ij].node = uv_e0[i].node;
395         }
396         i = nbdown - 1;
397         for (int j = 0; j < nbright; j++)
398         {
399                 int ij = j * nbdown + i;
400                 uv_grid[ij].node = uv_e1[j].node;
401         }
402         j = nbright - 1;
403         for (int i = 0; i < nbdown; i++)
404         {
405                 int ij = j * nbdown + i;
406                 uv_grid[ij].node = uv_e2[i].node;
407         }
408         i = 0;
409         for (int j = 0; j < nbright; j++)
410         {
411                 int ij = j * nbdown + i;
412                 uv_grid[ij].node = uv_e3[j].node;
413         }
414
415         // normalized 2d values on grid
416
417         for (int i = 0; i < nbdown; i++)
418                 for (int j = 0; j < nbright; j++)
419                 {
420                         int ij = j * nbdown + i;
421                         // --- droite i cste : x = x0 + y(x1-x0)
422                         double x0 = uv_e0[i].normParam; // bas - sud
423                         double x1 = uv_e2[i].normParam; // haut - nord
424                         // --- droite j cste : y = y0 + x(y1-y0)
425                         double y0 = uv_e3[j].normParam; // gauche-ouest
426                         double y1 = uv_e1[j].normParam; // droite - est
427                         // --- intersection : x=x0+(y0+x(y1-y0))(x1-x0)
428                         double x = (x0 + y0 * (x1 - x0)) / (1 - (y1 - y0) * (x1 - x0));
429                         double y = y0 + x * (y1 - y0);
430                         uv_grid[ij].x = x;
431                         uv_grid[ij].y = y;
432                         //MESSAGE("-xy-01 "<<x0<<" "<<x1<<" "<<y0<<" "<<y1);
433                         //MESSAGE("-xy-norm "<<i<<" "<<j<<" "<<x<<" "<<y);
434                 }
435
436         // 4 --- projection on 2d domain (u,v)
437
438         for (int i = 0; i < nbdown; i++)
439                 for (int j = 0; j < nbright; j++)
440                 {
441                         int ij = j * nbdown + i;
442                         double x = uv_grid[ij].x;
443                         double y = uv_grid[ij].y;
444                         double param_0 = uv_e0[0].param + x * (uv_e0[nbdown - 1].param - uv_e0[0].param);       // sud
445                         double param_2 = uv_e2[0].param + x * (uv_e2[nbdown - 1].param - uv_e2[0].param);       // nord
446                         double param_1 = uv_e1[0].param + y * (uv_e1[nbright - 1].param - uv_e1[0].param);      // est
447                         double param_3 = uv_e3[0].param + y * (uv_e3[nbright - 1].param - uv_e3[0].param);      // ouest
448
449                         //MESSAGE("params "<<param_0<<" "<<param_1<<" "<<param_2<<" "<<param_3);
450                         gp_Pnt2d p0 = c2d[0]->Value(param_0);
451                         gp_Pnt2d p1 = c2d[1]->Value(param_1);
452                         gp_Pnt2d p2 = c2d[2]->Value(param_2);
453                         gp_Pnt2d p3 = c2d[3]->Value(param_3);
454
455                         double u =
456                                 (1 - y) * p0.X() + x * p1.X() + y * p2.X() + (1 - x) * p3.X();
457                         double v =
458                                 (1 - y) * p0.Y() + x * p1.Y() + y * p2.Y() + (1 - x) * p3.Y();
459
460                         u -= (1 - x) * (1 - y) * a0.X() + x * (1 - y) * a1.X() +
461                                 x * y * a2.X() + (1 - x) * y * a3.X();
462                         v -= (1 - x) * (1 - y) * a0.Y() + x * (1 - y) * a1.Y() +
463                                 x * y * a2.Y() + (1 - x) * y * a3.Y();
464
465                         uv_grid[ij].u = u;
466                         uv_grid[ij].v = v;
467
468                         //MESSAGE("-uv- "<<i<<" "<<j<<" "<<uv_grid[ij].u<<" "<<uv_grid[ij].v);
469                 }
470 }
471
472 //=============================================================================
473 /*!
474  *  
475  */
476 //=============================================================================
477
478 UVPtStruct *SMESH_Quadrangle_2D::LoadEdgePoints(SMESH_Mesh & aMesh,
479         const TopoDS_Face & F, const TopoDS_Edge & E, double first, double last)
480   //                        bool isForward)
481 {
482         //MESSAGE("SMESH_Quadrangle_2D::LoadEdgePoints");
483
484         SMDS_Mesh * meshDS = aMesh.GetMeshDS();
485
486         // --- IDNodes of first and last Vertex
487
488         TopoDS_Vertex VFirst, VLast;
489         TopExp::Vertices(E, VFirst, VLast);     // corresponds to f and l
490
491     ASSERT(!VFirst.IsNull());
492     SMDS_Iterator<const SMDS_MeshNode *> * lid=
493         aMesh.GetSubMesh(VFirst)->GetSubMeshDS()->GetNodes();
494     const SMDS_MeshNode * idFirst = lid->next();
495     delete lid;
496
497     ASSERT(!VLast.IsNull());
498     lid=aMesh.GetSubMesh(VLast)->GetSubMeshDS()->GetNodes();
499     const SMDS_MeshNode * idLast = lid->next();
500     delete lid;
501
502         // --- edge internal IDNodes (relies on good order storage, not checked)
503
504         int nbPoints = aMesh.GetSubMesh(E)->GetSubMeshDS()->NbNodes();
505         //SCRUTE(nbPoints);
506         UVPtStruct *uvslf = new UVPtStruct[nbPoints + 2];
507
508         double f, l;
509         Handle(Geom2d_Curve) C2d = BRep_Tool::CurveOnSurface(E, F, f, l);
510
511         map<double, const SMDS_MeshNode *> params;
512         SMDS_Iterator<const SMDS_MeshNode *> * ite=
513                 aMesh.GetSubMesh(E)->GetSubMeshDS()->GetNodes();
514
515         while(ite->more())
516         {
517                 const SMDS_MeshNode * node = ite->next();
518                 const SMDS_EdgePosition* epos
519                         = static_cast<const SMDS_EdgePosition*>(node->GetPosition());
520                 double param = epos->GetUParameter();
521                 params[param] = node;
522         }
523         delete ite;
524
525         bool isForward = (((l - f) * (last - first)) > 0);
526         double paramin = 0;
527         double paramax = 0;
528         if (isForward)
529         {
530                 paramin = f;
531                 paramax = l;
532                 gp_Pnt2d p = C2d->Value(f);     // first point = Vertex Forward
533                 uvslf[0].x = p.X();
534                 uvslf[0].y = p.Y();
535                 uvslf[0].param = f;
536                 uvslf[0].node = idFirst;
537                 //MESSAGE("__ f "<<f<<" "<<uvslf[0].x <<" "<<uvslf[0].y);
538                 map < double, const SMDS_MeshNode* >::iterator itp = params.begin();
539                 for (int i = 1; i <= nbPoints; i++)     // nbPoints internal
540                 {
541                         double param = (*itp).first;
542                         gp_Pnt2d p = C2d->Value(param);
543                         uvslf[i].x = p.X();
544                         uvslf[i].y = p.Y();
545                         uvslf[i].param = param;
546                         uvslf[i].node = (*itp).second;
547                         //MESSAGE("__ "<<i<<" "<<param<<" "<<uvslf[i].x <<" "<<uvslf[i].y);
548                         itp++;
549                 }
550                 p = C2d->Value(l);              // last point = Vertex Reversed
551                 uvslf[nbPoints + 1].x = p.X();
552                 uvslf[nbPoints + 1].y = p.Y();
553                 uvslf[nbPoints + 1].param = l;
554                 uvslf[nbPoints + 1].node = idLast;
555                 //MESSAGE("__ l "<<l<<" "<<uvslf[nbPoints+1].x <<" "<<uvslf[nbPoints+1].y);
556         }
557         else
558         {
559                 paramin = l;
560                 paramax = f;
561                 gp_Pnt2d p = C2d->Value(l);     // first point = Vertex Reversed
562                 uvslf[0].x = p.X();
563                 uvslf[0].y = p.Y();
564                 uvslf[0].param = l;
565                 uvslf[0].node = idLast;
566                 //MESSAGE("__ l "<<l<<" "<<uvslf[0].x <<" "<<uvslf[0].y);
567                 map < double, const SMDS_MeshNode* >::reverse_iterator itp = params.rbegin();
568                 for (int j = nbPoints; j >= 1; j--)     // nbPoints internal
569                 {
570                         double param = (*itp).first;
571                         int i = nbPoints + 1 - j;
572                         gp_Pnt2d p = C2d->Value(param);
573                         uvslf[i].x = p.X();
574                         uvslf[i].y = p.Y();
575                         uvslf[i].param = param;
576                         uvslf[i].node = (*itp).second;
577                         //MESSAGE("__ "<<i<<" "<<param<<" "<<uvslf[i].x <<" "<<uvslf[i].y);
578                         itp++;
579                 }
580                 p = C2d->Value(f);              // last point = Vertex Forward
581                 uvslf[nbPoints + 1].x = p.X();
582                 uvslf[nbPoints + 1].y = p.Y();
583                 uvslf[nbPoints + 1].param = f;
584                 uvslf[nbPoints + 1].node = idFirst;
585                 //MESSAGE("__ f "<<f<<" "<<uvslf[nbPoints+1].x <<" "<<uvslf[nbPoints+1].y);
586         }
587
588         ASSERT(paramin != paramax);
589         for (int i = 0; i < nbPoints + 2; i++)
590         {
591                 uvslf[i].normParam = (uvslf[i].param - paramin) / (paramax - paramin);
592                 //SCRUTE(uvslf[i].normParam);
593         }
594
595         return uvslf;
596 }
597
598 //=============================================================================
599 /*!
600  *  
601  */
602 //=============================================================================
603
604 ostream & SMESH_Quadrangle_2D::SaveTo(ostream & save)
605 {
606         return save << this;
607 }
608
609 //=============================================================================
610 /*!
611  *  
612  */
613 //=============================================================================
614
615 istream & SMESH_Quadrangle_2D::LoadFrom(istream & load)
616 {
617         return load >> (*this);
618 }
619
620 //=============================================================================
621 /*!
622  *  
623  */
624 //=============================================================================
625
626 ostream & operator <<(ostream & save, SMESH_Quadrangle_2D & hyp)
627 {
628         return save;
629 }
630
631 //=============================================================================
632 /*!
633  *  
634  */
635 //=============================================================================
636
637 istream & operator >>(istream & load, SMESH_Quadrangle_2D & hyp)
638 {
639         return load;
640 }