From: eap Date: Tue, 4 Feb 2014 13:02:26 +0000 (+0000) Subject: 22359: Body Fitting algorithm: grid orientation X-Git-Tag: V7_4_0a1~91 X-Git-Url: http://git.salome-platform.org/gitweb/?a=commitdiff_plain;h=cd3ffac3fabc68b4d1dee2ad199302f04b20d2c8;p=modules%2Fsmesh.git 22359: Body Fitting algorithm: grid orientation 22358: Body Fitting algorithm: origin point of grid defined by spacing --- diff --git a/doc/salome/examples/cartesian_algo.py b/doc/salome/examples/cartesian_algo.py index dc0dc23cf..e5651cb67 100644 --- a/doc/salome/examples/cartesian_algo.py +++ b/doc/salome/examples/cartesian_algo.py @@ -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() diff --git a/doc/salome/gui/SMESH/images/cartesian3D_hyp.png b/doc/salome/gui/SMESH/images/cartesian3D_hyp.png index b8373ed6d..c9a605fea 100644 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 index 000000000..59ac9ba6f Binary files /dev/null and b/doc/salome/gui/SMESH/images/cartesian_implement_edge.png differ diff --git a/doc/salome/gui/SMESH/input/cartesian_algo.doc b/doc/salome/gui/SMESH/input/cartesian_algo.doc index 3240150c7..dfb9acad4 100644 --- a/doc/salome/gui/SMESH/input/cartesian_algo.doc +++ b/doc/salome/gui/SMESH/input/cartesian_algo.doc @@ -31,7 +31,7 @@ nodes are inside and some outside. To apply this algorithm when you define your mesh, select Body Fitting in the list of 3D algorithms and click "Add Hypothesis" button and "Body Fitting Parameters"" menu - item. Dialog of Body Fitting Parameters +item. Dialog of Body Fitting Parameters hypothesis will appear.
@@ -42,34 +42,64 @@ To apply this algorithm when you define your mesh, select Body This dialog allows to define
    -
  • \b Name of the algorithm
  • -
  • 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.
  • -
  • Cartesian structured grid. Each grid axis is defined - individually. Definition mode chooses a way of grid - definition:
      -
    • 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.
    • -
    • You can define the \b Spacing of a grid as an algebraic formula - f(t) 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; +
    • \b Name of the algorithm.
    • +
    • 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.
    • +
    • Implement Edges check-box activates incorporation of + geometrical edges in the mesh. +\image html cartesian_implement_edge.png "'Implement Edges' switched off (left) and on (right)" +
    • Cartesian structured grid. Location of nodes along each grid axis + is defined individually. Definition mode chooses a way of + grid definition: +
        +
      • 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.
      • +
      • You can define the \b Spacing of a grid as an algebraic formula + f(t) 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. -
      -
    • + 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. +
    +
  • +
  • Coordinates of a Fixed Point. 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 Fixed Point. If two directions are defined by spacing, + then there will be at least a link between mesh nodes passing through + the Fixed Point. If only one direction is defined by spacing, + then there will be at least an element facet passing through + the Fixed Point. If no directions are defined by spacing, + Fixed Point is disabled.
  • +
  • Directions of Axes. You can set up almost any + directions of grid axes that can help in generation as many as + possible hexahedral elements. +
      +
    • Orthogonal Axes check-box, if activated, keeps the + axes orthogonal during their modification.
    • +
    • 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.
    • +
    • Optimal Axes button runs an algorithm that tries to + set the axes so that a number of generated hexahedra to be + maximal.
    • +
    • Reset button returns the axes in a default position + parallel to the axes of the Global Coordinate System.
    • +

-See Also a sample TUI Script of a -\ref tui_cartesian_algo "Usage of Body Fitting algorithm". +See Also a sample TUI Script of a +\ref tui_cartesian_algo "Usage of Body Fitting algorithm". */ diff --git a/idl/SMESH_BasicHypothesis.idl b/idl/SMESH_BasicHypothesis.idl index 186b06681..27cfbd368 100644 --- a/idl/SMESH_BasicHypothesis.idl +++ b/idl/SMESH_BasicHypothesis.idl @@ -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); /*! @@ -985,6 +986,22 @@ module StdMeshers void GetGridSpacing(out SMESH::string_array spaceFunctions, 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 @@ -993,6 +1010,16 @@ module StdMeshers void SetToAddEdges(in boolean toAdd); 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 diff --git a/src/SMESH_I/SMESH_2smeshpy.cxx b/src/SMESH_I/SMESH_2smeshpy.cxx index 15e2a183f..b8358a795 100644 --- a/src/SMESH_I/SMESH_2smeshpy.cxx +++ b/src/SMESH_I/SMESH_2smeshpy.cxx @@ -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; } } diff --git a/src/SMESH_SWIG/StdMeshersBuilder.py b/src/SMESH_SWIG/StdMeshersBuilder.py index 4e5b2d217..265a9801b 100644 --- a/src/SMESH_SWIG/StdMeshersBuilder.py +++ b/src/SMESH_SWIG/StdMeshersBuilder.py @@ -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 diff --git a/src/StdMeshers/StdMeshers_CartesianParameters3D.cxx b/src/StdMeshers/StdMeshers_CartesianParameters3D.cxx index 369d4e424..00cd8615e 100644 --- a/src/StdMeshers/StdMeshers_CartesianParameters3D.cxx +++ b/src/StdMeshers/StdMeshers_CartesianParameters3D.cxx @@ -32,10 +32,24 @@ #include "utilities.h" +#include #include +#include +#include #include +#include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include #include 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 @@ -177,6 +212,36 @@ void StdMeshers_CartesianParameters3D::SetGridSpacing(std::vector& xSpac NotifySubMeshesHypothesisModification(); } +//======================================================================= +//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 @@ -322,9 +387,199 @@ void StdMeshers_CartesianParameters3D::GetCoordinates(std::vector& xNode zNodes = _coords[2]; } +//======================================================================= +//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; } diff --git a/src/StdMeshers/StdMeshers_CartesianParameters3D.hxx b/src/StdMeshers/StdMeshers_CartesianParameters3D.hxx index 98970956b..3ef431cc9 100644 --- a/src/StdMeshers/StdMeshers_CartesianParameters3D.hxx +++ b/src/StdMeshers/StdMeshers_CartesianParameters3D.hxx @@ -78,6 +78,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 @@ -101,13 +108,21 @@ public: std::vector& 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 _spaceFunctions[3]; std::vector _internalPoints[3]; - double _axisDirs[9]; + double _axisDirs [9]; + double _fixedPoint[3]; double _sizeThreshold; bool _toAddEdges; diff --git a/src/StdMeshers/StdMeshers_Cartesian_3D.cxx b/src/StdMeshers/StdMeshers_Cartesian_3D.cxx index 36b2e2d48..b8c4a92ff 100644 --- a/src/StdMeshers/StdMeshers_Cartesian_3D.cxx +++ b/src/StdMeshers/StdMeshers_Cartesian_3D.cxx @@ -37,12 +37,17 @@ #include #include +#include + #include #include #include #include +#include #include +#include #include +#include #include #include #include @@ -67,6 +72,7 @@ #include #include #include +#include #include #include #include @@ -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& yCoords, const vector& 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& 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 @@ -721,16 +728,6 @@ namespace return prevIsOut; // _transition == Trans_TANGENT } //================================================================================ - /* - * 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 */ @@ -793,11 +790,12 @@ namespace const vector& yCoords, const vector& 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,17 +867,34 @@ 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 */ @@ -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 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 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 diff --git a/src/StdMeshersGUI/StdMeshersGUI_CartesianParamCreator.cxx b/src/StdMeshersGUI/StdMeshersGUI_CartesianParamCreator.cxx index c2b58de8d..be926470e 100644 --- a/src/StdMeshersGUI/StdMeshersGUI_CartesianParamCreator.cxx +++ b/src/StdMeshersGUI/StdMeshersGUI_CartesianParamCreator.cxx @@ -25,18 +25,33 @@ // SMESH includes #include "StdMeshersGUI_CartesianParamCreator.h" -#include -#include -#include -#include +#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 -#include +#include #include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include // Qt includes #include @@ -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( 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( 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( 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( 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 ); +} diff --git a/src/StdMeshersGUI/StdMeshersGUI_CartesianParamCreator.h b/src/StdMeshersGUI/StdMeshersGUI_CartesianParamCreator.h index 1187bb90b..1a9fcd51e 100644 --- a/src/StdMeshersGUI/StdMeshersGUI_CartesianParamCreator.h +++ b/src/StdMeshersGUI/StdMeshersGUI_CartesianParamCreator.h @@ -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 diff --git a/src/StdMeshersGUI/StdMeshers_msg_en.ts b/src/StdMeshersGUI/StdMeshers_msg_en.ts index f2ccb30f8..8b94762e7 100644 --- a/src/StdMeshersGUI/StdMeshers_msg_en.ts +++ b/src/StdMeshersGUI/StdMeshers_msg_en.ts @@ -529,6 +529,30 @@ AXIS_Z Axis Z + + INVALID_AXES_DIR + Invalid directions of axes + + + FIXED_POINT + Fixed Point + + + AXES_DIRECTION + Directions of Axes + + + ORTHOGONAL_AXES + Orthogonal Axes + + + OPTIMAL_AXES + Optimal Axes + + + RESET_AXES + Reset + StdMeshersGUI::GridAxisTab diff --git a/src/StdMeshers_I/StdMeshers_CartesianParameters3D_i.cxx b/src/StdMeshers_I/StdMeshers_CartesianParameters3D_i.cxx index 3db734054..5b383a0f5 100644 --- a/src/StdMeshers_I/StdMeshers_CartesianParameters3D_i.cxx +++ b/src/StdMeshers_I/StdMeshers_CartesianParameters3D_i.cxx @@ -224,6 +224,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 //======================================================================= @@ -257,6 +353,37 @@ CORBA::Boolean StdMeshers_CartesianParameters3D_i::IsGridBySpacing(CORBA::Short return this->GetImpl()->IsGridBySpacing(axis); } +//======================================================================= +//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 xFuns; vector xPoints, coords; _array2vec( spaceFuns, xFuns, (const char*) ); _array2vec( points, xPoints, ); - + try { this->GetImpl()->ComputeCoordinates( x0, x1, xFuns, xPoints, coords, axisName ); } diff --git a/src/StdMeshers_I/StdMeshers_CartesianParameters3D_i.hxx b/src/StdMeshers_I/StdMeshers_CartesianParameters3D_i.hxx index abdda05f4..74df5de91 100644 --- a/src/StdMeshers_I/StdMeshers_CartesianParameters3D_i.hxx +++ b/src/StdMeshers_I/StdMeshers_CartesianParameters3D_i.hxx @@ -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 @@ -98,6 +114,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