示例#1
0
SMDS_MeshNode* SMESH_MesherHelper::AddNode(double x, double y, double z, int ID)
{
  SMESHDS_Mesh * meshDS = GetMeshDS();
  SMDS_MeshNode* node = 0;
  if ( ID )
    node = meshDS->AddNodeWithID( x, y, z, ID );
  else
    node = meshDS->AddNode( x, y, z );
  if ( mySetElemOnShape && myShapeID > 0 ) {
    switch ( myShape.ShapeType() ) {
    case TopAbs_SOLID:  meshDS->SetNodeInVolume( node, myShapeID); break;
    case TopAbs_SHELL:  meshDS->SetNodeInVolume( node, myShapeID); break;
    case TopAbs_FACE:   meshDS->SetNodeOnFace(   node, myShapeID); break;
    case TopAbs_EDGE:   meshDS->SetNodeOnEdge(   node, myShapeID); break;
    case TopAbs_VERTEX: meshDS->SetNodeOnVertex( node, myShapeID); break;
    default: ;
    }
  }
  return node;
}
//=============================================================================
bool NETGENPlugin_Mesher::Compute()
{
#ifdef WNT
  netgen::MeshingParameters& mparams = netgen::GlobalMeshingParameters();
#else
  netgen::MeshingParameters& mparams = netgen::mparam;
#endif  
  MESSAGE("Compute with:\n"
          " max size = " << mparams.maxh << "\n"
          " segments per edge = " << mparams.segmentsperedge);
  MESSAGE("\n"
          " growth rate = " << mparams.grading << "\n"
          " elements per radius = " << mparams.curvaturesafety << "\n"
          " second order = " << mparams.secondorder << "\n"
          " quad allowed = " << mparams.quad);

  SMESH_ComputeErrorPtr error = SMESH_ComputeError::New();
  nglib::Ng_Init();

  // -------------------------
  // Prepare OCC geometry
  // -------------------------

  netgen::OCCGeometry occgeo;
  list< SMESH_subMesh* > meshedSM;
  PrepareOCCgeometry( occgeo, _shape, *_mesh, &meshedSM );

  // -------------------------
  // Generate the mesh
  // -------------------------

  netgen::Mesh *ngMesh = NULL;

  SMESH_Comment comment;
  int err = 0;
  int nbInitNod = 0;
  int nbInitSeg = 0;
  int nbInitFac = 0;
  // vector of nodes in which node index == netgen ID
  vector< SMDS_MeshNode* > nodeVec;
  try
  {
    // ----------------
    // compute 1D mesh
    // ----------------
    // pass 1D simple parameters to NETGEN
    if ( _simpleHyp ) {
      if ( int nbSeg = _simpleHyp->GetNumberOfSegments() ) {
        // nb of segments
        mparams.segmentsperedge = nbSeg + 0.1;
        mparams.maxh = occgeo.boundingbox.Diam();
        mparams.grading = 0.01;
      }
      else {
        // segment length
        mparams.segmentsperedge = 1;
        mparams.maxh = _simpleHyp->GetLocalLength();
      }
    }
    // let netgen create ngMesh and calculate element size on not meshed shapes
    char *optstr = 0;
    int startWith = netgen::MESHCONST_ANALYSE;
    int endWith   = netgen::MESHCONST_ANALYSE;
    err = netgen::OCCGenerateMesh(occgeo, ngMesh, startWith, endWith, optstr);
    if (err) comment << "Error in netgen::OCCGenerateMesh() at MESHCONST_ANALYSE step";

    // fill ngMesh with nodes and elements of computed submeshes
    err = ! fillNgMesh(occgeo, *ngMesh, nodeVec, meshedSM);
    nbInitNod = ngMesh->GetNP();
    nbInitSeg = ngMesh->GetNSeg();
    nbInitFac = ngMesh->GetNSE();

    // compute mesh
    if (!err)
    {
      startWith = endWith = netgen::MESHCONST_MESHEDGES;
      err = netgen::OCCGenerateMesh(occgeo, ngMesh, startWith, endWith, optstr);
      if (err) comment << "Error in netgen::OCCGenerateMesh() at 1D mesh generation";
    }
    // ---------------------
    // compute surface mesh
    // ---------------------
    if (!err)
    {
      // pass 2D simple parameters to NETGEN
      if ( _simpleHyp ) {
        if ( double area = _simpleHyp->GetMaxElementArea() ) {
          // face area
          mparams.maxh = sqrt(2. * area/sqrt(3.0));
          mparams.grading = 0.4; // moderate size growth
        }
        else {
          // length from edges
          double length = 0;
          TopTools_MapOfShape tmpMap;
          for ( TopExp_Explorer exp( _shape, TopAbs_EDGE ); exp.More(); exp.Next() )
            if( tmpMap.Add(exp.Current()) )
              length += SMESH_Algo::EdgeLength( TopoDS::Edge( exp.Current() ));

          if ( ngMesh->GetNSeg() ) {
            // we have to multiply length by 2 since for each TopoDS_Edge there
            // are double set of NETGEN edges or, in other words, we have to
            // divide ngMesh->GetNSeg() on 2.
            mparams.maxh = 2*length / ngMesh->GetNSeg();
          }
          else
            mparams.maxh = 1000;
          mparams.grading = 0.2; // slow size growth
        }
        mparams.maxh = min( mparams.maxh, occgeo.boundingbox.Diam()/2 );
        ngMesh->SetGlobalH (mparams.maxh);
        netgen::Box<3> bb = occgeo.GetBoundingBox();
        bb.Increase (bb.Diam()/20);
        ngMesh->SetLocalH (bb.PMin(), bb.PMax(), mparams.grading);
      }
      // let netgen compute 2D mesh
      startWith = netgen::MESHCONST_MESHSURFACE;
      endWith = _optimize ? netgen::MESHCONST_OPTSURFACE : netgen::MESHCONST_MESHSURFACE;
      err = netgen::OCCGenerateMesh(occgeo, ngMesh, startWith, endWith, optstr);
      if (err) comment << "Error in netgen::OCCGenerateMesh() at surface mesh generation";
    }
    // ---------------------
    // generate volume mesh
    // ---------------------
    if (!err && _isVolume)
    {
      // add ng face descriptors of meshed faces
      std::map< int, std::pair<int,int> >::iterator fId_soIds = _faceDescriptors.begin();
      for ( ; fId_soIds != _faceDescriptors.end(); ++fId_soIds ) {
        int faceID   = fId_soIds->first;
        int solidID1 = fId_soIds->second.first;
        int solidID2 = fId_soIds->second.second;
        ngMesh->AddFaceDescriptor (netgen::FaceDescriptor(faceID, solidID1, solidID2, 0));
      }
      // pass 3D simple parameters to NETGEN
      const NETGENPlugin_SimpleHypothesis_3D* simple3d =
        dynamic_cast< const NETGENPlugin_SimpleHypothesis_3D* > ( _simpleHyp );
      if ( simple3d ) {
        if ( double vol = simple3d->GetMaxElementVolume() ) {
          // max volume
          mparams.maxh = pow( 72, 1/6. ) * pow( vol, 1/3. );
          mparams.maxh = min( mparams.maxh, occgeo.boundingbox.Diam()/2 );
        }
        else {
          // length from faces
          mparams.maxh = ngMesh->AverageH();
        }
//      netgen::ARRAY<double> maxhdom;
//      maxhdom.SetSize (occgeo.NrSolids());
//      maxhdom = mparams.maxh;
//      ngMesh->SetMaxHDomain (maxhdom);
        ngMesh->SetGlobalH (mparams.maxh);
        mparams.grading = 0.4;
        ngMesh->CalcLocalH();
      }
      // let netgen compute 3D mesh
      startWith = netgen::MESHCONST_MESHVOLUME;
      endWith = _optimize ? netgen::MESHCONST_OPTVOLUME : netgen::MESHCONST_MESHVOLUME;
      err = netgen::OCCGenerateMesh(occgeo, ngMesh, startWith, endWith, optstr);
      if (err) comment << "Error in netgen::OCCGenerateMesh()";
    }
    if (!err && mparams.secondorder > 0)
    {
      netgen::OCCRefinementSurfaces ref (occgeo);
      ref.MakeSecondOrder (*ngMesh);
    }
  }
  catch (netgen::NgException exc)
  {
    error->myName = err = COMPERR_ALGO_FAILED;
    comment << exc.What();
  }

  int nbNod = ngMesh->GetNP();
  int nbSeg = ngMesh->GetNSeg();
  int nbFac = ngMesh->GetNSE();
  int nbVol = ngMesh->GetNE();

  MESSAGE((err ? "Mesh Generation failure" : "End of Mesh Generation") <<
          ", nb nodes: " << nbNod <<
          ", nb segments: " << nbSeg <<
          ", nb faces: " << nbFac <<
          ", nb volumes: " << nbVol);

  // -----------------------------------------------------------
  // Feed back the SMESHDS with the generated Nodes and Elements
  // -----------------------------------------------------------

  SMESHDS_Mesh* meshDS = _mesh->GetMeshDS();
  bool isOK = ( !err && (_isVolume ? (nbVol > 0) : (nbFac > 0)) );
  if ( true /*isOK*/ ) // get whatever built
  {
    // map of nodes assigned to submeshes
    NCollection_Map<int> pindMap;
    // create and insert nodes into nodeVec
    nodeVec.resize( nbNod + 1 );
    int i;
    for (i = nbInitNod+1; i <= nbNod /*&& isOK*/; ++i )
    {
      const netgen::MeshPoint& ngPoint = ngMesh->Point(i);
      SMDS_MeshNode* node = NULL;
      bool newNodeOnVertex = false;
      TopoDS_Vertex aVert;
      if (i-nbInitNod <= occgeo.vmap.Extent())
      {
        // point on vertex
        aVert = TopoDS::Vertex(occgeo.vmap(i-nbInitNod));
        SMESHDS_SubMesh * submesh = meshDS->MeshElements(aVert);
        if (submesh)
        {
          SMDS_NodeIteratorPtr it = submesh->GetNodes();
          if (it->more())
          {
            node = const_cast<SMDS_MeshNode*> (it->next());
            pindMap.Add(i);
          }
        }
        if (!node)
          newNodeOnVertex = true;
      }
      if (!node)
        node = meshDS->AddNode(ngPoint.X(), ngPoint.Y(), ngPoint.Z());
      if (!node)
      {
        MESSAGE("Cannot create a mesh node");
        if ( !comment.size() ) comment << "Cannot create a mesh node";
        nbSeg = nbFac = nbVol = isOK = 0;
        break;
      }
      nodeVec.at(i) = node;
      if (newNodeOnVertex)
      {
        // point on vertex
        meshDS->SetNodeOnVertex(node, aVert);
        pindMap.Add(i);
      }
    }

    // create mesh segments along geometric edges
    NCollection_Map<Link> linkMap;
    for (i = nbInitSeg+1; i <= nbSeg/* && isOK*/; ++i )
    {
      const netgen::Segment& seg = ngMesh->LineSegment(i);
      Link link(seg.p1, seg.p2);
      if (linkMap.Contains(link))
        continue;
      linkMap.Add(link);
      TopoDS_Edge aEdge;
      int pinds[3] = { seg.p1, seg.p2, seg.pmid };
      int nbp = 0;
      double param2 = 0;
      for (int j=0; j < 3; ++j)
      {
        int pind = pinds[j];
        if (pind <= 0) continue;
        ++nbp;
        double param;
        if (j < 2)
        {
          if (aEdge.IsNull())
          {
            int aGeomEdgeInd = seg.epgeominfo[j].edgenr;
            if (aGeomEdgeInd > 0 && aGeomEdgeInd <= occgeo.emap.Extent())
              aEdge = TopoDS::Edge(occgeo.emap(aGeomEdgeInd));
          }
          param = seg.epgeominfo[j].dist;
          param2 += param;
        }
        else
          param = param2 * 0.5;
        if (pind <= nbInitNod || pindMap.Contains(pind))
          continue;
        if (!aEdge.IsNull())
        {
          meshDS->SetNodeOnEdge(nodeVec.at(pind), aEdge, param);
          pindMap.Add(pind);
        }
      }
      SMDS_MeshEdge* edge;
      if (nbp < 3) // second order ?
        edge = meshDS->AddEdge(nodeVec.at(pinds[0]), nodeVec.at(pinds[1]));
      else
        edge = meshDS->AddEdge(nodeVec.at(pinds[0]), nodeVec.at(pinds[1]),
                                nodeVec.at(pinds[2]));
      if (!edge)
      {
        if ( !comment.size() ) comment << "Cannot create a mesh edge";
        MESSAGE("Cannot create a mesh edge");
        nbSeg = nbFac = nbVol = isOK = 0;
        break;
      }
      if (!aEdge.IsNull())
        meshDS->SetMeshElementOnShape(edge, aEdge);
    }

    // create mesh faces along geometric faces
    for (i = nbInitFac+1; i <= nbFac/* && isOK*/; ++i )
    {
      const netgen::Element2d& elem = ngMesh->SurfaceElement(i);
      int aGeomFaceInd = elem.GetIndex();
      TopoDS_Face aFace;
      if (aGeomFaceInd > 0 && aGeomFaceInd <= occgeo.fmap.Extent())
        aFace = TopoDS::Face(occgeo.fmap(aGeomFaceInd));
      vector<SMDS_MeshNode*> nodes;
      for (int j=1; j <= elem.GetNP(); ++j)
      {
        int pind = elem.PNum(j);
        SMDS_MeshNode* node = nodeVec.at(pind);
        nodes.push_back(node);
        if (pind <= nbInitNod || pindMap.Contains(pind))
          continue;
        if (!aFace.IsNull())
        {
          const netgen::PointGeomInfo& pgi = elem.GeomInfoPi(j);
          meshDS->SetNodeOnFace(node, aFace, pgi.u, pgi.v);
          pindMap.Add(pind);
        }
      }
      SMDS_MeshFace* face = NULL;
      switch (elem.GetType())
      {
      case netgen::TRIG:
        face = meshDS->AddFace(nodes[0],nodes[1],nodes[2]);
        break;
      case netgen::QUAD:
        face = meshDS->AddFace(nodes[0],nodes[1],nodes[2],nodes[3]);
        break;
      case netgen::TRIG6:
        face = meshDS->AddFace(nodes[0],nodes[1],nodes[2],nodes[5],nodes[3],nodes[4]);
        break;
      case netgen::QUAD8:
        face = meshDS->AddFace(nodes[0],nodes[1],nodes[2],nodes[3],
                               nodes[4],nodes[7],nodes[5],nodes[6]);
        break;
      default:
        MESSAGE("NETGEN created a face of unexpected type, ignoring");
        continue;
      }
      if (!face)
      {
        if ( !comment.size() ) comment << "Cannot create a mesh face";
        MESSAGE("Cannot create a mesh face");
        nbSeg = nbFac = nbVol = isOK = 0;
        break;
      }
      if (!aFace.IsNull())
        meshDS->SetMeshElementOnShape(face, aFace);
    }

    // create tetrahedra
    for (i = 1; i <= nbVol/* && isOK*/; ++i)
    {
      const netgen::Element& elem = ngMesh->VolumeElement(i);      
      int aSolidInd = elem.GetIndex();
      TopoDS_Solid aSolid;
      if (aSolidInd > 0 && aSolidInd <= occgeo.somap.Extent())
        aSolid = TopoDS::Solid(occgeo.somap(aSolidInd));
      vector<SMDS_MeshNode*> nodes;
      for (int j=1; j <= elem.GetNP(); ++j)
      {
        int pind = elem.PNum(j);
        SMDS_MeshNode* node = nodeVec.at(pind);
        nodes.push_back(node);
        if (pind <= nbInitNod || pindMap.Contains(pind))
          continue;
        if (!aSolid.IsNull())
        {
          // point in solid
          meshDS->SetNodeInVolume(node, aSolid);
          pindMap.Add(pind);
        }
      }
      SMDS_MeshVolume* vol = NULL;
      switch (elem.GetType())
      {
      case netgen::TET:
        vol = meshDS->AddVolume(nodes[0],nodes[1],nodes[2],nodes[3]);
        break;
      case netgen::TET10:
        vol = meshDS->AddVolume(nodes[0],nodes[1],nodes[2],nodes[3],
                                nodes[4],nodes[7],nodes[5],nodes[6],nodes[8],nodes[9]);
        break;
      default:
        MESSAGE("NETGEN created a volume of unexpected type, ignoring");
        continue;
      }
      if (!vol)
      {
        if ( !comment.size() ) comment << "Cannot create a mesh volume";
        MESSAGE("Cannot create a mesh volume");
        nbSeg = nbFac = nbVol = isOK = 0;
        break;
      }
      if (!aSolid.IsNull())
        meshDS->SetMeshElementOnShape(vol, aSolid);
    }
  }

  if ( error->IsOK() && ( !isOK || comment.size() > 0 ))
    error->myName = COMPERR_ALGO_FAILED;
  if ( !comment.empty() )
    error->myComment = comment;

  // set bad compute error to subshapes of all failed subshapes shapes
  if ( !error->IsOK() && err )
  {
    for (int i = 1; i <= occgeo.fmap.Extent(); i++) {
      int status = occgeo.facemeshstatus[i-1];
      if (status == 1 ) continue;
      if ( SMESH_subMesh* sm = _mesh->GetSubMeshContaining( occgeo.fmap( i ))) {
        SMESH_ComputeErrorPtr& smError = sm->GetComputeError();
        if ( !smError || smError->IsOK() ) {
          if ( status == -1 )
            smError.reset( new SMESH_ComputeError( error->myName, error->myComment ));
          else
            smError.reset( new SMESH_ComputeError( COMPERR_ALGO_FAILED, "Ignored" ));
        }
      }
    }
  }

  nglib::Ng_DeleteMesh((nglib::Ng_Mesh*)ngMesh);
  nglib::Ng_Exit();

  RemoveTmpFiles();

  return error->IsOK();
}
示例#3
0
/*!
 * Special function for search or creation medium node
 */
const SMDS_MeshNode* SMESH_MesherHelper::GetMediumNode(const SMDS_MeshNode* n1,
                                                       const SMDS_MeshNode* n2,
                                                       bool force3d)
{
  TopAbs_ShapeEnum shapeType = myShape.IsNull() ? TopAbs_SHAPE : myShape.ShapeType();

  NLink link(( n1 < n2 ? n1 : n2 ), ( n1 < n2 ? n2 : n1 ));
  ItNLinkNode itLN = myNLinkNodeMap.find( link );
  if ( itLN != myNLinkNodeMap.end() ) {
    return (*itLN).second;
  }
  else {
    // create medium node
    SMDS_MeshNode* n12;
    SMESHDS_Mesh* meshDS = GetMeshDS();
    int faceID = -1, edgeID = -1;
    const SMDS_PositionPtr Pos1 = n1->GetPosition();
    const SMDS_PositionPtr Pos2 = n2->GetPosition();
  
    if( myShape.IsNull() )
    {
      if( Pos1->GetTypeOfPosition()==SMDS_TOP_FACE ) {
        faceID = Pos1->GetShapeId();
      }
      else if( Pos2->GetTypeOfPosition()==SMDS_TOP_FACE ) {
        faceID = Pos2->GetShapeId();
      }

      if( Pos1->GetTypeOfPosition()==SMDS_TOP_EDGE ) {
        edgeID = Pos1->GetShapeId();
      }
      if( Pos2->GetTypeOfPosition()==SMDS_TOP_EDGE ) {
        edgeID = Pos2->GetShapeId();
      }
    }

    if(!force3d) {
      // we try to create medium node using UV parameters of
      // nodes, else - medium between corresponding 3d points
      if(faceID>-1 || shapeType == TopAbs_FACE) {
	// obtaining a face and 2d points for nodes
	TopoDS_Face F;
	if( myShape.IsNull() )
          F = TopoDS::Face(meshDS->IndexToShape(faceID));
	else {
          F = TopoDS::Face(myShape);
          faceID = myShapeID;
        }

	gp_XY p1 = GetNodeUV(F,n1,n2);
        gp_XY p2 = GetNodeUV(F,n2,n1);

	if ( IsDegenShape( Pos1->GetShapeId() ))
	  p1.SetCoord( myParIndex, p2.Coord( myParIndex ));
	else if ( IsDegenShape( Pos2->GetShapeId() ))
	  p2.SetCoord( myParIndex, p1.Coord( myParIndex ));

	//checking if surface is periodic
	Handle(Geom_Surface) S = BRep_Tool::Surface(F);
	Standard_Real UF,UL,VF,VL;
	S->Bounds(UF,UL,VF,VL);

	Standard_Real u,v;
	Standard_Boolean isUPeriodic = S->IsUPeriodic();
	if(isUPeriodic) {
	  Standard_Real UPeriod = S->UPeriod();
	  Standard_Real p2x = p2.X()+ShapeAnalysis::AdjustByPeriod(p2.X(),p1.X(),UPeriod);
	  Standard_Real pmid = (p1.X()+p2x)/2.;
	  u = pmid+ShapeAnalysis::AdjustToPeriod(pmid,UF,UL);
	}
	else 
	  u= (p1.X()+p2.X())/2.;

	Standard_Boolean isVPeriodic = S->IsVPeriodic();
	if(isVPeriodic) {
	  Standard_Real VPeriod = S->VPeriod();
	  Standard_Real p2y = p2.Y()+ShapeAnalysis::AdjustByPeriod(p2.Y(),p1.Y(),VPeriod);
	  Standard_Real pmid = (p1.Y()+p2y)/2.;
	  v = pmid+ShapeAnalysis::AdjustToPeriod(pmid,VF,VL);
	}
	else
	  v = (p1.Y()+p2.Y())/2.;

        gp_Pnt P = S->Value(u, v);
        n12 = meshDS->AddNode(P.X(), P.Y(), P.Z());
        meshDS->SetNodeOnFace(n12, faceID, u, v);
        myNLinkNodeMap.insert(NLinkNodeMap::value_type(link,n12));
        return n12;
      }
      if (edgeID>-1 || shapeType == TopAbs_EDGE) {

	TopoDS_Edge E;
	if( myShape.IsNull() )
          E = TopoDS::Edge(meshDS->IndexToShape(edgeID));
	else {
          E = TopoDS::Edge(myShape);
          edgeID = myShapeID;
        }

	double p1 = GetNodeU(E,n1);
	double p2 = GetNodeU(E,n2);

	double f,l;
	Handle(Geom_Curve) C = BRep_Tool::Curve(E, f, l);
	if(!C.IsNull()) {

	  Standard_Boolean isPeriodic = C->IsPeriodic();
	  double u;
	  if(isPeriodic) {
	    Standard_Real Period = C->Period();
	    Standard_Real p = p2+ShapeAnalysis::AdjustByPeriod(p2,p1,Period);
	    Standard_Real pmid = (p1+p)/2.;
	    u = pmid+ShapeAnalysis::AdjustToPeriod(pmid,C->FirstParameter(),C->LastParameter());
	  }
	  else
	    u = (p1+p2)/2.;

          gp_Pnt P = C->Value( u );
          n12 = meshDS->AddNode(P.X(), P.Y(), P.Z());
          meshDS->SetNodeOnEdge(n12, edgeID, u);
          myNLinkNodeMap.insert(NLinkNodeMap::value_type(link,n12));
          return n12;
	}
      }
    }
    // 3d variant
    double x = ( n1->X() + n2->X() )/2.;
    double y = ( n1->Y() + n2->Y() )/2.;
    double z = ( n1->Z() + n2->Z() )/2.;
    n12 = meshDS->AddNode(x,y,z);
    if(edgeID>-1)
        meshDS->SetNodeOnEdge(n12, edgeID);
    else if(faceID>-1)
        meshDS->SetNodeOnFace(n12, faceID);
    else
      meshDS->SetNodeInVolume(n12, myShapeID);
    myNLinkNodeMap.insert(NLinkNodeMap::value_type(link,n12));
    return n12;
  }
}
bool NETGENPlugin_NETGEN_2D_ONLY::Compute(SMESH_Mesh&         aMesh,
                                          const TopoDS_Shape& aShape)
{
  MESSAGE("NETGENPlugin_NETGEN_2D_ONLY::Compute()");

  SMESHDS_Mesh* meshDS = aMesh.GetMeshDS();
  int faceID = meshDS->ShapeToIndex( aShape );

  SMESH_MesherHelper helper(aMesh);
  _quadraticMesh = helper.IsQuadraticSubMesh(aShape);
  helper.SetElementsOnShape( true );
  const bool ignoreMediumNodes = _quadraticMesh;
  
  // ------------------------
  // get all edges of a face
  // ------------------------
  const TopoDS_Face F = TopoDS::Face( aShape.Oriented( TopAbs_FORWARD ));
  TError problem;
  TSideVector wires = StdMeshers_FaceSide::GetFaceWires( F, aMesh, ignoreMediumNodes, problem );
  if ( problem && !problem->IsOK() )
    return error( problem );
  int nbWires = wires.size();
  if ( nbWires == 0 )
    return error( "Problem in StdMeshers_FaceSide::GetFaceWires()");
  if ( wires[0]->NbSegments() < 3 ) // ex: a circle with 2 segments
    return error(COMPERR_BAD_INPUT_MESH,
                 SMESH_Comment("Too few segments: ")<<wires[0]->NbSegments());

  // -------------------------
  // Make input netgen mesh
  // -------------------------

  Ng_Init();
  netgen::Mesh * ngMesh = new netgen::Mesh ();

  netgen::OCCGeometry occgeo;
  NETGENPlugin_Mesher::PrepareOCCgeometry( occgeo, F, aMesh );
  occgeo.fmap.Clear(); // face can be reversed, which is wrong in this case (issue 19978)
  occgeo.fmap.Add( F );

  vector< const SMDS_MeshNode* > nodeVec;
  problem = AddSegmentsToMesh( *ngMesh, occgeo, wires, helper, nodeVec );
  if ( problem && !problem->IsOK() ) {
    delete ngMesh; Ng_Exit();
    return error( problem );
  }

  // --------------------
  // compute edge length
  // --------------------

  double edgeLength = 0;
  if (_hypLengthFromEdges || (!_hypLengthFromEdges && !_hypMaxElementArea))
  {
    int nbSegments = 0;
    for ( int iW = 0; iW < nbWires; ++iW )
    {
      edgeLength += wires[ iW ]->Length();
      nbSegments += wires[ iW ]->NbSegments();
    }
    if ( nbSegments )
      edgeLength /= nbSegments;
  }
  if ( _hypMaxElementArea )
  {
    double maxArea = _hypMaxElementArea->GetMaxArea();
    edgeLength = sqrt(2. * maxArea/sqrt(3.0));
  }
  if ( edgeLength < DBL_MIN )
    edgeLength = occgeo.GetBoundingBox().Diam();

  //cout << " edgeLength = " << edgeLength << endl;

  netgen::mparam.maxh = edgeLength;
  netgen::mparam.quad = _hypQuadranglePreference ? 1 : 0;
  //ngMesh->SetGlobalH ( edgeLength );

  // -------------------------
  // Generate surface mesh
  // -------------------------

  char *optstr = 0;
  int startWith = MESHCONST_MESHSURFACE;
  int endWith   = MESHCONST_OPTSURFACE;
  int err = 1;

  try {
#if (OCC_VERSION_MAJOR << 16 | OCC_VERSION_MINOR << 8 | OCC_VERSION_MAINTENANCE) > 0x060100
    OCC_CATCH_SIGNALS;
#endif
#ifdef NETGEN_V5
    err = netgen::OCCGenerateMesh(occgeo, ngMesh,netgen::mparam, startWith, endWith);
#else
    err = netgen::OCCGenerateMesh(occgeo, ngMesh, startWith, endWith, optstr);
#endif
  }
  catch (Standard_Failure& ex) {
    string comment = ex.DynamicType()->Name();
    if ( ex.GetMessageString() && strlen( ex.GetMessageString() )) {
      comment += ": ";
      comment += ex.GetMessageString();
    }
    error(COMPERR_OCC_EXCEPTION, comment);
  }
  catch (NgException exc) {
    error( SMESH_Comment("NgException: ") << exc.What() );
  }
  catch (...) {
    error(COMPERR_EXCEPTION,"Exception in netgen::OCCGenerateMesh()");
  }

  // ----------------------------------------------------
  // Fill the SMESHDS with the generated nodes and faces
  // ----------------------------------------------------

  int nbNodes = ngMesh->GetNP();
  int nbFaces = ngMesh->GetNSE();

  int nbInputNodes = nodeVec.size();
  nodeVec.resize( nbNodes, 0 );

  // add nodes
  for ( int i = nbInputNodes + 1; i <= nbNodes; ++i )
  {
    const MeshPoint& ngPoint = ngMesh->Point(i);
    SMDS_MeshNode * node = meshDS->AddNode(ngPoint(0), ngPoint(1), ngPoint(2));
    nodeVec[ i-1 ] = node;
  }

  // create faces
  bool reverse = ( aShape.Orientation() == TopAbs_REVERSED );
  for ( int i = 1; i <= nbFaces ; ++i )
  {
    const Element2d& elem = ngMesh->SurfaceElement(i);
    vector<const SMDS_MeshNode*> nodes( elem.GetNP() );
    for (int j=1; j <= elem.GetNP(); ++j)
    {
      int pind = elem.PNum(j);
      const SMDS_MeshNode* node = nodeVec.at(pind-1);
      if ( reverse )
        nodes[ nodes.size()-j ] = node;
      else
        nodes[ j-1 ] = node;
      if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_3DSPACE )
      {
        const PointGeomInfo& pgi = elem.GeomInfoPi(j);
        meshDS->SetNodeOnFace((SMDS_MeshNode*)node, faceID, pgi.u, pgi.v);
      }
    }
    SMDS_MeshFace* face = 0;
    if ( elem.GetType() == TRIG )
      face = helper.AddFace(nodes[0],nodes[1],nodes[2]);
    else
      face = helper.AddFace(nodes[0],nodes[1],nodes[2],nodes[3]);
  }

  Ng_DeleteMesh((nglib::Ng_Mesh*)ngMesh);
  Ng_Exit();

  NETGENPlugin_Mesher::RemoveTmpFiles();

  return !err;
}
bool StdMeshers_RadialQuadrangle_1D2D::Compute(SMESH_Mesh&         aMesh,
                                               const TopoDS_Shape& aShape)
{
  TopExp_Explorer exp;
  SMESHDS_Mesh * meshDS = aMesh.GetMeshDS();

  myHelper = new SMESH_MesherHelper( aMesh );
  myHelper->IsQuadraticSubMesh( aShape );
  // to delete helper at exit from Compute()
  auto_ptr<SMESH_MesherHelper> helperDeleter( myHelper );

  myLayerPositions.clear();

  TopoDS_Edge CircEdge, LinEdge1, LinEdge2;
  int nbe = analyseFace( aShape, CircEdge, LinEdge1, LinEdge2 );
  if( nbe>3 || nbe < 1 || CircEdge.IsNull() )
    return error(COMPERR_BAD_SHAPE);
  
  gp_Pnt P0,P1;
  // points for rotation
  TColgp_SequenceOfPnt Points;
  // angles for rotation
  TColStd_SequenceOfReal Angles;
  // Nodes1 and Nodes2 - nodes along radiuses
  // CNodes - nodes on circle edge
  vector< const SMDS_MeshNode* > Nodes1, Nodes2, CNodes;
  SMDS_MeshNode * NC;
  // parameters edge nodes on face
  TColgp_SequenceOfPnt2d Pnts2d1;
  gp_Pnt2d PC;

  int faceID = meshDS->ShapeToIndex(aShape);
  TopoDS_Face F = TopoDS::Face(aShape);
  Handle(Geom_Surface) S = BRep_Tool::Surface(F);

  if(nbe==1)
  {
    Handle(Geom_Circle) aCirc = Handle(Geom_Circle)::DownCast( getCurve( CircEdge ));

    bool ok = _gen->Compute( aMesh, CircEdge );
    if( !ok ) return false;
    map< double, const SMDS_MeshNode* > theNodes;
    ok = GetSortedNodesOnEdge(aMesh.GetMeshDS(),CircEdge,true,theNodes);
    if( !ok ) return false;

    CNodes.clear();
    map< double, const SMDS_MeshNode* >::iterator itn = theNodes.begin();
    const SMDS_MeshNode* NF = (*itn).second;
    CNodes.push_back( (*itn).second );
    double fang = (*itn).first;
    if ( itn != theNodes.end() ) {
      itn++;
      for(; itn != theNodes.end(); itn++ ) {
        CNodes.push_back( (*itn).second );
        double ang = (*itn).first - fang;
        if( ang>M_PI ) ang = ang - 2*M_PI;
        if( ang<-M_PI ) ang = ang + 2*M_PI;
        Angles.Append( ang ); 
      }
    }
    P1 = gp_Pnt( NF->X(), NF->Y(), NF->Z() );
    P0 = aCirc->Location();

    myLayerPositions.clear();
    computeLayerPositions(P0,P1);

    exp.Init( CircEdge, TopAbs_VERTEX );
    TopoDS_Vertex V1 = TopoDS::Vertex( exp.Current() );
    gp_Pnt2d p2dV = BRep_Tool::Parameters( V1, TopoDS::Face(aShape) );

    NC = meshDS->AddNode(P0.X(), P0.Y(), P0.Z());
    GeomAPI_ProjectPointOnSurf PPS(P0,S);
    double U0,V0;
    PPS.Parameters(1,U0,V0);
    meshDS->SetNodeOnFace(NC, faceID, U0, V0);
    PC = gp_Pnt2d(U0,V0);

    gp_Vec aVec(P0,P1);
    gp_Vec2d aVec2d(PC,p2dV);
    Nodes1.resize( myLayerPositions.size()+1 );
    Nodes2.resize( myLayerPositions.size()+1 );
    int i = 0;
    for(; i<myLayerPositions.size(); i++) {
      gp_Pnt P( P0.X() + aVec.X()*myLayerPositions[i],
                P0.Y() + aVec.Y()*myLayerPositions[i],
                P0.Z() + aVec.Z()*myLayerPositions[i] );
      Points.Append(P);
      SMDS_MeshNode * node = meshDS->AddNode(P.X(), P.Y(), P.Z());
      Nodes1[i] = node;
      Nodes2[i] = node;
      double U = PC.X() + aVec2d.X()*myLayerPositions[i];
      double V = PC.Y() + aVec2d.Y()*myLayerPositions[i];
      meshDS->SetNodeOnFace( node, faceID, U, V );
      Pnts2d1.Append(gp_Pnt2d(U,V));
    }
    Nodes1[Nodes1.size()-1] = NF;
    Nodes2[Nodes1.size()-1] = NF;
  }
  else if(nbe==2 && LinEdge1.Orientation() != TopAbs_INTERNAL )
  {
    // one curve must be a half of circle and other curve must be
    // a segment of line
    double fp, lp;
    Handle(Geom_Circle) aCirc = Handle(Geom_Circle)::DownCast( getCurve( CircEdge, &fp, &lp ));
    if( fabs(fabs(lp-fp)-M_PI) > Precision::Confusion() ) {
      // not half of circle
      return error(COMPERR_BAD_SHAPE);
    }
    Handle(Geom_Line) aLine = Handle(Geom_Line)::DownCast( getCurve( LinEdge1 ));
    if( aLine.IsNull() ) {
      // other curve not line
      return error(COMPERR_BAD_SHAPE);
    }
    bool linEdgeComputed = false;
    if( SMESH_subMesh* sm1 = aMesh.GetSubMesh(LinEdge1) ) {
      if( !sm1->IsEmpty() ) {
        if( isEdgeCompitaballyMeshed( LinEdge1, aMesh.GetSubMesh(F) ))
          linEdgeComputed = true;
        else
          return error("Invalid set of hypotheses");
      }
    }

    bool ok = _gen->Compute( aMesh, CircEdge );
    if( !ok ) return false;
    map< double, const SMDS_MeshNode* > theNodes;
    GetSortedNodesOnEdge(aMesh.GetMeshDS(),CircEdge,true,theNodes);

    CNodes.clear();
    map< double, const SMDS_MeshNode* >::iterator itn = theNodes.begin();
    double fang = (*itn).first;
    itn++;
    for(; itn != theNodes.end(); itn++ ) {
      CNodes.push_back( (*itn).second );
      double ang = (*itn).first - fang;
      if( ang>M_PI ) ang = ang - 2*M_PI;
      if( ang<-M_PI ) ang = ang + 2*M_PI;
      Angles.Append( ang );
    }
    const SMDS_MeshNode* NF = theNodes.begin()->second;
    const SMDS_MeshNode* NL = theNodes.rbegin()->second;
    CNodes.push_back( NF );
    P1 = gp_Pnt( NF->X(), NF->Y(), NF->Z() );
    gp_Pnt P2( NL->X(), NL->Y(), NL->Z() );
    P0 = aCirc->Location();

    myLayerPositions.clear();
    computeLayerPositions(P0,P1);

    if ( linEdgeComputed )
    {
      if (!GetSortedNodesOnEdge(aMesh.GetMeshDS(),LinEdge1,true,theNodes))
        return error("Invalid mesh on a straight edge");

      vector< const SMDS_MeshNode* > *pNodes1 = &Nodes1, *pNodes2 = &Nodes2;
      bool nodesFromP0ToP1 = ( theNodes.rbegin()->second == NF );
      if ( !nodesFromP0ToP1 ) std::swap( pNodes1, pNodes2 );

      map< double, const SMDS_MeshNode* >::reverse_iterator ritn = theNodes.rbegin();
      itn = theNodes.begin();
      for ( int i = Nodes1.size()-1; i > -1; ++itn, ++ritn, --i )
      {
        (*pNodes1)[i] = ritn->second;
        (*pNodes2)[i] =  itn->second;
        Points.Append( gpXYZ( Nodes1[i]));
        Pnts2d1.Append( myHelper->GetNodeUV( F, Nodes1[i]));
      }
      NC = const_cast<SMDS_MeshNode*>( itn->second );
      Points.Remove( Nodes1.size() );
    }
    else
    {
      gp_Vec aVec(P0,P1);
      int edgeID = meshDS->ShapeToIndex(LinEdge1);
      // check orientation
      Handle(Geom_Curve) Crv = BRep_Tool::Curve(LinEdge1,fp,lp);
      gp_Pnt Ptmp;
      Crv->D0(fp,Ptmp);
      bool ori = true;
      if( P1.Distance(Ptmp) > Precision::Confusion() )
        ori = false;
      // get UV points for edge
      gp_Pnt2d PF,PL;
      BRep_Tool::UVPoints( LinEdge1, TopoDS::Face(aShape), PF, PL );
      PC = gp_Pnt2d( (PF.X()+PL.X())/2, (PF.Y()+PL.Y())/2 );
      gp_Vec2d V2d;
      if(ori) V2d = gp_Vec2d(PC,PF);
      else V2d = gp_Vec2d(PC,PL);
      // add nodes on edge
      double cp = (fp+lp)/2;
      double dp2 = (lp-fp)/2;
      NC = meshDS->AddNode(P0.X(), P0.Y(), P0.Z());
      meshDS->SetNodeOnEdge(NC, edgeID, cp);
      Nodes1.resize( myLayerPositions.size()+1 );
      Nodes2.resize( myLayerPositions.size()+1 );
      int i = 0;
      for(; i<myLayerPositions.size(); i++) {
        gp_Pnt P( P0.X() + aVec.X()*myLayerPositions[i],
                  P0.Y() + aVec.Y()*myLayerPositions[i],
                  P0.Z() + aVec.Z()*myLayerPositions[i] );
        Points.Append(P);
        SMDS_MeshNode * node = meshDS->AddNode(P.X(), P.Y(), P.Z());
        Nodes1[i] = node;
        double param;
        if(ori)
          param = fp + dp2*(1-myLayerPositions[i]);
        else
          param = cp + dp2*myLayerPositions[i];
        meshDS->SetNodeOnEdge(node, edgeID, param);
        P = gp_Pnt( P0.X() - aVec.X()*myLayerPositions[i],
                    P0.Y() - aVec.Y()*myLayerPositions[i],
                    P0.Z() - aVec.Z()*myLayerPositions[i] );
        node = meshDS->AddNode(P.X(), P.Y(), P.Z());
        Nodes2[i] = node;
        if(!ori)
          param = fp + dp2*(1-myLayerPositions[i]);
        else
          param = cp + dp2*myLayerPositions[i];
        meshDS->SetNodeOnEdge(node, edgeID, param);
        // parameters on face
        gp_Pnt2d P2d( PC.X() + V2d.X()*myLayerPositions[i],
                      PC.Y() + V2d.Y()*myLayerPositions[i] );
        Pnts2d1.Append(P2d);
      }
      Nodes1[ myLayerPositions.size() ] = NF;
      Nodes2[ myLayerPositions.size() ] = NL;
      // create 1D elements on edge
      vector< const SMDS_MeshNode* > tmpNodes;
      tmpNodes.resize(2*Nodes1.size()+1);
      for(i=0; i<Nodes2.size(); i++)
        tmpNodes[Nodes2.size()-i-1] = Nodes2[i];
      tmpNodes[Nodes2.size()] = NC;
      for(i=0; i<Nodes1.size(); i++)
        tmpNodes[Nodes2.size()+1+i] = Nodes1[i];
      for(i=1; i<tmpNodes.size(); i++) {
        SMDS_MeshEdge* ME = myHelper->AddEdge( tmpNodes[i-1], tmpNodes[i] );
        if(ME) meshDS->SetMeshElementOnShape(ME, edgeID);
      }
      markLinEdgeAsComputedByMe( LinEdge1, aMesh.GetSubMesh( F ));
    }
  }
  else // nbe==3 or ( nbe==2 && linEdge is INTERNAL )
  {
    if (nbe==2 && LinEdge1.Orientation() == TopAbs_INTERNAL )
      LinEdge2 = LinEdge1;

    // one curve must be a part of circle and other curves must be
    // segments of line
    double fp, lp;
    Handle(Geom_Circle) aCirc = Handle(Geom_Circle)::DownCast( getCurve( CircEdge ));
    Handle(Geom_Line) aLine1 = Handle(Geom_Line)::DownCast( getCurve( LinEdge1 ));
    Handle(Geom_Line) aLine2 = Handle(Geom_Line)::DownCast( getCurve( LinEdge2 ));
    if( aLine1.IsNull() || aLine2.IsNull() ) {
      // other curve not line
      return error(COMPERR_BAD_SHAPE);
    }

    bool linEdge1Computed = false;
    if ( SMESH_subMesh* sm1 = aMesh.GetSubMesh(LinEdge1))
      if( !sm1->IsEmpty() ) {
        if( isEdgeCompitaballyMeshed( LinEdge1, aMesh.GetSubMesh(F) ))
          linEdge1Computed = true;
        else
          return error("Invalid set of hypotheses");
      }
    bool linEdge2Computed = false;
    if ( SMESH_subMesh* sm2 = aMesh.GetSubMesh(LinEdge2))
      if( !sm2->IsEmpty() ) {
        if( isEdgeCompitaballyMeshed( LinEdge2, aMesh.GetSubMesh(F)  ))
          linEdge2Computed = true;
        else
          return error("Invalid set of hypotheses");
      }
    bool ok = _gen->Compute( aMesh, CircEdge );
    if( !ok ) return false;
    map< double, const SMDS_MeshNode* > theNodes;
    GetSortedNodesOnEdge(aMesh.GetMeshDS(),CircEdge,true,theNodes);

    const SMDS_MeshNode* NF = theNodes.begin()->second;
    const SMDS_MeshNode* NL = theNodes.rbegin()->second;
    CNodes.clear();
    CNodes.push_back( NF );
    map< double, const SMDS_MeshNode* >::iterator itn = theNodes.begin();
    double fang = (*itn).first;
    itn++;
    for(; itn != theNodes.end(); itn++ ) {
      CNodes.push_back( (*itn).second );
      double ang = (*itn).first - fang;
      if( ang>M_PI ) ang = ang - 2*M_PI;
      if( ang<-M_PI ) ang = ang + 2*M_PI;
      Angles.Append( ang );
    }
    P1 = gp_Pnt( NF->X(), NF->Y(), NF->Z() );
    gp_Pnt P2( NL->X(), NL->Y(), NL->Z() );
    P0 = aCirc->Location();

    myLayerPositions.clear();
    computeLayerPositions(P0,P1);

    Nodes1.resize( myLayerPositions.size()+1 );
    Nodes2.resize( myLayerPositions.size()+1 );

    exp.Init( LinEdge1, TopAbs_VERTEX );
    TopoDS_Vertex V1 = TopoDS::Vertex( exp.Current() );
    exp.Next();
    TopoDS_Vertex V2 = TopoDS::Vertex( exp.Current() );
    gp_Pnt PE1 = BRep_Tool::Pnt(V1);
    gp_Pnt PE2 = BRep_Tool::Pnt(V2);
    if( ( P1.Distance(PE1) > Precision::Confusion() ) &&
        ( P1.Distance(PE2) > Precision::Confusion() ) )
    {
      std::swap( LinEdge1, LinEdge2 );
      std::swap( linEdge1Computed, linEdge2Computed );
    }
    TopoDS_Vertex VC = V2;
    if( ( P1.Distance(PE1) > Precision::Confusion() ) &&
        ( P2.Distance(PE1) > Precision::Confusion() ) )
      VC = V1;
    int vertID = meshDS->ShapeToIndex(VC);

    // LinEdge1
    if ( linEdge1Computed )
    {
      if (!GetSortedNodesOnEdge(aMesh.GetMeshDS(),LinEdge1,true,theNodes))
        return error("Invalid mesh on a straight edge");

      bool nodesFromP0ToP1 = ( theNodes.rbegin()->second == NF );
      NC = const_cast<SMDS_MeshNode*>
        ( nodesFromP0ToP1 ? theNodes.begin()->second : theNodes.rbegin()->second );
      int i = 0, ir = Nodes1.size()-1;
      int * pi = nodesFromP0ToP1 ? &i : &ir;
      itn = theNodes.begin();
      if ( nodesFromP0ToP1 ) ++itn;
      for ( ; i < Nodes1.size(); ++i, --ir, ++itn )
      {
        Nodes1[*pi] = itn->second;
      }
      for ( i = 0; i < Nodes1.size()-1; ++i )
      {
        Points.Append( gpXYZ( Nodes1[i]));
        Pnts2d1.Append( myHelper->GetNodeUV( F, Nodes1[i]));
      }
    }
    else
    {
      int edgeID = meshDS->ShapeToIndex(LinEdge1);
      gp_Vec aVec(P0,P1);
      // check orientation
      Handle(Geom_Curve) Crv = BRep_Tool::Curve(LinEdge1,fp,lp);
      gp_Pnt Ptmp = Crv->Value(fp);
      bool ori = false;
      if( P1.Distance(Ptmp) > Precision::Confusion() )
        ori = true;
      // get UV points for edge
      gp_Pnt2d PF,PL;
      BRep_Tool::UVPoints( LinEdge1, TopoDS::Face(aShape), PF, PL );
      gp_Vec2d V2d;
      if(ori) {
        V2d = gp_Vec2d(PF,PL);
        PC = PF;
      }
      else {
        V2d = gp_Vec2d(PL,PF);
        PC = PL;
      }
      NC = const_cast<SMDS_MeshNode*>( VertexNode( VC, meshDS ));
      if ( !NC )
      {
        NC = meshDS->AddNode(P0.X(), P0.Y(), P0.Z());
        meshDS->SetNodeOnVertex(NC, vertID);
      }
      double dp = lp-fp;
      int i = 0;
      for(; i<myLayerPositions.size(); i++) {
        gp_Pnt P( P0.X() + aVec.X()*myLayerPositions[i],
                  P0.Y() + aVec.Y()*myLayerPositions[i],
                  P0.Z() + aVec.Z()*myLayerPositions[i] );
        Points.Append(P);
        SMDS_MeshNode * node = meshDS->AddNode(P.X(), P.Y(), P.Z());
        Nodes1[i] = node;
        double param;
        if(!ori)
          param = fp + dp*(1-myLayerPositions[i]);
        else
          param = fp + dp*myLayerPositions[i];
        meshDS->SetNodeOnEdge(node, edgeID, param);
        // parameters on face
        gp_Pnt2d P2d( PC.X() + V2d.X()*myLayerPositions[i],
                      PC.Y() + V2d.Y()*myLayerPositions[i] );
        Pnts2d1.Append(P2d);
      }
      Nodes1[ myLayerPositions.size() ] = NF;
      // create 1D elements on edge
      SMDS_MeshEdge* ME = myHelper->AddEdge( NC, Nodes1[0] );
      if(ME) meshDS->SetMeshElementOnShape(ME, edgeID);
      for(i=1; i<Nodes1.size(); i++) {
        ME = myHelper->AddEdge( Nodes1[i-1], Nodes1[i] );
        if(ME) meshDS->SetMeshElementOnShape(ME, edgeID);
      }
      if (nbe==2 && LinEdge1.Orientation() == TopAbs_INTERNAL )
        Nodes2 = Nodes1;
    }
    markLinEdgeAsComputedByMe( LinEdge1, aMesh.GetSubMesh( F ));

    // LinEdge2
    if ( linEdge2Computed )
    {
      if (!GetSortedNodesOnEdge(aMesh.GetMeshDS(),LinEdge2,true,theNodes))
        return error("Invalid mesh on a straight edge");

      bool nodesFromP0ToP2 = ( theNodes.rbegin()->second == NL );
      int i = 0, ir = Nodes1.size()-1;
      int * pi = nodesFromP0ToP2 ? &i : &ir;
      itn = theNodes.begin();
      if ( nodesFromP0ToP2 ) ++itn;
      for ( ; i < Nodes2.size(); ++i, --ir, ++itn )
        Nodes2[*pi] = itn->second;
    }
    else
    {
      int edgeID = meshDS->ShapeToIndex(LinEdge2);
      gp_Vec aVec = gp_Vec(P0,P2);
      // check orientation
      Handle(Geom_Curve) Crv = BRep_Tool::Curve(LinEdge2,fp,lp);
      gp_Pnt Ptmp = Crv->Value(fp);
      bool ori = false;
      if( P2.Distance(Ptmp) > Precision::Confusion() )
        ori = true;
      // get UV points for edge
      gp_Pnt2d PF,PL;
      BRep_Tool::UVPoints( LinEdge2, TopoDS::Face(aShape), PF, PL );
      gp_Vec2d V2d;
      if(ori) {
        V2d = gp_Vec2d(PF,PL);
        PC = PF;
      }
      else {
        V2d = gp_Vec2d(PL,PF);
        PC = PL;
      }
      double dp = lp-fp;
      for(int i=0; i<myLayerPositions.size(); i++) {
        gp_Pnt P( P0.X() + aVec.X()*myLayerPositions[i],
                  P0.Y() + aVec.Y()*myLayerPositions[i],
                  P0.Z() + aVec.Z()*myLayerPositions[i] );
        SMDS_MeshNode * node = meshDS->AddNode(P.X(), P.Y(), P.Z());
        Nodes2[i] = node;
        double param;
        if(!ori)
          param = fp + dp*(1-myLayerPositions[i]);
        else
          param = fp + dp*myLayerPositions[i];
        meshDS->SetNodeOnEdge(node, edgeID, param);
        // parameters on face
        gp_Pnt2d P2d( PC.X() + V2d.X()*myLayerPositions[i],
                      PC.Y() + V2d.Y()*myLayerPositions[i] );
      }
      Nodes2[ myLayerPositions.size() ] = NL;
      // create 1D elements on edge
      SMDS_MeshEdge* ME = myHelper->AddEdge( NC, Nodes2[0] );
      if(ME) meshDS->SetMeshElementOnShape(ME, edgeID);
      for(int i=1; i<Nodes2.size(); i++) {
        ME = myHelper->AddEdge( Nodes2[i-1], Nodes2[i] );
        if(ME) meshDS->SetMeshElementOnShape(ME, edgeID);
      }
    }
    markLinEdgeAsComputedByMe( LinEdge2, aMesh.GetSubMesh( F ));
  }

  // orientation
  bool IsForward = ( CircEdge.Orientation()==TopAbs_FORWARD );

  // create nodes and mesh elements on face
  // find axis of rotation
  gp_Pnt P2 = gp_Pnt( CNodes[1]->X(), CNodes[1]->Y(), CNodes[1]->Z() );
  gp_Vec Vec1(P0,P1);
  gp_Vec Vec2(P0,P2);
  gp_Vec Axis = Vec1.Crossed(Vec2);
  // create elements
  int i = 1;
  //cout<<"Angles.Length() = "<<Angles.Length()<<"   Points.Length() = "<<Points.Length()<<endl;
  //cout<<"Nodes1.size() = "<<Nodes1.size()<<"   Pnts2d1.Length() = "<<Pnts2d1.Length()<<endl;
  for(; i<Angles.Length(); i++) {
    vector< const SMDS_MeshNode* > tmpNodes;
    tmpNodes.reserve(Nodes1.size());
    gp_Trsf aTrsf;
    gp_Ax1 theAxis(P0,gp_Dir(Axis));
    aTrsf.SetRotation( theAxis, Angles.Value(i) );
    gp_Trsf2d aTrsf2d;
    aTrsf2d.SetRotation( PC, Angles.Value(i) );
    // create nodes
    int j = 1;
    for(; j<=Points.Length(); j++) {
      double cx,cy,cz;
      Points.Value(j).Coord( cx, cy, cz );
      aTrsf.Transforms( cx, cy, cz );
      SMDS_MeshNode* node = myHelper->AddNode( cx, cy, cz );
      // find parameters on face
      Pnts2d1.Value(j).Coord( cx, cy );
      aTrsf2d.Transforms( cx, cy );
      // set node on face
      meshDS->SetNodeOnFace( node, faceID, cx, cy );
      tmpNodes[j-1] = node;
    }
    // create faces
    tmpNodes[Points.Length()] = CNodes[i];
    // quad
    for(j=0; j<Nodes1.size()-1; j++) {
      SMDS_MeshFace* MF;
      if(IsForward)
        MF = myHelper->AddFace( tmpNodes[j], Nodes1[j],
                                Nodes1[j+1], tmpNodes[j+1] );
      else
        MF = myHelper->AddFace( tmpNodes[j], tmpNodes[j+1],
                                Nodes1[j+1], Nodes1[j] );
      if(MF) meshDS->SetMeshElementOnShape(MF, faceID);
    }
    // tria
    SMDS_MeshFace* MF;
    if(IsForward)
      MF = myHelper->AddFace( NC, Nodes1[0], tmpNodes[0] );
    else
      MF = myHelper->AddFace( NC, tmpNodes[0], Nodes1[0] );
    if(MF) meshDS->SetMeshElementOnShape(MF, faceID);
    for(j=0; j<Nodes1.size(); j++) {
      Nodes1[j] = tmpNodes[j];
    }
  }
  // create last faces
  // quad
  for(i=0; i<Nodes1.size()-1; i++) {
    SMDS_MeshFace* MF;
    if(IsForward)
      MF = myHelper->AddFace( Nodes2[i], Nodes1[i],
                              Nodes1[i+1], Nodes2[i+1] );
    else
      MF = myHelper->AddFace( Nodes2[i],  Nodes2[i+1],
                              Nodes1[i+1], Nodes1[i] );
    if(MF) meshDS->SetMeshElementOnShape(MF, faceID);
  }
  // tria
  SMDS_MeshFace* MF;
  if(IsForward)
    MF = myHelper->AddFace( NC, Nodes1[0], Nodes2[0] );
  else
    MF = myHelper->AddFace( NC, Nodes2[0], Nodes1[0] );
  if(MF) meshDS->SetMeshElementOnShape(MF, faceID);

  return true;
}
bool NETGENPlugin_NETGEN_2D_ONLY::Compute(SMESH_Mesh&         aMesh,
        const TopoDS_Shape& aShape)
{
    netgen::multithread.terminate = 0;
    //netgen::multithread.task = "Surface meshing";

    SMESHDS_Mesh* meshDS = aMesh.GetMeshDS();
    SMESH_MesherHelper helper(aMesh);
    helper.SetElementsOnShape( true );

    NETGENPlugin_NetgenLibWrapper ngLib;
    ngLib._isComputeOk = false;

    netgen::Mesh   ngMeshNoLocSize;
#if NETGEN_VERSION < 6
    netgen::Mesh * ngMeshes[2] = { (netgen::Mesh*) ngLib._ngMesh,  & ngMeshNoLocSize };
#else
    netgen::Mesh * ngMeshes[2] = { (netgen::Mesh*) ngLib._ngMesh.get(),  & ngMeshNoLocSize };
#endif
    netgen::OCCGeometry occgeoComm;

    // min / max sizes are set as follows:
    // if ( _hypParameters )
    //    min and max are defined by the user
    // else if ( _hypLengthFromEdges )
    //    min = aMesher.GetDefaultMinSize()
    //    max = average segment len of a FACE
    // else if ( _hypMaxElementArea )
    //    min = aMesher.GetDefaultMinSize()
    //    max = f( _hypMaxElementArea )
    // else
    //    min = aMesher.GetDefaultMinSize()
    //    max = max segment len of a FACE

    NETGENPlugin_Mesher aMesher( &aMesh, aShape, /*isVolume=*/false);
    aMesher.SetParameters( _hypParameters ); // _hypParameters -> netgen::mparam
    const bool toOptimize = _hypParameters ? _hypParameters->GetOptimize() : true;
    if ( _hypMaxElementArea )
    {
        netgen::mparam.maxh = sqrt( 2. * _hypMaxElementArea->GetMaxArea() / sqrt(3.0) );
    }
    if ( _hypQuadranglePreference )
        netgen::mparam.quad = true;

    // local size is common for all FACEs in aShape?
    const bool isCommonLocalSize = ( !_hypLengthFromEdges && !_hypMaxElementArea && netgen::mparam.uselocalh );
    const bool isDefaultHyp = ( !_hypLengthFromEdges && !_hypMaxElementArea && !_hypParameters );

    if ( isCommonLocalSize ) // compute common local size in ngMeshes[0]
    {
        //list< SMESH_subMesh* > meshedSM[4]; --> all sub-shapes are added to occgeoComm
        aMesher.PrepareOCCgeometry( occgeoComm, aShape, aMesh );//, meshedSM );

        // local size set at MESHCONST_ANALYSE step depends on
        // minh, face_maxh, grading and curvaturesafety; find minh if not set by the user
        if ( !_hypParameters || netgen::mparam.minh < DBL_MIN )
        {
            if ( !_hypParameters )
                netgen::mparam.maxh = occgeoComm.GetBoundingBox().Diam() / 3.;
            netgen::mparam.minh = aMesher.GetDefaultMinSize( aShape, netgen::mparam.maxh );
        }
        // set local size depending on curvature and NOT closeness of EDGEs
        netgen::occparam.resthcloseedgeenable = false;
        //netgen::occparam.resthcloseedgefac = 1.0 + netgen::mparam.grading;
        occgeoComm.face_maxh = netgen::mparam.maxh;
        netgen::OCCSetLocalMeshSize( occgeoComm, *ngMeshes[0] );
        occgeoComm.emap.Clear();
        occgeoComm.vmap.Clear();

        // set local size according to size of existing segments
        const double factor = netgen::occparam.resthcloseedgefac;
        TopTools_IndexedMapOfShape edgeMap;
        TopExp::MapShapes( aMesh.GetShapeToMesh(), TopAbs_EDGE, edgeMap );
        for ( int iE = 1; iE <= edgeMap.Extent(); ++iE )
        {
            const TopoDS_Shape& edge = edgeMap( iE );
            if ( SMESH_Algo::isDegenerated( TopoDS::Edge( edge ))/* ||
           helper.IsSubShape( edge, aShape )*/)
                continue;
            SMESHDS_SubMesh* smDS = meshDS->MeshElements( edge );
            if ( !smDS ) continue;
            SMDS_ElemIteratorPtr segIt = smDS->GetElements();
            while ( segIt->more() )
            {
                const SMDS_MeshElement* seg = segIt->next();
                SMESH_TNodeXYZ n1 = seg->GetNode(0);
                SMESH_TNodeXYZ n2 = seg->GetNode(1);
                gp_XYZ p = 0.5 * ( n1 + n2 );
                netgen::Point3d pi(p.X(), p.Y(), p.Z());
                ngMeshes[0]->RestrictLocalH( pi, factor * ( n1 - n2 ).Modulus() );
            }
        }
    }
    netgen::mparam.uselocalh = toOptimize; // restore as it is used at surface optimization

    // ==================
    // Loop on all FACEs
    // ==================

    vector< const SMDS_MeshNode* > nodeVec;

    TopExp_Explorer fExp( aShape, TopAbs_FACE );
    for ( int iF = 0; fExp.More(); fExp.Next(), ++iF )
    {
        TopoDS_Face F = TopoDS::Face( fExp.Current() /*.Oriented( TopAbs_FORWARD )*/);
        int    faceID = meshDS->ShapeToIndex( F );
        SMESH_ComputeErrorPtr& faceErr = aMesh.GetSubMesh( F )->GetComputeError();

        _quadraticMesh = helper.IsQuadraticSubMesh( F );
        const bool ignoreMediumNodes = _quadraticMesh;

        // build viscous layers if required
        if ( F.Orientation() != TopAbs_FORWARD &&
                F.Orientation() != TopAbs_REVERSED )
            F.Orientation( TopAbs_FORWARD ); // avoid pb with TopAbs_INTERNAL
        SMESH_ProxyMesh::Ptr proxyMesh = StdMeshers_ViscousLayers2D::Compute( aMesh, F );
        if ( !proxyMesh )
            continue;

        // ------------------------
        // get all EDGEs of a FACE
        // ------------------------
        TSideVector wires =
            StdMeshers_FaceSide::GetFaceWires( F, aMesh, ignoreMediumNodes, faceErr, proxyMesh );
        if ( faceErr && !faceErr->IsOK() )
            continue;
        int nbWires = wires.size();
        if ( nbWires == 0 )
        {
            faceErr.reset
            ( new SMESH_ComputeError
              ( COMPERR_ALGO_FAILED, "Problem in StdMeshers_FaceSide::GetFaceWires()" ));
            continue;
        }
        if ( wires[0]->NbSegments() < 3 ) // ex: a circle with 2 segments
        {
            faceErr.reset
            ( new SMESH_ComputeError
              ( COMPERR_BAD_INPUT_MESH, SMESH_Comment("Too few segments: ")<<wires[0]->NbSegments()) );
            continue;
        }

        // ----------------------
        // compute maxh of a FACE
        // ----------------------

        if ( !_hypParameters )
        {
            double edgeLength = 0;
            if (_hypLengthFromEdges )
            {
                // compute edgeLength as an average segment length
                int nbSegments = 0;
                for ( int iW = 0; iW < nbWires; ++iW )
                {
                    edgeLength += wires[ iW ]->Length();
                    nbSegments += wires[ iW ]->NbSegments();
                }
                if ( nbSegments )
                    edgeLength /= nbSegments;
                netgen::mparam.maxh = edgeLength;
            }
            else if ( isDefaultHyp )
            {
                // set edgeLength by a longest segment
                double maxSeg2 = 0;
                for ( int iW = 0; iW < nbWires; ++iW )
                {
                    const UVPtStructVec& points = wires[ iW ]->GetUVPtStruct();
                    if ( points.empty() )
                        return error( COMPERR_BAD_INPUT_MESH );
                    gp_Pnt pPrev = SMESH_TNodeXYZ( points[0].node );
                    for ( size_t i = 1; i < points.size(); ++i )
                    {
                        gp_Pnt p = SMESH_TNodeXYZ( points[i].node );
                        maxSeg2 = Max( maxSeg2, p.SquareDistance( pPrev ));
                        pPrev = p;
                    }
                }
                edgeLength = sqrt( maxSeg2 ) * 1.05;
                netgen::mparam.maxh = edgeLength;
            }
            if ( netgen::mparam.maxh < DBL_MIN )
                netgen::mparam.maxh = occgeoComm.GetBoundingBox().Diam();

            if ( !isCommonLocalSize )
            {
                netgen::mparam.minh = aMesher.GetDefaultMinSize( F, netgen::mparam.maxh );
            }
        }

        // prepare occgeom
        netgen::OCCGeometry occgeom;
        occgeom.shape = F;
        occgeom.fmap.Add( F );
        occgeom.CalcBoundingBox();
        occgeom.facemeshstatus.SetSize(1);
        occgeom.facemeshstatus = 0;
        occgeom.face_maxh_modified.SetSize(1);
        occgeom.face_maxh_modified = 0;
        occgeom.face_maxh.SetSize(1);
        occgeom.face_maxh = netgen::mparam.maxh;

        // -------------------------
        // Fill netgen mesh
        // -------------------------

        // MESHCONST_ANALYSE step may lead to a failure, so we make an attempt
        // w/o MESHCONST_ANALYSE at the second loop
        int err = 0;
        enum { LOC_SIZE, NO_LOC_SIZE };
        int iLoop = isCommonLocalSize ? 0 : 1;
        for ( ; iLoop < 2; iLoop++ )
        {
            //bool isMESHCONST_ANALYSE = false;
            InitComputeError();

            netgen::Mesh * ngMesh = ngMeshes[ iLoop ];
            ngMesh->DeleteMesh();

            if ( iLoop == NO_LOC_SIZE )
            {
                ngMesh->SetGlobalH ( mparam.maxh );
                ngMesh->SetMinimalH( mparam.minh );
                Box<3> bb = occgeom.GetBoundingBox();
                bb.Increase (bb.Diam()/10);
                ngMesh->SetLocalH (bb.PMin(), bb.PMax(), mparam.grading);
            }

            nodeVec.clear();
            faceErr = aMesher.AddSegmentsToMesh( *ngMesh, occgeom, wires, helper, nodeVec,
                                                 /*overrideMinH=*/!_hypParameters);
            if ( faceErr && !faceErr->IsOK() )
                break;

            //if ( !isCommonLocalSize )
            //limitSize( ngMesh, mparam.maxh * 0.8);

            // -------------------------
            // Generate surface mesh
            // -------------------------

            const int startWith = MESHCONST_MESHSURFACE;
            const int endWith   = toOptimize ? MESHCONST_OPTSURFACE : MESHCONST_MESHSURFACE;

            SMESH_Comment str;
            try {
                OCC_CATCH_SIGNALS;

#if NETGEN_VERSION >=6
                std::shared_ptr<netgen::Mesh> mesh_ptr(ngMesh,  [](netgen::Mesh*) {});
                err = netgen::OCCGenerateMesh(occgeom, mesh_ptr, netgen::mparam, startWith, endWith);
#elif NETGEN_VERSION > 4
                err = netgen::OCCGenerateMesh(occgeom, ngMesh, netgen::mparam, startWith, endWith);
#else
                char *optstr = 0;
                err = netgen::OCCGenerateMesh(occgeom, ngMesh, startWith, endWith, optstr);
#endif
                if ( netgen::multithread.terminate )
                    return false;
                if ( err )
                    str << "Error in netgen::OCCGenerateMesh() at " << netgen::multithread.task;
            }
            catch (Standard_Failure& ex)
            {
                err = 1;
                str << "Exception in  netgen::OCCGenerateMesh()"
                    << " at " << netgen::multithread.task
                    << ": " << ex.DynamicType()->Name();
                if ( ex.GetMessageString() && strlen( ex.GetMessageString() ))
                    str << ": " << ex.GetMessageString();
            }
            catch (...) {
                err = 1;
                str << "Exception in  netgen::OCCGenerateMesh()"
                    << " at " << netgen::multithread.task;
            }
            if ( err )
            {
                if ( aMesher.FixFaceMesh( occgeom, *ngMesh, 1 ))
                    break;
                if ( iLoop == LOC_SIZE )
                {
                    netgen::mparam.minh = netgen::mparam.maxh;
                    netgen::mparam.maxh = 0;
                    for ( int iW = 0; iW < wires.size(); ++iW )
                    {
                        StdMeshers_FaceSidePtr wire = wires[ iW ];
                        const vector<UVPtStruct>& uvPtVec = wire->GetUVPtStruct();
                        for ( size_t iP = 1; iP < uvPtVec.size(); ++iP )
                        {
                            SMESH_TNodeXYZ   p( uvPtVec[ iP ].node );
                            netgen::Point3d np( p.X(),p.Y(),p.Z());
                            double segLen = p.Distance( uvPtVec[ iP-1 ].node );
                            double   size = ngMesh->GetH( np );
                            netgen::mparam.minh = Min( netgen::mparam.minh, size );
                            netgen::mparam.maxh = Max( netgen::mparam.maxh, segLen );
                        }
                    }
                    //cerr << "min " << netgen::mparam.minh << " max " << netgen::mparam.maxh << endl;
                    netgen::mparam.minh *= 0.9;
                    netgen::mparam.maxh *= 1.1;
                    continue;
                }
                else
                {
                    faceErr.reset( new SMESH_ComputeError( COMPERR_ALGO_FAILED, str ));
                }
            }


            // ----------------------------------------------------
            // Fill the SMESHDS with the generated nodes and faces
            // ----------------------------------------------------

            int nbNodes = ngMesh->GetNP();
            int nbFaces = ngMesh->GetNSE();

            int nbInputNodes = nodeVec.size()-1;
            nodeVec.resize( nbNodes+1, 0 );

            // add nodes
            for ( int ngID = nbInputNodes + 1; ngID <= nbNodes; ++ngID )
            {
                const MeshPoint& ngPoint = ngMesh->Point( ngID );
                SMDS_MeshNode * node = meshDS->AddNode(ngPoint(0), ngPoint(1), ngPoint(2));
                nodeVec[ ngID ] = node;
            }

            // create faces
            int i,j;
            vector<const SMDS_MeshNode*> nodes;
            for ( i = 1; i <= nbFaces ; ++i )
            {
                const Element2d& elem = ngMesh->SurfaceElement(i);
                nodes.resize( elem.GetNP() );
                for (j=1; j <= elem.GetNP(); ++j)
                {
                    int pind = elem.PNum(j);
                    if ( pind < 1 )
                        break;
                    nodes[ j-1 ] = nodeVec[ pind ];
                    if ( nodes[ j-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_3DSPACE )
                    {
                        const PointGeomInfo& pgi = elem.GeomInfoPi(j);
                        meshDS->SetNodeOnFace( nodes[ j-1 ], faceID, pgi.u, pgi.v);
                    }
                }
                if ( j > elem.GetNP() )
                {
                    SMDS_MeshFace* face = 0;
                    if ( elem.GetType() == TRIG )
                        face = helper.AddFace(nodes[0],nodes[1],nodes[2]);
                    else
                        face = helper.AddFace(nodes[0],nodes[1],nodes[2],nodes[3]);
                }
            }

            break;
        } // two attempts
    } // loop on FACEs

    return true;
}