Salome HOME
Update copyright information
[plugins/blsurfplugin.git] / src / BLSURFPlugin / BLSURFPlugin_BLSURF.cxx
1 //  Copyright (C) 2007-2008  CEA/DEN, EDF R&D
2 //
3 //  This library is free software; you can redistribute it and/or
4 //  modify it under the terms of the GNU Lesser General Public
5 //  License as published by the Free Software Foundation; either
6 //  version 2.1 of the License.
7 //
8 //  This library is distributed in the hope that it will be useful,
9 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 //  Lesser General Public License for more details.
12 //
13 //  You should have received a copy of the GNU Lesser General Public
14 //  License along with this library; if not, write to the Free Software
15 //  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 //  See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19 //  BLSURFPlugin : C++ implementation
20 // File    : BLSURFPlugin_BLSURF.cxx
21 // Authors : Francis KLOSS (OCC) & Patrick LAUG (INRIA) & Lioka RAZAFINDRAZAKA (CEA)
22 //           & Aurelien ALLEAUME (DISTENE)
23 // Date    : 20/03/2006
24 // Project : SALOME
25 //=============================================================================
26 //
27 using namespace std;
28
29 #include "BLSURFPlugin_BLSURF.hxx"
30 #include "BLSURFPlugin_Hypothesis.hxx"
31
32 #include <SMESH_Gen.hxx>
33 #include <SMESH_Mesh.hxx>
34 #include <SMESH_ControlsDef.hxx>
35
36 #include <SMESHDS_Mesh.hxx>
37 #include <SMDS_MeshElement.hxx>
38 #include <SMDS_MeshNode.hxx>
39
40 #include <utilities.h>
41
42 #include <list>
43 #include <vector>
44
45 #include <BRep_Tool.hxx>
46 #include <TopExp.hxx>
47 #include <TopExp_Explorer.hxx>
48 #include <TopoDS.hxx>
49 #include <NCollection_Map.hxx>
50 #include <Standard_ErrorHandler.hxx>
51
52 extern "C"{
53 #include <distene/api.h>
54 }
55
56 #include <Geom_Surface.hxx>
57 #include <Handle_Geom_Surface.hxx>
58 #include <Geom2d_Curve.hxx>
59 #include <Handle_Geom2d_Curve.hxx>
60 #include <Geom_Curve.hxx>
61 #include <Handle_Geom_Curve.hxx>
62 #include <TopoDS_Vertex.hxx>
63 #include <TopoDS_Edge.hxx>
64 #include <TopoDS_Wire.hxx>
65 #include <TopoDS_Face.hxx>
66 #include <TopoDS_Shape.hxx>
67 #include <gp_Pnt2d.hxx>
68 #include <TopTools_IndexedMapOfShape.hxx>
69 #include <BRepTools.hxx>
70
71 #ifndef WNT
72 #include <fenv.h>
73 #endif
74
75 //=============================================================================
76 /*!
77  *  
78  */
79 //=============================================================================
80
81 BLSURFPlugin_BLSURF::BLSURFPlugin_BLSURF(int hypId, int studyId,
82                                                SMESH_Gen* gen)
83   : SMESH_2D_Algo(hypId, studyId, gen)
84 {
85   MESSAGE("BLSURFPlugin_BLSURF::BLSURFPlugin_BLSURF");
86
87   _name = "BLSURF";
88   _shapeType = (1 << TopAbs_FACE); // 1 bit /shape type
89   _compatibleHypothesis.push_back("BLSURF_Parameters");
90   _requireDescretBoundary = false;
91   _onlyUnaryInput = false;
92   _hypothesis = NULL;
93 }
94
95 //=============================================================================
96 /*!
97  *  
98  */
99 //=============================================================================
100
101 BLSURFPlugin_BLSURF::~BLSURFPlugin_BLSURF()
102 {
103   MESSAGE("BLSURFPlugin_BLSURF::~BLSURFPlugin_BLSURF");
104 }
105
106 //=============================================================================
107 /*!
108  *  
109  */
110 //=============================================================================
111
112 bool BLSURFPlugin_BLSURF::CheckHypothesis
113                          (SMESH_Mesh&                          aMesh,
114                           const TopoDS_Shape&                  aShape,
115                           SMESH_Hypothesis::Hypothesis_Status& aStatus)
116 {
117   _hypothesis = NULL;
118
119   list<const SMESHDS_Hypothesis*>::const_iterator itl;
120   const SMESHDS_Hypothesis* theHyp;
121
122   const list<const SMESHDS_Hypothesis*>& hyps = GetUsedHypothesis(aMesh, aShape);
123   int nbHyp = hyps.size();
124   if (!nbHyp)
125   {
126     aStatus = SMESH_Hypothesis::HYP_OK;
127     return true;  // can work with no hypothesis
128   }
129
130   itl = hyps.begin();
131   theHyp = (*itl); // use only the first hypothesis
132
133   string hypName = theHyp->GetName();
134
135   if (hypName == "BLSURF_Parameters")
136   {
137     _hypothesis = static_cast<const BLSURFPlugin_Hypothesis*> (theHyp);
138     ASSERT(_hypothesis);
139     if ( _hypothesis->GetPhysicalMesh() == BLSURFPlugin_Hypothesis::DefaultSize &&
140          _hypothesis->GetGeometricMesh() == BLSURFPlugin_Hypothesis::DefaultGeom )
141       //  hphy_flag = 0 and hgeo_flag = 0 is not allowed (spec)
142       aStatus = SMESH_Hypothesis::HYP_BAD_PARAMETER;
143     else
144       aStatus = SMESH_Hypothesis::HYP_OK;
145   }
146   else
147     aStatus = SMESH_Hypothesis::HYP_INCOMPATIBLE;
148
149   return aStatus == SMESH_Hypothesis::HYP_OK;
150 }
151
152 //=============================================================================
153 /*!
154  * Pass parameters to BLSURF
155  */
156 //=============================================================================
157
158 inline std::string to_string(double d)
159 {
160    std::ostringstream o;
161    o << d;
162    return o.str();
163 }
164
165 inline std::string to_string(int i)
166 {
167    std::ostringstream o;
168    o << i;
169    return o.str();
170 }
171
172 void BLSURFPlugin_BLSURF::SetParameters(const BLSURFPlugin_Hypothesis* hyp, blsurf_session_t *bls)
173 {
174   int    _topology      = BLSURFPlugin_Hypothesis::GetDefaultTopology();
175   int    _physicalMesh  = BLSURFPlugin_Hypothesis::GetDefaultPhysicalMesh();
176   double _phySize       = BLSURFPlugin_Hypothesis::GetDefaultPhySize();
177   int    _geometricMesh = BLSURFPlugin_Hypothesis::GetDefaultGeometricMesh();
178   double _angleMeshS    = BLSURFPlugin_Hypothesis::GetDefaultAngleMeshS();
179   double _angleMeshC    = BLSURFPlugin_Hypothesis::GetDefaultAngleMeshC();
180   double _gradation     = BLSURFPlugin_Hypothesis::GetDefaultGradation();
181   bool   _quadAllowed   = BLSURFPlugin_Hypothesis::GetDefaultQuadAllowed();
182   bool   _decimesh      = BLSURFPlugin_Hypothesis::GetDefaultDecimesh();
183   int    _verb          = BLSURFPlugin_Hypothesis::GetDefaultVerbosity();
184
185   if (hyp) {
186     MESSAGE("BLSURFPlugin_BLSURF::SetParameters");
187     _topology      = (int) hyp->GetTopology();
188     _physicalMesh  = (int) hyp->GetPhysicalMesh();
189     _phySize       = hyp->GetPhySize();
190     _geometricMesh = (int) hyp->GetGeometricMesh();
191     _angleMeshS    = hyp->GetAngleMeshS();
192     _angleMeshC    = hyp->GetAngleMeshC();
193     _gradation     = hyp->GetGradation();
194     _quadAllowed   = hyp->GetQuadAllowed();
195     _decimesh      = hyp->GetDecimesh();
196     _verb          = hyp->GetVerbosity();
197
198     if ( hyp->GetPhyMin() != ::BLSURFPlugin_Hypothesis::undefinedDouble() )
199       blsurf_set_param(bls, "hphymin", to_string(hyp->GetPhyMin()).c_str());
200     if ( hyp->GetPhyMax() != ::BLSURFPlugin_Hypothesis::undefinedDouble() )
201       blsurf_set_param(bls, "hphymax", to_string(hyp->GetPhyMax()).c_str());
202     if ( hyp->GetGeoMin() != ::BLSURFPlugin_Hypothesis::undefinedDouble() )
203       blsurf_set_param(bls, "hgeomin", to_string(hyp->GetGeoMin()).c_str());
204     if ( hyp->GetGeoMax() != ::BLSURFPlugin_Hypothesis::undefinedDouble() )
205       blsurf_set_param(bls, "hgeomax", to_string(hyp->GetGeoMax()).c_str());
206
207     const BLSURFPlugin_Hypothesis::TOptionValues & opts = hyp->GetOptionValues();
208     BLSURFPlugin_Hypothesis::TOptionValues::const_iterator opIt;
209     for ( opIt = opts.begin(); opIt != opts.end(); ++opIt )
210       if ( !opIt->second.empty() ) {
211 #ifdef _DEBUG_
212         cout << "blsurf_set_param(): " << opIt->first << " = " << opIt->second << endl;
213 #endif
214         blsurf_set_param(bls, opIt->first.c_str(), opIt->second.c_str());
215       }
216
217   } else {
218     MESSAGE("BLSURFPlugin_BLSURF::SetParameters using defaults");
219   }
220   
221   blsurf_set_param(bls, "topo_points",       _topology > 0 ? "1" : "0");
222   blsurf_set_param(bls, "topo_curves",       _topology > 0 ? "1" : "0");
223   blsurf_set_param(bls, "topo_project",      _topology > 0 ? "1" : "0");
224   blsurf_set_param(bls, "clean_boundary",    _topology > 1 ? "1" : "0");
225   blsurf_set_param(bls, "close_boundary",    _topology > 1 ? "1" : "0");
226   blsurf_set_param(bls, "hphy_flag",         to_string(_physicalMesh).c_str());
227   blsurf_set_param(bls, "hphydef",           to_string(_phySize).c_str());
228   blsurf_set_param(bls, "hgeo_flag",         to_string(_geometricMesh).c_str());
229   blsurf_set_param(bls, "angle_meshs",       to_string(_angleMeshS).c_str());
230   blsurf_set_param(bls, "angle_meshc",       to_string(_angleMeshC).c_str());
231   blsurf_set_param(bls, "gradation",         to_string(_gradation).c_str());
232   blsurf_set_param(bls, "patch_independent", _decimesh ? "1" : "0");
233   blsurf_set_param(bls, "element",           _quadAllowed ? "q1.0" : "p1");
234   blsurf_set_param(bls, "verb",              to_string(_verb).c_str());
235 }
236
237 status_t curv_fun(real t, real *uv, real *dt, real *dtt, void *user_data);
238 status_t surf_fun(real *uv, real *xyz, real*du, real *dv,
239                   real *duu, real *duv, real *dvv, void *user_data);
240 status_t message_callback(message_t *msg, void *user_data);
241
242 //=============================================================================
243 /*!
244  *
245  */
246 //=============================================================================
247
248 bool BLSURFPlugin_BLSURF::Compute(SMESH_Mesh& aMesh, const TopoDS_Shape& aShape) {
249
250   MESSAGE("BLSURFPlugin_BLSURF::Compute");
251
252   if (aShape.ShapeType() == TopAbs_COMPOUND) {
253     cout << "  the shape is a COMPOUND" << endl;
254   }
255   else {
256     cout << "  the shape is UNKNOWN" << endl;
257   };
258
259   context_t *ctx =  context_new();
260   context_set_message_callback(ctx, message_callback, &_comment);
261
262   cad_t *c = cad_new(ctx);
263  
264   TopTools_IndexedMapOfShape fmap;
265   TopTools_IndexedMapOfShape emap;
266   TopTools_IndexedMapOfShape pmap;
267   vector<Handle(Geom2d_Curve)> curves;
268   vector<Handle(Geom_Surface)> surfaces;
269
270   fmap.Clear();
271   emap.Clear();
272   pmap.Clear();
273   surfaces.resize(0);
274   curves.resize(0);
275
276   int iface = 0;
277   for (TopExp_Explorer face_iter(aShape,TopAbs_FACE);face_iter.More();face_iter.Next()) {
278     TopoDS_Face f=TopoDS::Face(face_iter.Current());
279     if (fmap.FindIndex(f) > 0)
280       continue;
281     
282     fmap.Add(f);
283     iface++;
284     surfaces.push_back(BRep_Tool::Surface(f));
285     cad_face_t *fce = cad_face_new(c, iface, surf_fun, surfaces.back());  
286     cad_face_set_tag(fce, iface);
287     if(f.Orientation() != TopAbs_FORWARD){
288       cad_face_set_orientation(fce, CAD_ORIENTATION_REVERSED);
289     } else {
290       cad_face_set_orientation(fce, CAD_ORIENTATION_FORWARD);
291     }
292     
293     for (TopExp_Explorer edge_iter(f,TopAbs_EDGE);edge_iter.More();edge_iter.Next()) {
294       TopoDS_Edge e = TopoDS::Edge(edge_iter.Current());
295       int ic = emap.FindIndex(e);
296       if (ic <= 0)
297         ic = emap.Add(e);
298       
299       double tmin,tmax;
300       curves.push_back(BRep_Tool::CurveOnSurface(e, f, tmin, tmax));
301       cad_edge_t *edg = cad_edge_new(fce, ic, tmin, tmax, curv_fun, curves.back());
302       cad_edge_set_tag(edg, ic);
303       cad_edge_set_property(edg, EDGE_PROPERTY_SOFT_REQUIRED);
304       if (e.Orientation() == TopAbs_INTERNAL)
305         cad_edge_set_property(edg, EDGE_PROPERTY_INTERNAL);
306
307       int npts = 0;
308       int ip1, ip2, *ip;
309       gp_Pnt2d e0 = curves.back()->Value(tmin);
310       gp_Pnt ee0 = surfaces.back()->Value(e0.X(), e0.Y());
311       Standard_Real d1=0,d2=0;
312       for (TopExp_Explorer ex_edge(e ,TopAbs_VERTEX); ex_edge.More(); ex_edge.Next()) {
313         TopoDS_Vertex v = TopoDS::Vertex(ex_edge.Current());
314
315         ++npts;
316         if (npts == 1){
317           ip = &ip1;
318           d1 = ee0.SquareDistance(BRep_Tool::Pnt(v));
319         } else {
320           ip = &ip2;
321           d2 = ee0.SquareDistance(BRep_Tool::Pnt(v));
322         }
323         *ip = pmap.FindIndex(v);
324         if(*ip <= 0)
325           *ip = pmap.Add(v);
326       }
327       if (npts != 2) {
328         // should not happen 
329         cout << "An edge does not have 2 extremities." << endl;
330       } else {
331         if (d1 < d2)
332           cad_edge_set_extremities(edg, ip1, ip2);
333         else
334           cad_edge_set_extremities(edg, ip2, ip1);
335       }
336     } // for edge
337   } //for face
338
339
340
341
342   blsurf_session_t *bls = blsurf_session_new(ctx);
343   blsurf_data_set_cad(bls, c);
344
345   SetParameters(_hypothesis, bls);
346
347   cout << endl;
348   cout << "Beginning of Surface Mesh generation" << endl;
349   cout << endl;
350
351   // Issue 0019864. On DebianSarge, FE signals do not obey to OSD::SetSignal(false)
352 #ifndef WNT
353   feclearexcept( FE_ALL_EXCEPT );
354   int oldFEFlags = fedisableexcept( FE_ALL_EXCEPT );
355 #endif
356
357     status_t status = STATUS_ERROR;
358
359   try {
360     OCC_CATCH_SIGNALS;
361
362     status = blsurf_compute_mesh(bls);
363
364   }
365   catch ( std::exception& exc ) {
366     _comment += exc.what();
367   }
368   catch (Standard_Failure& ex) {
369     _comment += ex.DynamicType()->Name();
370     if ( ex.GetMessageString() && strlen( ex.GetMessageString() )) {
371       _comment += ": ";
372       _comment += ex.GetMessageString();
373     }
374   }
375   catch (...) {
376     if ( _comment.empty() )
377       _comment = "Exception in blsurf_compute_mesh()";
378   }
379   if ( status != STATUS_OK) {
380     blsurf_session_delete(bls);
381     cad_delete(c);
382     context_delete(ctx);
383
384     return error(_comment);
385   }
386
387   cout << endl;
388   cout << "End of Surface Mesh generation" << endl;
389   cout << endl;
390
391   mesh_t *msh;
392   blsurf_data_get_mesh(bls, &msh);
393   if(!msh){
394     blsurf_session_delete(bls);
395     cad_delete(c);
396     context_delete(ctx);
397     
398     return error(_comment);
399     //return false;
400   }
401   
402   integer nv, ne, nt, nq, vtx[4], tag;
403   real xyz[3];
404
405   mesh_get_vertex_count(msh, &nv);
406   mesh_get_edge_count(msh, &ne);
407   mesh_get_triangle_count(msh, &nt);
408   mesh_get_quadrangle_count(msh, &nq);
409
410   
411   SMESHDS_Mesh* meshDS = aMesh.GetMeshDS();
412   SMDS_MeshNode** nodes = new SMDS_MeshNode*[nv+1];
413   bool* tags = new bool[nv+1];
414
415   for(int iv=1;iv<=nv;iv++) {
416     mesh_get_vertex_coordinates(msh, iv, xyz);
417     mesh_get_vertex_tag(msh, iv, &tag);    
418     nodes[iv] = meshDS->AddNode(xyz[0], xyz[1], xyz[2]);
419     // internal point are tagged to zero
420     if(tag){
421       meshDS->SetNodeOnVertex(nodes[iv], TopoDS::Vertex(pmap(tag)));
422       tags[iv] = false;
423     } else {
424       tags[iv] = true;
425     }
426   }
427
428   for(int it=1;it<=ne;it++) {
429     mesh_get_edge_vertices(msh, it, vtx);
430     SMDS_MeshEdge* edg = meshDS->AddEdge(nodes[vtx[0]], nodes[vtx[1]]);
431     mesh_get_edge_tag(msh, it, &tag);    
432
433     if (tags[vtx[0]]) {
434       meshDS->SetNodeOnEdge(nodes[vtx[0]], TopoDS::Edge(emap(tag)));
435       tags[vtx[0]] = false;
436     };
437     if (tags[vtx[1]]) {
438       meshDS->SetNodeOnEdge(nodes[vtx[1]], TopoDS::Edge(emap(tag)));
439       tags[vtx[1]] = false;
440     };
441     meshDS->SetMeshElementOnShape(edg, TopoDS::Edge(emap(tag)));
442     
443   }
444
445   for(int it=1;it<=nt;it++) {
446     mesh_get_triangle_vertices(msh, it, vtx);
447     SMDS_MeshFace* tri = meshDS->AddFace(nodes[vtx[0]], nodes[vtx[1]], nodes[vtx[2]]);
448     mesh_get_triangle_tag(msh, it, &tag);    
449     meshDS->SetMeshElementOnShape(tri, TopoDS::Face(fmap(tag)));
450     if (tags[vtx[0]]) {
451       meshDS->SetNodeOnFace(nodes[vtx[0]], TopoDS::Face(fmap(tag)));
452       tags[vtx[0]] = false;
453     };
454     if (tags[vtx[1]]) {
455       meshDS->SetNodeOnFace(nodes[vtx[1]], TopoDS::Face(fmap(tag)));
456       tags[vtx[1]] = false;
457     };
458     if (tags[vtx[2]]) {
459       meshDS->SetNodeOnFace(nodes[vtx[2]], TopoDS::Face(fmap(tag)));
460       tags[vtx[2]] = false;
461     };
462   }
463
464   for(int it=1;it<=nq;it++) {
465     mesh_get_quadrangle_vertices(msh, it, vtx);
466     SMDS_MeshFace* quad = meshDS->AddFace(nodes[vtx[0]], nodes[vtx[1]], nodes[vtx[2]], nodes[vtx[3]]);
467     mesh_get_quadrangle_tag(msh, it, &tag);    
468     meshDS->SetMeshElementOnShape(quad, TopoDS::Face(fmap(tag)));
469     if (tags[vtx[0]]) {
470       meshDS->SetNodeOnFace(nodes[vtx[0]], TopoDS::Face(fmap(tag)));
471       tags[vtx[0]] = false;
472     };
473     if (tags[vtx[1]]) {
474       meshDS->SetNodeOnFace(nodes[vtx[1]], TopoDS::Face(fmap(tag)));
475       tags[vtx[1]] = false;
476     };
477     if (tags[vtx[2]]) {
478       meshDS->SetNodeOnFace(nodes[vtx[2]], TopoDS::Face(fmap(tag)));
479       tags[vtx[2]] = false;
480     };
481     if (tags[vtx[3]]) {
482       meshDS->SetNodeOnFace(nodes[vtx[3]], TopoDS::Face(fmap(tag)));
483       tags[vtx[3]] = false;
484     };
485   }
486
487   delete nodes;
488
489   /* release the mesh object */
490   blsurf_data_regain_mesh(bls, msh);
491
492   /* clean up everything */
493   blsurf_session_delete(bls);
494   cad_delete(c);
495
496   context_delete(ctx);
497
498   // Issue 0019864. On DebianSarge, FE signals do not obey to OSD::SetSignal(false)
499 #ifndef WNT
500   if ( oldFEFlags > 0 )    
501     feenableexcept( oldFEFlags );
502   feclearexcept( FE_ALL_EXCEPT );
503 #endif
504
505   return true;
506 }
507
508 //=============================================================================
509 /*!
510  *  
511  */
512 //=============================================================================
513
514 ostream & BLSURFPlugin_BLSURF::SaveTo(ostream & save)
515 {
516   return save;
517 }
518
519 //=============================================================================
520 /*!
521  *  
522  */
523 //=============================================================================
524
525 istream & BLSURFPlugin_BLSURF::LoadFrom(istream & load)
526 {
527   return load;
528 }
529
530 //=============================================================================
531 /*!
532  *  
533  */
534 //=============================================================================
535
536 ostream & operator << (ostream & save, BLSURFPlugin_BLSURF & hyp)
537 {
538   return hyp.SaveTo( save );
539 }
540
541 //=============================================================================
542 /*!
543  *  
544  */
545 //=============================================================================
546
547 istream & operator >> (istream & load, BLSURFPlugin_BLSURF & hyp)
548 {
549   return hyp.LoadFrom( load );
550 }
551
552 status_t curv_fun(real t, real *uv, real *dt, real *dtt, void *user_data)
553 {
554   const Geom2d_Curve*pargeo = (const Geom2d_Curve*) user_data;
555
556   if (uv){
557     gp_Pnt2d P;
558     P=pargeo->Value(t);
559     uv[0]=P.X(); uv[1]=P.Y();
560   }
561
562   if(dt) {
563     gp_Vec2d V1;
564     V1=pargeo->DN(t,1);
565     dt[0]=V1.X(); dt[1]=V1.Y();
566   }
567
568   if(dtt){
569     gp_Vec2d V2;
570     V2=pargeo->DN(t,2);
571     dtt[0]=V2.X(); dtt[1]=V2.Y();
572   }
573
574   return 0;
575 }
576
577 status_t surf_fun(real *uv, real *xyz, real*du, real *dv,
578                   real *duu, real *duv, real *dvv, void *user_data)
579 {
580   const Geom_Surface* geometry = (const Geom_Surface*) user_data;
581
582   if(xyz){
583    gp_Pnt P;
584    P=geometry->Value(uv[0],uv[1]);   // S.D0(U,V,P);
585    xyz[0]=P.X(); xyz[1]=P.Y(); xyz[2]=P.Z();
586   }
587
588   if(du && dv){
589     gp_Pnt P;
590     gp_Vec D1U,D1V;
591     
592     geometry->D1(uv[0],uv[1],P,D1U,D1V);
593     du[0]=D1U.X(); du[1]=D1U.Y(); du[2]=D1U.Z();
594     dv[0]=D1V.X(); dv[1]=D1V.Y(); dv[2]=D1V.Z();
595   }
596
597   if(duu && duv && dvv){
598     gp_Pnt P;
599     gp_Vec D1U,D1V;
600     gp_Vec D2U,D2V,D2UV;
601     
602     geometry->D2(uv[0],uv[1],P,D1U,D1V,D2U,D2V,D2UV);
603     duu[0]=D2U.X(); duu[1]=D2U.Y(); duu[2]=D2U.Z();
604     duv[0]=D2UV.X(); duv[1]=D2UV.Y(); duv[2]=D2UV.Z();
605     dvv[0]=D2V.X(); dvv[1]=D2V.Y(); dvv[2]=D2V.Z();    
606   }
607
608   return 0;
609 }
610
611 status_t message_callback(message_t *msg, void *user_data)
612 {
613   integer errnumber = 0;
614   char *desc;
615   message_get_number(msg, &errnumber);
616   message_get_description(msg, &desc);
617   if ( errnumber < 0 ) {
618     string * error = (string*)user_data;
619 //   if ( !error->empty() )
620 //     *error += "\n";
621     // remove ^A from the tail
622     int len = strlen( desc );
623     while (len > 0 && desc[len-1] != '\n')
624       len--;
625     error->append( desc, len );
626   }
627   else {
628     cout << desc;
629   }
630   return STATUS_OK;
631 }