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