Salome HOME
22359: Body Fitting algorithm: grid orientation
authoreap <eap@opencascade.com>
Tue, 4 Feb 2014 13:02:26 +0000 (13:02 +0000)
committereap <eap@opencascade.com>
Tue, 4 Feb 2014 13:02:26 +0000 (13:02 +0000)
22358: Body Fitting algorithm: origin point of grid defined by spacing

15 files changed:
doc/salome/examples/cartesian_algo.py
doc/salome/gui/SMESH/images/cartesian3D_hyp.png
doc/salome/gui/SMESH/images/cartesian_implement_edge.png [new file with mode: 0644]
doc/salome/gui/SMESH/input/cartesian_algo.doc
idl/SMESH_BasicHypothesis.idl
src/SMESH_I/SMESH_2smeshpy.cxx
src/SMESH_SWIG/StdMeshersBuilder.py
src/StdMeshers/StdMeshers_CartesianParameters3D.cxx
src/StdMeshers/StdMeshers_CartesianParameters3D.hxx
src/StdMeshers/StdMeshers_Cartesian_3D.cxx
src/StdMeshersGUI/StdMeshersGUI_CartesianParamCreator.cxx
src/StdMeshersGUI/StdMeshersGUI_CartesianParamCreator.h
src/StdMeshersGUI/StdMeshers_msg_en.ts
src/StdMeshers_I/StdMeshers_CartesianParameters3D_i.cxx
src/StdMeshers_I/StdMeshers_CartesianParameters3D_i.hxx

index dc0dc23..e5651cb 100644 (file)
@@ -50,3 +50,40 @@ print "nb hexahedra",mesh.NbHexas()
 print "nb tetrahedra",mesh.NbTetras()
 print "nb polyhedra",mesh.NbPolyhedrons()
 print
+
+# Example of customization of dirtections of the grid axes
+
+# make a box with non-orthogonal edges
+xDir = geompy.MakeVectorDXDYDZ( 1.0, 0.1, 0.0, "xDir" )
+yDir = geompy.MakeVectorDXDYDZ(-0.1, 1.0, 0.0, "yDir"  )
+zDir = geompy.MakeVectorDXDYDZ( 0.2, 0.3, 1.0, "zDir"  )
+face = geompy.MakePrismVecH( xDir, yDir, 1.0 )
+box  = geompy.MakePrismVecH( face, zDir, 1.0, theName="box" )
+
+spc = "0.1" # spacing
+
+# default axes
+mesh = smesh.Mesh( box, "custom axes")
+algo = mesh.BodyFitted()
+algo.SetGrid( spc, spc, spc, 10000 )
+mesh.Compute()
+print "Default axes"
+print "   nb hex:",mesh.NbHexas()
+
+# set axes using edges of the box
+algo.SetAxesDirs( xDir, [-0.1,1,0], zDir )
+mesh.Compute()
+print "Manual axes"
+print "   nb hex:",mesh.NbHexas()
+
+# set optimal orthogonal axes
+algo.SetOptimalAxesDirs( isOrthogonal=True )
+mesh.Compute()
+print "Optimal orthogonal axes"
+print "   nb hex:",mesh.NbHexas()
+
+# set optimal non-orthogonal axes
+algo.SetOptimalAxesDirs( isOrthogonal=False )
+mesh.Compute()
+print "Optimal non-orthogonal axes"
+print "   nb hex:",mesh.NbHexas()
index b8373ed..c9a605f 100644 (file)
Binary files a/doc/salome/gui/SMESH/images/cartesian3D_hyp.png and b/doc/salome/gui/SMESH/images/cartesian3D_hyp.png differ
diff --git a/doc/salome/gui/SMESH/images/cartesian_implement_edge.png b/doc/salome/gui/SMESH/images/cartesian_implement_edge.png
new file mode 100644 (file)
index 0000000..59ac9ba
Binary files /dev/null and b/doc/salome/gui/SMESH/images/cartesian_implement_edge.png differ
index 3240150..dfb9aca 100644 (file)
@@ -31,7 +31,7 @@ nodes are inside and some outside. </li>
 To apply this algorithm when you define your mesh, select <b>Body
   Fitting</b> in the list of 3D algorithms and click <em> "Add
   Hypothesis" </em> button and <em>"Body Fitting Parameters"</em>" menu
-  item. Dialog of <b>Body Fitting Parameters
+item. Dialog of <b>Body Fitting Parameters
   hypothesis</b> will appear.
 
 <br>
@@ -42,34 +42,64 @@ To apply this algorithm when you define your mesh, select <b>Body
 
 This dialog allows to define
 <ul>
-<li>\b Name of the algorithm </li>
-<li> Minimal size of a cell truncated by the geometry boundary. If the
-  size of a truncated grid cell is \b Threshold times less than a
-  initial cell size, then a mesh element is not created. </li>
-<li> Cartesian structured grid. Each grid axis is defined
-  individually. <b> Definition mode </b> chooses a way of grid
-  definition: <ul> 
-  <li> You can specify the \b Coordinates of grid nodes. \b Insert button
-    inserts a node at distance \b Step (negative or positive) from a
-    selected node. \b Delete button removes a selected node. Double
-    click on a coordinate in the list enables its edition. A grid
-    defined by \b Coordinates should enclose the geometry, else the
-    algorithm will fail. </li>
-  <li> You can define the \b Spacing of a grid as an algebraic formula 
-    <em>f(t)</em> where \a t is a position along a grid axis
-    normalized at [0.0,1.0]. The whole range of geometry can be
-    divided into sub-ranges with their own spacing formulas to apply;
+  <li>\b Name of the algorithm. </li>
+  <li> Minimal size of a cell truncated by the geometry boundary. If the
+    size of a truncated grid cell is \b Threshold times less than a
+    initial cell size, then a mesh element is not created. </li>
+  <li> <b> Implement Edges </b> check-box activates incorporation of
+  geometrical edges in the mesh.
+\image html cartesian_implement_edge.png "'Implement Edges' switched off (left) and on (right)"
+  <li> Cartesian structured grid. Location of nodes along each grid axis
+    is defined individually. <b> Definition mode </b> chooses a way of
+    grid definition:
+    <ul>
+      <li> You can specify the \b Coordinates of grid nodes. \b Insert button
+        inserts a node at distance \b Step (negative or positive) from a
+        selected node. \b Delete button removes a selected node. Double
+        click on a coordinate in the list enables its edition. 
+        \b Note that node coordinates are measured along directions of
+        axes that can differ from the directions of the Global Coordinate
+        System.</li>
+      <li> You can define the \b Spacing of a grid as an algebraic formula
+        <em>f(t)</em> where \a t is a position along a grid axis
+        normalized at [0.0,1.0]. The whole range of geometry can be
+        divided into sub-ranges with their own spacing formulas to apply;
     \a t varies between 0.0 and 1.0 within each sub-range. \b Insert button
-    divides a selected range into two ones. \b Delete button adds the
-    selected sub-range to the previous one. Double click on a range in
-    the list enables edition of its right boundary. Double click on a
-    function in the list enables its edition. 
-  </li> </ul>
-</li>
+        divides a selected range into two ones. \b Delete button adds the
+        selected sub-range to the previous one. Double click on a range in
+        the list enables edition of its right boundary. Double click on a
+        function in the list enables its edition.
+    </li> </ul>
+  </li>
+  <li> Coordinates of a <b> Fixed Point</b>. They allow to exactly
+    locate a grid node in a direction defined by spacing. If all the three
+    directions are defined by spacing, then there will be a mesh node at
+    the <b> Fixed Point</b>. If two directions are defined by spacing,
+    then there will be at least a link between mesh nodes passing through
+    the <b> Fixed Point</b>. If only one direction is defined by spacing,
+    then there will be at least an element facet passing through
+    the <b> Fixed Point</b>. If no directions are defined by spacing,
+    <b> Fixed Point</b> is disabled.</li>
+  <li> <b> Directions of Axes</b>. You can set up almost any
+    directions of grid axes that can help in generation as many as
+    possible hexahedral elements.
+    <ul>
+      <li><b> Orthogonal Axes </b> check-box, if activated, keeps the
+        axes orthogonal during their modification. </li>
+      <li> Selection buttons enable snapping corresponding axes to
+        direction of a geometrical edge selected in the Object
+        Browser. Edge direction is defined by coordinates of its end
+        points.</li>
+      <li><b> Optimal Axes</b> button runs an algorithm that tries to
+        set the axes so that a number of generated hexahedra to be
+        maximal.</li>
+      <li><b> Reset </b> button returns the axes in a default position
+      parallel to the axes of the Global Coordinate System.</li> 
+  </ul></li>
 </ul>
 
 <br>
-<b>See Also</b> a sample TUI Script of a 
-\ref tui_cartesian_algo "Usage of Body Fitting algorithm".  
+<b>See Also</b> a sample TUI Script of a
+\ref tui_cartesian_algo "Usage of Body Fitting algorithm".
 
 */
index 186b066..27cfbd3 100644 (file)
@@ -942,8 +942,9 @@ module StdMeshers
   /*!
    * interface of "Body fitting Parameters" hypothesis.
    * This hypothesis specifies 
-   * - Definition of the Cartesian grid
    * - Size threshold
+   * - Definition of the Cartesian grid
+   * - Direction of grid axes
    */
   interface StdMeshers_CartesianParameters3D : SMESH::SMESH_Hypothesis
   {
@@ -965,8 +966,8 @@ module StdMeshers
     /*!
      * Set coordinates of nodes along an axis (countered from zero)
      */
-    void SetGrid(in SMESH::double_array coords,
-                 in short               axis) raises (SALOME::SALOME_Exception);
+    void SetGrid(in SMESH::double_array  coords,
+                 in short                axis) raises (SALOME::SALOME_Exception);
     SMESH::double_array GetGrid(in short axis) raises (SALOME::SALOME_Exception);
 
     /*!
@@ -986,6 +987,22 @@ module StdMeshers
                         out SMESH::double_array internalPoints,
                         in short                axis) raises (SALOME::SALOME_Exception);
     /*!
+     * Set custom direction of axes
+     */
+    void SetAxesDirs(in SMESH::DirStruct x,
+                     in SMESH::DirStruct y,
+                     in SMESH::DirStruct z ) raises (SALOME::SALOME_Exception);
+    void GetAxesDirs(out SMESH::DirStruct x,
+                     out SMESH::DirStruct y,
+                     out SMESH::DirStruct z );
+    /*!
+     * Set/unset a fixed point, at which a node will be created provided that grid
+     * is defined by spacing in all directions
+     */
+    void SetFixedPoint(in SMESH::PointStruct p, in boolean toUnset);
+    boolean GetFixedPoint(out SMESH::PointStruct p);
+
+    /*!
      * Enables implementation of geometrical edges into the mesh. If this feature
      * is disabled, sharp edges of the shape are lost ("smoothed") in the mesh if
      * they don't coincide with the grid lines
@@ -994,6 +1011,16 @@ module StdMeshers
     boolean GetToAddEdges();
 
     /*!
+     * Returns axes at which a number of generated hexahedra is maximal
+     */
+    void ComputeOptimalAxesDirs(in GEOM::GEOM_Object shape,
+                                in boolean           isOrthogonal,
+                                out SMESH::DirStruct x,
+                                out SMESH::DirStruct y,
+                                out SMESH::DirStruct z ) 
+      raises (SALOME::SALOME_Exception);
+
+    /*!
      * \brief Computes node coordinates by spacing functions
      *  \param x0 - lower coordinate
      *  \param x1 - upper coordinate
index 15e2a18..b8358a7 100644 (file)
@@ -2632,6 +2632,8 @@ Handle(_pyHypothesis) _pyHypothesis::NewHypothesis( const Handle(_pyCommand)& th
     hyp->SetConvMethodAndType( "SetGrid", "Cartesian_3D");
     for ( int iArg = 0; iArg < 4; ++iArg )
       hyp->setCreationArg( iArg+1, "[]");
+    hyp->AddAccumulativeMethod( "SetGrid" );
+    hyp->AddAccumulativeMethod( "SetGridSpacing" );
   }
   else
   {
@@ -3114,7 +3116,9 @@ void _pyComplexParamHypo::Process( const Handle(_pyCommand)& theCommand)
         myCurCrMethod->myArgs[ iArg ] += "]";
       }
       myArgCommands.push_back( theCommand );
-      rememberCmdOfParameter( theCommand );
+      //rememberCmdOfParameter( theCommand ); -- these commands are marked as
+      //                                  accumulative, else, if the creation
+      //                 is not converted, commands for axes 1 and 2 are lost
       return;
     }
   }
index 4e5b2d2..265a980 100644 (file)
@@ -597,7 +597,7 @@ class StdMeshersBuilder_Quadrangle(Mesh_Algorithm):
     #                    must be created by the mesher. Shapes can be of any type,
     #                    vertices of given shapes define positions of enforced nodes.
     #                    Only vertices successfully projected to the face are used.
-    #  @param enfPoint: list of points giving positions of enforced nodes.
+    #  @param enfPoints: list of points giving positions of enforced nodes.
     #                    Point can be defined either as SMESH.PointStruct's
     #                    ([SMESH.PointStruct(x1,y1,z1), SMESH.PointStruct(x2,y2,z2),...])
     #                    or triples of values ([[x1,y1,z1], [x2,y2,z2], ...]).
@@ -1415,7 +1415,7 @@ class StdMeshersBuilder_Cartesian_3D(Mesh_Algorithm):
         if not self.mesh.IsUsedHypothesis( self.hyp, self.geom ):
             self.mesh.AddHypothesis( self.hyp, self.geom )
 
-        for axis, gridDef in enumerate( [xGridDef, yGridDef, zGridDef]):
+        for axis, gridDef in enumerate( [xGridDef, yGridDef, zGridDef] ):
             if not gridDef: raise ValueError, "Empty grid definition"
             if isinstance( gridDef, str ):
                 self.hyp.SetGridSpacing( [gridDef], [], axis )
@@ -1429,6 +1429,64 @@ class StdMeshersBuilder_Cartesian_3D(Mesh_Algorithm):
         self.hyp.SetSizeThreshold( sizeThreshold )
         return self.hyp
 
+    ## Defines custom directions of axes of the grid
+    #  @param xAxis either SMESH.DirStruct or a vector, or 3 vector components
+    #  @param yAxis either SMESH.DirStruct or a vector, or 3 vector components
+    #  @param zAxis either SMESH.DirStruct or a vector, or 3 vector components
+    def SetAxesDirs( self, xAxis, yAxis, zAxis ):
+        import GEOM
+        if hasattr( xAxis, "__getitem__" ):
+            xAxis = self.mesh.smeshpyD.MakeDirStruct( xAxis[0],xAxis[1],xAxis[2] )
+        elif isinstance( xAxis, GEOM._objref_GEOM_Object ):
+            xAxis = self.mesh.smeshpyD.GetDirStruct( xAxis )
+        if hasattr( yAxis, "__getitem__" ):
+            yAxis = self.mesh.smeshpyD.MakeDirStruct( yAxis[0],yAxis[1],yAxis[2] )
+        elif isinstance( yAxis, GEOM._objref_GEOM_Object ):
+            yAxis = self.mesh.smeshpyD.GetDirStruct( yAxis )
+        if hasattr( zAxis, "__getitem__" ):
+            zAxis = self.mesh.smeshpyD.MakeDirStruct( zAxis[0],zAxis[1],zAxis[2] )
+        elif isinstance( zAxis, GEOM._objref_GEOM_Object ):
+            zAxis = self.mesh.smeshpyD.GetDirStruct( zAxis )
+        if not self.hyp:
+            self.hyp = self.Hypothesis("CartesianParameters3D")
+        if not self.mesh.IsUsedHypothesis( self.hyp, self.geom ):
+            self.mesh.AddHypothesis( self.hyp, self.geom )
+        self.hyp.SetAxesDirs( xAxis, yAxis, zAxis )
+        return self.hyp
+
+    ## Automatically defines directions of axes of the grid at which
+    #  a number of generated hexahedra is maximal
+    #  @param isOrthogonal defines whether the axes mush be orthogonal
+    def SetOptimalAxesDirs(self, isOrthogonal=True):
+        if not self.hyp:
+            self.hyp = self.Hypothesis("CartesianParameters3D")
+        if not self.mesh.IsUsedHypothesis( self.hyp, self.geom ):
+            self.mesh.AddHypothesis( self.hyp, self.geom )
+        x,y,z = self.hyp.ComputeOptimalAxesDirs( self.geom, isOrthogonal )
+        self.hyp.SetAxesDirs( x,y,z )
+        return self.hyp
+
+    ## Sets/unsets a fixed point. The algorithm makes a plane of the grid pass
+    #  through the fixed point in each direction at which the grid is defined
+    #  by spacing
+    #  @param p coordinates of the fixed point. Either SMESH.PointStruct or
+    #         3 components of coordinates.
+    #  @param toUnset defines whether the fixed point is defined or removed.
+    def SetFixedPoint( self, p, toUnset=False ):
+        import SMESH
+        if toUnset:
+            if not self.hyp: return
+            p = SMESH.PointStruct(0,0,0)
+        if hasattr( p, "__getitem__" ):
+            p = SMESH.PointStruct( p[0],p[1],p[2] )
+        if not self.hyp:
+            self.hyp = self.Hypothesis("CartesianParameters3D")
+        if not self.mesh.IsUsedHypothesis( self.hyp, self.geom ):
+            self.mesh.AddHypothesis( self.hyp, self.geom )
+        self.hyp.SetFixedPoint( p, toUnset )
+        return self.hyp
+        
+
     pass # end of StdMeshersBuilder_Cartesian_3D class
 
 ## Defines a stub 1D algorithm, which enables "manual" creation of nodes and
index 369d4e4..00cd861 100644 (file)
 
 #include "utilities.h"
 
+#include <map>
 #include <limits>
 
+#include <BRepGProp.hxx>
+#include <BRep_Tool.hxx>
 #include <Bnd_Box.hxx>
+#include <GProp_GProps.hxx>
+#include <GeomLib_IsPlanarSurface.hxx>
+#include <Geom_Surface.hxx>
 #include <Precision.hxx>
+#include <TopExp_Explorer.hxx>
+#include <TopLoc_Location.hxx>
+#include <TopTools_MapIteratorOfMapOfShape.hxx>
+#include <TopTools_MapOfShape.hxx>
+#include <TopoDS.hxx>
+#include <TopoDS_Face.hxx>
+#include <gp_Dir.hxx>
+#include <gp_Pln.hxx>
 #include <gp_Vec.hxx>
 
 using namespace std;
@@ -66,6 +80,11 @@ StdMeshers_CartesianParameters3D::StdMeshers_CartesianParameters3D(int         h
   _axisDirs[6] = 0.;
   _axisDirs[7] = 0.;
   _axisDirs[8] = 1.;
+
+  _fixedPoint[0] = 0.;
+  _fixedPoint[1] = 0.;
+  _fixedPoint[2] = 0.;
+  SetFixedPoint( _fixedPoint, /*toUnset=*/true );
 }
 
 
@@ -73,6 +92,22 @@ namespace
 {
   const char* axisName[3] = { "X", "Y", "Z" };
 
+  typedef std::pair< double, std::pair< double, double > > TCooTriple;
+
+#define gpXYZ( cTriple ) gp_XYZ( (cTriple).first, (cTriple).second.first, (cTriple).second.second )
+
+  //================================================================================
+  /*!
+   * \brief Compare two normals
+   */
+  //================================================================================
+
+  bool sameDir( const TCooTriple& n1, const TCooTriple& n2 )
+  {
+    gp_XYZ xyz1 = gpXYZ( n1 ), xyz2 = gpXYZ( n2 );
+    return ( xyz1 - xyz2 ).Modulus() < 0.01;
+  }
+
   //================================================================================
   /*!
    * \brief Checks validity of an axis index, throws in case of invalidity
@@ -178,6 +213,36 @@ void StdMeshers_CartesianParameters3D::SetGridSpacing(std::vector<string>& xSpac
 }
 
 //=======================================================================
+//function : SetFixedPoint
+//purpose  : * Set/unset a fixed point, at which a node will be created provided that grid
+//           * is defined by spacing in all directions
+//=======================================================================
+
+void StdMeshers_CartesianParameters3D::SetFixedPoint(const double p[3], bool toUnset)
+{
+  if ( toUnset != Precision::IsInfinite( _fixedPoint[0] ))
+    NotifySubMeshesHypothesisModification();
+
+  if ( toUnset )
+    _fixedPoint[0] = Precision::Infinite();
+  else
+    std::copy( &p[0], &p[0]+3, &_fixedPoint[0] );
+}
+
+//=======================================================================
+//function : GetFixedPoint
+//purpose  : Returns either false or (true + point coordinates)
+//=======================================================================
+
+bool StdMeshers_CartesianParameters3D::GetFixedPoint(double p[3])
+{
+  if ( Precision::IsInfinite( _fixedPoint[0] ))
+    return false;
+  std::copy( &_fixedPoint[0], &_fixedPoint[0]+3, &p[0] );
+}
+
+
+//=======================================================================
 //function : SetSizeThreshold
 //purpose  : Set size threshold
 //=======================================================================
@@ -323,8 +388,198 @@ void StdMeshers_CartesianParameters3D::GetCoordinates(std::vector<double>& xNode
 }
 
 //=======================================================================
+//function : ComputeOptimalAxesDirs
+//purpose  : Returns axes at which number of hexahedra is maximal
+//=======================================================================
+
+void StdMeshers_CartesianParameters3D::
+ComputeOptimalAxesDirs(const TopoDS_Shape& shape,
+                       const bool          isOrthogonal,
+                       double              dirCoords[9])
+{
+  for ( int i = 0; i < 9; ++i ) dirCoords[i] = 0.;
+  dirCoords[0] = dirCoords[4] = dirCoords[8] = 1.;
+
+  if ( shape.IsNull() ) return;
+
+  TopLoc_Location loc;
+  TopExp_Explorer exp;
+
+  // get external FACEs of the shape
+  TopTools_MapOfShape faceMap;
+  for ( exp.Init( shape, TopAbs_FACE ); exp.More(); exp.Next() )
+    if ( !faceMap.Add( exp.Current() ))
+      faceMap.Remove( exp.Current() );
+
+  // sort areas of planar faces by normal direction
+
+  std::multimap< TCooTriple, double > areasByNormal;
+
+  TopTools_MapIteratorOfMapOfShape fIt ( faceMap );
+  for ( ; fIt.More(); fIt.Next() )
+  {
+    const TopoDS_Face&   face = TopoDS::Face( fIt.Key() );
+    Handle(Geom_Surface) surf = BRep_Tool::Surface( face, loc );
+    if ( surf.IsNull() ) continue;
+
+    GeomLib_IsPlanarSurface check( surf, 1e-5 );
+    if ( !check.IsPlanar() ) continue;
+
+    GProp_GProps SProps;
+    BRepGProp::SurfaceProperties( face, SProps );
+    double area = SProps.Mass();
+
+    gp_Pln pln  = check.Plan();
+    gp_Dir norm = pln.Axis().Direction().Transformed( loc );
+    if ( norm.X() < -1e-3 ) { // negative X
+      norm.Reverse();
+    } else if ( norm.X() < 1e-3 ) { // zero X
+      if ( norm.Y() < -1e-3 ) { // negative Y
+        norm.Reverse();
+      } else if ( norm.Y() < 1e-3 ) { // zero X && zero Y
+        if ( norm.Y() < -1e-3 ) // negative Z
+          norm.Reverse();
+      }
+    }
+    TCooTriple coo3( norm.X(), make_pair( norm.Y(), norm.Z() ));
+    areasByNormal.insert( make_pair( coo3, area ));
+  }
+
+  // group coplanar normals and sort groups by sum area
+
+  std::multimap< double, vector< const TCooTriple* > > normsByArea;
+  std::multimap< TCooTriple, double >::iterator norm2a = areasByNormal.begin();
+  const TCooTriple*           norm1 = 0;
+  double                      sumArea = 0;
+  vector< const TCooTriple* > norms;
+  for ( int iF = 1; norm2a != areasByNormal.end(); ++norm2a, ++iF )
+  {
+
+    if ( !norm1 || !sameDir( *norm1, norm2a->first ))
+    {
+      if ( !norms.empty() )
+      {
+        normsByArea.insert( make_pair( sumArea, norms ));
+        norms.clear();
+      }
+      norm1   = & norm2a->first;
+      sumArea = norm2a->second;
+      norms.push_back( norm1 );
+    }
+    else
+    {
+      sumArea += norm2a->second;
+      norms.push_back( & norm2a->first );
+    }
+    if ( iF == areasByNormal.size() )
+      normsByArea.insert( make_pair( sumArea, norms ));
+  }
+
+  // try to set dirs by planar faces
+
+  gp_XYZ normDirs[3]; // normals to largest planes
+
+  if ( !normsByArea.empty() )
+  {
+    norm1 = normsByArea.rbegin()->second[0];
+    normDirs[0] = gpXYZ( *norm1 );
+
+    if ( normsByArea.size() == 1 )
+    {
+      normDirs[1] = normDirs[0];
+      if ( Abs( normDirs[0].Y() ) < 1e-100 &&
+           Abs( normDirs[0].Z() ) < 1e-100 ) // normDirs[0] || OX
+        normDirs[1].SetY( normDirs[0].Y() + 1. );
+      else
+        normDirs[1].SetX( normDirs[0].X() + 1. );
+    }
+    else
+    {
+      // look for 2 other directions
+      gp_XYZ testDir = normDirs[0], minDir, maxDir;
+      for ( int is2nd = 0; is2nd < 2; ++is2nd )
+      {
+        double maxMetric = 0, minMetric = 1e100;
+        std::multimap< double, vector< const TCooTriple* > >::iterator a2n;
+        for ( a2n = normsByArea.begin(); a2n != normsByArea.end(); ++a2n )
+        {
+          gp_XYZ n = gpXYZ( *( a2n->second[0]) );
+          double dot = Abs( n * testDir );
+          double metric = ( 1. - dot ) * ( isOrthogonal ? 1 : a2n->first );
+          if ( metric > maxMetric )
+          {
+            maxDir = n;
+            maxMetric = metric;
+          }
+          if ( metric < minMetric )
+          {
+            minDir = n;
+            minMetric = metric;
+          }
+        }
+        if ( is2nd )
+        {
+          normDirs[2] = minDir;
+        }
+        else
+        {
+          normDirs[1] = maxDir;
+          normDirs[2] = normDirs[0] ^ normDirs[1];
+          if ( isOrthogonal || normsByArea.size() < 3 )
+            break;
+          testDir = normDirs[2];
+        }
+      }
+    }
+    if ( isOrthogonal || normsByArea.size() == 1 )
+    {
+      normDirs[2] = normDirs[0] ^ normDirs[1];
+      normDirs[1] = normDirs[2] ^ normDirs[0];
+    }
+  }
+  else
+  {
+    return;
+  }
+
+  gp_XYZ dirs[3];
+  dirs[0] = normDirs[0] ^ normDirs[1];
+  dirs[1] = normDirs[1] ^ normDirs[2];
+  dirs[2] = normDirs[2] ^ normDirs[0];
+
+  dirs[0].Normalize();
+  dirs[1].Normalize();
+  dirs[2].Normalize();
+
+  // Select dirs for X, Y and Z axes
+  int iX = ( Abs( dirs[0].X() ) > Abs( dirs[1].X() )) ? 0 : 1;
+  if ( Abs( dirs[iX].X() ) < Abs( dirs[2].X() ))
+    iX = 2;
+  int iY = ( iX == 0 ) ? 1 : (( Abs( dirs[0].Y() ) > Abs( dirs[1].Y() )) ? 0 : 1 );
+  if ( Abs( dirs[iY].Y() ) < Abs( dirs[2].Y() ) && iX != 2 )
+    iY = 2;
+  int iZ = 3 - iX - iY;
+
+  if ( dirs[iX].X() < 0 ) dirs[iX].Reverse();
+  if ( dirs[iY].Y() < 0 ) dirs[iY].Reverse();
+  gp_XYZ zDir = dirs[iX] ^ dirs[iY];
+  if ( dirs[iZ] * zDir < 0 )
+    dirs[iZ].Reverse();
+
+  dirCoords[0] = dirs[iX].X();
+  dirCoords[1] = dirs[iX].Y();
+  dirCoords[2] = dirs[iX].Z();
+  dirCoords[3] = dirs[iY].X();
+  dirCoords[4] = dirs[iY].Y();
+  dirCoords[5] = dirs[iY].Z();
+  dirCoords[6] = dirs[iZ].X();
+  dirCoords[7] = dirs[iZ].Y();
+  dirCoords[8] = dirs[iZ].Z();
+}
+
+//=======================================================================
 //function : SetAxisDirs
-//purpose  : Sets directions of axes
+//purpose  : Sets custom direction of axes
 //=======================================================================
 
 void StdMeshers_CartesianParameters3D::SetAxisDirs(const double* the9DirComps)
@@ -349,6 +604,10 @@ void StdMeshers_CartesianParameters3D::SetAxisDirs(const double* the9DirComps)
        y.IsParallel( z, M_PI / 180. ))
     throw SALOME_Exception("Parallel axis directions");
 
+  gp_Vec normXY = x ^ y, normYZ = y ^ z;
+  if ( normXY.IsParallel( normYZ, M_PI / 180. ))
+    throw SALOME_Exception("Axes lie in one plane");
+
   bool isChanged = false;
   for ( int i = 0; i < 9; ++i )
   {
@@ -450,6 +709,14 @@ std::ostream & StdMeshers_CartesianParameters3D::SaveTo(std::ostream & save)
   }
   save << _toAddEdges << " ";
 
+  save.setf( save.scientific );
+  save.precision( 12 );
+  for ( int i = 0; i < 9; ++i )
+    save << _axisDirs[i] << " ";
+
+  for ( int i = 0; i < 3; ++i )
+    save << _fixedPoint[i] << " ";
+
   return save;
 }
 
@@ -500,7 +767,13 @@ std::istream & StdMeshers_CartesianParameters3D::LoadFrom(std::istream & load)
     }
   }
 
-  load >> _toAddEdges;
+  ok = ( load >> _toAddEdges );
+
+  for ( int i = 0; i < 9 && ok; ++i )
+    ok = ( load >> _axisDirs[i]);
+
+  for ( int i = 0; i < 3 && ok ; ++i )
+    ok = ( load >> _fixedPoint[i]);
 
   return load;
 }
index 9897095..3ef431c 100644 (file)
@@ -79,6 +79,13 @@ public:
   bool IsGridBySpacing(const int axis) const throw ( SALOME_Exception );
 
   /*!
+   * Set/unset a fixed point, at which a node will be created provided that grid
+   * is defined by spacing in all directions
+   */
+  void SetFixedPoint(const double p[3], bool toUnset);
+  bool GetFixedPoint(double p[3]);
+
+  /*!
    * \brief Computes node coordinates by spacing functions
    *  \param x0 - lower coordinate
    *  \param x1 - upper coordinate
@@ -101,13 +108,21 @@ public:
                       std::vector<double>& zNodes,
                       const Bnd_Box&       bndBox) const throw ( SALOME_Exception );
 
+  /*!
+   * \brief Set custom direction of axes
+   */
   void SetAxisDirs(const double* the9DirComps) throw ( SALOME_Exception );
   const double* GetAxisDirs() const { return _axisDirs; }
-
+  /*!
+   * \brief Returns axes at which number of hexahedra is maximal
+   */
+  static void ComputeOptimalAxesDirs(const TopoDS_Shape& shape,
+                                     const bool          isOrthogonal,
+                                     double              dirCoords[9]);
   /*!
    * Set size threshold. A polyhedral cell got by cutting an initial
    * hexahedron by geometry boundary is considered small and is removed if
-   * it's size is \athreshold times less than the size of the initial hexahedron. 
+   * it's size is \athreshold times less than the size of the initial hexahedron.
    */
   void SetSizeThreshold(const double threshold) throw ( SALOME_Exception );
   /*!
@@ -150,7 +165,8 @@ public:
   std::vector<std::string> _spaceFunctions[3];
   std::vector<double>      _internalPoints[3];
 
-  double _axisDirs[9];
+  double _axisDirs  [9];
+  double _fixedPoint[3];
 
   double _sizeThreshold;
   bool   _toAddEdges;
index 36b2e2d..b8c4a92 100644 (file)
 #include <Utils_ExceptHandlers.hxx>
 #include <Basics_OCCTVersion.hxx>
 
+#include <GEOMUtils.hxx>
+
 #include <BRepAdaptor_Curve.hxx>
 #include <BRepAdaptor_Surface.hxx>
 #include <BRepBndLib.hxx>
 #include <BRepBuilderAPI_Copy.hxx>
+#include <BRepBuilderAPI_MakeFace.hxx>
 #include <BRepTools.hxx>
+#include <BRep_Builder.hxx>
 #include <BRep_Tool.hxx>
+#include <Bnd_B3d.hxx>
 #include <Bnd_Box.hxx>
 #include <ElSLib.hxx>
 #include <GCPnts_UniformDeflection.hxx>
@@ -67,6 +72,7 @@
 #include <TopLoc_Location.hxx>
 #include <TopTools_MapOfShape.hxx>
 #include <TopoDS.hxx>
+#include <TopoDS_Compound.hxx>
 #include <TopoDS_Face.hxx>
 #include <TopoDS_TShape.hxx>
 #include <gp_Cone.hxx>
@@ -218,12 +224,9 @@ namespace
    */
   struct GridPlanes
   {
-    double _factor;
     gp_XYZ _uNorm, _vNorm, _zNorm;
     vector< gp_XYZ > _origins; // origin points of all planes in one direction
     vector< double > _zProjs;  // projections of origins to _zNorm
-
-    gp_XY GetUV( const gp_Pnt& p, const gp_Pnt& origin );
   };
   // --------------------------------------------------------------------------
   /*!
@@ -276,6 +279,9 @@ namespace
     gp_XYZ             _axes  [3]; // axis directions
     vector< GridLine > _lines [3]; //  in 3 directions
     double             _tol, _minCellSize;
+    gp_XYZ             _origin;
+    gp_Mat             _invB; // inverted basis of _axes
+    //bool               _isOrthogonalAxes;
 
     vector< const SMDS_MeshNode* >    _nodes; // mesh nodes at grid nodes
     vector< const F_IntersectPoint* > _gridIntP; // grid node intersection with geometry
@@ -301,7 +307,8 @@ namespace
                         const vector<double>& yCoords,
                         const vector<double>& zCoords,
                         const double*         axesDirs,
-                        const TopoDS_Shape&   shape );
+                        const Bnd_Box&        bndBox );
+    void ComputeUVW(const gp_XYZ& p, double uvw[3]);
     void ComputeNodes(SMESH_MesherHelper& helper);
   };
 #ifdef ELLIPSOLID_WORKAROUND
@@ -646,7 +653,7 @@ namespace
   inline void locateValue( int & i, double val, const vector<double>& values,
                            int& di, double tol )
   {
-    val += values[0]; // input \a val is measured from 0.
+    //val += values[0]; // input \a val is measured from 0.
     if ( i > values.size()-2 )
       i = values.size()-2;
     else
@@ -722,16 +729,6 @@ namespace
   }
   //================================================================================
   /*
-   * Returns parameters of a point in i-th plane
-   */
-  gp_XY GridPlanes::GetUV( const gp_Pnt& p, const gp_Pnt& origin )
-  {
-    gp_Vec v( origin, p );
-    return gp_XY( v.Dot( _uNorm ) * _factor,
-                  v.Dot( _vNorm ) * _factor );
-  }
-  //================================================================================
-  /*
    * Adds face IDs
    */
   void B_IntersectPoint::Add( const vector< TGeomID >& fIDs,
@@ -793,11 +790,12 @@ namespace
                             const vector<double>& yCoords,
                             const vector<double>& zCoords,
                             const double*         axesDirs,
-                            const TopoDS_Shape&   shape)
+                            const Bnd_Box&        shapeBox)
   {
     _coords[0] = xCoords;
     _coords[1] = yCoords;
     _coords[2] = zCoords;
+
     _axes[0].SetCoord( axesDirs[0],
                        axesDirs[1],
                        axesDirs[2]);
@@ -807,6 +805,16 @@ namespace
     _axes[2].SetCoord( axesDirs[6],
                        axesDirs[7],
                        axesDirs[8]);
+    _axes[0].Normalize();
+    _axes[1].Normalize();
+    _axes[2].Normalize();
+
+    _invB.SetCols( _axes[0], _axes[1], _axes[2] );
+    _invB.Invert();
+
+    // _isOrthogonalAxes = ( Abs( _axes[0] * _axes[1] ) < 1e-20 &&
+    //                       Abs( _axes[1] * _axes[2] ) < 1e-20 &&
+    //                       Abs( _axes[2] * _axes[0] ) < 1e-20 );
 
     // compute tolerance
     _minCellSize = Precision::Infinite();
@@ -821,21 +829,37 @@ namespace
     }
     if ( _minCellSize < Precision::Confusion() )
       throw SMESH_ComputeError (COMPERR_ALGO_FAILED,
-                                SMESH_Comment("Too small cell size: ") << _tol );
+                                SMESH_Comment("Too small cell size: ") << _minCellSize );
     _tol = _minCellSize / 1000.;
 
-    // attune grid extremities to shape bounding box computed by vertices
-    Bnd_Box shapeBox;
-    for ( TopExp_Explorer vExp( shape, TopAbs_VERTEX ); vExp.More(); vExp.Next() )
-      shapeBox.Add( BRep_Tool::Pnt( TopoDS::Vertex( vExp.Current() )));
-    
+    // attune grid extremities to shape bounding box
+
     double sP[6]; // aXmin, aYmin, aZmin, aXmax, aYmax, aZmax
     shapeBox.Get(sP[0],sP[1],sP[2],sP[3],sP[4],sP[5]);
     double* cP[6] = { &_coords[0].front(), &_coords[1].front(), &_coords[2].front(),
                       &_coords[0].back(),  &_coords[1].back(),  &_coords[2].back() };
     for ( int i = 0; i < 6; ++i )
       if ( fabs( sP[i] - *cP[i] ) < _tol )
-        *cP[i] = sP[i] + _tol/1000. * ( i < 3 ? +1 : -1 );
+        *cP[i] = sP[i];// + _tol/1000. * ( i < 3 ? +1 : -1 );
+
+    for ( int iDir = 0; iDir < 3; ++iDir )
+    {
+      if ( _coords[iDir][0] - sP[iDir] > _tol )
+      {
+        _minCellSize = Min( _minCellSize, _coords[iDir][0] - sP[iDir] );
+        _coords[iDir].insert( _coords[iDir].begin(), sP[iDir] + _tol/1000.);
+      }
+      if ( sP[iDir+3] - _coords[iDir].back() > _tol  )
+      {
+        _minCellSize = Min( _minCellSize, sP[iDir+3] - _coords[iDir].back() );
+        _coords[iDir].push_back( sP[iDir+3] - _tol/1000.);
+      }
+    }
+    _tol = _minCellSize / 1000.;
+
+    _origin = ( _coords[0][0] * _axes[0] +
+                _coords[1][0] * _axes[1] +
+                _coords[2][0] * _axes[2] );
 
     // create lines
     for ( int iDir = 0; iDir < 3; ++iDir ) // loop on 3 line directions
@@ -843,18 +867,35 @@ namespace
       LineIndexer li = GetLineIndexer( iDir );
       _lines[iDir].resize( li.NbLines() );
       double len = _coords[ iDir ].back() - _coords[iDir].front();
-      gp_Vec dir( iDir==0, iDir==1, iDir==2 );
       for ( ; li.More(); ++li )
       {
         GridLine& gl = _lines[iDir][ li.LineIndex() ];
-        gl._line.SetLocation(gp_Pnt(_coords[0][li.I()], _coords[1][li.J()], _coords[2][li.K()])); 
-        gl._line.SetDirection( dir );
+        gl._line.SetLocation( _coords[0][li.I()] * _axes[0] +
+                              _coords[1][li.J()] * _axes[1] +
+                              _coords[2][li.K()] * _axes[2] );
+        gl._line.SetDirection( _axes[ iDir ]);
         gl._length = len;
       }
     }
   }
   //================================================================================
   /*
+   * Computes coordinates of a point in the grid CS
+   */
+  void Grid::ComputeUVW(const gp_XYZ& P, double UVW[3])
+  {
+    // gp_XYZ p = P - _origin;
+    // UVW[ 0 ] = p.X() * _invB( 1, 1 ) + p.Y() * _invB( 1, 2 ) + p.Z() * _invB( 1, 3 );
+    // UVW[ 1 ] = p.X() * _invB( 2, 1 ) + p.Y() * _invB( 2, 2 ) + p.Z() * _invB( 2, 3 );
+    // UVW[ 2 ] = p.X() * _invB( 3, 1 ) + p.Y() * _invB( 3, 2 ) + p.Z() * _invB( 3, 3 );
+    // UVW[ 0 ] += _coords[0][0];
+    // UVW[ 1 ] += _coords[1][0];
+    // UVW[ 2 ] += _coords[2][0];
+    gp_XYZ p = P * _invB;
+    p.Coord( UVW[0], UVW[1], UVW[2] );
+  }
+  //================================================================================
+  /*
    * Creates all nodes
    */
   void Grid::ComputeNodes(SMESH_MesherHelper& helper)
@@ -882,12 +923,16 @@ namespace
         nIndex0 = NodeIndex( li.I(), li.J(), li.K() );
 
         GridLine& line = _lines[ iDir ][ li.LineIndex() ];
+        const gp_XYZ lineLoc = line._line.Location().XYZ();
+        const gp_XYZ lineDir = line._line.Direction().XYZ();
         line.RemoveExcessIntPoints( _tol );
         multiset< F_IntersectPoint >& intPnts = _lines[ iDir ][ li.LineIndex() ]._intPoints;
         multiset< F_IntersectPoint >::iterator ip = intPnts.begin();
 
         bool isOut = true;
-        const double* nodeCoord = & coords[0], *coord0 = nodeCoord, *coordEnd = coord0 + coords.size();
+        const double* nodeCoord = & coords[0];
+        const double* coord0    = nodeCoord;
+        const double* coordEnd  = coord0 + coords.size();
         double nodeParam = 0;
         for ( ; ip != intPnts.end(); ++ip )
         {
@@ -910,10 +955,11 @@ namespace
           // create a mesh node on a GridLine at ip if it does not coincide with a grid node
           if ( nodeParam > ip->_paramOnLine + _tol )
           {
-            li.SetIndexOnLine( 0 );
-            double xyz[3] = { _coords[0][ li.I() ], _coords[1][ li.J() ], _coords[2][ li.K() ]};
-            xyz[ li._iConst ] += ip->_paramOnLine;
-            ip->_node = helper.AddNode( xyz[0], xyz[1], xyz[2] );
+            // li.SetIndexOnLine( 0 );
+            // double xyz[3] = { _coords[0][ li.I() ], _coords[1][ li.J() ], _coords[2][ li.K() ]};
+            // xyz[ li._iConst ] += ip->_paramOnLine;
+            gp_XYZ xyz = lineLoc + ip->_paramOnLine * lineDir;
+            ip->_node = helper.AddNode( xyz.X(), xyz.Y(), xyz.Z() );
             ip->_indexOnLine = nodeCoord-coord0-1;
           }
           // create a mesh node at ip concident with a grid node
@@ -922,9 +968,10 @@ namespace
             int nodeIndex = nIndex0 + nShift * ( nodeCoord-coord0 );
             if ( !_nodes[ nodeIndex ] )
             {
-              li.SetIndexOnLine( nodeCoord-coord0 );
-              double xyz[3] = { _coords[0][ li.I() ], _coords[1][ li.J() ], _coords[2][ li.K() ]};
-              _nodes   [ nodeIndex ] = helper.AddNode( xyz[0], xyz[1], xyz[2] );
+              //li.SetIndexOnLine( nodeCoord-coord0 );
+              //double xyz[3] = { _coords[0][ li.I() ], _coords[1][ li.J() ], _coords[2][ li.K() ]};
+              gp_XYZ xyz = lineLoc + nodeParam * lineDir;
+              _nodes   [ nodeIndex ] = helper.AddNode( xyz.X(), xyz.Y(), xyz.Z() );
               _gridIntP[ nodeIndex ] = & * ip;
             }
             if ( _gridIntP[ nodeIndex ] )
@@ -951,7 +998,13 @@ namespace
         {
           size_t nodeIndex = NodeIndex( x, y, z );
           if ( !isNodeOut[ nodeIndex ] && !_nodes[ nodeIndex] )
-            _nodes[ nodeIndex ] = helper.AddNode( _coords[0][x], _coords[1][y], _coords[2][z] );
+          {
+            //_nodes[ nodeIndex ] = helper.AddNode( _coords[0][x], _coords[1][y], _coords[2][z] );
+            gp_XYZ xyz = ( _coords[0][x] * _axes[0] +
+                           _coords[1][y] * _axes[1] +
+                           _coords[2][z] * _axes[2] );
+            _nodes[ nodeIndex ] = helper.AddNode( xyz.X(), xyz.Y(), xyz.Z() );
+          }
         }
 
 #ifdef _MY_DEBUG_
@@ -1000,72 +1053,72 @@ namespace
    */
   bool FaceGridIntersector::IsInGrid(const Bnd_Box& gridBox)
   {
-    double x0,y0,z0, x1,y1,z1;
-    const Bnd_Box& faceBox = GetFaceBndBox();
-    faceBox.Get(x0,y0,z0, x1,y1,z1);
-
-    if ( !gridBox.IsOut( gp_Pnt( x0,y0,z0 )) &&
-         !gridBox.IsOut( gp_Pnt( x1,y1,z1 )))
-      return true;
-
-    double X0,Y0,Z0, X1,Y1,Z1;
-    gridBox.Get(X0,Y0,Z0, X1,Y1,Z1);
-    double faceP[6] = { x0,y0,z0, x1,y1,z1 };
-    double gridP[6] = { X0,Y0,Z0, X1,Y1,Z1 };
-    gp_Dir axes[3]  = { gp::DX(), gp::DY(), gp::DZ() };
-    for ( int iDir = 0; iDir < 6; ++iDir )
-    {
-      if ( iDir < 3  && gridP[ iDir ] <= faceP[ iDir ] ) continue;
-      if ( iDir >= 3 && gridP[ iDir ] >= faceP[ iDir ] ) continue;
-
-      // check if the face intersects a side of a gridBox
+    // double x0,y0,z0, x1,y1,z1;
+    // const Bnd_Box& faceBox = GetFaceBndBox();
+    // faceBox.Get(x0,y0,z0, x1,y1,z1);
+
+    // if ( !gridBox.IsOut( gp_Pnt( x0,y0,z0 )) &&
+    //      !gridBox.IsOut( gp_Pnt( x1,y1,z1 )))
+    //   return true;
+
+    // double X0,Y0,Z0, X1,Y1,Z1;
+    // gridBox.Get(X0,Y0,Z0, X1,Y1,Z1);
+    // double faceP[6] = { x0,y0,z0, x1,y1,z1 };
+    // double gridP[6] = { X0,Y0,Z0, X1,Y1,Z1 };
+    // gp_Dir axes[3]  = { gp::DX(), gp::DY(), gp::DZ() };
+    // for ( int iDir = 0; iDir < 6; ++iDir )
+    // {
+    //   if ( iDir < 3  && gridP[ iDir ] <= faceP[ iDir ] ) continue;
+    //   if ( iDir >= 3 && gridP[ iDir ] >= faceP[ iDir ] ) continue;
 
-      gp_Pnt p = iDir < 3 ? gp_Pnt( X0,Y0,Z0 ) : gp_Pnt( X1,Y1,Z1 );
-      gp_Ax1 norm( p, axes[ iDir % 3 ] );
-      if ( iDir < 3 ) norm.Reverse();
+    //   // check if the face intersects a side of a gridBox
 
-      gp_XYZ O = norm.Location().XYZ(), N = norm.Direction().XYZ();
+    //   gp_Pnt p = iDir < 3 ? gp_Pnt( X0,Y0,Z0 ) : gp_Pnt( X1,Y1,Z1 );
+    //   gp_Ax1 norm( p, axes[ iDir % 3 ] );
+    //   if ( iDir < 3 ) norm.Reverse();
 
-      TopLoc_Location loc = _face.Location();
-      Handle(Poly_Triangulation) aPoly = BRep_Tool::Triangulation(_face,loc);
-      if ( !aPoly.IsNull() )
-      {
-        if ( !loc.IsIdentity() )
-        {
-          norm.Transform( loc.Transformation().Inverted() );
-          O = norm.Location().XYZ(), N = norm.Direction().XYZ();
-        }
-        const double deflection = aPoly->Deflection();
+    //   gp_XYZ O = norm.Location().XYZ(), N = norm.Direction().XYZ();
 
-        const TColgp_Array1OfPnt& nodes = aPoly->Nodes();
-        for ( int i = nodes.Lower(); i <= nodes.Upper(); ++i )
-          if (( nodes( i ).XYZ() - O ) * N > _grid->_tol + deflection )
-            return false;
-      }
-      else
-      {
-        BRepAdaptor_Surface surf( _face );
-        double u0, u1, v0, v1, du, dv, u, v;
-        BRepTools::UVBounds( _face, u0, u1, v0, v1);
-        if ( surf.GetType() == GeomAbs_Plane ) {
-          du = u1 - u0, dv = v1 - v0;
-        }
-        else {
-          du = surf.UResolution( _grid->_minCellSize / 10. );
-          dv = surf.VResolution( _grid->_minCellSize / 10. );
-        }
-        for ( u = u0, v = v0; u <= u1 && v <= v1; u += du, v += dv )
-        {
-          gp_Pnt p = surf.Value( u, v );
-          if (( p.XYZ() - O ) * N > _grid->_tol )
-          {
-            TopAbs_State state = GetCurveFaceIntersector()->ClassifyUVPoint(gp_Pnt2d( u, v ));
-            if ( state == TopAbs_IN || state == TopAbs_ON )
-              return false;
-          }
-        }
-      }
-    }
+    //   TopLoc_Location loc = _face.Location();
+    //   Handle(Poly_Triangulation) aPoly = BRep_Tool::Triangulation(_face,loc);
+    //   if ( !aPoly.IsNull() )
+    //   {
+    //     if ( !loc.IsIdentity() )
+    //     {
+    //       norm.Transform( loc.Transformation().Inverted() );
+    //       O = norm.Location().XYZ(), N = norm.Direction().XYZ();
+    //     }
+    //     const double deflection = aPoly->Deflection();
+
+    //     const TColgp_Array1OfPnt& nodes = aPoly->Nodes();
+    //     for ( int i = nodes.Lower(); i <= nodes.Upper(); ++i )
+    //       if (( nodes( i ).XYZ() - O ) * N > _grid->_tol + deflection )
+    //         return false;
+    //   }
+    //   else
+    //   {
+    //     BRepAdaptor_Surface surf( _face );
+    //     double u0, u1, v0, v1, du, dv, u, v;
+    //     BRepTools::UVBounds( _face, u0, u1, v0, v1);
+    //     if ( surf.GetType() == GeomAbs_Plane ) {
+    //       du = u1 - u0, dv = v1 - v0;
+    //     }
+    //     else {
+    //       du = surf.UResolution( _grid->_minCellSize / 10. );
+    //       dv = surf.VResolution( _grid->_minCellSize / 10. );
+    //     }
+    //     for ( u = u0, v = v0; u <= u1 && v <= v1; u += du, v += dv )
+    //     {
+    //       gp_Pnt p = surf.Value( u, v );
+    //       if (( p.XYZ() - O ) * N > _grid->_tol )
+    //       {
+    //         TopAbs_State state = GetCurveFaceIntersector()->ClassifyUVPoint(gp_Pnt2d( u, v ));
+    //         if ( state == TopAbs_IN || state == TopAbs_ON )
+    //           return false;
+    //       }
+    //     }
+    //   }
+    // }
     return true;
   }
   //=============================================================================
@@ -1139,7 +1192,7 @@ namespace
         if ( _bndBox.IsOut( gridLine._line )) continue;
 
         intersector._intPoints.clear();
-        (intersector.*interFunction)( gridLine );
+        (intersector.*interFunction)( gridLine ); // <- intersection with gridLine
         for ( size_t i = 0; i < intersector._intPoints.size(); ++i )
           _intersections.push_back( make_pair( &gridLine, intersector._intPoints[i] ));
       }
@@ -1188,7 +1241,7 @@ namespace
    */
   void FaceLineIntersector::IntersectWithCylinder(const GridLine& gridLine)
   {
-    IntAna_IntConicQuad linCylinder( gridLine._line,_cylinder);
+    IntAna_IntConicQuad linCylinder( gridLine._line, _cylinder );
     if ( linCylinder.IsDone() && linCylinder.NbPoints() > 0 )
     {
       _w = linCylinder.ParamOnConic(1);
@@ -1644,6 +1697,23 @@ namespace
 
       } // loop on _edgeIntPnts
     }
+    else if ( 3 < _nbCornerNodes && _nbCornerNodes < 8 ) // _nbIntNodes == 0
+    {
+      _Link split;
+      // create sub-links (_splits) of whole links
+      for ( int iLink = 0; iLink < 12; ++iLink )
+      {
+        _Link& link = _hexLinks[ iLink ];
+        link._splits.clear();
+        if ( link._nodes[ 0 ]->Node() && link._nodes[ 1 ]->Node() )
+        {
+          split._nodes[ 0 ] = link._nodes[0];
+          split._nodes[ 1 ] = link._nodes[1];
+          link._splits.push_back( split );
+        }
+      }
+    }
+
   }
   //================================================================================
   /*!
@@ -1877,8 +1947,8 @@ namespace
             {
               curLink = freeLinks[ iL ];
               freeLinks[ iL ] = 0;
-              polygon._links.push_back( *curLink );
               --nbFreeLinks;
+              polygon._links.push_back( *curLink );
             }
         } while ( curLink );
       }
@@ -1983,16 +2053,23 @@ namespace
 
       } // if there are intersections with EDGEs
 
-      if ( polygon._links.size() < 3 ||
+      if ( polygon._links.size() < 2 ||
            polygon._links[0].LastNode() != polygon._links.back().FirstNode() )
         return; // closed polygon not found -> invalid polyhedron
 
-      // add polygon to its links
-      for ( size_t iL = 0; iL < polygon._links.size(); ++iL )
+      if ( polygon._links.size() == 2 )
       {
-        polygon._links[ iL ]._link->_faces.reserve( 2 );
-        polygon._links[ iL ]._link->_faces.push_back( &polygon );
-        polygon._links[ iL ].Reverse();
+        _polygons.pop_back();
+      }
+      else
+      {
+        // add polygon to its links
+        for ( size_t iL = 0; iL < polygon._links.size(); ++iL )
+        {
+          polygon._links[ iL ]._link->_faces.reserve( 2 );
+          polygon._links[ iL ]._link->_faces.push_back( &polygon );
+          polygon._links[ iL ].Reverse();
+        }
       }
     } // while ( nbFreeLinks > 0 )
 
@@ -2096,12 +2173,12 @@ namespace
       if ( hex )
       {
         intHexInd[ nbIntHex++ ] = i;
-        if ( hex->_nbIntNodes > 0 ) continue;
-        init( hex->_i, hex->_j, hex->_k );
+        if ( hex->_nbIntNodes > 0 ) continue; // treat intersected hex later
+        this->init( hex->_i, hex->_j, hex->_k );
       }
       else
       {    
-        init( i );
+        this->init( i );
       }
       if ( _nbCornerNodes == 8 && ( _nbBndNodes < _nbCornerNodes || !isInHole() ))
       {
@@ -2124,7 +2201,10 @@ namespace
       {
         // all intersection of hex with geometry are at grid nodes
         hex = new Hexahedron( *this );
-        hex->init( i );
+        //hex->init( i );
+        hex->_i = _i;
+        hex->_j = _j;
+        hex->_k = _k;
         intHexInd.push_back(0);
         intHexInd[ nbIntHex++ ] = i;
       }
@@ -2168,39 +2248,27 @@ namespace
     // Prepare planes for intersecting with EDGEs
     GridPlanes pln[3];
     {
-      gp_XYZ origPnt = ( _grid->_coords[0][0] * _grid->_axes[0] +
-                         _grid->_coords[1][0] * _grid->_axes[1] +
-                         _grid->_coords[2][0] * _grid->_axes[2] );
       for ( int iDirZ = 0; iDirZ < 3; ++iDirZ ) // iDirZ gives normal direction to planes
       {
         GridPlanes& planes = pln[ iDirZ ];
         int iDirX = ( iDirZ + 1 ) % 3;
         int iDirY = ( iDirZ + 2 ) % 3;
-        planes._uNorm  = ( _grid->_axes[ iDirY ] ^ _grid->_axes[ iDirZ ] ).Normalized();
-        planes._vNorm  = ( _grid->_axes[ iDirZ ] ^ _grid->_axes[ iDirX ] ).Normalized();
+        // planes._uNorm  = ( _grid->_axes[ iDirY ] ^ _grid->_axes[ iDirZ ] ).Normalized();
+        // planes._vNorm  = ( _grid->_axes[ iDirZ ] ^ _grid->_axes[ iDirX ] ).Normalized();
         planes._zNorm  = ( _grid->_axes[ iDirX ] ^ _grid->_axes[ iDirY ] ).Normalized();
-        double   uvDot = planes._uNorm * planes._vNorm;
-        planes._factor = sqrt( 1. - uvDot * uvDot );
-        planes._origins.resize( _grid->_coords[ iDirZ ].size() );
         planes._zProjs.resize ( _grid->_coords[ iDirZ ].size() );
-        planes._origins[0] = origPnt;
         planes._zProjs [0] = 0;
         const double       zFactor = _grid->_axes[ iDirZ ] * planes._zNorm;
         const vector< double > & u = _grid->_coords[ iDirZ ];
-        for ( int i = 1; i < planes._origins.size(); ++i )
+        for ( int i = 1; i < planes._zProjs.size(); ++i )
         {
-          planes._origins[i] = origPnt + _grid->_axes[ iDirZ ] * ( u[i] - u[0] );
           planes._zProjs [i] = zFactor * ( u[i] - u[0] );
         }
       }
     }
     const double deflection = _grid->_minCellSize / 20.;
     const double tol        = _grid->_tol;
-    // int facets[6] = { SMESH_Block::ID_F0yz, SMESH_Block::ID_F1yz,
-    //                   SMESH_Block::ID_Fx0z, SMESH_Block::ID_Fx1z,
-    //                   SMESH_Block::ID_Fxy0, SMESH_Block::ID_Fxy1 };
     E_IntersectPoint ip;
-    //ip._faceIDs.reserve(2);
 
     // Intersect EDGEs with the planes
     map< TGeomID, vector< TGeomID > >::const_iterator e2fIt = edge2faceIDsMap.begin();
@@ -2209,6 +2277,8 @@ namespace
       const TGeomID  edgeID = e2fIt->first;
       const TopoDS_Edge & E = TopoDS::Edge( _grid->_shapes( edgeID ));
       BRepAdaptor_Curve curve( E );
+      TopoDS_Vertex v1 = helper.IthVertex( 0, E, false ); 
+      TopoDS_Vertex v2 = helper.IthVertex( 1, E, false ); 
 
       ip._faceIDs = e2fIt->second;
       ip._shapeID = edgeID;
@@ -2226,34 +2296,34 @@ namespace
         int      iDirY = ( iDirZ + 2 ) % 3;
         double    xLen = _grid->_coords[ iDirX ].back() - _grid->_coords[ iDirX ][0];
         double    yLen = _grid->_coords[ iDirY ].back() - _grid->_coords[ iDirY ][0];
-        double zFactor = _grid->_axes[ iDirZ ] * planes._zNorm;
+        double    zLen = _grid->_coords[ iDirZ ].back() - _grid->_coords[ iDirZ ][0];
+        //double zFactor = _grid->_axes[ iDirZ ] * planes._zNorm;
         int dIJK[3], d000[3] = { 0,0,0 };
+        double o[3] = { _grid->_coords[0][0],
+                        _grid->_coords[1][0],
+                        _grid->_coords[2][0] };
 
         // locate the 1st point of a segment within the grid
         gp_XYZ p1     = discret.Value( 1 ).XYZ();
         double u1     = discret.Parameter( 1 );
-        double zProj1 = planes._zNorm * ( p1 - planes._origins[0] );
-        gp_Pnt orig   = planes._origins[0] + planes._zNorm * zProj1;
-        gp_XY uv      = planes.GetUV( p1, orig );
-        int iX1       = int( uv.X() / xLen * ( _grid->_coords[ iDirX ].size() - 1. ));
-        int iY1       = int( uv.Y() / yLen * ( _grid->_coords[ iDirY ].size() - 1. ));
-        int iZ1       = int( zProj1 / planes._zProjs.back() * ( planes._zProjs.size() - 1. ));
-        locateValue( iX1, uv.X(), _grid->_coords[ iDirX ], dIJK[ iDirX ], tol );
-        locateValue( iY1, uv.Y(), _grid->_coords[ iDirY ], dIJK[ iDirY ], tol );
-        locateValue( iZ1, zProj1, planes._zProjs         , dIJK[ iDirZ ], tol );
+        double zProj1 = planes._zNorm * ( p1 - _grid->_origin );
+
+        _grid->ComputeUVW( p1, ip._uvw );
+        int iX1 = int(( ip._uvw[iDirX] - o[iDirX]) / xLen * (_grid->_coords[ iDirX ].size() - 1));
+        int iY1 = int(( ip._uvw[iDirY] - o[iDirY]) / yLen * (_grid->_coords[ iDirY ].size() - 1));
+        int iZ1 = int(( ip._uvw[iDirZ] - o[iDirZ]) / zLen * (_grid->_coords[ iDirZ ].size() - 1));
+        locateValue( iX1, ip._uvw[iDirX], _grid->_coords[ iDirX ], dIJK[ iDirX ], tol );
+        locateValue( iY1, ip._uvw[iDirY], _grid->_coords[ iDirY ], dIJK[ iDirY ], tol );
+        locateValue( iZ1, ip._uvw[iDirZ], _grid->_coords[ iDirZ ], dIJK[ iDirZ ], tol );
 
         int ijk[3]; // grid index where a segment intersect a plane
         ijk[ iDirX ] = iX1;
         ijk[ iDirY ] = iY1;
         ijk[ iDirZ ] = iZ1;
-        ip._uvw[ iDirX ] = uv.X()           + _grid->_coords[ iDirX ][0];
-        ip._uvw[ iDirY ] = uv.Y()           + _grid->_coords[ iDirY ][0];
-        ip._uvw[ iDirZ ] = zProj1 / zFactor + _grid->_coords[ iDirZ ][0];
 
         // add the 1st vertex point to a hexahedron
         if ( iDirZ == 0 )
         {
-          //ip._shapeID = _grid->_shapes.Add( helper.IthVertex( 0, curve.Edge(),/*CumOri=*/false));
           ip._point   = p1;
           _grid->_edgeIntP.push_back( ip );
           if ( !addIntersection( _grid->_edgeIntP.back(), hexes, ijk, d000 ))
@@ -2264,7 +2334,7 @@ namespace
           // locate the 2nd point of a segment within the grid
           gp_XYZ p2     = discret.Value( iP ).XYZ();
           double u2     = discret.Parameter( iP );
-          double zProj2 = planes._zNorm * ( p2 - planes._origins[0] );
+          double zProj2 = planes._zNorm * ( p2 - _grid->_origin );
           int iZ2       = iZ1;
           locateValue( iZ2, zProj2, planes._zProjs, dIJK[ iDirZ ], tol );
 
@@ -2275,14 +2345,11 @@ namespace
           {
             ip._point = findIntPoint( u1, zProj1, u2, zProj2,
                                       planes._zProjs[ iZ ],
-                                      curve, planes._zNorm, planes._origins[0] );
-            gp_XY uv  = planes.GetUV( ip._point, planes._origins[ iZ ]);
-            locateValue( ijk[ iDirX ], uv.X(), _grid->_coords[ iDirX ], dIJK[ iDirX ], tol );
-            locateValue( ijk[ iDirY ], uv.Y(), _grid->_coords[ iDirY ], dIJK[ iDirY ], tol );
+                                      curve, planes._zNorm, _grid->_origin );
+            _grid->ComputeUVW( ip._point.XYZ(), ip._uvw );
+            locateValue( ijk[iDirX], ip._uvw[iDirX], _grid->_coords[iDirX], dIJK[iDirX], tol );
+            locateValue( ijk[iDirY], ip._uvw[iDirY], _grid->_coords[iDirY], dIJK[iDirY], tol );
             ijk[ iDirZ ] = iZ;
-            ip._uvw[ iDirX ] = uv.X()                         + _grid->_coords[ iDirX ][0];
-            ip._uvw[ iDirY ] = uv.Y()                         + _grid->_coords[ iDirY ][0];
-            ip._uvw[ iDirZ ] = planes._zProjs[ iZ ] / zFactor + _grid->_coords[ iDirZ ][0];
 
             // add ip to hex "above" the plane
             _grid->_edgeIntP.push_back( ip );
@@ -2303,15 +2370,11 @@ namespace
         // add the 2nd vertex point to a hexahedron
         if ( iDirZ == 0 )
         {
-          orig = planes._origins[0] + planes._zNorm * zProj1;
-          uv   = planes.GetUV( p1, orig );
-          locateValue( ijk[ iDirX ], uv.X(), _grid->_coords[ iDirX ], dIJK[ iDirX ], tol );
-          locateValue( ijk[ iDirY ], uv.Y(), _grid->_coords[ iDirY ], dIJK[ iDirY ], tol );
-          ijk[ iDirZ ] = iZ1;
-          ip._uvw[ iDirX ] = uv.X()           + _grid->_coords[ iDirX ][0];
-          ip._uvw[ iDirY ] = uv.Y()           + _grid->_coords[ iDirY ][0];
-          ip._uvw[ iDirZ ] = zProj1 / zFactor + _grid->_coords[ iDirZ ][0];
           ip._point = p1;
+          _grid->ComputeUVW( p1, ip._uvw );
+          locateValue( ijk[iDirX], ip._uvw[iDirX], _grid->_coords[iDirX], dIJK[iDirX], tol );
+          locateValue( ijk[iDirY], ip._uvw[iDirY], _grid->_coords[iDirY], dIJK[iDirY], tol );
+          ijk[ iDirZ ] = iZ1;
           _grid->_edgeIntP.push_back( ip );
           if ( !addIntersection( _grid->_edgeIntP.back(), hexes, ijk, d000 ))
             _grid->_edgeIntP.pop_back();
@@ -2356,8 +2419,6 @@ namespace
    *  \param [in] origin - the plane origin
    *  \return gp_Pnt - the found intersection point
    */
-  //================================================================================
-
   gp_Pnt Hexahedron::findIntPoint( double u1, double proj1,
                                    double u2, double proj2,
                                    double proj,
@@ -2381,7 +2442,7 @@ namespace
 
   //================================================================================
   /*!
-   * \brief Returns index of a hexahedron sub-entities holding a point
+   * \brief Returns indices of a hexahedron sub-entities holding a point
    *  \param [in] ip - intersection point
    *  \param [out] facets - 0-3 facets holding a point
    *  \param [out] sub - index of a vertex or an edge holding a point
@@ -2466,7 +2527,7 @@ namespace
     };
     for ( int i = 0; i < 4; ++i )
     {
-      if ( 0 <= hexIndex[i] && hexIndex[i] < hexes.size() && hexes[ hexIndex[i] ] )
+      if ( /*0 <= hexIndex[i] &&*/ hexIndex[i] < hexes.size() && hexes[ hexIndex[i] ] )
       {
         Hexahedron* h = hexes[ hexIndex[i] ];
         // check if ip is really inside the hex
@@ -2789,6 +2850,90 @@ namespace
     return false;
   }
 
+  //================================================================================
+  /*!
+   * \brief computes exact bounding box with axes parallel to given ones
+   */
+  //================================================================================
+
+  void getExactBndBox( const vector< TopoDS_Shape >& faceVec,
+                       const double*                 axesDirs,
+                       Bnd_Box&                      shapeBox )
+  {
+    BRep_Builder b;
+    TopoDS_Compound allFacesComp;
+    b.MakeCompound( allFacesComp );
+    for ( size_t iF = 0; iF < faceVec.size(); ++iF )
+      b.Add( allFacesComp, faceVec[ iF ] );
+
+    double sP[6]; // aXmin, aYmin, aZmin, aXmax, aYmax, aZmax
+    shapeBox.Get(sP[0],sP[1],sP[2],sP[3],sP[4],sP[5]);
+    double farDist = 0;
+    for ( int i = 0; i < 6; ++i )
+      farDist = Max( farDist, 10 * sP[i] );
+
+    gp_XYZ axis[3] = { gp_XYZ( axesDirs[0], axesDirs[1], axesDirs[2] ),
+                       gp_XYZ( axesDirs[3], axesDirs[4], axesDirs[5] ),
+                       gp_XYZ( axesDirs[6], axesDirs[7], axesDirs[8] ) };
+    axis[0].Normalize();
+    axis[1].Normalize();
+    axis[2].Normalize();
+
+    gp_Mat basis( axis[0], axis[1], axis[2] );
+    gp_Mat bi = basis.Inverted();
+
+    gp_Pnt pMin, pMax;
+    for ( int iDir = 0; iDir < 3; ++iDir )
+    {
+      gp_XYZ axis0 = axis[ iDir ];
+      gp_XYZ axis1 = axis[ ( iDir + 1 ) % 3 ];
+      gp_XYZ axis2 = axis[ ( iDir + 2 ) % 3 ];
+      for ( int isMax = 0; isMax < 2; ++isMax )
+      {
+        double shift = isMax ? farDist : -farDist;
+        gp_XYZ orig = shift * axis0;
+        gp_XYZ norm = axis1 ^ axis2;
+        gp_Pln pln( orig, norm );
+        norm = pln.Axis().Direction().XYZ();
+        BRepBuilderAPI_MakeFace plane( pln, -farDist, farDist, -farDist, farDist );
+
+        gp_Pnt& pAxis = isMax ? pMax : pMin;
+        gp_Pnt pPlane, pFaces;
+        double dist = GEOMUtils::GetMinDistance( plane, allFacesComp, pPlane, pFaces );
+        if ( dist < 0 )
+        {
+          Bnd_B3d bb;
+          gp_XYZ corner;
+          for ( int i = 0; i < 2; ++i ) {
+            corner.SetCoord( 1, sP[ i*3 ]);
+            for ( int j = 0; j < 2; ++j ) {
+              corner.SetCoord( 2, sP[ i*3 + 1 ]);
+              for ( int k = 0; k < 2; ++k )
+              {
+                corner.SetCoord( 3, sP[ i*3 + 2 ]);
+                corner *= bi;
+                bb.Add( corner );
+              }
+            }
+          }
+          corner = isMax ? bb.CornerMax() : bb.CornerMin();
+          pAxis.SetCoord( iDir+1, corner.Coord( iDir+1 ));
+        }
+        else
+        {
+          gp_XYZ pf = pFaces.XYZ() * bi;
+          pAxis.SetCoord( iDir+1, pf.Coord( iDir+1 ) );
+        }
+      }
+    } // loop on 3 axes
+
+    shapeBox.SetVoid();
+    shapeBox.Add( pMin );
+    shapeBox.Add( pMax );
+
+    return;
+  }
+
 } // namespace
 
 //=============================================================================
@@ -2826,14 +2971,19 @@ bool StdMeshers_Cartesian_3D::Compute(SMESH_Mesh &         theMesh,
     vector< TopoDS_Shape > faceVec;
     {
       TopTools_MapOfShape faceMap;
-      for ( TopExp_Explorer fExp( theShape, TopAbs_FACE ); fExp.More(); fExp.Next() )
-        if ( faceMap.Add( fExp.Current() )) // skip a face shared by two solids
+      TopExp_Explorer fExp;
+      for ( fExp.Init( theShape, TopAbs_FACE ); fExp.More(); fExp.Next() )
+        if ( !faceMap.Add( fExp.Current() ))
+          faceMap.Remove( fExp.Current() ); // remove a face shared by two solids
+
+      for ( fExp.ReInit(); fExp.More(); fExp.Next() )
+        if ( faceMap.Contains( fExp.Current() ))
           faceVec.push_back( fExp.Current() );
     }
-    Bnd_Box shapeBox;
     vector<FaceGridIntersector> facesItersectors( faceVec.size() );
     map< TGeomID, vector< TGeomID > > edge2faceIDsMap;
     TopExp_Explorer eExp;
+    Bnd_Box shapeBox;
     for ( int i = 0; i < faceVec.size(); ++i )
     {
       facesItersectors[i]._face   = TopoDS::Face    ( faceVec[i] );
@@ -2850,32 +3000,13 @@ bool StdMeshers_Cartesian_3D::Compute(SMESH_Mesh &         theMesh,
         }
     }
 
+    getExactBndBox( faceVec, _hyp->GetAxisDirs(), shapeBox );
+
     vector<double> xCoords, yCoords, zCoords;
     _hyp->GetCoordinates( xCoords, yCoords, zCoords, shapeBox );
 
-    grid.SetCoordinates( xCoords, yCoords, zCoords, _hyp->GetAxisDirs(), theShape );
-
-    // check if the grid encloses the shape
-    if ( !_hyp->IsGridBySpacing(0) ||
-         !_hyp->IsGridBySpacing(1) ||
-         !_hyp->IsGridBySpacing(2) )
-    {
-      Bnd_Box gridBox;
-      gridBox.Add( gp_Pnt( xCoords[0], yCoords[0], zCoords[0] ));
-      gridBox.Add( gp_Pnt( xCoords.back(), yCoords.back(), zCoords.back() ));
-      double x0,y0,z0, x1,y1,z1;
-      shapeBox.Get(x0,y0,z0, x1,y1,z1);
-      if ( gridBox.IsOut( gp_Pnt( x0,y0,z0 )) ||
-           gridBox.IsOut( gp_Pnt( x1,y1,z1 )))
-        for ( size_t i = 0; i < facesItersectors.size(); ++i )
-        {
-          if ( !facesItersectors[i].IsInGrid( gridBox ))
-            return error("The grid doesn't enclose the geometry");
-#ifdef ELLIPSOLID_WORKAROUND
-          delete facesItersectors[i]._surfaceInt, facesItersectors[i]._surfaceInt = 0;
-#endif
-        }
-    }
+    grid.SetCoordinates( xCoords, yCoords, zCoords, _hyp->GetAxisDirs(), shapeBox );
+
     if ( _computeCanceled ) return false;
 
 #ifdef WITH_TBB
index c2b58de..be92647 100644 (file)
 // SMESH includes
 #include "StdMeshersGUI_CartesianParamCreator.h"
 
-#include <SMESHGUI.h>
-#include <SMESHGUI_Utils.h>
-#include <SMESHGUI_HypothesesUtils.h>
-#include <SMESHGUI_SpinBox.h>
+#include "SMESHGUI.h"
+#include "SMESHGUI_Utils.h"
+#include "SMESHGUI_VTKUtils.h"
+#include "SMESHGUI_HypothesesUtils.h"
+#include "SMESHGUI_SpinBox.h"
+#include "SMESHGUI_MeshEditPreview.h"
 
 // IDL includes
 #include CORBA_SERVER_HEADER(SMESH_BasicHypothesis)
 
 // SALOME GUI includes
-#include <SalomeApp_Tools.h>
-#include <SalomeApp_IntSpinBox.h>
+#include <LightApp_SelectionMgr.h>
 #include <QtxComboBox.h>
+#include <SALOME_InteractiveObject.hxx>
+#include <SALOME_ListIO.hxx>
+#include <SALOME_ListIteratorOfListIO.hxx>
+#include <SUIT_ResourceMgr.h>
+#include <SalomeApp_IntSpinBox.h>
+#include <SalomeApp_Tools.h>
+
+#include <GEOMBase.h>
+
+#include <BRepBndLib.hxx>
+#include <Bnd_Box.hxx>
+#include <TopoDS_Iterator.hxx>
+#include <TopoDS_Shape.hxx>
+#include <gp_Pnt.hxx>
 
 // Qt includes
 #include <QAbstractItemModel>
@@ -201,11 +216,18 @@ namespace StdMeshersGUI
     connect( myInsertBtn,      SIGNAL( clicked() ),             SLOT( onInsert() ));
     connect( myDeleteBtn,      SIGNAL( clicked() ),             SLOT( onDelete() ));
     connect( myModeGroup,      SIGNAL( buttonClicked ( int )),  SLOT( onMode(int)));
+    connect( myModeGroup,      SIGNAL( buttonClicked ( int )),  SIGNAL( gridModeChanged(int)));
     connect( mySpacingTreeWdg, SIGNAL( itemSelectionChanged()), SLOT( updateButtons() ));
     connect( myCoordList,      SIGNAL( itemSelectionChanged()), SLOT( updateButtons() ));
     connect( myStepSpin,       SIGNAL( valueChanged(double)),   SLOT( onStepChange() ));
   }
 
+  //================================================================================
+  /*!
+   * \brief SLOT onInsert
+   */
+  //================================================================================
+
   void GridAxisTab::onInsert()
   {
     if ( isGridBySpacing() )
@@ -254,6 +276,12 @@ namespace StdMeshersGUI
     updateButtons();
   }
 
+  //================================================================================
+  /*!
+   * \brief SLOT onDelete
+   */
+  //================================================================================
+
   void GridAxisTab::onDelete()
   {
     if ( isGridBySpacing() )
@@ -283,6 +311,12 @@ namespace StdMeshersGUI
     updateButtons();
   }
 
+  //================================================================================
+  /*!
+   * \brief SLOT onMode
+   */
+  //================================================================================
+
   void GridAxisTab::onMode(int isSpacing)
   {
     mySpacingTreeWdg->setShown( isSpacing );
@@ -313,6 +347,12 @@ namespace StdMeshersGUI
     updateButtons();
   }
 
+  //================================================================================
+  /*!
+   * \brief SLOT onStepChange
+   */
+  //================================================================================
+
   void GridAxisTab::onStepChange()
   {
     if ( fabs( myStepSpin->GetValue() ) < 1e-100 )
@@ -323,6 +363,12 @@ namespace StdMeshersGUI
     myStep = myStepSpin->GetValue();
   }
 
+  //================================================================================
+  /*!
+   * \brief Enables/disables buttons
+   */
+  //================================================================================
+
   void GridAxisTab::updateButtons()
   {
     bool insertEnable = false, deleteEnable = false;
@@ -347,6 +393,12 @@ namespace StdMeshersGUI
     myDeleteBtn->setEnabled( deleteEnable );
   }
 
+  //================================================================================
+  /*!
+   * \brief Inserts coordinates into myCoordList
+   */
+  //================================================================================
+
   void GridAxisTab::setCoordinates( SMESH::double_array_var coords )
   {
     myCoordList->clear();
@@ -357,6 +409,12 @@ namespace StdMeshersGUI
     onMode( COORD_BUT );
   }
 
+  //================================================================================
+  /*!
+   * \brief Sets spacing got from hypothesis
+   */
+  //================================================================================
+
   void GridAxisTab::setSpacing( SMESH::string_array_var funs, SMESH::double_array_var points )
   {
     mySpacingTreeWdg->clear();
@@ -370,11 +428,23 @@ namespace StdMeshersGUI
     onMode( SPACING_BUT );
   }
 
+  //================================================================================
+  /*!
+   * \brief Checks grid definintion mode
+   */
+  //================================================================================
+
   bool GridAxisTab::isGridBySpacing() const
   {
     return ( myModeGroup->checkedId() == SPACING_BUT );
   }
 
+  //================================================================================
+  /*!
+   * \brief Returns coordinates to set to a hypothesis
+   */
+  //================================================================================
+
   SMESH::double_array* GridAxisTab::getCoordinates()
   {
     SMESH::double_array_var coords = new SMESH::double_array;
@@ -385,6 +455,12 @@ namespace StdMeshersGUI
     return coords._retn();
   }
 
+  //================================================================================
+  /*!
+   * \brief Returms spacing to set to a hypothesis
+   */
+  //================================================================================
+
   void GridAxisTab::getSpacing(SMESH::string_array_out funs,
                                SMESH::double_array_out points) const
   {
@@ -404,6 +480,12 @@ namespace StdMeshersGUI
   }
 
 
+  //================================================================================
+  /*!
+   * \brief Verifies parameters
+   */
+  //================================================================================
+
   bool GridAxisTab::checkParams(QString& msg, SMESH::SMESH_Hypothesis_var& hyp) const
   {
     if ( isGridBySpacing() )
@@ -432,6 +514,12 @@ namespace StdMeshersGUI
     return true;
   }
 
+  //================================================================================
+  /*!
+   * \brief LineDelegate constructor
+   */
+  //================================================================================
+
   LineDelegate::LineDelegate( QWidget* parent ):
     QItemDelegate( parent ),
     mySpacingTreeWdg( qobject_cast<QTreeWidget*>( parent )),
@@ -439,6 +527,12 @@ namespace StdMeshersGUI
   {
   }
 
+  //================================================================================
+  /*!
+   * \brief Creates an editor depending on a current item
+   */
+  //================================================================================
+
   QWidget* LineDelegate::createEditor( QWidget*                    parent,
                                        const QStyleOptionViewItem& opt,
                                        const QModelIndex&          index) const
@@ -472,6 +566,12 @@ namespace StdMeshersGUI
     return w;
   }
 
+  //================================================================================
+  /*!
+   * \brief Limit value range in the spin of a neighbor range
+   */
+  //================================================================================
+
   void LineDelegate::setEditorData ( QWidget * editor, const QModelIndex & index ) const
   {
     if ( mySpacingTreeWdg && index.column() == 0 )
@@ -494,6 +594,13 @@ namespace StdMeshersGUI
       QItemDelegate::setEditorData( editor, index );
     }
   }
+
+  //================================================================================
+  /*!
+   * \brief
+   */
+  //================================================================================
+
   void LineDelegate::setModelData( QWidget*            editor,
                                    QAbstractItemModel* model,
                                    const QModelIndex&  index ) const
@@ -530,6 +637,53 @@ namespace StdMeshersGUI
 
 } // namespace StdMeshersGUI
 
+namespace
+{
+  const double theAngTol = M_PI / 180.;
+
+  //================================================================================
+  /*!
+   * \brief Set variables to groups of spin boxes
+   */
+  //================================================================================
+
+  void setText( const QString& vars, SMESHGUI_SpinBox** spins )
+  {
+    QStringList varList = vars.split( ':' );
+    for ( int i = 0; i < 3 && i < varList.count(); ++i )
+      if ( !varList[i].isEmpty() )
+        spins[i]->setText( varList[i] );
+  }
+  
+  //================================================================================
+  /*!
+   * \brief Computes more 2 axes by one
+   *  \param [in] iOk - index of a given axis
+   *  \param [in,out] dirs - directions of 3 axes
+   */
+  //================================================================================
+
+  void get3Dirs( int iOk, gp_XYZ dirs[3] )
+  {
+    dirs[ ( iOk+1 ) % 3 ] = dirs[ iOk ];
+
+    if ( Abs( dirs[ iOk ].Y() ) < 1e-100 &&
+         Abs( dirs[ iOk ].Z() ) < 1e-100 )
+      // dirs[ iOk ] || OX
+      dirs[ ( iOk+1 ) % 3 ].SetY( dirs[ iOk ].Y() + 1. );
+    else
+      dirs[ ( iOk+1 ) % 3 ].SetX( dirs[ iOk ].X() + 1. );
+
+    dirs[( iOk+2 ) % 3] = dirs[ iOk ] ^ dirs[ ( iOk+1 ) % 3 ];
+    dirs[( iOk+1 ) % 3] = dirs[ ( iOk+2 ) % 3 ] ^ dirs[ iOk ];
+  }
+}
+
+//================================================================================
+/*!
+ * \brief StdMeshersGUI_CartesianParamCreator constructor
+ */
+//================================================================================
 
 StdMeshersGUI_CartesianParamCreator::StdMeshersGUI_CartesianParamCreator(const QString& aHypType)
   : StdMeshersGUI_StdHypothesisCreator( aHypType ),
@@ -538,8 +692,23 @@ StdMeshersGUI_CartesianParamCreator::StdMeshersGUI_CartesianParamCreator(const Q
   myAxisTabs[0] = 0;
   myAxisTabs[1] = 0;
   myAxisTabs[2] = 0;
+
+  myAxesPreview = new SMESHGUI_MeshEditPreview( SMESH::GetViewWindow( SMESHGUI::GetSMESHGUI() ));
+  myAxesPreview->SetArrowShapeAndNb( /*nbArrows=*/3,
+                                     /*headLength=*/0.1,
+                                     /*headRadius=*/0.01,
+                                     /*start=*/0.,
+                                     /*labels=*/"XYZ");
+
+  myDirTic[0] = myDirTic[1] = myDirTic[2] = 0;
 }
 
+//================================================================================
+/*!
+ * \brief StdMeshersGUI_CartesianParamCreator destructor
+ */
+//================================================================================
+
 StdMeshersGUI_CartesianParamCreator::~StdMeshersGUI_CartesianParamCreator()
 {
   if ( myAxisTabs[0] ) delete myAxisTabs[0];
@@ -548,8 +717,16 @@ StdMeshersGUI_CartesianParamCreator::~StdMeshersGUI_CartesianParamCreator()
   myAxisTabs[0] = 0;
   myAxisTabs[1] = 0;
   myAxisTabs[2] = 0;
+
+  delete myAxesPreview;
 }
 
+//================================================================================
+/*!
+ * \brief Validate parameters
+ */
+//================================================================================
+
 bool StdMeshersGUI_CartesianParamCreator::checkParams( QString& msg ) const
 {
   if( !SMESHGUI_GenericHypothesisCreator::checkParams( msg ) )
@@ -568,9 +745,22 @@ bool StdMeshersGUI_CartesianParamCreator::checkParams( QString& msg ) const
   if ( !myAxisTabs[1]->checkParams( msg, hyp )) return false;
   if ( !myAxisTabs[2]->checkParams( msg, hyp )) return false;
 
+  StdMeshersGUI_CartesianParamCreator* me = (StdMeshersGUI_CartesianParamCreator*) this;
+  if ( !me->updateAxesPreview() )
+  {
+    msg = tr("INVALID_AXES_DIR");
+    return false;
+  }
+
   return true;
 }
 
+//================================================================================
+/*!
+ * \brief Create widgets
+ */
+//================================================================================
+
 QFrame* StdMeshersGUI_CartesianParamCreator::buildFrame()
 {
   QFrame* fr = new QFrame();
@@ -625,10 +815,154 @@ QFrame* StdMeshersGUI_CartesianParamCreator::buildFrame()
   tabWdg->addTab( myAxisTabs[ 1 ], tr( "AXIS_Y" ) );
   tabWdg->addTab( myAxisTabs[ 2 ], tr( "AXIS_Z" ) );
   argGroupLayout->addWidget( tabWdg, row, 0, 1, 2 );
+  row++;
+
+  QPixmap aPix = SMESHGUI::resourceMgr()->loadPixmap("SMESH", tr("ICON_SELECT"));
+
+  // 4) Fixed point
+  myFixedPointGrp = new QGroupBox( tr("FIXED_POINT"), fr );
+  myFixedPointGrp->setCheckable( true );
+  //QPushButton* pointBtn = new QPushButton( QIcon(aPix), "", myFixedPointGrp );
+  QLabel* pXLbl = new QLabel( tr("SMESH_X"), myFixedPointGrp );
+  QLabel* pYLbl = new QLabel( tr("SMESH_Y"), myFixedPointGrp );
+  QLabel* pZLbl = new QLabel( tr("SMESH_Z"), myFixedPointGrp );
+  for ( int i = 0; i < 3; ++i )
+  {
+    myPointSpin[i] = new SMESHGUI_SpinBox( myFixedPointGrp );
+    myPointSpin[i]->RangeStepAndValidator( -1e20, 1e20, 10 );
+    myPointSpin[i]->SetValue( 0. );
+  }
+  QHBoxLayout* aFixedPointLay = new QHBoxLayout( myFixedPointGrp );
+  aFixedPointLay->addWidget( pXLbl, 0, Qt::AlignRight );
+  aFixedPointLay->addWidget( myPointSpin[0], 1 );
+  aFixedPointLay->addWidget( pYLbl, 0, Qt::AlignRight );
+  aFixedPointLay->addWidget( myPointSpin[1], 1 );
+  aFixedPointLay->addWidget( pZLbl, 0, Qt::AlignRight );
+  aFixedPointLay->addWidget( myPointSpin[2], 1 );
+  argGroupLayout->addWidget( myFixedPointGrp, row, 0, 1, 2 );
+  row++;
+
+  // 5) Axes direction
+  QGroupBox* axesDirGrp = new QGroupBox( tr("AXES_DIRECTION"), fr );
+  QGridLayout* axisDirLay = new QGridLayout( axesDirGrp );
+  axisDirLay->setSpacing( SPACING );
+  axisDirLay->setMargin( MARGIN );
+  axisDirLay->setColumnStretch( 0, 2 );
+  // is orthogonal
+  myOrthogonalChk = new QCheckBox( tr("ORTHOGONAL_AXES"), axesDirGrp );
+  axisDirLay->addWidget( myOrthogonalChk, 0, 0, 1, 7 );
+  // axes
+  QLabel* axisLbl[3];
+  axisLbl[0] = new QLabel( tr( "AXIS_X"), axesDirGrp );
+  axisLbl[1] = new QLabel( tr( "AXIS_Y"), axesDirGrp );
+  axisLbl[2] = new QLabel( tr( "AXIS_Z"), axesDirGrp );
+  QLabel* dLbl[3];
+  myAxisBtnGrp = new QButtonGroup( axesDirGrp );
+  SMESHGUI_SpinBox** spins[3] = { &myXDirSpin[0], &myYDirSpin[0], &myZDirSpin[0] };
+  for ( int i = 0; i < 3; ++i )
+  {
+    QPushButton* axisBtn = new QPushButton( QIcon(aPix), "", axesDirGrp );
+    axisBtn->setCheckable( true );
+    myAxisBtnGrp->addButton( axisBtn, i );
+    myXDirSpin[i] = new SMESHGUI_SpinBox( axesDirGrp );
+    myYDirSpin[i] = new SMESHGUI_SpinBox( axesDirGrp );
+    myZDirSpin[i] = new SMESHGUI_SpinBox( axesDirGrp );
+    myXDirSpin[i]->RangeStepAndValidator( -1, 1, 0.1, "len_tol_precision" );
+    myYDirSpin[i]->RangeStepAndValidator( -1, 1, 0.1, "len_tol_precision" );
+    myZDirSpin[i]->RangeStepAndValidator( -1, 1, 0.1, "len_tol_precision" );
+    dLbl[0] = new QLabel( tr("SMESH_DX"), axesDirGrp );
+    dLbl[1] = new QLabel( tr("SMESH_DY"), axesDirGrp );
+    dLbl[2] = new QLabel( tr("SMESH_DZ"), axesDirGrp );
+    axisDirLay->addWidget( axisLbl[i],    i+1, 0 );
+    axisDirLay->addWidget( axisBtn,       i+1, 1 );
+    axisDirLay->addWidget( dLbl[0],       i+1, 2 );
+    axisDirLay->addWidget( dLbl[1],       i+1, 4 );
+    axisDirLay->addWidget( dLbl[2],       i+1, 6 );
+    axisDirLay->addWidget( myXDirSpin[i], 1, 3+i*2 );
+    axisDirLay->addWidget( myYDirSpin[i], 2, 3+i*2 );
+    axisDirLay->addWidget( myZDirSpin[i], 3, 3+i*2 );
+  }
+  axisDirLay->setColumnStretch( 3, 10 );
+  axisDirLay->setColumnStretch( 5, 10 );
+  axisDirLay->setColumnStretch( 7, 10 );
+
+  // set optimal axes
+  QPushButton* optimBtn = new QPushButton( tr("OPTIMAL_AXES"), axesDirGrp );
+  QPushButton* resetBtn = new QPushButton( tr("RESET_AXES"), axesDirGrp );
+  axisDirLay->addWidget( optimBtn, 4, 0, 1, 4 );
+  axisDirLay->addWidget( resetBtn, 4, 4, 1, 4 );
+
+  argGroupLayout->addWidget( axesDirGrp, row, 0, 1, 2 );
+  row++;
+
+  // Signals
+
+  LightApp_SelectionMgr* selMgr = SMESH::GetSelectionMgr( SMESHGUI::GetSMESHGUI() );
+
+  connect( selMgr,          SIGNAL( currentSelectionChanged()), SLOT( onSelectionChange()));
+  connect( myOrthogonalChk, SIGNAL( toggled(bool)),             SLOT( onOrthogonalAxes(bool)));
+  connect( optimBtn,        SIGNAL( clicked(bool)),             SLOT( onOptimalAxes(bool)));
+  connect( resetBtn,        SIGNAL( clicked(bool)),             SLOT( onResetAxes(bool)));
+  for ( int i = 0; i < 3; ++i )
+  {
+    connect( myXDirSpin[i], SIGNAL(valueChanged   (const QString&)),
+             this,          SLOT  (onAxisDirChange(const QString&)) );
+    connect( myYDirSpin[i], SIGNAL(valueChanged   (const QString&)),
+             this,          SLOT  (onAxisDirChange(const QString&)) );
+    connect( myZDirSpin[i], SIGNAL(valueChanged   (const QString&)),
+             this,          SLOT  (onAxisDirChange(const QString&)) );
+    connect( myAxisTabs[i], SIGNAL(gridModeChanged(int)),
+             this,          SLOT  (onGridModeChanged(int)));
+  }
+
+  // Show axes
+  myAxesLen = 1;
+  myOrigin[0] = myOrigin[1] = myOrigin[2] = 0.;
+  TopoDS_Shape shape;
+  QString shapeEntry = getMainShapeEntry();
+  if ( !shapeEntry.isEmpty() )
+  {
+    // find origin
+    Handle(SALOME_InteractiveObject) io =
+      new SALOME_InteractiveObject( shapeEntry.toStdString().c_str(), "GEOM" );
+    GEOM::GEOM_Object_var geomObj = SMESH::IObjectToInterface<GEOM::GEOM_Object>( io );
+    if ( GEOMBase::GetShape( geomObj, shape ) && !shape.IsNull())
+    {
+      Bnd_Box box;
+      BRepBndLib::Add( shape, box );
+      double max[3];
+      if ( !box.IsVoid() )
+      {
+        box.Get( myOrigin[0], myOrigin[1], myOrigin[2], max[0], max[1], max[2] );
+        gp_Pnt o( myOrigin[0], myOrigin[1], myOrigin[2] );
+        gp_Pnt x( max[0], max[1], max[2] );
+        myAxesLen = o.Distance( x );
+
+        double step = 1e20;
+        while ( step > myAxesLen / 5 )
+          step /= 10;
+        myPointSpin[0]->SetStep( step );
+        myPointSpin[1]->SetStep( step );
+        myPointSpin[2]->SetStep( step );
+      }
+    }
+  }
+  myAxisBtnGrp->button(0)->setEnabled( !shape.IsNull() );
+  myAxisBtnGrp->button(1)->setEnabled( !shape.IsNull() );
+  myAxisBtnGrp->button(2)->setEnabled( !shape.IsNull() );
+  optimBtn->setEnabled( !shape.IsNull() );
+
+  updateAxesPreview();
 
   return fr;
 }
 
+//================================================================================
+/*!
+ * \brief Tranfer parameters from hypothesis to widgets
+ */
+//================================================================================
+
 void StdMeshersGUI_CartesianParamCreator::retrieveParams() const
 {
   StdMeshers::StdMeshers_CartesianParameters3D_var h =
@@ -645,6 +979,7 @@ void StdMeshersGUI_CartesianParamCreator::retrieveParams() const
 
   myAddEdges->setChecked( h->GetToAddEdges() );
 
+  // grid definition
   for ( int ax = 0; ax < 3; ++ax )
   {
     if ( h->IsGridBySpacing( ax ))
@@ -660,11 +995,62 @@ void StdMeshersGUI_CartesianParamCreator::retrieveParams() const
       myAxisTabs[ax]->setCoordinates( coords );
     }
   }
+
+  // fixed point
+  SMESH::PointStruct fp;
+  StdMeshersGUI_CartesianParamCreator* me = (StdMeshersGUI_CartesianParamCreator*) this;
+  if ( h->GetFixedPoint( fp ))
+  {
+    me->myPointSpin[0]->SetValue( fp.x );
+    me->myPointSpin[1]->SetValue( fp.y );
+    me->myPointSpin[2]->SetValue( fp.z );
+    setText( getVariableName("GetFixedPoint"), &me->myPointSpin[0] );
+    myFixedPointGrp->setChecked( true );
+  }
+  else
+  {
+    myFixedPointGrp->setChecked( false );
+  }
+
+  // axes directions
+  SMESHGUI_SpinBox** spins[3] = { &me->myXDirSpin[0], &me->myYDirSpin[0], &me->myZDirSpin[0] };
+  SMESH::DirStruct axisDir[3];
+  h->GetAxesDirs( axisDir[0],
+                  axisDir[1],
+                  axisDir[2]);
+  QString vars = getVariableName("GetAxesDirs");
+  for ( int i = 0; i < 3; ++i )
+  {
+    spins[i][0]->SetValue( axisDir[i].PS.x );
+    spins[i][1]->SetValue( axisDir[i].PS.y );
+    spins[i][2]->SetValue( axisDir[i].PS.z );
+    setText( vars, spins[i] );
+
+    // cut off 3 used vars
+    if ( !vars.isEmpty() )
+    {
+      int ind = -1;
+      for ( int j = 0; j < 3; ++j )
+        if (( ind = vars.indexOf(':', ind+1 )) < 0 )
+          break;
+      if ( ind < 0 )
+        vars.clear();
+      else
+        vars.remove( 0, ind+1 );
+    }
+  }
+
   if ( dlg() )
     dlg()->setMinimumSize( dlg()->minimumSizeHint().width(),
                            dlg()->minimumSizeHint().height() );
 }
 
+//================================================================================
+/*!
+ * \brief Tranfer parameters from widgets to hypothesis
+ */
+//================================================================================
+
 QString StdMeshersGUI_CartesianParamCreator::storeParams() const
 {
   StdMeshers::StdMeshers_CartesianParameters3D_var h =
@@ -675,10 +1061,12 @@ QString StdMeshersGUI_CartesianParamCreator::storeParams() const
     if( isCreation() )
       SMESH::SetName( SMESH::FindSObject( h ), myName->text().toLatin1().constData() );
 
+    // threshold
     h->SetVarParameter( myThreshold->text().toLatin1().constData(), "SetSizeThreshold" );
     h->SetSizeThreshold( myThreshold->text().toDouble() );
     h->SetToAddEdges( myAddEdges->isChecked() );
 
+    // grid
     for ( int ax = 0; ax < 3; ++ax )
     {
       if ( myAxisTabs[ax]->isGridBySpacing())
@@ -694,6 +1082,40 @@ QString StdMeshersGUI_CartesianParamCreator::storeParams() const
         h->SetGrid( coords, ax );
       }
     }
+
+    // fixed point
+    QStringList params;
+    params << myPointSpin[0]->text();
+    params << myPointSpin[1]->text();
+    params << myPointSpin[2]->text();
+    h->SetVarParameter( params.join(":").toLatin1().constData(), "SetFixedPoint" );
+    params.clear();
+
+    SMESH::PointStruct ps;
+    ps.x = myPointSpin[0]->GetValue();
+    ps.y = myPointSpin[1]->GetValue();
+    ps.z = myPointSpin[2]->GetValue();
+    h->SetFixedPoint( ps, !myFixedPointGrp->isEnabled() || !myFixedPointGrp->isChecked() );
+
+    // axes directions
+    SMESHGUI_SpinBox* const * spins[3] = { &myXDirSpin[0], &myYDirSpin[0], &myZDirSpin[0] };
+    for ( int ax = 0; ax < 3; ++ax )
+    {
+      params << spins[ax][0]->text();
+      params << spins[ax][1]->text();
+      params << spins[ax][2]->text();
+    }
+    h->SetVarParameter( params.join(":").toLatin1().constData(), "SetAxesDirs" );
+
+    SMESH::DirStruct axDir[3];
+    for ( int ax = 0; ax < 3; ++ax )
+    {
+      axDir[ax].PS.x = spins[ax][0]->GetValue();
+      axDir[ax].PS.y = spins[ax][1]->GetValue();
+      axDir[ax].PS.z = spins[ax][2]->GetValue();
+    }
+    h->SetAxesDirs( axDir[0], axDir[1], axDir[2] );
+
   }
   catch(const SALOME::SALOME_Exception& ex)
   {
@@ -702,7 +1124,298 @@ QString StdMeshersGUI_CartesianParamCreator::storeParams() const
   return "";
 }
 
+//================================================================================
+/*!
+ * \brief Returns a name of help page
+ */
+//================================================================================
+
 QString StdMeshersGUI_CartesianParamCreator::helpPage() const
 {
   return "cartesian_algo_page.html#cartesian_hyp_anchor";
 }
+
+//================================================================================
+/*!
+ * \brief Show axes if they are OK
+ */
+//================================================================================
+
+bool StdMeshersGUI_CartesianParamCreator::updateAxesPreview()
+{
+  bool isOk = true;
+  gp_Ax1 axes[3];
+  SMESHGUI_SpinBox** spins[3] = { &myXDirSpin[0], &myYDirSpin[0], &myZDirSpin[0] };
+  for ( int i = 0; i < 3 && isOk; ++i )
+  {
+    gp_XYZ dir( spins[i][0]->GetValue(),
+                spins[i][1]->GetValue(),
+                spins[i][2]->GetValue());
+    if (( isOk = ( dir.Modulus() > 1e-100 )))
+      axes[i].SetDirection( gp_Dir( dir ));
+
+    axes[i].SetLocation ( gp_Pnt( myOrigin[0],
+                                  myOrigin[1],
+                                  myOrigin[2]));
+  }
+  gp_Vec norm01 = axes[0].Direction().XYZ() ^ axes[1].Direction().XYZ();
+  gp_Vec norm12 = axes[1].Direction().XYZ() ^ axes[2].Direction().XYZ();
+  if ( isOk )
+    isOk = ( !axes[0].Direction().IsParallel( axes[1].Direction(), theAngTol ) &&
+             !axes[1].Direction().IsParallel( axes[2].Direction(), theAngTol ) &&
+             !axes[2].Direction().IsParallel( axes[0].Direction(), theAngTol ) &&
+             !norm01.IsParallel( norm12, theAngTol ) );
+  if ( isOk )
+    myAxesPreview->SetArrows( axes, myAxesLen );
+
+  myAxesPreview->SetVisibility( isOk );
+
+  return isOk;
+}
+
+//================================================================================
+/*!
+ * \brief Makes axes orthogonal if necessary
+ */
+//================================================================================
+
+void StdMeshersGUI_CartesianParamCreator::onOrthogonalAxes(bool isOrtho)
+{
+  if ( !isOrtho )
+  {
+    updateAxesPreview();
+    return;
+  }
+
+  std::multimap< int, int > ageOfAxis;
+  gp_XYZ dirs[3];
+  SMESHGUI_SpinBox** spins[3] = { &myXDirSpin[0], &myYDirSpin[0], &myZDirSpin[0] };
+  int nbOk = 0, isOk;
+  for ( int iAx = 0; iAx < 3; ++iAx )
+  {
+    dirs[iAx].SetCoord( spins[iAx][0]->GetValue(),
+                        spins[iAx][1]->GetValue(),
+                        spins[iAx][2]->GetValue());
+    if (( isOk = ( dirs[iAx].Modulus() > 1e-100 )))
+      ageOfAxis.insert( std::make_pair( myDirTic[iAx], iAx ));
+    else
+      ageOfAxis.insert( std::make_pair( -1, iAx ));
+    nbOk += isOk;
+  }
+  switch ( nbOk )
+  {
+  case 0:
+  {
+    dirs[0].SetCoord( 1, 0, 0 );
+    dirs[1].SetCoord( 0, 1, 0 );
+    dirs[2].SetCoord( 0, 0, 1 );
+    break;
+  }
+  case 1:
+  {
+    int iOk = ageOfAxis.rbegin()->second;
+    get3Dirs( iOk, dirs );
+    break;
+  }
+  default:
+    std::multimap< int, int >::reverse_iterator ag2ax = ageOfAxis.rbegin();
+    int iOk1 = ag2ax->second;
+    int iOk2 = (++ag2ax)->second;
+    int iKo  = (++ag2ax)->second;
+    if ( gp_Vec( dirs[ iOk1 ]).IsParallel( gp_Vec( dirs[ iOk2 ]), theAngTol ))
+      std::swap( iOk2, iKo );
+    if ( gp_Vec( dirs[ iOk1 ]).IsParallel( gp_Vec( dirs[ iOk2 ]), theAngTol ))
+    {
+      get3Dirs( iOk1, dirs );
+    }
+    else
+    {
+      dirs[ iKo  ] = dirs[ iOk1 ] ^ dirs[ iOk2 ];
+      dirs[ iOk2 ] = dirs[ iKo  ] ^ dirs[ iOk1 ];
+      if ( ( iOk1+1 ) % 3 != iOk2 )
+        dirs[ iKo ].Reverse();
+    }
+  }
+
+  for ( int iAx = 0; iAx < 3; ++iAx )
+  {
+    double size = dirs[iAx].Modulus();
+    if ( size > 1e-100 )
+      dirs[iAx] /= size;
+    for (int i = 0; i < 3; ++i )
+    {
+      bool isBlocked = spins[iAx][i]->blockSignals( true );
+      spins[iAx][i]->SetValue( dirs[iAx].Coord( i+1 ));
+      spins[iAx][i]->blockSignals( isBlocked );
+    }
+  }
+
+  updateAxesPreview();
+}
+
+//================================================================================
+/*!
+ * \brief Increment myDirTic and update the preview of axes
+ */
+//================================================================================
+
+void StdMeshersGUI_CartesianParamCreator::onAxisDirChange(const QString&)
+{
+  QObject* changedSpin = sender();
+  SMESHGUI_SpinBox** spins[3] = { &myXDirSpin[0], &myYDirSpin[0], &myZDirSpin[0] };
+  for ( int iAx = 0; iAx < 3; ++iAx )
+    if ( spins[iAx][0] == changedSpin ||
+         spins[iAx][1] == changedSpin ||
+         spins[iAx][2] == changedSpin )
+    {
+      myDirTic[ iAx ] = 1 + Max( Max( myDirTic[0], myDirTic[1] ), myDirTic[2] );
+      break;
+    }
+
+  onOrthogonalAxes( myOrthogonalChk->isChecked() );
+}
+
+//================================================================================
+/*!
+ * \brief Sets axis direction by a selected EDGE
+ */
+//================================================================================
+
+void StdMeshersGUI_CartesianParamCreator::onSelectionChange()
+{
+  int iAxis = myAxisBtnGrp->checkedId();
+  if ( iAxis < 0 )
+    return;
+
+  SALOME_ListIO aList;
+  SMESHGUI::GetSMESHGUI()->selectionMgr()->selectedObjects(aList);
+
+  TopoDS_Shape edge, shape;
+  for( SALOME_ListIteratorOfListIO anIt( aList ); anIt.More(); anIt.Next() )
+  {
+    GEOM::GEOM_Object_var go = SMESH::IObjectToInterface<GEOM::GEOM_Object>( anIt.Value() );
+    if ( GEOMBase::GetShape( go, shape ) && shape.ShapeType() == TopAbs_EDGE )
+    {
+      if ( !edge.IsNull() )
+        return; // several EDGEs selected
+      edge = shape;
+    }
+  }
+  if ( edge.IsNull() )
+    return;
+
+  TopoDS_Shape vv[2];
+  TopoDS_Iterator vIt( edge );
+  for ( ; vIt.More() && vv[1].IsNull(); vIt.Next() )
+    vv[ !vv[0].IsNull() ] = vIt.Value();
+
+  gp_Pnt pp[2];
+  if ( !GEOMBase::VertexToPoint( vv[0], pp[0] ) ||
+       !GEOMBase::VertexToPoint( vv[1], pp[1] ))
+    return;
+
+  SMESHGUI_SpinBox** spins[3] = { &myXDirSpin[0], &myYDirSpin[0], &myZDirSpin[0] };
+
+  gp_Vec newDir( pp[0], pp[1] );
+  gp_Vec curDir( spins[iAxis][0]->GetValue(),
+                 spins[iAxis][1]->GetValue(),
+                 spins[iAxis][2]->GetValue());
+  if ( newDir * curDir < 0 )
+    newDir.Reverse();
+
+  double size = newDir.Magnitude();
+  if ( size < 1e-100 )
+    return;
+  newDir /= size;
+
+  for (int i = 0; i < 3; ++i )
+  {
+    bool isBlocked = spins[iAxis][i]->blockSignals( true );
+    spins[iAxis][i]->SetValue( newDir.Coord( i+1 ));
+    spins[iAxis][i]->blockSignals( isBlocked );
+  }
+  myDirTic[ iAxis ] = 1 + Max( Max( myDirTic[0], myDirTic[1] ), myDirTic[2] );
+
+  onOrthogonalAxes( myOrthogonalChk->isChecked() );
+}
+
+//================================================================================
+/*!
+ * \brief Sets axes at which number of hexahedra is maximal
+ */
+//================================================================================
+
+void StdMeshersGUI_CartesianParamCreator::onOptimalAxes(bool)
+{
+  StdMeshers::StdMeshers_CartesianParameters3D_var h =
+    StdMeshers::StdMeshers_CartesianParameters3D::_narrow( hypothesis() );
+  if ( h->_is_nil() )
+    return;
+
+  QString shapeEntry = getMainShapeEntry();
+  if ( shapeEntry.isEmpty() )
+    return;
+
+  Handle(SALOME_InteractiveObject) io =
+    new SALOME_InteractiveObject( shapeEntry.toStdString().c_str(), "GEOM" );
+  GEOM::GEOM_Object_var geomObj = SMESH::IObjectToInterface<GEOM::GEOM_Object>( io );
+  if ( geomObj->_is_nil() )
+    return;
+
+  SMESH::DirStruct axDirs[3];
+  h->ComputeOptimalAxesDirs( geomObj,
+                             myOrthogonalChk->isChecked(),
+                             axDirs[0],
+                             axDirs[1],
+                             axDirs[2]);
+
+  SMESHGUI_SpinBox** spins[3] = { &myXDirSpin[0], &myYDirSpin[0], &myZDirSpin[0] };
+  for ( int iAx = 0; iAx < 3; ++iAx )
+  {
+    double coords[3] = { axDirs[iAx].PS.x, axDirs[iAx].PS.y, axDirs[iAx].PS.z };
+    for (int i = 0; i < 3; ++i )
+    {
+      bool isBlocked = spins[iAx][i]->blockSignals( true );
+      spins[iAx][i]->SetValue( coords[ i ]);
+      spins[iAx][i]->blockSignals( isBlocked );
+    }
+  }
+  updateAxesPreview();
+}
+
+//================================================================================
+/*!
+ * \brief Sets axes || to the axes of global CS
+ */
+//================================================================================
+
+void StdMeshersGUI_CartesianParamCreator::onResetAxes(bool)
+{
+  SMESHGUI_SpinBox** spins[3] = { &myXDirSpin[0], &myYDirSpin[0], &myZDirSpin[0] };
+  for ( int iAx = 0; iAx < 3; ++iAx )
+  {
+    for (int i = 0; i < 3; ++i )
+    {
+      bool isBlocked = spins[iAx][i]->blockSignals( true );
+      spins[iAx][i]->SetValue( iAx == i ? 1. : 0. );
+      spins[iAx][i]->blockSignals( isBlocked );
+    }
+    myDirTic[iAx] = 0;
+  }
+  updateAxesPreview();
+}
+
+//================================================================================
+/*!
+ * \brief SLOT called when the grid definintion mode changes
+ */
+//================================================================================
+
+void StdMeshersGUI_CartesianParamCreator::onGridModeChanged(int)
+{
+  bool haveSpacing = ( myAxisTabs[0]->isGridBySpacing() ||
+                       myAxisTabs[1]->isGridBySpacing() ||
+                       myAxisTabs[2]->isGridBySpacing() );
+
+  myFixedPointGrp->setEnabled( haveSpacing );
+}
index 1187bb9..1a9fcd5 100644 (file)
@@ -42,6 +42,7 @@
 class QAbstractItemModel;
 class QButtonGroup;
 class QCheckBox;
+class QGroupBox;
 class QLineEdit;
 class QListWidget;
 class QListWidgetItem;
@@ -51,6 +52,7 @@ class QStyleOptionViewItem;
 class QTreeWidget;
 class QTreeWidgetItem;
 class QWidget;
+class SMESHGUI_MeshEditPreview;
 class SMESHGUI_SpinBox;
 
 namespace StdMeshersGUI
@@ -79,6 +81,9 @@ namespace StdMeshersGUI
     SMESH::double_array* getCoordinates();
     void getSpacing(SMESH::string_array_out funs, SMESH::double_array_out points) const;
 
+  signals:
+    void gridModeChanged(int);
+
   private slots:
     void onInsert();
     void onDelete();
@@ -126,19 +131,40 @@ public:
   StdMeshersGUI_CartesianParamCreator( const QString& aHypType );
   virtual ~StdMeshersGUI_CartesianParamCreator();
 
-  virtual bool    checkParams( QString& ) const;
-  virtual QString helpPage() const;
+  virtual bool     checkParams( QString& ) const;
+  virtual QString  helpPage() const;
 
 protected:
   virtual QFrame*  buildFrame();
   virtual void     retrieveParams() const;
   virtual QString  storeParams() const;
 
+private slots:
+  bool             updateAxesPreview();
+  void             onOrthogonalAxes(bool);
+  void             onAxisDirChange(const QString&);
+  void             onSelectionChange();
+  void             onOptimalAxes(bool);
+  void             onResetAxes(bool);
+  void             onGridModeChanged(int);
+
 private:
   QLineEdit*                  myName;
   SMESHGUI_SpinBox*           myThreshold;
   QCheckBox*                  myAddEdges;
+
   StdMeshersGUI::GridAxisTab* myAxisTabs[3];
+  QGroupBox*                  myFixedPointGrp;
+  SMESHGUI_SpinBox*           myPointSpin[3];
+  QCheckBox*                  myOrthogonalChk;
+  QButtonGroup*               myAxisBtnGrp;
+  SMESHGUI_SpinBox*           myXDirSpin[3];
+  SMESHGUI_SpinBox*           myYDirSpin[3];
+  SMESHGUI_SpinBox*           myZDirSpin[3];
+  SMESHGUI_MeshEditPreview*   myAxesPreview;
+  double                      myOrigin[3];
+  double                      myAxesLen;
+  int                         myDirTic[3];
 };
 
 #endif // STDMESHERSGUI_CartesianParamCreator_H
index f2ccb30..8b94762 100644 (file)
         <source>AXIS_Z</source>
         <translation>Axis Z</translation>
     </message>
+    <message>
+        <source>INVALID_AXES_DIR</source>
+        <translation>Invalid directions of axes</translation>
+    </message>
+    <message>
+        <source>FIXED_POINT</source>
+        <translation>Fixed Point</translation>
+    </message>
+    <message>
+        <source>AXES_DIRECTION</source>
+        <translation>Directions of Axes</translation>
+    </message>
+    <message>
+        <source>ORTHOGONAL_AXES</source>
+        <translation>Orthogonal Axes</translation>
+    </message>
+    <message>
+        <source>OPTIMAL_AXES</source>
+        <translation>Optimal Axes</translation>
+    </message>
+    <message>
+        <source>RESET_AXES</source>
+        <translation>Reset</translation>
+    </message>
 </context>
 <context>
     <name>StdMeshersGUI::GridAxisTab</name>
index 3db7340..5b383a0 100644 (file)
@@ -225,6 +225,102 @@ void StdMeshers_CartesianParameters3D_i::GetGridSpacing(SMESH::string_array_out
 }
 
 //=======================================================================
+//function : SetAxesDirs
+//purpose  : Set custom direction of axes
+//=======================================================================
+
+void StdMeshers_CartesianParameters3D_i::SetAxesDirs(const SMESH::DirStruct& xDir,
+                                                     const SMESH::DirStruct& yDir,
+                                                     const SMESH::DirStruct& zDir)
+  throw (SALOME::SALOME_Exception)
+{
+  double coords[9];
+  coords[0] = xDir.PS.x;
+  coords[1] = xDir.PS.y;
+  coords[2] = xDir.PS.z;
+  coords[3] = yDir.PS.x;
+  coords[4] = yDir.PS.y;
+  coords[5] = yDir.PS.z;
+  coords[6] = zDir.PS.x;
+  coords[7] = zDir.PS.y;
+  coords[8] = zDir.PS.z;
+  try {
+    this->GetImpl()->SetAxisDirs(coords);
+
+    SMESH::TPythonDump() << _this() << ".SetAxesDirs( "
+                         << xDir << ", "
+                         << yDir << ", "
+                         << zDir << " )";
+  }
+  catch ( SALOME_Exception& S_ex ) {
+    THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), SALOME::BAD_PARAM );
+  }
+}
+
+//=======================================================================
+//function : GetAxesDirs
+//purpose  : Returns direction of axes
+//=======================================================================
+
+void StdMeshers_CartesianParameters3D_i::GetAxesDirs(SMESH::DirStruct& xDir,
+                                                     SMESH::DirStruct& yDir,
+                                                     SMESH::DirStruct& zDir)
+{
+  const double* coords = GetImpl()->GetAxisDirs();
+  xDir.PS.x = coords[0];
+  xDir.PS.y = coords[1];
+  xDir.PS.z = coords[2];
+  yDir.PS.x = coords[3];
+  yDir.PS.y = coords[4];
+  yDir.PS.z = coords[5];
+  zDir.PS.x = coords[6];
+  zDir.PS.y = coords[7];
+  zDir.PS.z = coords[8];
+}
+
+//=======================================================================
+//function : SetFixedPoint
+//purpose  : * Set/unset a fixed point, at which a node will be created provided that grid
+//           * is defined by spacing in all directions
+//=======================================================================
+
+void StdMeshers_CartesianParameters3D_i::SetFixedPoint(const SMESH::PointStruct& ps,
+                                                       CORBA::Boolean            toUnset)
+{
+  double p[3] = { ps.x, ps.y, ps.z };
+  GetImpl()->SetFixedPoint( p, toUnset );
+
+  if ( toUnset )
+    SMESH::TPythonDump() << _this() << ".SetFixedPoint([0,0,0], True)";
+  else
+    SMESH::TPythonDump() << _this() << ".SetFixedPoint(" << p << ", " << toUnset << " )";
+}
+
+//=======================================================================
+//function : GetFixedPoint
+//purpose  : Returns a fixed point
+//=======================================================================
+
+CORBA::Boolean StdMeshers_CartesianParameters3D_i::GetFixedPoint(SMESH::PointStruct& ps)
+{
+  double p[3];
+  if ( GetImpl()->GetFixedPoint( p ) )
+  {
+    ps.x = p[0];
+    ps.y = p[1];
+    ps.z = p[2];
+    return true;
+  }
+  else
+  {
+    ps.x = 0.;
+    ps.y = 0.;
+    ps.z = 0.;
+  }
+  return false;
+}
+
+//=======================================================================
 //function : SetToAddEdges
 //purpose  : Enables implementation of geometrical edges into the mesh.
 //=======================================================================
@@ -248,7 +344,7 @@ CORBA::Boolean StdMeshers_CartesianParameters3D_i::GetToAddEdges()
 
 //=======================================================================
 //function : IsGridBySpacing
-//purpose  : Return true if the grid is defined by spacing functions and 
+//purpose  : Return true if the grid is defined by spacing functions and
 //           not by node coordinates
 //=======================================================================
 
@@ -258,6 +354,37 @@ CORBA::Boolean StdMeshers_CartesianParameters3D_i::IsGridBySpacing(CORBA::Short
 }
 
 //=======================================================================
+//function : ComputeOptimalAxesDirs
+//purpose  : Returns axes at which number of hexahedra is maximal
+//=======================================================================
+
+void StdMeshers_CartesianParameters3D_i::
+ComputeOptimalAxesDirs(GEOM::GEOM_Object_ptr go,
+                       CORBA::Boolean        isOrthogonal,
+                       SMESH::DirStruct&     xDir,
+                       SMESH::DirStruct&     yDir,
+                       SMESH::DirStruct&     zDir)
+  throw (SALOME::SALOME_Exception)
+{
+  TopoDS_Shape shape = SMESH_Gen_i::GetSMESHGen()->GeomObjectToShape( go );
+  if ( shape.IsNull() )
+    THROW_SALOME_CORBA_EXCEPTION( "Null shape", SALOME::BAD_PARAM );
+
+  double c[9];
+  ::StdMeshers_CartesianParameters3D::ComputeOptimalAxesDirs( shape, isOrthogonal, c );
+
+  xDir.PS.x = c[0];
+  xDir.PS.y = c[1];
+  xDir.PS.z = c[2];
+  yDir.PS.x = c[3];
+  yDir.PS.y = c[4];
+  yDir.PS.z = c[5];
+  zDir.PS.x = c[6];
+  zDir.PS.y = c[7];
+  zDir.PS.z = c[8];
+}
+
+//=======================================================================
 //function : ComputeCoordinates
 //purpose  : Computes node coordinates by spacing functions
 //=======================================================================
@@ -268,13 +395,13 @@ StdMeshers_CartesianParameters3D_i::ComputeCoordinates(CORBA::Double
                                                        const SMESH::string_array& spaceFuns,
                                                        const SMESH::double_array& points,
                                                        const char*                axisName )
-    throw (SALOME::SALOME_Exception)
+  throw (SALOME::SALOME_Exception)
 {
   vector<string> xFuns;
   vector<double> xPoints, coords;
   _array2vec( spaceFuns, xFuns, (const char*) );
   _array2vec( points, xPoints, );
-  
+
   try {
     this->GetImpl()->ComputeCoordinates( x0, x1, xFuns, xPoints, coords, axisName );
   }
index abdda05..74df5de 100644 (file)
@@ -83,6 +83,22 @@ class STDMESHERS_I_EXPORT StdMeshers_CartesianParameters3D_i:
   void GetGridSpacing(SMESH::string_array_out xSpaceFunctions,
                       SMESH::double_array_out xInternalPoints,
                       CORBA::Short            axis) throw (SALOME::SALOME_Exception);
+  /*!
+   * Set custom direction of axes
+   */
+  void SetAxesDirs(const SMESH::DirStruct& x,
+                   const SMESH::DirStruct& y,
+                   const SMESH::DirStruct& z) throw (SALOME::SALOME_Exception);
+  void GetAxesDirs(SMESH::DirStruct& x,
+                   SMESH::DirStruct& y,
+                   SMESH::DirStruct& z);
+  /*!
+   * Set/unset a fixed point, at which a node will be created provided that grid
+   * is defined by spacing in all directions
+   */
+  void SetFixedPoint(const ::SMESH::PointStruct& p, CORBA::Boolean toUnset);
+  CORBA::Boolean GetFixedPoint(::SMESH::PointStruct& p);
+
 
   /*!
    * \brief Enables implementation of geometrical edges into the mesh. If this feature
@@ -99,6 +115,14 @@ class STDMESHERS_I_EXPORT StdMeshers_CartesianParameters3D_i:
   CORBA::Boolean IsGridBySpacing(CORBA::Short axis);
 
   /*!
+   * Returns axes at which number of hexahedra is maximal
+   */
+  void ComputeOptimalAxesDirs(GEOM::GEOM_Object_ptr shape,
+                              CORBA::Boolean        isOrthogonal,
+                              SMESH::DirStruct&     x,
+                              SMESH::DirStruct&     y,
+                              SMESH::DirStruct&     z) throw (SALOME::SALOME_Exception);
+  /*!
    * \brief Computes node coordinates by spacing functions
    *  \param x0 - lower coordinate
    *  \param x1 - upper coordinate