Salome HOME
Merge branch 'master' into V9_dev
[modules/med.git] / src / MEDCalc / cmp / MEDPresentation.cxx
index a11077b64108d0ae80aa25e376bf4916e343a4e2..7a7e4cbd39ab91aee67ad040602ffd74d8596a2b 100644 (file)
 
 #include <sstream>
 
+#if PY_VERSION_HEX < 0x03050000
+static char*
+Py_EncodeLocale(const wchar_t *text, size_t *error_pos)
+{
+       return _Py_wchar2char(text, error_pos);
+}
+#endif
+
 const std::string MEDPresentation::PROP_NAME  = "name";
 const std::string MEDPresentation::PROP_NB_COMPONENTS = "nbComponents";
 const std::string MEDPresentation::PROP_SELECTED_COMPONENT = "selectedComponent";
@@ -36,36 +44,20 @@ const std::string MEDPresentation::PROP_COMPONENT = "component_";
 const std::string MEDPresentation::PROP_COLOR_MAP = "colorMap";
 const std::string MEDPresentation::PROP_SCALAR_BAR_RANGE = "scalarBarRange";
 
-MEDPresentation::MEDPresentation(MEDPresentation::TypeID fieldHandlerId, const std::string& name,
+MEDPresentation::MEDPresentation(MEDPresentation::TypeID handlerId, const std::string& name,
                                  const MEDCALC::ViewModeType viewMode,
                                  const MEDCALC::ColorMapType colorMap,
                                  const MEDCALC::ScalarBarRangeType sbRange)
-    : _fieldHandlerId(fieldHandlerId), _propertiesStr(),
-      //_pipeline(0), _display(0)
+    : _handlerId(handlerId), _propertiesStr(),
+      _mcFieldType(MEDCoupling::ON_CELLS),
+      _pvFieldType(""), _meshName(""), _fieldName(""), _fileName(""),
       _selectedComponentIndex(-1),
       _viewMode(viewMode),
       _colorMap(colorMap),
       _sbRange(sbRange),
-      _renderViewPyId(-1),  // will be set by getRenderViewCommand()
+      _renderViewPyId(-1),  // will be set by MEDPresentationManager_i::_makePresentation()
       _globalDict(0)
 {
-  MEDCALC::MEDDataManager_ptr dataManager(MEDFactoryClient::getDataManager());
-  MEDCALC::FieldHandler* fieldHandler = dataManager->getFieldHandler(fieldHandlerId);
-  MEDCALC::MeshHandler* meshHandler = dataManager->getMesh(fieldHandler->meshid);
-  MEDCALC::DatasourceHandler* dataSHandler = dataManager->getDatasourceHandlerFromID(meshHandler->sourceid);
-
-  _fileName = dataSHandler->uri;
-  _fieldName = fieldHandler->fieldname;
-  _fieldType = getFieldTypeString((MEDCoupling::TypeOfField) fieldHandler->type);
-  _meshName = meshHandler->name;
-
-  if (_fileName.substr(0, 7) != std::string("file://")) {
-    const char* msg = "MEDPresentation(): Data source is not a file! Can not proceed.";
-    STDLOG(msg);
-    throw MEDPresentationException(msg);
-  }
-  _fileName = _fileName.substr(7, _fileName.size());
-
   setStringProperty(MEDPresentation::PROP_NAME, name);
 
   setIntProperty(MEDPresentation::PROP_NB_COMPONENTS, 0);
@@ -76,15 +68,51 @@ MEDPresentation::MEDPresentation(MEDPresentation::TypeID fieldHandlerId, const s
 
   // Python variables:
   int id = GeneratePythonId();
-  std::ostringstream oss_o, oss_d, oss_l, oss_s;
+  std::ostringstream oss_o, oss_d, oss_l, oss_s, oss_r;
   oss_o << "__obj" << id;
   oss_s << "__srcObj" << id;
   oss_d << "__disp" << id;
   oss_l << "__lut" << id;
+  oss_r << "__range" << id;
   _objVar = oss_o.str();
   _srcObjVar = oss_s.str();
   _dispVar = oss_d.str();
   _lutVar = oss_l.str();
+  _rangeVar = oss_r.str();
+}
+
+/**
+ * For most of the presentations the field name etc is required.
+ * For the MEDPresentationMeshView however, the handler ID is a mesh handler ID, not a field, and the
+ * treatment is specific.
+ */
+void
+MEDPresentation::initFieldMeshInfos()
+{
+  MEDCALC::MEDDataManager_ptr dataManager(MEDFactoryClient::getDataManager());
+  MEDCALC::FieldHandler* fieldHandler = dataManager->getFieldHandler(_handlerId);
+  MEDCALC::MeshHandler* meshHandler = dataManager->getMeshHandler(fieldHandler->meshid);
+  MEDCALC::DatasourceHandler* dataSHandler = dataManager->getDatasourceHandlerFromID(meshHandler->sourceid);
+
+  extractFileName(std::string(dataSHandler->uri));
+
+  _fieldName = fieldHandler->fieldname;
+  _mcFieldType = (MEDCoupling::TypeOfField) fieldHandler->type;
+  _pvFieldType = getPVFieldTypeString(_mcFieldType);
+  _colorByType = _pvFieldType;  // by default the same; overridden in DeflectionShape, VectorField, PointSprite and Contour
+  _meshName = meshHandler->name;
+}
+
+void
+MEDPresentation::extractFileName(const std::string& name)
+{
+  _fileName = name;
+  if (_fileName.substr(0, 7) != std::string("file://")) {
+    const char* msg = "MEDPresentation(): Data source is not a file! Can not proceed.";
+    STDLOG(msg);
+    throw MEDPresentationException(msg);
+  }
+  _fileName = _fileName.substr(7, _fileName.size());
 }
 
 MEDPresentation::~MEDPresentation()
@@ -92,12 +120,15 @@ MEDPresentation::~MEDPresentation()
   STDLOG("~MEDPresentation(): clear display");
   {
     MEDPyLockWrapper lock;
-    std::ostringstream oss_v, oss;
-    oss_v << "__view" << _renderViewPyId;
-    oss << "pvs.Hide(" << _objVar <<  ", view=" << oss_v.str() << ");";
-    oss << "pvs.Render();";
-
-    PyRun_SimpleString(oss.str().c_str());
+    std::ostringstream oss;
+
+    oss << "pvs.Hide(" << _objVar <<  ", view=" << getRenderViewVar() << ");";
+    execPyLine(oss.str());
+    // :TRICKY: The two following lines raise an exception when closing MED module
+    //          after sequence: MED - load file - PARAVIS - MED - close SALOME
+    //          (see Mantis #23461)
+    //execPyLine(getRenderViewVar() + ".ResetCamera();");
+    //execPyLine("pvs.Render();");
   }
 }
 
@@ -207,6 +238,7 @@ MEDPresentation::getIntProperty(const std::string& propName) const
  {
    MEDPyLockWrapper lock;
    pushAndExecPyLine( "import pvsimple as pvs;");
+   pushAndExecPyLine( "import medcalc");
  }
 
 
@@ -226,7 +258,7 @@ MEDPresentation::getPythonObjectFromMain(const char* python_var) const
 }
 
 std::string
-MEDPresentation::getFieldTypeString(MEDCoupling::TypeOfField fieldType) const
+MEDPresentation::getPVFieldTypeString(MEDCoupling::TypeOfField fieldType) const
 {
   switch(fieldType)
   {
@@ -234,8 +266,12 @@ MEDPresentation::getFieldTypeString(MEDCoupling::TypeOfField fieldType) const
       return "CELLS";
     case MEDCoupling::ON_NODES:
       return "POINTS";
+    case MEDCoupling::ON_GAUSS_PT:
+      return "POINTS"; // because internally after application of the ELGA filter, the field will appear as a POINT field
+    case MEDCoupling::ON_GAUSS_NE:
+      return "POINTS"; // because internally after application of the ELNO mesh filter, the field will appear as a POINT field
     default:
-      STDLOG("MEDPresentation::getFieldTypeString() -- Not implemented ! Gauss points?");
+      STDLOG("MEDPresentation::getPVFieldTypeString() -- Not implemented ! ELNO field?");
       return "";
   }
 }
@@ -248,14 +284,59 @@ MEDPresentation::getRenderViewVar() const
   return oss.str();
 }
 
+/*!
+ * Creates the MEDReader source in the pipeline, and potentially apply GAUSS/ELNO filters.
+ */
 void
 MEDPresentation::createSource()
 {
+  std::string typ;
+  switch(_mcFieldType) {
+    case MEDCoupling::ON_CELLS: typ = "P0"; break;
+    case MEDCoupling::ON_NODES: typ = "P1"; break;
+    case MEDCoupling::ON_GAUSS_PT: typ = "GAUSS"; break;
+    case MEDCoupling::ON_GAUSS_NE: typ = "GSSNE"; break;
+    default:
+      const char * msg ="MEDPresentation::createSource(): field type not impl. yet!";
+      STDLOG(msg);
+      throw KERNEL::createSalomeException(msg);
+  }
+
   std::ostringstream oss;
   oss << _srcObjVar << " = pvs.MEDReader(FileName='" << _fileName << "');";
   pushAndExecPyLine(oss.str()); oss.str("");
+  oss << "medcalc.SelectSourceField(" << _srcObjVar << ", '" << _meshName << "', '"
+      << _fieldName << "', '" << typ << "');";
+  pushAndExecPyLine(oss.str()); oss.str("");
+  // Generate complete vector fields: fields with 2 components will copied into <name>_vector and
+  // have a third null component added.
   oss << _srcObjVar << ".GenerateVectors = 1;";
   pushAndExecPyLine(oss.str()); oss.str("");
+
+  // Make sure this is set so we stick to time steps:
+  pushAndExecPyLine("pvs.GetAnimationScene().PlayMode = 'Snap To TimeSteps'");
+
+  // Deal with GAUSS fields:
+  if(_mcFieldType == MEDCoupling::ON_GAUSS_PT)
+    {
+      std::ostringstream oss, oss2;
+      oss2 << "__srcObj" << GeneratePythonId();
+      oss << oss2.str() << " = pvs.GaussPoints(Input=" << _srcObjVar << ");";
+      pushAndExecPyLine(oss.str()); oss.str("");
+      // Now the source becomes the result of the CellDatatoPointData:
+      _srcObjVar = oss2.str();
+      oss << _srcObjVar << ".SelectSourceArray = ['CELLS', 'ELGA@0'];";
+      pushAndExecPyLine(oss.str()); oss.str("");
+    }
+  if(_mcFieldType == MEDCoupling::ON_GAUSS_NE)
+    {
+      std::ostringstream oss, oss2;
+      oss2 << "__srcObj" << GeneratePythonId();
+      oss << oss2.str() << " = pvs.ELNOMesh(Input=" << _srcObjVar << ");";
+      pushAndExecPyLine(oss.str()); oss.str("");
+      // Now the source becomes the result of the CellDatatoPointData:
+      _srcObjVar = oss2.str();
+    }
 }
 
 void
@@ -268,7 +349,7 @@ MEDPresentation::setOrCreateRenderView()
   pushAndExecPyLine(oss2.str()); oss2.str("");
   if (_viewMode == MEDCALC::VIEW_MODE_OVERLAP) {
       // this might potentially re-assign to an existing view variable, but this is OK, we
-      // normally reassign exaclty the same RenderView object.
+      // normally reassign exactly the same RenderView object.
       oss2 << view << " = pvs.GetActiveViewOrCreate('RenderView');";
       pushAndExecPyLine(oss2.str()); oss2.str("");
   } else if (_viewMode == MEDCALC::VIEW_MODE_REPLACE) {
@@ -317,6 +398,32 @@ MEDPresentation::selectFieldComponent()
     }
 }
 
+/**
+ * Needs the LUT, so to be called after selectColorMap for the first time.
+ */
+void
+MEDPresentation::scalarBarTitle()
+{
+  // get selected component name:
+  std::string compoName;
+  if (_selectedComponentIndex != -1)
+    {
+      std::ostringstream oss1;
+      oss1 << MEDPresentation::PROP_COMPONENT << _selectedComponentIndex;
+      compoName = getStringProperty(oss1.str());
+    }
+  else
+    {
+      if (getIntProperty(MEDPresentation::PROP_NB_COMPONENTS) == 1)
+        compoName = "";
+      else
+        compoName = "Magnitude";
+    }
+  std::ostringstream oss;
+  oss << "pvs.GetScalarBar(" << _lutVar << ").ComponentTitle = '" << compoName << "';";
+  pushAndExecPyLine(oss.str()); oss.str("");
+}
+
 void
 MEDPresentation::selectColorMap()
 {
@@ -337,6 +444,8 @@ MEDPresentation::selectColorMap()
     throw KERNEL::createSalomeException("MEDPresentation::getColorMapCommand(): invalid colormap!");
   }
   pushAndExecPyLine(oss.str());
+
+  selectFieldComponent(); // somehow PV keeps the LUT parameters of the previous presentation, so better reset this.
 }
 
 void
@@ -356,32 +465,40 @@ MEDPresentation::showScalarBar()
 }
 
 void
-MEDPresentation::colorBy(const std::string & fieldType)
+MEDPresentation::colorBy()
 {
   std::ostringstream oss;
-  oss << "pvs.ColorBy(" << _dispVar << ", ('" << fieldType << "', '" << _fieldName << "'));";
+  oss << "pvs.ColorBy(" << _dispVar << ", ('" << _colorByType << "', '" << _fieldName << "'));";
   pushAndExecPyLine(oss.str());
 }
 
 void
 MEDPresentation::rescaleTransferFunction()
 {
-  std::string ret;
+  std::ostringstream oss;
   switch(_sbRange)
   {
     case MEDCALC::SCALAR_BAR_ALL_TIMESTEPS:
-      ret = _dispVar + ".RescaleTransferFunctionToDataRangeOverTime();";
+      oss << _dispVar << ".RescaleTransferFunctionToDataRangeOverTime();";
       break;
     case MEDCALC::SCALAR_BAR_CURRENT_TIMESTEP:
-      ret = _dispVar + ".RescaleTransferFunctionToDataRange(False);";
+      oss << _dispVar << ".RescaleTransferFunctionToDataRange(False);";
       break;
     default:
       STDLOG("MEDPresentation::getRescaleCommand(): invalid range!");
       throw KERNEL::createSalomeException("MEDPresentation::getRescaleCommand(): invalid range!");
   }
-  pushAndExecPyLine(ret);
+  pushAndExecPyLine(oss.str()); oss.str("");
+  // Get min-max
+  oss << _rangeVar << " = [" << _dispVar << ".LookupTable.RGBPoints[0], " << _dispVar << ".LookupTable.RGBPoints[-4]];";
+  pushAndExecPyLine(oss.str());
+
+  // Adapt scalar bar title
+  scalarBarTitle();
 }
 
+
+
 int
 MEDPresentation::GeneratePythonId()
 {
@@ -389,13 +506,46 @@ MEDPresentation::GeneratePythonId()
   return INIT_ID++;
 }
 
-void
+bool
 MEDPresentation::activateView()
 {
   MEDPyLockWrapper lock;
-  pushAndExecPyLine("pvs.SetActiveView(" + getRenderViewVar() + ");");
+
+  execPyLine("__alive = " + getRenderViewVar() + " in pvs.GetRenderViews()");
+  PyObject * obj = getPythonObjectFromMain("__alive");
+  bool alive = true;
+  if (obj && PyBool_Check(obj))
+    alive = (obj == Py_True);
+
+  if (alive)
+    // The view is still there,just activate it:
+    pushAndExecPyLine("pvs.SetActiveView(" + getRenderViewVar() + ");");
+  else
+    {
+      // The view disappeared, recreate it in a new layout. The transfer of the objects is to be done by the caller.
+      std::ostringstream oss;
+      oss <<  "pvs.servermanager.misc.ViewLayout(registrationGroup='layouts');";
+      pushAndExecPyLine(oss.str()); oss.str("");
+      oss << getRenderViewVar() << " = pvs.CreateView('RenderView');";
+      pushAndExecPyLine(oss.str()); oss.str("");
+    }
+  return alive;
 }
 
+/**!
+ * Called when the view has been recreated (because the user closed it).
+ * All the objects and set up are re-shown in the new view (which is stored in the same Python variable).
+ */
+void
+MEDPresentation::recreateViewSetup()
+{
+  showObject();
+  colorBy();
+  showScalarBar();
+  selectColorMap();
+  rescaleTransferFunction();
+  resetCameraAndRender();
+}
 
 std::string
 MEDPresentation::paravisDump() const
@@ -424,14 +574,14 @@ MEDPresentation::fillAvailableFieldComponents()
   MEDPyLockWrapper lock;  // GIL!
   std::string typ;
 
-  if(_fieldType == "CELLS") {
+  if(_pvFieldType == "CELLS") {
       typ = "CellData";
   }
-  else if (_fieldType == "POINTS") {
+  else if (_pvFieldType == "POINTS") {
       typ = "PointData";
   }
   else {
-      std::string msg("Unsupported spatial discretisation: " + _fieldType);
+      std::string msg("Unsupported spatial discretisation: " + _pvFieldType);
       STDLOG(msg);
       throw KERNEL::createSalomeException(msg.c_str());
   }
@@ -441,8 +591,8 @@ MEDPresentation::fillAvailableFieldComponents()
   execPyLine(oss.str());
   PyObject* p_obj = getPythonObjectFromMain("__nbCompo");
   long nbCompo;
-  if (p_obj && PyInt_Check(p_obj))
-    nbCompo = PyInt_AS_LONG(p_obj);
+  if (p_obj && PyLong_Check(p_obj))
+    nbCompo = PyLong_AS_LONG(p_obj);
   else
     {
       STDLOG("Unexpected Python error");
@@ -456,8 +606,8 @@ MEDPresentation::fillAvailableFieldComponents()
       execPyLine(oss2.str());
       PyObject* p_obj = getPythonObjectFromMain("__compo");
       std::string compo;
-      if (p_obj && PyString_Check(p_obj))
-        compo = std::string(PyString_AsString(p_obj));  // pointing to internal Python memory, so make a copy!!
+      if (p_obj && PyUnicode_Check(p_obj))
+        compo = std::string(Py_EncodeLocale(PyUnicode_AS_UNICODE(p_obj), NULL));  // pointing to internal Python memory, so make a copy!!
       else
         {
           STDLOG("Unexpected Python error");
@@ -476,90 +626,60 @@ MEDPresentation::fillAvailableFieldComponents()
 void
 MEDPresentation::applyCellToPointIfNeeded()
 {
-  std::ostringstream oss, oss2;
-  // Apply Cell data to point data:
-  oss2 << "__srcObj" << GeneratePythonId();
-  oss << oss2.str() << " = pvs.CellDatatoPointData(Input=" << _srcObjVar << ");";
-  pushAndExecPyLine(oss.str()); oss.str("");
-  // Now the source becomes the result of the CellDatatoPointData:
-  _srcObjVar = oss2.str();
-}
-
-/**
- * Convert a vector field into a 3D vector field:
- *  - if the vector field is already 3D, nothing to do
- *  - if it is 2D, then add a null component
- *  - otherwise (tensor field, scalar field) throw
- */
-void
-MEDPresentation::convertTo3DVectorField()
-{
-  std::ostringstream oss, oss1, oss2, oss3;
-
-  int nbCompo = getIntProperty(MEDPresentation::PROP_NB_COMPONENTS);
-  if (nbCompo < 2 || nbCompo > 3)
-    {
-      oss << "The field '" << _fieldName << "' must have 2 or 3 components for this presentation!";
-      STDLOG(oss.str());
-      throw KERNEL::createSalomeException(oss.str().c_str());
-    }
-  if (nbCompo == 3)
-    return;
-
-  // Apply calculator:
-  oss2 << "__srcObj" << GeneratePythonId();
-  oss << oss2.str() << " = pvs.Calculator(Input=" << _srcObjVar << ");";
-  pushAndExecPyLine(oss.str()); oss.str("");
-  // Now the source becomes the result of the CellDatatoPointData:
-  _srcObjVar = oss2.str();
-  std::string typ;
-  if(_fieldType == "CELLS")
-    typ = "Cell Data";
-  else if(_fieldType == "POINTS")
-    typ = "Point Data";
-  else
+  if (_pvFieldType == "CELLS")
     {
-      oss3 << "Field '" << _fieldName << "' has invalid field type";
-      STDLOG(oss3.str());
-      throw KERNEL::createSalomeException(oss3.str().c_str());
+      std::ostringstream oss, oss2;
+      // Apply Cell data to point data:
+      oss2 << "__srcObj" << GeneratePythonId();
+      oss << oss2.str() << " = pvs.CellDatatoPointData(Input=" << _srcObjVar << ");";
+      pushAndExecPyLine(oss.str()); oss.str("");
+      // Now the source becomes the result of the CellDatatoPointData:
+      _srcObjVar = oss2.str();
     }
-  oss << _srcObjVar << ".AttributeMode = '" <<  typ << "';";
-  pushAndExecPyLine(oss.str()); oss.str("");
-  oss << _srcObjVar << ".ResultArrayName = '" <<  _fieldName << "_CALC';";  // will never be needed I think
-  pushAndExecPyLine(oss.str()); oss.str("");
-  oss << _srcObjVar << ".Function = '" <<  _fieldName << "_0*iHat + " << _fieldName << "_1*jHat + 0.0*zHat';";
-  pushAndExecPyLine(oss.str()); oss.str("");
 }
 
-//double
-//MEDPresentation::computeCellAverageSize()
+///**
+// * Convert a vector field into a 3D vector field:
+// *  - if the vector field is already 3D, nothing to do
+// *  - if it is 2D, then add a null component
+// *  - otherwise (tensor field, scalar field) throw
+// */
+//void
+//MEDPresentation::convertTo3DVectorField()
 //{
-//  std::ostringstream oss;
-//  oss << "import MEDLoader;";
-//  pushAndExecPyLine(oss.str()); oss.str("");
-//  oss << "__mesh = MEDLoader.ReadMeshFromFile('" << _fileName << "', '" << _meshName << "');";
-//  pushAndExecPyLine(oss.str()); oss.str("");
+//  std::ostringstream oss, oss1, oss2, oss3;
+//
+//  int nbCompo = getIntProperty(MEDPresentation::PROP_NB_COMPONENTS);
+//  if (nbCompo < 2 || nbCompo > 3)
+//    {
+//      oss << "The field '" << _fieldName << "' must have 2 or 3 components for this presentation!";
+//      STDLOG(oss.str());
+//      throw KERNEL::createSalomeException(oss.str().c_str());
+//    }
+//  if (nbCompo == 3)
+//    return;
 //
-//  oss << "__bb = __mesh.getBoundingBox()";
+//  // Apply calculator:
+//  oss2 << "__srcObj" << GeneratePythonId();
+//  oss << oss2.str() << " = pvs.Calculator(Input=" << _srcObjVar << ");";
 //  pushAndExecPyLine(oss.str()); oss.str("");
-//  oss << "__deltas = [x[1]-x[0] for x in __bb];";
+//  // Now the source becomes the result of the CellDatatoPointData:
+//  _srcObjVar = oss2.str();
+//  std::string typ;
+//  if(_pvFieldType == "CELLS")
+//    typ = "Cell Data";
+//  else if(_pvFieldType == "POINTS")
+//    typ = "Point Data";
+//  else
+//    {
+//      oss3 << "Field '" << _fieldName << "' has invalid field type";
+//      STDLOG(oss3.str());
+//      throw KERNEL::createSalomeException(oss3.str().c_str());
+//    }
+//  oss << _srcObjVar << ".AttributeMode = '" <<  typ << "';";
 //  pushAndExecPyLine(oss.str()); oss.str("");
-//  oss << "__vol = reduce(lambda x,y:x*y, __deltas, 1.0);";
+//  oss << _srcObjVar << ".ResultArrayName = '" <<  _fieldName << "_CALC';";  // will never be needed I think
 //  pushAndExecPyLine(oss.str()); oss.str("");
-//  // Average cell size is the the n-th root of average volume of a cell, with n being the space dimension
-//  oss << "__cellSize = (__vol/__mesh.getNumberOfCells())**(1.0/len(__bb));";
+//  oss << _srcObjVar << ".Function = '" <<  _fieldName << "_0*iHat + " << _fieldName << "_1*jHat + 0.0*zHat';";
 //  pushAndExecPyLine(oss.str()); oss.str("");
-//
-//  PyObject * pyObj = getPythonObjectFromMain("__cellSize");
-//  bool err = false;
-//  if (!pyObj || !PyFloat_Check(pyObj)) {  /* nothing to do, err handler below */}
-//  else {
-//      double ret= PyFloat_AsDouble(pyObj);
-//      if(!PyErr_Occurred())
-//        return ret;
-//    }
-//  // From here, an error for sure.
-//  const char * msg = "MEDPresentation::computeCellAverageSize(): Python error.";
-//  STDLOG(msg);
-//  throw KERNEL::createSalomeException(msg);
 //}