X-Git-Url: http://git.salome-platform.org/gitweb/?p=modules%2Fsmesh.git;a=blobdiff_plain;f=src%2FStdMeshers%2FStdMeshers_Prism_3D.cxx;h=0ee4041ce5ef74b936d21ad7b4fc5273cea105ff;hp=75219db95b4374617c7a473e00266b303a097d4b;hb=f6b5d2f920970263bd4aa50e6ef7116d5c1b1625;hpb=922336dfb7873d5449ccd871a22339113c3bd645 diff --git a/src/StdMeshers/StdMeshers_Prism_3D.cxx b/src/StdMeshers/StdMeshers_Prism_3D.cxx index 75219db95..0ee4041ce 100644 --- a/src/StdMeshers/StdMeshers_Prism_3D.cxx +++ b/src/StdMeshers/StdMeshers_Prism_3D.cxx @@ -51,6 +51,7 @@ #include #include #include +#include #include #include #include @@ -528,6 +529,27 @@ namespace { return nbSides; } + //================================================================================ + /*! + * \brief Set/get wire index to FaceQuadStruct + */ + //================================================================================ + + void setWireIndex( TFaceQuadStructPtr& quad, int iWire ) + { + quad->iSize = iWire; + } + int getWireIndex( const TFaceQuadStructPtr& quad ) + { + return quad->iSize; + } + + //================================================================================ + /*! + * \brief Print Python commands adding given points to a mesh + */ + //================================================================================ + void pointsToPython(const std::vector& p) { #ifdef _DEBUG_ @@ -558,6 +580,7 @@ StdMeshers_Prism_3D::StdMeshers_Prism_3D(int hypId, int studyId, SMESH_Gen* gen) //myProjectTriangles = false; mySetErrorToSM = true; // to pass an error to a sub-mesh of a current solid or not + myPrevBottomSM = 0; // last treated bottom sub-mesh with a suitable algorithm } //================================================================================ @@ -580,39 +603,6 @@ bool StdMeshers_Prism_3D::CheckHypothesis(SMESH_Mesh& a const TopoDS_Shape& aShape, SMESH_Hypothesis::Hypothesis_Status& aStatus) { - // Check shape geometry -/* PAL16229 - aStatus = SMESH_Hypothesis::HYP_BAD_GEOMETRY; - - // find not quadrangle faces - list< TopoDS_Shape > notQuadFaces; - int nbEdge, nbWire, nbFace = 0; - TopExp_Explorer exp( aShape, TopAbs_FACE ); - for ( ; exp.More(); exp.Next() ) { - ++nbFace; - const TopoDS_Shape& face = exp.Current(); - nbEdge = NSProjUtils::Count( face, TopAbs_EDGE, 0 ); - nbWire = NSProjUtils::Count( face, TopAbs_WIRE, 0 ); - if ( nbEdge!= 4 || nbWire!= 1 ) { - if ( !notQuadFaces.empty() ) { - if ( NSProjUtils::Count( notQuadFaces.back(), TopAbs_EDGE, 0 ) != nbEdge || - NSProjUtils::Count( notQuadFaces.back(), TopAbs_WIRE, 0 ) != nbWire ) - RETURN_BAD_RESULT("Different not quad faces"); - } - notQuadFaces.push_back( face ); - } - } - if ( !notQuadFaces.empty() ) - { - if ( notQuadFaces.size() != 2 ) - RETURN_BAD_RESULT("Bad nb not quad faces: " << notQuadFaces.size()); - - // check total nb faces - nbEdge = NSProjUtils::Count( notQuadFaces.back(), TopAbs_EDGE, 0 ); - if ( nbFace != nbEdge + 2 ) - RETURN_BAD_RESULT("Bad nb of faces: " << nbFace << " but must be " << nbEdge + 2); - } -*/ // no hypothesis aStatus = SMESH_Hypothesis::HYP_OK; return true; @@ -627,6 +617,7 @@ bool StdMeshers_Prism_3D::Compute(SMESH_Mesh& theMesh, const TopoDS_Shape& theSh { SMESH_MesherHelper helper( theMesh ); myHelper = &helper; + myPrevBottomSM = 0; int nbSolids = helper.Count( theShape, TopAbs_SOLID, /*skipSame=*/false ); if ( nbSolids < 1 ) @@ -872,7 +863,7 @@ bool StdMeshers_Prism_3D::Compute(SMESH_Mesh& theMesh, const TopoDS_Shape& theSh // TODO. there are other ways to find out the source FACE: - // propagation, topological similarity, ect. + // propagation, topological similarity, etc... // simply try to mesh all not meshed SOLIDs if ( meshedFaces.empty() ) @@ -943,7 +934,7 @@ bool StdMeshers_Prism_3D::getWallFaces( Prism_3D::TPrismTopo & thePrism, list< TopoDS_Edge >::iterator edge = thePrism.myBottomEdges.begin(); std::list< int >::iterator nbE = thePrism.myNbEdgesInWires.begin(); std::list< int > nbQuadsPerWire; - int iE = 0; + int iE = 0, iWire = 0; while ( edge != thePrism.myBottomEdges.end() ) { ++iE; @@ -977,7 +968,10 @@ bool StdMeshers_Prism_3D::getWallFaces( Prism_3D::TPrismTopo & thePrism, return toSM( error(TCom("Composite 'horizontal' edges are not supported"))); } if ( faceMap.Add( face )) + { + setWireIndex( quadList.back(), iWire ); // for use in makeQuadsForOutInProjection() thePrism.myWallQuads.push_back( quadList ); + } break; } } @@ -995,6 +989,7 @@ bool StdMeshers_Prism_3D::getWallFaces( Prism_3D::TPrismTopo & thePrism, if ( iE == *nbE ) { iE = 0; + ++iWire; ++nbE; int nbQuadPrev = std::accumulate( nbQuadsPerWire.begin(), nbQuadsPerWire.end(), 0 ); nbQuadsPerWire.push_back( thePrism.myWallQuads.size() - nbQuadPrev ); @@ -1129,13 +1124,8 @@ bool StdMeshers_Prism_3D::compute(const Prism_3D::TPrismTopo& thePrism) return toSM( error( SMESH_ComputeError::New(COMPERR_CANCELED))); // Assure the bottom is meshed - SMESH_subMesh * botSM = myHelper->GetMesh()->GetSubMesh( thePrism.myBottom ); - if (( botSM->IsEmpty() ) && - ( ! botSM->GetAlgo() || - ! _gen->Compute( *botSM->GetFather(), botSM->GetSubShape(), /*shapeOnly=*/true ))) - return error( COMPERR_BAD_INPUT_MESH, - TCom( "No mesher defined to compute the base face #") - << shapeID( thePrism.myBottom )); + if ( !computeBase( thePrism )) + return false; // Make all side FACEs of thePrism meshed with quads if ( !computeWalls( thePrism )) @@ -1160,7 +1150,8 @@ bool StdMeshers_Prism_3D::compute(const Prism_3D::TPrismTopo& thePrism) // else if ( !trsf.empty() ) // bottomToTopTrsf = trsf.back(); - // To compute coordinates of a node inside a block, it is necessary to know + // To compute coordinates of a node inside a block using "block approach", + // it is necessary to know // 1. normalized parameters of the node by which // 2. coordinates of node projections on all block sub-shapes are computed @@ -1173,7 +1164,7 @@ bool StdMeshers_Prism_3D::compute(const Prism_3D::TPrismTopo& thePrism) // Projections on the top and bottom faces are taken from nodes existing // on these faces; find correspondence between bottom and top nodes - myUseBlock = false; + myUseBlock = false; // is set to true if projection is done using "block approach" myBotToColumnMap.clear(); if ( !assocOrProjBottom2Top( bottomToTopTrsf, thePrism ) ) // it also fills myBotToColumnMap return false; @@ -1181,38 +1172,63 @@ bool StdMeshers_Prism_3D::compute(const Prism_3D::TPrismTopo& thePrism) // Create nodes inside the block - // use transformation (issue 0020680, IPAL0052499) - StdMeshers_Sweeper sweeper; - double tol; - bool allowHighBndError; - if ( !myUseBlock ) { + // use transformation (issue 0020680, IPAL0052499) or a "straight line" approach + StdMeshers_Sweeper sweeper; + sweeper.myHelper = myHelper; + sweeper.myBotFace = thePrism.myBottom; + sweeper.myTopFace = thePrism.myTop; + // load boundary nodes into sweeper bool dummy; + const SMDS_MeshNode* prevN0 = 0, *prevN1 = 0; list< TopoDS_Edge >::const_iterator edge = thePrism.myBottomEdges.begin(); for ( ; edge != thePrism.myBottomEdges.end(); ++edge ) { int edgeID = meshDS->ShapeToIndex( *edge ); TParam2ColumnMap* u2col = const_cast ( myBlock.GetParam2ColumnMap( edgeID, dummy )); - TParam2ColumnMap::iterator u2colIt = u2col->begin(); - for ( ; u2colIt != u2col->end(); ++u2colIt ) + + TParam2ColumnMap::iterator u2colIt = u2col->begin(), u2colEnd = u2col->end(); + const SMDS_MeshNode* n0 = u2colIt->second[0]; + const SMDS_MeshNode* n1 = u2col->rbegin()->second[0]; + if ( n0 == prevN0 || n0 == prevN1 ) ++u2colIt; + if ( n1 == prevN0 || n1 == prevN1 ) --u2colEnd; + prevN0 = n0; prevN1 = n1; + + for ( ; u2colIt != u2colEnd; ++u2colIt ) sweeper.myBndColumns.push_back( & u2colIt->second ); } - // load node columns inside the bottom face + // load node columns inside the bottom FACE TNode2ColumnMap::iterator bot_column = myBotToColumnMap.begin(); + sweeper.myIntColumns.reserve( myBotToColumnMap.size() ); for ( ; bot_column != myBotToColumnMap.end(); ++bot_column ) sweeper.myIntColumns.push_back( & bot_column->second ); - tol = getSweepTolerance( thePrism ); - allowHighBndError = !isSimpleBottom( thePrism ); - } + myHelper->SetElementsOnShape( true ); - if ( !myUseBlock && sweeper.ComputeNodes( *myHelper, tol, allowHighBndError )) - { + // If all "vertical" EDGEs are straight, then all nodes of an internal node column + // are located on a line connecting the top node and the bottom node. + bool isStrightColunm = allVerticalEdgesStraight( thePrism ); + if ( !isStrightColunm ) + { + double tol = getSweepTolerance( thePrism ); + bool allowHighBndError = !isSimpleBottom( thePrism ); + myUseBlock = !sweeper.ComputeNodesByTrsf( tol, allowHighBndError ); + } + else if ( sweeper.CheckSameZ() ) + { + myUseBlock = !sweeper.ComputeNodesOnStraightSameZ(); + } + else + { + myUseBlock = !sweeper.ComputeNodesOnStraight(); + } + myHelper->SetElementsOnShape( false ); } - else // use block approach + + if ( myUseBlock ) // use block approach { // loop on nodes inside the bottom face Prism_3D::TNode prevBNode; @@ -1361,6 +1377,75 @@ bool StdMeshers_Prism_3D::compute(const Prism_3D::TPrismTopo& thePrism) return true; } +//======================================================================= +//function : computeBase +//purpose : Compute the base face of a prism +//======================================================================= + +bool StdMeshers_Prism_3D::computeBase(const Prism_3D::TPrismTopo& thePrism) +{ + SMESH_Mesh* mesh = myHelper->GetMesh(); + SMESH_subMesh* botSM = mesh->GetSubMesh( thePrism.myBottom ); + if (( botSM->IsEmpty() ) && + ( ! botSM->GetAlgo() || + ! _gen->Compute( *botSM->GetFather(), botSM->GetSubShape(), /*shapeOnly=*/true ))) + { + // find any applicable algorithm assigned to any FACE of the main shape + std::vector< TopoDS_Shape > faces; + if ( myPrevBottomSM && + myPrevBottomSM->GetAlgo()->IsApplicableToShape( thePrism.myBottom, /*all=*/false )) + faces.push_back( myPrevBottomSM->GetSubShape() ); + + TopExp_Explorer faceIt( mesh->GetShapeToMesh(), TopAbs_FACE ); + for ( ; faceIt.More(); faceIt.Next() ) + faces.push_back( faceIt.Current() ); + + faces.push_back( TopoDS_Shape() ); // to try quadrangle algorithm + + SMESH_Algo* algo = 0; + for ( size_t i = 0; i < faces.size() && botSM->IsEmpty(); ++i ) + { + if ( faces[i].IsNull() ) algo = TQuadrangleAlgo::instance( this, myHelper ); + else algo = mesh->GetSubMesh( faces[i] )->GetAlgo(); + if ( algo && algo->IsApplicableToShape( thePrism.myBottom, /*all=*/false )) + { + // try to compute the bottom FACE + if ( algo->NeedDiscreteBoundary() ) + { + // compute sub-shapes + SMESH_subMeshIteratorPtr smIt = botSM->getDependsOnIterator(false,false); + bool subOK = true; + while ( smIt->more() && subOK ) + { + SMESH_subMesh* sub = smIt->next(); + sub->ComputeStateEngine( SMESH_subMesh::COMPUTE ); + subOK = sub->IsMeshComputed(); + } + if ( !subOK ) + continue; + } + try { + OCC_CATCH_SIGNALS; + algo->InitComputeError(); + algo->Compute( *mesh, botSM->GetSubShape() ); + } + catch (...) { + } + } + } + } + + if ( botSM->IsEmpty() ) + return error( COMPERR_BAD_INPUT_MESH, + TCom( "No mesher defined to compute the base face #") + << shapeID( thePrism.myBottom )); + + if ( botSM->GetAlgo() ) + myPrevBottomSM = botSM; + + return true; +} + //======================================================================= //function : computeWalls //purpose : Compute 2D mesh on walls FACEs of a prism @@ -1391,6 +1476,7 @@ bool StdMeshers_Prism_3D::computeWalls(const Prism_3D::TPrismTopo& thePrism) for ( ; quad != thePrism.myWallQuads[iW].end(); ++quad ) { StdMeshers_FaceSidePtr lftSide = (*quad)->side[ QUAD_LEFT_SIDE ]; + lftSide->Reverse(); // to go up for ( int i = 0; i < lftSide->NbEdges(); ++i ) { ++wgt[ iW ]; @@ -1418,6 +1504,11 @@ bool StdMeshers_Prism_3D::computeWalls(const Prism_3D::TPrismTopo& thePrism) for ( size_t iW = 0; iW != nbWalls; ++iW ) wgt2quad.insert( make_pair( wgt[ iW ], iW )); + // artificial quads to do outer <-> inner wall projection + std::map< int, FaceQuadStruct > iW2oiQuads; + std::map< int, FaceQuadStruct >::iterator w2oiq; + makeQuadsForOutInProjection( thePrism, wgt2quad, iW2oiQuads ); + // Project 'vertical' EDGEs, from left to right multimap< int, int >::reverse_iterator w2q = wgt2quad.rbegin(); for ( ; w2q != wgt2quad.rend(); ++w2q ) @@ -1434,10 +1525,25 @@ bool StdMeshers_Prism_3D::computeWalls(const Prism_3D::TPrismTopo& thePrism) if ( swapLeftRight ) std::swap( lftSide, rgtSide ); + bool isArtificialQuad = (( w2oiq = iW2oiQuads.find( iW )) != iW2oiQuads.end() ); + if ( isArtificialQuad ) + { + // reset sides to perform the outer <-> inner projection + FaceQuadStruct& oiQuad = w2oiq->second; + rgtSide = oiQuad.side[ QUAD_RIGHT_SIDE ]; + lftSide = oiQuad.side[ QUAD_LEFT_SIDE ]; + iW2oiQuads.erase( w2oiq ); + } + // assure that all the source (left) EDGEs are meshed int nbSrcSegments = 0; for ( int i = 0; i < lftSide->NbEdges(); ++i ) { + if ( isArtificialQuad ) + { + nbSrcSegments = lftSide->NbPoints()-1; + continue; + } const TopoDS_Edge& srcE = lftSide->Edge(i); SMESH_subMesh* srcSM = mesh->GetSubMesh( srcE ); if ( !srcSM->IsMeshComputed() ) { @@ -1513,7 +1619,7 @@ bool StdMeshers_Prism_3D::computeWalls(const Prism_3D::TPrismTopo& thePrism) const UVPtStructVec& srcNodeStr = lftSide->GetUVPtStruct(); if ( srcNodeStr.size() == 0 ) return toSM( error( TCom("Invalid node positions on edge #") << - shapeID( lftSide->Edge(0) ))); + lftSide->EdgeID(0) )); vector< SMDS_MeshNode* > newNodes( srcNodeStr.size() ); for ( int is2ndV = 0; is2ndV < 2; ++is2ndV ) { @@ -1526,7 +1632,7 @@ bool StdMeshers_Prism_3D::computeWalls(const Prism_3D::TPrismTopo& thePrism) // compute nodes on target EDGEs DBGOUT( "COMPUTE V edge (proj) " << shapeID( lftSide->Edge(0))); - rgtSide->Reverse(); // direct it same as the lftSide + //rgtSide->Reverse(); // direct it same as the lftSide myHelper->SetElementsOnShape( false ); // myHelper holds the prism shape TopoDS_Edge tgtEdge; for ( size_t iN = 1; iN < srcNodeStr.size()-1; ++iN ) // add nodes @@ -1603,7 +1709,7 @@ bool StdMeshers_Prism_3D::computeWalls(const Prism_3D::TPrismTopo& thePrism) if ( ! fSM->IsMeshComputed() ) { // Top EDGEs must be projections from the bottom ones - // to compute stuctured quad mesh on wall FACEs + // to compute structured quad mesh on wall FACEs // --------------------------------------------------- const TopoDS_Edge& botE = (*quad)->side[ QUAD_BOTTOM_SIDE ].grid->Edge(0); const TopoDS_Edge& topE = (*quad)->side[ QUAD_TOP_SIDE ].grid->Edge(0); @@ -1695,9 +1801,8 @@ bool StdMeshers_Prism_3D::computeWalls(const Prism_3D::TPrismTopo& thePrism) } //======================================================================= -/*! - * \brief Returns a source EDGE of propagation to a given EDGE - */ +//function : findPropagationSource +//purpose : Returns a source EDGE of propagation to a given EDGE //======================================================================= TopoDS_Edge StdMeshers_Prism_3D::findPropagationSource( const TopoDS_Edge& E ) @@ -1710,9 +1815,93 @@ TopoDS_Edge StdMeshers_Prism_3D::findPropagationSource( const TopoDS_Edge& E ) return TopoDS_Edge(); } +//======================================================================= +//function : makeQuadsForOutInProjection +//purpose : Create artificial wall quads for vertical projection between +// the outer and inner walls +//======================================================================= + +void StdMeshers_Prism_3D::makeQuadsForOutInProjection( const Prism_3D::TPrismTopo& thePrism, + multimap< int, int >& wgt2quad, + map< int, FaceQuadStruct >& iQ2oiQuads) +{ + if ( thePrism.NbWires() <= 1 ) + return; + + std::set< int > doneWires; // processed wires + + SMESH_Mesh* mesh = myHelper->GetMesh(); + const bool isForward = true; + const bool skipMedium = myHelper->GetIsQuadratic(); + + // make a source side for all projections + + multimap< int, int >::reverse_iterator w2q = wgt2quad.rbegin(); + const int iQuad = w2q->second; + const int iWire = getWireIndex( thePrism.myWallQuads[ iQuad ].front() ); + doneWires.insert( iWire ); + + UVPtStructVec srcNodes; + + Prism_3D::TQuadList::const_iterator quad = thePrism.myWallQuads[ iQuad ].begin(); + for ( ; quad != thePrism.myWallQuads[ iQuad ].end(); ++quad ) + { + StdMeshers_FaceSidePtr lftSide = (*quad)->side[ QUAD_LEFT_SIDE ]; + + // assure that all the source (left) EDGEs are meshed + for ( int i = 0; i < lftSide->NbEdges(); ++i ) + { + const TopoDS_Edge& srcE = lftSide->Edge(i); + SMESH_subMesh* srcSM = mesh->GetSubMesh( srcE ); + if ( !srcSM->IsMeshComputed() ) { + srcSM->ComputeSubMeshStateEngine( SMESH_subMesh::COMPUTE ); + srcSM->ComputeStateEngine ( SMESH_subMesh::COMPUTE ); + } + if ( !srcSM->IsMeshComputed() ) + return; + } + const UVPtStructVec& subNodes = lftSide->GetUVPtStruct(); + UVPtStructVec::const_iterator subBeg = subNodes.begin(), subEnd = subNodes.end(); + if ( !srcNodes.empty() ) ++subBeg; + srcNodes.insert( srcNodes.end(), subBeg, subEnd ); + } + StdMeshers_FaceSidePtr srcSide = StdMeshers_FaceSide::New( srcNodes ); + + // make the quads + + list< TopoDS_Edge > sideEdges; + TopoDS_Face face; + for ( ++w2q; w2q != wgt2quad.rend(); ++w2q ) + { + const int iQuad = w2q->second; + const Prism_3D::TQuadList& quads = thePrism.myWallQuads[ iQuad ]; + const int iWire = getWireIndex( quads.front() ); + if ( !doneWires.insert( iWire ).second ) + continue; + + sideEdges.clear(); + for ( quad = quads.begin(); quad != quads.end(); ++quad ) + { + StdMeshers_FaceSidePtr lftSide = (*quad)->side[ QUAD_LEFT_SIDE ]; + for ( int i = 0; i < lftSide->NbEdges(); ++i ) + sideEdges.push_back( lftSide->Edge( i )); + face = lftSide->Face(); + } + StdMeshers_FaceSidePtr tgtSide = + StdMeshers_FaceSide::New( face, sideEdges, mesh, isForward, skipMedium, myHelper ); + + FaceQuadStruct& newQuad = iQ2oiQuads[ iQuad ]; + newQuad.side.resize( 4 ); + newQuad.side[ QUAD_LEFT_SIDE ] = srcSide; + newQuad.side[ QUAD_RIGHT_SIDE ] = tgtSide; + + wgt2quad.insert( *w2q ); // to process this quad after processing the newQuad + } +} + //======================================================================= //function : Evaluate -//purpose : +//purpose : //======================================================================= bool StdMeshers_Prism_3D::Evaluate(SMESH_Mesh& theMesh, @@ -2274,7 +2463,7 @@ bool StdMeshers_Prism_3D::projectBottomToTop( const gp_Trsf & bottom // Check the projected mesh - if ( thePrism.myNbEdgesInWires.size() > 1 && // there are holes + if ( thePrism.NbWires() > 1 && // there are holes topHelper.IsDistorted2D( topSM, /*checkUV=*/false )) { SMESH_MeshEditor editor( topHelper.GetMesh() ); @@ -2400,7 +2589,7 @@ double StdMeshers_Prism_3D::getSweepTolerance( const Prism_3D::TPrismTopo& thePr bool StdMeshers_Prism_3D::isSimpleBottom( const Prism_3D::TPrismTopo& thePrism ) { - if ( thePrism.myBottomEdges.size() != 4 ) + if ( thePrism.myNbEdgesInWires.front() != 4 ) return false; // analyse angles between edges @@ -2432,6 +2621,43 @@ bool StdMeshers_Prism_3D::isSimpleBottom( const Prism_3D::TPrismTopo& thePrism ) return true; } +//======================================================================= +//function : allVerticalEdgesStraight +//purpose : Defines if all "vertical" EDGEs are straight +//======================================================================= + +bool StdMeshers_Prism_3D::allVerticalEdgesStraight( const Prism_3D::TPrismTopo& thePrism ) +{ + for ( size_t i = 0; i < thePrism.myWallQuads.size(); ++i ) + { + const Prism_3D::TQuadList& quads = thePrism.myWallQuads[i]; + Prism_3D::TQuadList::const_iterator quadIt = quads.begin(); + TopoDS_Edge prevQuadEdge; + for ( ; quadIt != quads.end(); ++quadIt ) + { + StdMeshers_FaceSidePtr rightSide = (*quadIt)->side[ QUAD_RIGHT_SIDE ]; + + if ( !prevQuadEdge.IsNull() && + !SMESH_Algo::IsContinuous( rightSide->Edge( 0 ), prevQuadEdge )) + return false; + + for ( int iE = 0; iE < rightSide->NbEdges(); ++iE ) + { + const TopoDS_Edge & rightE = rightSide->Edge( iE ); + if ( !SMESH_Algo::IsStraight( rightE, /*degenResult=*/true )) + return false; + + if ( iE > 0 && + !SMESH_Algo::IsContinuous( rightSide->Edge( iE-1 ), rightE )) + return false; + + prevQuadEdge = rightE; + } + } + } + return true; +} + //======================================================================= //function : project2dMesh //purpose : Project mesh faces from a source FACE of one prism (theSrcFace) @@ -3258,9 +3484,11 @@ bool StdMeshers_Prism_3D::initPrism(Prism_3D::TPrismTopo& thePrism, "Non-quadrilateral faces are not opposite")); // check that the found top and bottom FACEs are opposite + TopTools_IndexedMapOfShape topEdgesMap( thePrism.myBottomEdges.size() ); + TopExp::MapShapes( thePrism.myTop, topEdgesMap ); list< TopoDS_Edge >::iterator edge = thePrism.myBottomEdges.begin(); for ( ; edge != thePrism.myBottomEdges.end(); ++edge ) - if ( myHelper->IsSubShape( *edge, thePrism.myTop )) + if ( topEdgesMap.Contains( *edge )) return toSM( error (notQuadGeomSubMesh.empty() ? COMPERR_BAD_INPUT_MESH : COMPERR_BAD_SHAPE, "Non-quadrilateral faces are not opposite")); @@ -4730,13 +4958,13 @@ void StdMeshers_Sweeper::applyBoundaryError(const vector< gp_XYZ >& bndPoints, //================================================================================ /*! - * \brief Creates internal nodes of the prism + * \brief Create internal nodes of the prism by computing an affine transformation + * from layer to layer */ //================================================================================ -bool StdMeshers_Sweeper::ComputeNodes( SMESH_MesherHelper& helper, - const double tol, - const bool allowHighBndError) +bool StdMeshers_Sweeper::ComputeNodesByTrsf( const double tol, + const bool allowHighBndError) { const size_t zSize = myBndColumns[0]->size(); const size_t zSrc = 0, zTgt = zSize-1; @@ -4892,8 +5120,8 @@ bool StdMeshers_Sweeper::ComputeNodes( SMESH_MesherHelper& helper, } } - //centerIntErrorIsSmall = true; - //bndErrorIsSmall = true; + centerIntErrorIsSmall = true; // 3D_mesh_Extrusion_00/A3 + bndErrorIsSmall = true; if ( !centerIntErrorIsSmall ) { // Compensate the central error; continue adding projection @@ -5003,10 +5231,226 @@ bool StdMeshers_Sweeper::ComputeNodes( SMESH_MesherHelper& helper, for ( size_t z = zSrc + 1; z < zTgt; ++z ) // vertical loop on layers { const gp_XYZ & xyz = intPntsOfLayer[ z ][ iP ]; - if ( !( nodeCol[ z ] = helper.AddNode( xyz.X(), xyz.Y(), xyz.Z() ))) + if ( !( nodeCol[ z ] = myHelper->AddNode( xyz.X(), xyz.Y(), xyz.Z() ))) return false; } } return true; } + +//================================================================================ +/*! + * \brief Check if all nodes of each layers have same logical Z + */ +//================================================================================ + +bool StdMeshers_Sweeper::CheckSameZ() +{ + myZColumns.resize( myBndColumns.size() ); + fillZColumn( myZColumns[0], *myBndColumns[0] ); + + bool sameZ = true; + const double tol = 0.1 * 1./ myBndColumns[0]->size(); + + // check columns based on VERTEXes + + vector< int > vertexIndex; + vertexIndex.push_back( 0 ); + for ( size_t iC = 1; iC < myBndColumns.size() && sameZ; ++iC ) + { + if ( myBndColumns[iC]->front()->GetPosition()->GetDim() > 0 ) + continue; // not on VERTEX + + vertexIndex.push_back( iC ); + fillZColumn( myZColumns[iC], *myBndColumns[iC] ); + + for ( size_t iZ = 0; iZ < myZColumns[0].size() && sameZ; ++iZ ) + sameZ = ( Abs( myZColumns[0][iZ] - myZColumns[iC][iZ]) < tol ); + } + + // check columns based on EDGEs, one per EDGE + + for ( size_t i = 1; i < vertexIndex.size() && sameZ; ++i ) + { + if ( vertexIndex[i] - vertexIndex[i-1] < 2 ) + continue; + + int iC = ( vertexIndex[i] + vertexIndex[i-1] ) / 2; + fillZColumn( myZColumns[iC], *myBndColumns[iC] ); + + for ( size_t iZ = 0; iZ < myZColumns[0].size() && sameZ; ++iZ ) + sameZ = ( Abs( myZColumns[0][iZ] - myZColumns[iC][iZ]) < tol ); + } + + if ( sameZ ) + { + myZColumns.resize(1); + } + else + { + for ( size_t iC = 1; iC < myBndColumns.size(); ++iC ) + fillZColumn( myZColumns[iC], *myBndColumns[iC] ); + } + + return sameZ; +} + +//================================================================================ +/*! + * \brief Create internal nodes of the prism all located on straight lines with + * the same distribution along the lines. + */ +//================================================================================ + +bool StdMeshers_Sweeper::ComputeNodesOnStraightSameZ() +{ + TZColumn& z = myZColumns[0]; + + for ( size_t i = 0; i < myIntColumns.size(); ++i ) + { + TNodeColumn& nodes = *myIntColumns[i]; + SMESH_NodeXYZ n0( nodes[0] ), n1( nodes.back() ); + + for ( size_t iZ = 0; iZ < z.size(); ++iZ ) + { + gp_XYZ p = n0 * ( 1 - z[iZ] ) + n1 * z[iZ]; + nodes[ iZ+1 ] = myHelper->AddNode( p.X(), p.Y(), p.Z() ); + } + } + + return true; +} + +//================================================================================ +/*! + * \brief Create internal nodes of the prism all located on straight lines with + * different distributions along the lines. + */ +//================================================================================ + +bool StdMeshers_Sweeper::ComputeNodesOnStraight() +{ + prepareTopBotDelaunay(); + + const SMDS_MeshNode *botNode, *topNode; + const BRepMesh_Triangle *topTria; + double botBC[3], topBC[3]; // barycentric coordinates + int botTriaNodes[3], topTriaNodes[3]; + bool checkUV = true; + + int nbInternalNodes = myIntColumns.size(); + myBotDelaunay->InitTraversal( nbInternalNodes ); + + while (( botNode = myBotDelaunay->NextNode( botBC, botTriaNodes ))) + { + TNodeColumn* column = myIntColumns[ myNodeID2ColID( botNode->GetID() )]; + + // find a Delaunay triangle containing the topNode + topNode = column->back(); + gp_XY topUV = myHelper->GetNodeUV( myTopFace, topNode, NULL, &checkUV ); + // get a starting triangle basing on that top and bot boundary nodes have same index + topTria = myTopDelaunay->GetTriangleNear( botTriaNodes[0] ); + topTria = myTopDelaunay->FindTriangle( topUV, topTria, topBC, topTriaNodes ); + if ( !topTria ) + return false; + + // create nodes along a line + SMESH_NodeXYZ botP( botNode ), topP( topNode); + for ( size_t iZ = 0; iZ < myZColumns[0].size(); ++iZ ) + { + // use barycentric coordinates as weight of Z of boundary columns + double botZ = 0, topZ = 0; + for ( int i = 0; i < 3; ++i ) + { + botZ += botBC[i] * myZColumns[ botTriaNodes[i] ][ iZ ]; + topZ += topBC[i] * myZColumns[ topTriaNodes[i] ][ iZ ]; + } + double rZ = double( iZ + 1 ) / ( myZColumns[0].size() + 1 ); + double z = botZ * ( 1 - rZ ) + topZ * rZ; + gp_XYZ p = botP * ( 1 - z ) + topP * z; + (*column)[ iZ+1 ] = myHelper->AddNode( p.X(), p.Y(), p.Z() ); + } + } + + return myBotDelaunay->NbVisitedNodes() == nbInternalNodes; +} + +//================================================================================ +/*! + * \brief Compute Z of nodes of a straight column + */ +//================================================================================ + +void StdMeshers_Sweeper::fillZColumn( TZColumn& zColumn, + TNodeColumn& nodes ) +{ + if ( zColumn.size() == nodes.size() - 2 ) + return; + + gp_Pnt p0 = SMESH_NodeXYZ( nodes[0] ); + gp_Vec line( p0, SMESH_NodeXYZ( nodes.back() )); + double len2 = line.SquareMagnitude(); + + zColumn.resize( nodes.size() - 2 ); + for ( size_t i = 0; i < zColumn.size(); ++i ) + { + gp_Vec vec( p0, SMESH_NodeXYZ( nodes[ i+1] )); + zColumn[i] = ( line * vec ) / len2; // param [0,1] on the line + } +} + +//================================================================================ +/*! + * \brief Initialize *Delaunay members + */ +//================================================================================ + +void StdMeshers_Sweeper::prepareTopBotDelaunay() +{ + UVPtStructVec botUV( myBndColumns.size() ); + UVPtStructVec topUV( myBndColumns.size() ); + for ( size_t i = 0; i < myBndColumns.size(); ++i ) + { + TNodeColumn& nodes = *myBndColumns[i]; + botUV[i].node = nodes[0]; + botUV[i].SetUV( myHelper->GetNodeUV( myBotFace, nodes[0] )); + topUV[i].node = nodes.back(); + topUV[i].SetUV( myHelper->GetNodeUV( myTopFace, nodes.back() )); + botUV[i].node->setIsMarked( true ); + } + TopoDS_Edge dummyE; + SMESH_Mesh* mesh = myHelper->GetMesh(); + TSideVector botWires( 1, StdMeshers_FaceSide::New( botUV, myBotFace, dummyE, mesh )); + TSideVector topWires( 1, StdMeshers_FaceSide::New( topUV, myTopFace, dummyE, mesh )); + + // Delaunay mesh on the FACEs. + bool checkUV = false; + myBotDelaunay.reset( new NSProjUtils::Delaunay( botWires, checkUV )); + myTopDelaunay.reset( new NSProjUtils::Delaunay( topWires, checkUV )); + + if ( myHelper->GetIsQuadratic() ) + { + // mark all medium nodes of faces on botFace to avoid their treating + SMESHDS_SubMesh* smDS = myHelper->GetMeshDS()->MeshElements( myBotFace ); + SMDS_ElemIteratorPtr eIt = smDS->GetElements(); + while ( eIt->more() ) + { + const SMDS_MeshElement* e = eIt->next(); + for ( int i = e->NbCornerNodes(), nb = e->NbNodes(); i < nb; ++i ) + e->GetNode( i )->setIsMarked( true ); + } + } + + // map to get a node column by a bottom node + myNodeID2ColID.Clear(/*doReleaseMemory=*/false); + myNodeID2ColID.ReSize( myIntColumns.size() ); + + // un-mark nodes to treat (internal bottom nodes) to be returned by myBotDelaunay + for ( size_t i = 0; i < myIntColumns.size(); ++i ) + { + const SMDS_MeshNode* botNode = myIntColumns[i]->front(); + botNode->setIsMarked( false ); + myNodeID2ColID.Bind( botNode->GetID(), i ); + } +}