Merge branch 'master' into V7_5_BR

This commit is contained in:
vsr 2014-11-07 18:39:11 +03:00
commit b21a1e5b25
15 changed files with 346 additions and 144 deletions

View File

@ -17,8 +17,10 @@
# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
#
# examples that cant be used for testing because they use external mesher plug-ins
SET(BAD_TESTS
3dmesh.py
a3DmeshOnModified2Dmesh.py
creating_meshes_ex01.py
creating_meshes_ex03.py
creating_meshes_ex05.py
@ -39,14 +41,14 @@ SET(BAD_TESTS
quality_controls_ex20.py
quality_controls_ex21.py
quality_controls_ex22.py
viewing_meshes_ex01.py
)
viewing_meshes_ex01.py
)
SET(GOOD_TESTS
cartesian_algo.py
creating_meshes_ex02.py
creating_meshes_ex04.py
creating_meshes_ex06.py
cartesian_algo.py
creating_meshes_ex02.py
creating_meshes_ex04.py
creating_meshes_ex06.py
creating_meshes_ex07.py
creating_meshes_ex08.py
defining_hypotheses_ex01.py

View File

@ -0,0 +1,62 @@
import salome
salome.salome_init()
from salome.geom import geomBuilder
geompy = geomBuilder.New(salome.myStudy)
# This script demonstrates generation of 3D mesh basing on a modified 2D mesh
#
# Purpose is to get a tetrahedral mesh in a sphere cut by a cube.
# The requirement is to have a surface mesh on the cube comprised of
# triangles of exactly the same size arranged in a grid pattern.
#
# To fulfill this requirement we mesh the box using Quadrangle (Mapping)
# meshing algorithm, split quadrangles into triangles and then generate
# tetrahedrons.
# Make the geometry
Box_1 = geompy.MakeBox(-100,-100,-100, 100, 100, 100)
Sphere_1 = geompy.MakeSphereR( 300 )
Cut_1 = geompy.MakeCut(Sphere_1, Box_1, theName="Cut_1")
# get a spherical face
Sph_Face = geompy.ExtractShapes( Sphere_1, geompy.ShapeType["FACE"] )[0]
# get the shape Sph_Face turned into during MakeCut()
Sph_Face = geompy.GetInPlace(Cut_1, Sph_Face, isNewImplementation=True, theName="Sphere_1")
# 1) Define a mesh with 1D and 2D meshers
import SMESH
from salome.smesh import smeshBuilder
smesh = smeshBuilder.New(salome.myStudy)
Mesh_1 = smesh.Mesh(Cut_1)
# "global" meshers (assigned to Cut_1) that will be used for the box
Regular_1D = Mesh_1.Segment()
Local_Length_1 = Regular_1D.LocalLength(20)
Quadrangle_2D = Mesh_1.Quadrangle()
# a "local" mesher (assigned to a sub-mesh on Sphere_1) to mesh the sphere
algo_2D = Mesh_1.Triangle( smeshBuilder.NETGEN_1D2D, Sph_Face )
algo_2D.SetMaxSize( 70. )
algo_2D.SetFineness( smeshBuilder.Moderate )
algo_2D.SetMinSize( 7. )
# 2) Compute 2D mesh
isDone = Mesh_1.Compute()
# 3) Split quadrangles into triangles
isDone = Mesh_1.SplitQuadObject( Mesh_1, Diag13=True )
# 4) Define a 3D mesher
Mesh_1.Tetrahedron()
# 5) Compute 3D mesh
Mesh_1.Compute()
if salome.sg.hasDesktop():
salome.sg.updateObjBrowser(1)

View File

@ -1,9 +1,14 @@
# Use 3D extrusion meshing algorithm
import salome, smesh, SMESH, geompy
import salome
salome.salome_init()
smesh.SetCurrentStudy( salome.myStudy )
from salome.geom import geomBuilder
geompy = geomBuilder.New(salome.myStudy)
import SMESH
from salome.smesh import smeshBuilder
smesh = smeshBuilder.New(salome.myStudy)
OX = geompy.MakeVectorDXDYDZ(1,0,0)
OY = geompy.MakeVectorDXDYDZ(0,1,0)

View File

@ -7,10 +7,12 @@ a set of entities with a simple topology.
It is possible to \subpage constructing_meshes_page "construct meshes"
on the basis of geometrical shapes produced in the GEOM module.
It is also possible to
\subpage constructing_submeshes_page "construct mesh on a part of the geometrical object",
for example, a face, with different meshing parameters or using
another meshing algorithm.
Construction of \subpage constructing_submeshes_page "sub-meshes"
allows to mesh parts of the geometrical object, for example a face,
with different meshing parameters or using another meshing algorithm
than other parts.
3D mesh can be generated basing on a 2D closed mesh.
Several created meshes can be \subpage building_compounds_page "combined into another mesh".
@ -62,44 +64,4 @@ described in the following way:
coordinates of the corresponding vertex.</li>
</ul>
<br><h2>Connections</h2>
Each mesh entity bounds 0 or more mesh entities of higher
dimension. In the same way each mesh entity is bounded by 0 or more
mesh entities of lower dimension:
<ul>
<li>A node bounds edges, faces and volumes</li>
<li>An edge bounds faces, and volumes</li>
<li>A face bounds volumes</li>
<li>A volume is bounded by faces, edges and nodes</li>
<li>A face is bounded by edges, and nodes</li>
<li>An edge is bounded by nodes</li>
</ul>
You can notice that there are two types of connections: \b inverse and
\b direct connections.
<br><h2>Inverse connections</h2>
This relationship has a particularity that the order of bounded
entities has not a direct meaning. Also the number of bounded entities
is not fixed.
\b Example: The edges surrounding a node. The 3rd edge has no more
sense that the 5th one.
<br><h2>Direct connections</h2>
This relationship has a particularity that the order of bounding
entities is meaningful. The number of bounding entities is fixed and
depends on the type of the entity (hexahedron, tetrahedron,?).
\b Example: An edge is composed of two nodes. A face is composed of 3
or 4 edges depending if we are dealing with triangles or quadrangles.
The connections are not only restricted to entities of one dimension
higher or lower. For example some algorithms may be interested to
retrieve all the faces surrounding a node.
*/

View File

@ -52,15 +52,15 @@ geometrical objects.
There is also a number of more specific algorithms:
<ul>
<li>\subpage prism_3d_algo_page "for meshing prismatic shapes"</li>
<li>\subpage projection_algos_page "for meshing by projection of another mesh"</li>
<li>\subpage import_algos_page "for meshing by importing elements from another mesh"</li>
<li>\subpage radial_prism_algo_page "for meshing geometrical objects with cavities"</li>
<li>\subpage segments_around_vertex_algo_page "for defining the local size of elements around a certain node"</li>
<li>\subpage prism_3d_algo_page "for meshing prismatic shapes"</li>
<li>\subpage radial_quadrangle_1D2D_algo_page "for meshing special 2d faces (circles and part of circles)"</li>
<li>\subpage use_existing_page "Use Edges to be Created Manually" and
\ref use_existing_page "Use Faces to be Created Manually" algorithms can be
used to create a 1D or a 2D mesh in a python script.</li>
<li>\subpage segments_around_vertex_algo_page "for defining the local size of elements around a certain node"</li>
</ul>
\ref constructing_meshes_page "Constructing meshes" page describes in

View File

@ -9,6 +9,7 @@
<li> \ref preview_anchor "Previewing the mesh" (optional)</li>
<li> \ref submesh_order_anchor "Changing sub-mesh priority" (optional)</li>
<li> \ref compute_anchor "Computing the mesh"</li>
<li> \ref edit_anchor "Editing the mesh" (optional)</li>
</ul>
\anchor create_mesh_anchor
@ -357,8 +358,29 @@ computation reporting. There are the following possibilities: always
show the information box, show only if an error occurs or never.
By default, the information box is always shown after mesh computation operation.
<br><br>
<p><p>
\anchor edit_anchor
<h2>Editing the mesh</h2>
It is possible to \ref modifying_meshes_page "edit the mesh" of
lower dimension before generation of mesh of higher dimension.
For example you can generate 2D mesh, modify it using e.g.
\ref pattern_mapping_page, and then generate 3D mesh basing on the
modified 2D mesh. The workflow is following:
- Define 1D and 2D meshing algorithms.
- Compute the mesh. 2D mesh is generated.
- Apply \ref pattern_mapping_page.
- Define 3D meshing algorithms without modifying 1D and 2D algorithms
and hypotheses.
- Compute the mesh. 3D mesh is generated.
\note Nodes and elements added \ref adding_nodes_and_elements_page
"manually" can't be used in this workflow because the manually created
entities are not attached to any geometry and thus (usually) can't be
found by a mesher paving some geometry.
<b>See Also</b> a sample TUI Script demonstrates the possibility of
\ref tui_editing_while_meshing "Intermediate edition while meshing"
*/

View File

@ -51,6 +51,11 @@ or vice versa.</li>
<li>\subpage cut_mesh_by_plane_page "Cut a tetrahedron mesh by a plane".</li>
</ul>
It is possible to \ref edit_anchor "modify the mesh" of lower
dimension before generation of mesh of higher dimension.
<p><br></p>
\note It is possible to use the variables defined in the SALOME \b NoteBook
to specify the numerical parameters used for modification of any object.

View File

@ -14,7 +14,7 @@ The smp file contains 4 sections:
-# The first line indicates the total number of pattern nodes (N).
-# The next N lines describe nodes coordinates. Each line contains 2
node coordinates for a 2D pattern or 3 node cordinates for a 3D pattern.
node coordinates for a 2D pattern or 3 node coordinates for a 3D pattern.
Note, that node coordinates of a 3D pattern can be defined only by relative values in range [0;1].
-# The key-points line contains the indices of the nodes to be mapped on geometrical
vertices (for a 2D pattern only). Index n refers to the node described
@ -89,7 +89,7 @@ An example of a simple 3D pattern smp file:
<br><h2>Application of pattern mapping</h2>
<em>To apply pattern mapping to a geometrical object:</em>
<em>To apply pattern mapping to a geometrical object or mesh elements:</em>
From the \b Modification menu choose the <b>Pattern Mapping</b> item or click
<em>"Pattern mapping"</em> button in the toolbar.
@ -113,16 +113,17 @@ created manually or generated automatically from an existing mesh or submesh.</l
boundaries of the pattern must also be equal to the number of vertices
on internal boundaries of the face;</li>
<li> \b Vertex to which the first key-point should be mapped;</li>
</ul>
Alternatively, it is possible to select <b>Refine selected mesh elements</b>
checkbox and apply the pattern to
check-box and apply the pattern to <ul>
<li> <b>Mesh Face</b> instead of a geometric Face</li>
<li> and select \b Node instead of vertex.</li>
Additionally it is possible to:
<li> <b>Reverse the order of key-points</b> By default, the vertices of
a face are ordered counterclockwise.<li>
</ul>
Additionally it is possible to: <ul>
<li> <b>Reverse the order of key-points</b>. By default, the vertices of
a face are ordered counterclockwise.</li>
<li> Enable to <b> Create polygons near boundary</b> </li>
<li> and <b>Create polyhedrons near boundary</b><li>
<li> and <b>Create polyhedrons near boundary</b></li>
</ul>
\n For a <b>3D pattern</b>
@ -133,21 +134,27 @@ In this dialog you should specify:
<ul>
<li> \b Pattern, which can be loaded from .smp pattern file previously
created manually or generated automatically from an existing mesh or submesh.</li>
<li> A 3D block (Solid) object;</li>
<li> Two vertices that specify the order of nodes in the resulting mesh.</li>
<li> A 3D block (Solid) object.</li>
<li> Two vertices that specify the order of nodes in the resulting
mesh.</li>
</ul>
Alternatively, it is possible to select <b>Refine selected mesh elements</b>
checkbox and apply the pattern to
<ul>
<li> One or several <b>Mesh volumes</b> instead of a geometric 3D
object</li>
<li> and select two /b Nodes instead of vertices.</li>
</ul>
Additionally it is possible to:
<ul>
<li> Enable to <b> Create polygons near boundary</b> </li>
<li> and <b>Create polyhedrons near boundary</b><li>
<li> and <b>Create polyhedrons near boundary</b></li>
</ul>
\n Automatic Generation
<br>
<h3> Automatic Generation </h3>
To generate a pattern automatically from an existing mesh or submesh,
To generate a pattern automatically from an existing mesh or sub-mesh,
click \b New button.
The following dialog box will appear:

View File

@ -18,6 +18,11 @@
<h2>Change priority of submeshes in Mesh</h2>
\tui_script{creating_meshes_ex03.py}
<br>
\anchor tui_editing_while_meshing
<h2>Intermediate edition while meshing</h2>
\tui_script{a3DmeshOnModified2Dmesh.py}
<br>
\anchor tui_editing_mesh
<h2>Editing a mesh</h2>

View File

@ -4251,11 +4251,11 @@ void BelongToGeom::init()
myIsSubshape = IsSubShape(aMap, myShape);
}
if (!myIsSubshape)
//if (!myIsSubshape) // to be always ready to check an element not bound to geometry
{
myElementsOnShapePtr.reset(new ElementsOnShape());
myElementsOnShapePtr->SetTolerance(myTolerance);
myElementsOnShapePtr->SetAllNodes(true); // belong, while false means "lays on"
myElementsOnShapePtr->SetAllNodes(true); // "belong", while false means "lays on"
myElementsOnShapePtr->SetMesh(myMeshDS);
myElementsOnShapePtr->SetShape(myShape, myType);
}
@ -4296,36 +4296,43 @@ bool BelongToGeom::IsSatisfy (long theId)
{
if( const SMDS_MeshNode* aNode = myMeshDS->FindNode( theId ) )
{
if ( aNode->getshapeId() < 1 )
return myElementsOnShapePtr->IsSatisfy(theId);
const SMDS_PositionPtr& aPosition = aNode->GetPosition();
SMDS_TypeOfPosition aTypeOfPosition = aPosition->GetTypeOfPosition();
switch( aTypeOfPosition )
{
case SMDS_TOP_VERTEX : return IsContains( myMeshDS,myShape,aNode,TopAbs_VERTEX );
case SMDS_TOP_EDGE : return IsContains( myMeshDS,myShape,aNode,TopAbs_EDGE );
case SMDS_TOP_FACE : return IsContains( myMeshDS,myShape,aNode,TopAbs_FACE );
case SMDS_TOP_3DSPACE: return IsContains( myMeshDS,myShape,aNode,TopAbs_SHELL );
case SMDS_TOP_VERTEX : return ( IsContains( myMeshDS,myShape,aNode,TopAbs_VERTEX ));
case SMDS_TOP_EDGE : return ( IsContains( myMeshDS,myShape,aNode,TopAbs_EDGE ));
case SMDS_TOP_FACE : return ( IsContains( myMeshDS,myShape,aNode,TopAbs_FACE ));
case SMDS_TOP_3DSPACE: return ( IsContains( myMeshDS,myShape,aNode,TopAbs_SOLID ) ||
IsContains( myMeshDS,myShape,aNode,TopAbs_SHELL ));
}
}
}
else
{
if( const SMDS_MeshElement* anElem = myMeshDS->FindElement( theId ) )
if ( const SMDS_MeshElement* anElem = myMeshDS->FindElement( theId ))
{
if ( anElem->getshapeId() < 1 )
return myElementsOnShapePtr->IsSatisfy(theId);
if( myType == SMDSAbs_All )
{
return IsContains( myMeshDS,myShape,anElem,TopAbs_EDGE ) ||
IsContains( myMeshDS,myShape,anElem,TopAbs_FACE ) ||
IsContains( myMeshDS,myShape,anElem,TopAbs_SHELL )||
IsContains( myMeshDS,myShape,anElem,TopAbs_SOLID );
return ( IsContains( myMeshDS,myShape,anElem,TopAbs_EDGE ) ||
IsContains( myMeshDS,myShape,anElem,TopAbs_FACE ) ||
IsContains( myMeshDS,myShape,anElem,TopAbs_SOLID )||
IsContains( myMeshDS,myShape,anElem,TopAbs_SHELL ));
}
else if( myType == anElem->GetType() )
{
switch( myType )
{
case SMDSAbs_Edge : return IsContains( myMeshDS,myShape,anElem,TopAbs_EDGE );
case SMDSAbs_Face : return IsContains( myMeshDS,myShape,anElem,TopAbs_FACE );
case SMDSAbs_Volume: return IsContains( myMeshDS,myShape,anElem,TopAbs_SHELL )||
IsContains( myMeshDS,myShape,anElem,TopAbs_SOLID );
case SMDSAbs_Edge : return ( IsContains( myMeshDS,myShape,anElem,TopAbs_EDGE ));
case SMDSAbs_Face : return ( IsContains( myMeshDS,myShape,anElem,TopAbs_FACE ));
case SMDSAbs_Volume: return ( IsContains( myMeshDS,myShape,anElem,TopAbs_SOLID )||
IsContains( myMeshDS,myShape,anElem,TopAbs_SHELL ));
}
}
}

View File

@ -82,20 +82,25 @@ namespace SMESH
return GEOM::GEOM_Object::_nil();
}
GEOM::GEOM_Object_ptr GetGeom (_PTR(SObject) theSO)
GEOM::GEOM_Object_var GetGeom (_PTR(SObject) theSO)
{
GEOM::GEOM_Object_var aMeshShape;
if (!theSO)
return GEOM::GEOM_Object::_nil();
return aMeshShape;
CORBA::Object_var obj = _CAST( SObject,theSO )->GetObject();
aMeshShape = GEOM::GEOM_Object::_narrow( obj );
if ( !aMeshShape->_is_nil() )
return aMeshShape;
_PTR(Study) aStudy = SMESH::GetActiveStudyDocument();
if (!aStudy)
return GEOM::GEOM_Object::_nil();
return aMeshShape;
_PTR(ChildIterator) anIter (aStudy->NewChildIterator(theSO));
for ( ; anIter->More(); anIter->Next()) {
_PTR(SObject) aSObject = anIter->Value();
_PTR(SObject) aRefSOClient;
GEOM::GEOM_Object_var aMeshShape;
if (aSObject->ReferencedObject(aRefSOClient)) {
SALOMEDS_SObject* aRefSO = _CAST(SObject,aRefSOClient);
@ -104,11 +109,10 @@ namespace SMESH
SALOMEDS_SObject* aSO = _CAST(SObject,aSObject);
aMeshShape = GEOM::GEOM_Object::_narrow(aSO->GetObject());
}
if (!aMeshShape->_is_nil())
return aMeshShape._retn();
if ( !aMeshShape->_is_nil() )
return aMeshShape;
}
return GEOM::GEOM_Object::_nil();
return aMeshShape;
}
SMESHGUI_EXPORT char* GetGeomName( _PTR(SObject) smeshSO )

View File

@ -47,7 +47,7 @@ namespace SMESH
SMESHGUI_EXPORT GEOM::GEOM_Object_var GetShapeOnMeshOrSubMesh( _PTR(SObject), bool* isMesh=0 );
SMESHGUI_EXPORT GEOM::GEOM_Object_ptr GetGeom( _PTR(SObject) );
SMESHGUI_EXPORT GEOM::GEOM_Object_var GetGeom( _PTR(SObject) );
SMESHGUI_EXPORT char* GetGeomName( _PTR(SObject) smeshSO );

View File

@ -511,10 +511,14 @@ namespace SMESH
return SMESH::SMESH_Hypothesis::_nil();
}
bool IsApplicable(const QString& aHypType,
GEOM::GEOM_Object_ptr theGeomObject,
const bool toCheckAll)
{
if ( getenv("NO_LIMIT_ALGO_BY_SHAPE")) // allow a workaround for a case if
return true; // IsApplicable() returns false due to a bug
HypothesisData* aHypData = GetHypothesisData(aHypType);
QString aServLib = aHypData->ServerLibName;
return SMESHGUI::GetSMESHGen()->IsApplicable( aHypType.toLatin1().data(),

View File

@ -247,8 +247,9 @@ void SMESHGUI_MeshOp::startOperation()
myDlg->activateObject( myIsMesh ? SMESHGUI_MeshDlg::Geom : SMESHGUI_MeshDlg::Mesh );
}
else
{
myDlg->activateObject( SMESHGUI_MeshDlg::Obj );
}
myDlg->setCurrentTab( SMESH::DIM_3D );
QStringList TypeMeshList;
@ -2614,11 +2615,11 @@ void SMESHGUI_MeshOp::setFilteredAlgoData( const int theTabIndex, const int theI
bool toCheckIsApplicableToAll = !myIsMesh;
GEOM::GEOM_Object_var aGeomVar;
QString anEntry = myDlg->selectedObject( SMESHGUI_MeshDlg::Geom );
QString anEntry =
myDlg->selectedObject( myToCreate ? SMESHGUI_MeshDlg::Geom : SMESHGUI_MeshDlg::Obj );
if ( _PTR(SObject) so = studyDS()->FindObjectID( anEntry.toLatin1().data() ))
{
CORBA::Object_var obj = _CAST( SObject,so )->GetObject();
aGeomVar = GEOM::GEOM_Object::_narrow( obj );
aGeomVar = SMESH::GetGeom( so );
if ( !aGeomVar->_is_nil() && toCheckIsApplicableToAll )
toCheckIsApplicableToAll = ( aGeomVar->GetType() == GEOM_GROUP );
}

View File

@ -4280,16 +4280,17 @@ int StdMeshers_Quadrangle_2D::getCorners(const TopoDS_Face& theFace,
vMap.Add( (*a2v).second );
// check if there are possible variations in choosing corners
bool isThereVariants = false;
bool haveVariants = false;
if ( vertexByAngle.size() > nbCorners )
{
double lostAngle = a2v->first;
double lastAngle = ( --a2v, a2v->first );
isThereVariants = ( lostAngle * 1.1 >= lastAngle );
haveVariants = ( lostAngle * 1.1 >= lastAngle );
}
const double angleTol = 5.* M_PI/180;
myCheckOri = ( vertexByAngle.size() > nbCorners ||
vertexByAngle.begin()->first < 5.* M_PI/180 );
vertexByAngle.begin()->first < angleTol );
// make theWire begin from a corner vertex or triaVertex
if ( nbCorners == 3 )
@ -4306,9 +4307,10 @@ int StdMeshers_Quadrangle_2D::getCorners(const TopoDS_Face& theFace,
vector< double > angles;
vector< TopoDS_Edge > edgeVec;
vector< int > cornerInd, nbSeg;
angles.reserve( vertexByAngle.size() );
int nbSegTot = 0;
angles .reserve( vertexByAngle.size() );
edgeVec.reserve( vertexByAngle.size() );
nbSeg.reserve( vertexByAngle.size() );
nbSeg .reserve( vertexByAngle.size() );
cornerInd.reserve( nbCorners );
for ( edge = theWire.begin(); edge != theWire.end(); ++edge )
{
@ -4321,105 +4323,219 @@ int StdMeshers_Quadrangle_2D::getCorners(const TopoDS_Face& theFace,
theVertices.push_back( v );
cornerInd.push_back( angles.size() );
}
angles.push_back( angleByVertex.IsBound( v ) ? angleByVertex( v ) : -M_PI );
angles .push_back( angleByVertex.IsBound( v ) ? angleByVertex( v ) : -M_PI );
edgeVec.push_back( *edge );
if ( theConsiderMesh && isThereVariants )
if ( theConsiderMesh && haveVariants )
{
if ( SMESHDS_SubMesh* sm = helper.GetMeshDS()->MeshElements( *edge ))
nbSeg.push_back( sm->NbNodes() + 1 );
else
nbSeg.push_back( 0 );
nbSegTot += nbSeg.back();
}
}
// refine the result vector - make sides elual by length if
// refine the result vector - make sides equal by length if
// there are several equal angles
if ( isThereVariants )
if ( haveVariants )
{
if ( nbCorners == 3 )
angles[0] = 2 * M_PI; // not to move the base triangle VERTEX
set< int > refinedCorners;
// here we refer to VERTEX'es and EDGEs by indices in angles and edgeVec vectors
typedef int TGeoIndex;
// for each vertex find a vertex till which there are nbSegHalf segments
const int nbSegHalf = ( nbSegTot % 2 || nbCorners == 3 ) ? 0 : nbSegTot / 2;
vector< TGeoIndex > halfDivider( angles.size(), -1 );
int nbHalfDividers = 0;
if ( nbSegHalf )
{
// get min angle of corners
double minAngle = 10.;
for ( size_t iC = 0; iC < cornerInd.size(); ++iC )
minAngle = Min( minAngle, angles[ cornerInd[ iC ]]);
// find halfDivider's
for ( TGeoIndex iV1 = 0; iV1 < TGeoIndex( angles.size() ); ++iV1 )
{
int nbSegs = 0;
TGeoIndex iV2 = iV1;
do {
nbSegs += nbSeg[ iV2 ];
iV2 = helper.WrapIndex( iV2 + 1, nbSeg.size() );
} while ( nbSegs < nbSegHalf );
if ( nbSegs == nbSegHalf &&
angles[ iV1 ] + angleTol >= minAngle &&
angles[ iV2 ] + angleTol >= minAngle )
{
halfDivider[ iV1 ] = iV2;
++nbHalfDividers;
}
}
}
set< TGeoIndex > refinedCorners, treatedCorners;
for ( size_t iC = 0; iC < cornerInd.size(); ++iC )
{
int iV = cornerInd[iC];
if ( !refinedCorners.insert( iV ).second )
TGeoIndex iV = cornerInd[iC];
if ( !treatedCorners.insert( iV ).second )
continue;
list< int > equalVertices;
equalVertices.push_back( iV );
list< TGeoIndex > equVerts; // inds of vertices that can become corners
equVerts.push_back( iV );
int nbC[2] = { 0, 0 };
// find equal angles backward and forward from the iV-th corner vertex
for ( int isFwd = 0; isFwd < 2; ++isFwd )
{
int dV = isFwd ? +1 : -1;
int iCNext = helper.WrapIndex( iC + dV, cornerInd.size() );
int iVNext = helper.WrapIndex( iV + dV, angles.size() );
int dV = isFwd ? +1 : -1;
int iCNext = helper.WrapIndex( iC + dV, cornerInd.size() );
TGeoIndex iVNext = helper.WrapIndex( iV + dV, angles.size() );
while ( iVNext != iV )
{
bool equal = Abs( angles[iV] - angles[iVNext] ) < 0.1 * angles[iV];
bool equal = Abs( angles[iV] - angles[iVNext] ) < angleTol;
if ( equal )
equalVertices.insert( isFwd ? equalVertices.end() : equalVertices.begin(), iVNext );
equVerts.insert( isFwd ? equVerts.end() : equVerts.begin(), iVNext );
if ( iVNext == cornerInd[ iCNext ])
{
if ( !equal )
{
if ( angles[iV] < angles[iVNext] )
refinedCorners.insert( iVNext );
break;
}
nbC[ isFwd ]++;
refinedCorners.insert( cornerInd[ iCNext ] );
treatedCorners.insert( cornerInd[ iCNext ] );
iCNext = helper.WrapIndex( iCNext + dV, cornerInd.size() );
}
iVNext = helper.WrapIndex( iVNext + dV, angles.size() );
}
if ( iVNext == iV )
break; // all angles equal
}
// move corners to make sides equal by length
int nbEqualV = equalVertices.size();
int nbExcessV = nbEqualV - ( 1 + nbC[0] + nbC[1] );
if ( nbExcessV > 0 )
const bool allCornersSame = ( nbC[0] == 3 );
if ( allCornersSame && nbHalfDividers > 0 )
{
// calculate normalized length of each side enclosed between neighbor equalVertices
vector< double > curLengths;
double totalLen = 0;
vector< int > evVec( equalVertices.begin(), equalVertices.end() );
int iEV = 0;
int iE = cornerInd[ helper.WrapIndex( iC - nbC[0] - 1, cornerInd.size() )];
int iEEnd = cornerInd[ helper.WrapIndex( iC + nbC[1] + 1, cornerInd.size() )];
while ( curLengths.size() < nbEqualV + 1 )
// select two halfDivider's as corners
TGeoIndex hd1, hd2 = -1;
int iC2;
for ( iC2 = 0; iC2 < cornerInd.size() && hd2 < 0; ++iC2 )
{
curLengths.push_back( totalLen );
hd1 = cornerInd[ iC2 ];
hd2 = halfDivider[ hd1 ];
if ( std::find( equVerts.begin(), equVerts.end(), hd2 ) == equVerts.end() )
hd2 = -1; // hd2-th vertex can't become a corner
else
break;
}
if ( hd2 >= 0 )
{
angles[ hd1 ] = 2 * M_PI; // make hd1-th vertex no more "equal"
angles[ hd2 ] = 2 * M_PI;
refinedCorners.insert( hd1 );
refinedCorners.insert( hd2 );
treatedCorners = refinedCorners;
// update cornerInd
equVerts.push_front( equVerts.back() );
equVerts.push_back( equVerts.front() );
list< TGeoIndex >::iterator hdPos =
std::find( equVerts.begin(), equVerts.end(), hd2 );
if ( hdPos == equVerts.end() ) break;
cornerInd[ helper.WrapIndex( iC2 + 0, cornerInd.size()) ] = hd1;
cornerInd[ helper.WrapIndex( iC2 + 1, cornerInd.size()) ] = *( --hdPos );
cornerInd[ helper.WrapIndex( iC2 + 2, cornerInd.size()) ] = hd2;
cornerInd[ helper.WrapIndex( iC2 + 3, cornerInd.size()) ] = *( ++hdPos, ++hdPos );
theVertices[ 0 ] = helper.IthVertex( 0, edgeVec[ cornerInd[0] ]);
theVertices[ 1 ] = helper.IthVertex( 0, edgeVec[ cornerInd[1] ]);
theVertices[ 2 ] = helper.IthVertex( 0, edgeVec[ cornerInd[2] ]);
theVertices[ 3 ] = helper.IthVertex( 0, edgeVec[ cornerInd[3] ]);
iC = -1;
continue;
}
}
// move corners to make sides equal by length
int nbEqualV = equVerts.size();
int nbExcessV = nbEqualV - ( 1 + nbC[0] + nbC[1] );
if ( nbExcessV > 0 ) // there is nbExcessV vertices that can become corners
{
// calculate normalized length of each "side" enclosed between neighbor equVerts
vector< double > accuLength;
double totalLen = 0;
vector< TGeoIndex > evVec( equVerts.begin(), equVerts.end() );
int iEV = 0;
TGeoIndex iE = cornerInd[ helper.WrapIndex( iC - nbC[0] - 1, cornerInd.size() )];
TGeoIndex iEEnd = cornerInd[ helper.WrapIndex( iC + nbC[1] + 1, cornerInd.size() )];
while ( accuLength.size() < nbEqualV + int( !allCornersSame ) )
{
// accumulate length of edges before iEV-th equal vertex
accuLength.push_back( totalLen );
do {
curLengths.back() += SMESH_Algo::EdgeLength( edgeVec[ iE ]);
accuLength.back() += SMESH_Algo::EdgeLength( edgeVec[ iE ]);
iE = helper.WrapIndex( iE + 1, edgeVec.size());
if ( iEV < evVec.size() && iE == evVec[ iEV++ ] )
break;
if ( iEV < evVec.size() && iE == evVec[ iEV ] ) {
iEV++;
break; // equal vertex reached
}
}
while( iE != iEEnd );
totalLen = curLengths.back();
totalLen = accuLength.back();
}
curLengths.resize( equalVertices.size() );
for ( size_t iS = 0; iS < curLengths.size(); ++iS )
curLengths[ iS ] /= totalLen;
accuLength.resize( equVerts.size() );
for ( size_t iS = 0; iS < accuLength.size(); ++iS )
accuLength[ iS ] /= totalLen;
// find equalVertices most close to the ideal sub-division of all sides
// find equVerts most close to the ideal sub-division of all sides
int iBestEV = 0;
int iCorner = helper.WrapIndex( iC - nbC[0], cornerInd.size() );
int nbSides = 2 + nbC[0] + nbC[1];
int nbSides = Min( nbCorners, 2 + nbC[0] + nbC[1] );
for ( int iS = 1; iS < nbSides; ++iS, ++iBestEV )
{
double idealLen = iS / double( nbSides );
double d, bestDist = 1.;
for ( iEV = iBestEV; iEV < curLengths.size(); ++iEV )
if (( d = Abs( idealLen - curLengths[ iEV ])) < bestDist )
double d, bestDist = 2.;
for ( iEV = iBestEV; iEV < accuLength.size(); ++iEV )
{
d = Abs( idealLen - accuLength[ iEV ]);
// take into account presence of a coresponding halfDivider
const double cornerWgt = 0.5 / nbSides;
const double vertexWgt = 0.25 / nbSides;
TGeoIndex hd = halfDivider[ evVec[ iEV ]];
if ( hd < 0 )
d += vertexWgt;
else if( refinedCorners.count( hd ))
d -= cornerWgt;
else
d -= vertexWgt;
// choose vertex with the best d
if ( d < bestDist )
{
bestDist = d;
iBestEV = iEV;
}
}
if ( iBestEV > iS-1 + nbExcessV )
iBestEV = iS-1 + nbExcessV;
theVertices[ iCorner ] = helper.IthVertex( 0, edgeVec[ evVec[ iBestEV ]]);
refinedCorners.insert( evVec[ iBestEV ]);
iCorner = helper.WrapIndex( iCorner + 1, cornerInd.size() );
}
} // if ( nbExcessV > 0 )
else
{
refinedCorners.insert( cornerInd[ iC ]);
}
}
}
} // loop on cornerInd
// make theWire begin from the cornerInd[0]-th EDGE
while ( !theWire.front().IsSame( edgeVec[ cornerInd[0] ]))
theWire.splice( theWire.begin(), theWire, --theWire.end() );
} // if ( haveVariants )
return nbCorners;
}