//=======================================================================
//function : MakeScaledPrism
//purpose  :
//=======================================================================
TopoDS_Shape GEOMImpl_PrismDriver::MakeScaledPrism (const TopoDS_Shape& theShapeBase,
                                                    const gp_Vec&       theVector,
                                                    const Standard_Real theScaleFactor,
                                                    const gp_Pnt&       theCDG,
                                                    bool                isCDG)
{
  TopoDS_Shape aShape;
  BRep_Builder B;

  // 1. aCDG = geompy.MakeCDG(theBase)
  gp_Pnt aCDG = theCDG;
  if (!isCDG) {
    gp_Ax3 aPos = GEOMImpl_IMeasureOperations::GetPosition(theShapeBase);
    aCDG = aPos.Location();
  }
  TopoDS_Shape aShapeCDG_1 = BRepBuilderAPI_MakeVertex(aCDG).Shape();

  // Process case of several given shapes
  if (theShapeBase.ShapeType() == TopAbs_COMPOUND ||
      theShapeBase.ShapeType() == TopAbs_SHELL) {
    int nbSub = 0;
    TopoDS_Shape aShapeI;
    TopoDS_Compound aCompound;
    B.MakeCompound(aCompound);
    TopoDS_Iterator It (theShapeBase, Standard_True, Standard_True);
    for (; It.More(); It.Next()) {
      nbSub++;
      aShapeI = MakeScaledPrism(It.Value(), theVector, theScaleFactor, aCDG, true);
      B.Add(aCompound, aShapeI);
    }
    if (nbSub == 1)
      aShape = aShapeI;
    else if (nbSub > 1)
      aShape = GEOMImpl_GlueDriver::GlueFaces(aCompound, Precision::Confusion(), Standard_True);
    return aShape;
  }

  // 2. Scale = geompy.MakeScaleTransform(theBase, aCDG, theScaleFactor)

  // Bug 6839: Check for standalone (not included in faces) degenerated edges
  TopTools_IndexedDataMapOfShapeListOfShape aEFMap;
  TopExp::MapShapesAndAncestors(theShapeBase, TopAbs_EDGE, TopAbs_FACE, aEFMap);
  Standard_Integer i, nbE = aEFMap.Extent();
  for (i = 1; i <= nbE; i++) {
    TopoDS_Shape anEdgeSh = aEFMap.FindKey(i);
    if (BRep_Tool::Degenerated(TopoDS::Edge(anEdgeSh))) {
      const TopTools_ListOfShape& aFaces = aEFMap.FindFromIndex(i);
      if (aFaces.IsEmpty())
        Standard_ConstructionError::Raise
          ("Scaling aborted : cannot scale standalone degenerated edge");
    }
  }

  // Perform Scaling
  gp_Trsf aTrsf;
  aTrsf.SetScale(aCDG, theScaleFactor);
  BRepBuilderAPI_Transform aBRepTrsf (theShapeBase, aTrsf, Standard_False);
  TopoDS_Shape aScale = aBRepTrsf.Shape();

  // 3. aBase2 = geompy.MakeTranslationVectorDistance(Scale, theVec, theH)
  gp_Trsf aTrsf3;
  aTrsf3.SetTranslation(theVector);
  TopLoc_Location aLocOrig = aScale.Location();
  gp_Trsf aTrsfOrig = aLocOrig.Transformation();
  TopLoc_Location aLocRes (aTrsf3 * aTrsfOrig);
  TopoDS_Shape aBase2 = aScale.Located(aLocRes);

  // 4. aCDG_2 = geompy.MakeTranslationVectorDistance(aCDG, theVec, theH)
  gp_Pnt aCDG_2 = aCDG.Translated(theVector);
  TopoDS_Shape aShapeCDG_2 = BRepBuilderAPI_MakeVertex(aCDG_2).Shape();

  // 5. Vector = geompy.MakeVector(aCDG, aCDG_2)
  TopoDS_Shape aShapeVec = BRepBuilderAPI_MakeEdge(aCDG, aCDG_2).Shape();
  TopoDS_Edge anEdge = TopoDS::Edge(aShapeVec);
  TopoDS_Wire aWirePath = BRepBuilderAPI_MakeWire(anEdge);

  // 6. aPrism = geompy.MakePipeWithDifferentSections([theBase, aBase2], [aCDG, aCDG_2], Vector, False, False)
  Handle(TopTools_HSequenceOfShape) aBases = new TopTools_HSequenceOfShape;
  aBases->Append(theShapeBase);
  aBases->Append(aBase2);

  Handle(TopTools_HSequenceOfShape) aLocs = new TopTools_HSequenceOfShape;
  aLocs->Append(aShapeCDG_1);
  aLocs->Append(aShapeCDG_2);

  aShape = GEOMImpl_PipeDriver::CreatePipeWithDifferentSections(aWirePath, aBases, aLocs, false, false);

  // 7. Make a solid, if possible
  if (theShapeBase.ShapeType() == TopAbs_FACE) {
    BRepBuilderAPI_Sewing aSewing (Precision::Confusion()*10.0);
    TopExp_Explorer expF (aShape, TopAbs_FACE);
    Standard_Integer ifa = 0;
    for (; expF.More(); expF.Next()) {
      aSewing.Add(expF.Current());
      ifa++;
    }
    if (ifa > 0) {
      aSewing.Perform();
      TopoDS_Shape aShell;

      TopoDS_Shape sh = aSewing.SewedShape();
      if (sh.ShapeType() == TopAbs_FACE && ifa == 1) {
        // case for creation of shell from one face
        TopoDS_Shell ss;
        B.MakeShell(ss);
        B.Add(ss,sh);
        aShell = ss;
      }
      else {
        TopExp_Explorer exp (sh, TopAbs_SHELL);
        Standard_Integer ish = 0;
        for (; exp.More(); exp.Next()) {
          aShell = exp.Current();
          ish++;
        }
        if (ish != 1)
          aShell = sh;
      }
      BRepCheck_Shell chkShell (TopoDS::Shell(aShell));
      if (chkShell.Closed() == BRepCheck_NoError) {
        TopoDS_Solid Sol;
        B.MakeSolid(Sol);
        B.Add(Sol, aShell);
        BRepClass3d_SolidClassifier SC (Sol);
        SC.PerformInfinitePoint(Precision::Confusion());
        if (SC.State() == TopAbs_IN) {
          B.MakeSolid(Sol);
          B.Add(Sol, aShell.Reversed());
        }
        aShape = Sol;
      }
    }
  }

  return aShape;
}
bool StdMeshers_RadialPrism_3D::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()
  std::auto_ptr<SMESH_MesherHelper> helperDeleter( myHelper );

  // get 2 shells
  TopoDS_Solid solid = TopoDS::Solid( aShape );
  TopoDS_Shell outerShell = BRepClass3d::OuterShell( solid );
  TopoDS_Shape innerShell;
  int nbShells = 0;
  for ( TopoDS_Iterator It (solid); It.More(); It.Next(), ++nbShells )
    if ( !outerShell.IsSame( It.Value() ))
      innerShell = It.Value();
  if ( nbShells != 2 )
    return error(COMPERR_BAD_SHAPE, SMESH_Comment("Must be 2 shells but not ")<<nbShells);

  // ----------------------------------
  // Associate sub-shapes of the shells
  // ----------------------------------

  ProjectionUtils::TShapeShapeMap shape2ShapeMaps[2];
  bool mapOk1 = ProjectionUtils::FindSubShapeAssociation( innerShell, &aMesh,
                                                          outerShell, &aMesh,
                                                          shape2ShapeMaps[0]);
  bool mapOk2 = ProjectionUtils::FindSubShapeAssociation( innerShell.Reversed(), &aMesh,
                                                          outerShell, &aMesh,
                                                          shape2ShapeMaps[1]);
  if ( !mapOk1 && !mapOk2 )
    return error(COMPERR_BAD_SHAPE,"Topology of inner and outer shells seems different" );

  int iMap;
  if ( shape2ShapeMaps[0].Extent() == shape2ShapeMaps[1].Extent() )
  {
    // choose an assiciation by shortest distance between VERTEXes
    double dist1 = 0, dist2 = 0;
    TopTools_DataMapIteratorOfDataMapOfShapeShape ssIt( shape2ShapeMaps[0]._map1to2 );
    for (; ssIt.More(); ssIt.Next() )
    {
      if ( ssIt.Key().ShapeType() != TopAbs_VERTEX ) continue;
      gp_Pnt pIn   = BRep_Tool::Pnt( TopoDS::Vertex( ssIt.Key() ));
      gp_Pnt pOut1 = BRep_Tool::Pnt( TopoDS::Vertex( ssIt.Value() ));
      gp_Pnt pOut2 = BRep_Tool::Pnt( TopoDS::Vertex( shape2ShapeMaps[1]( ssIt.Key() )));
      dist1 += pIn.SquareDistance( pOut1 );
      dist2 += pIn.SquareDistance( pOut2 );
    }
    iMap = ( dist1 < dist2 ) ? 0 : 1;
  }
  else
  {
    iMap = ( shape2ShapeMaps[0].Extent() > shape2ShapeMaps[1].Extent() ) ? 0 : 1;
  }
  ProjectionUtils::TShapeShapeMap& shape2ShapeMap = shape2ShapeMaps[iMap];

  // ------------------
  // Make mesh
  // ------------------

  TNode2ColumnMap node2columnMap;
  myLayerPositions.clear();

  for ( exp.Init( outerShell, TopAbs_FACE ); exp.More(); exp.Next() )
  {
    // Corresponding sub-shapes
    TopoDS_Face outFace = TopoDS::Face( exp.Current() );
    TopoDS_Face inFace;
    if ( !shape2ShapeMap.IsBound( outFace, /*isOut=*/true )) {
      return error(SMESH_Comment("Corresponding inner face not found for face #" )
                   << meshDS->ShapeToIndex( outFace ));
    } else {
      inFace = TopoDS::Face( shape2ShapeMap( outFace, /*isOut=*/true ));
    }

    // Find matching nodes of in and out faces
    ProjectionUtils::TNodeNodeMap nodeIn2OutMap;
    if ( ! ProjectionUtils::FindMatchingNodesOnFaces( inFace, &aMesh, outFace, &aMesh,
                                                      shape2ShapeMap, nodeIn2OutMap ))
      return error(COMPERR_BAD_INPUT_MESH,SMESH_Comment("Mesh on faces #")
                   << meshDS->ShapeToIndex( outFace ) << " and "
                   << meshDS->ShapeToIndex( inFace ) << " seems different" );

    // Create volumes

    SMDS_ElemIteratorPtr faceIt = meshDS->MeshElements( inFace )->GetElements();
    while ( faceIt->more() ) // loop on faces on inFace
    {
      const SMDS_MeshElement* face = faceIt->next();
      if ( !face || face->GetType() != SMDSAbs_Face )
        continue;
      int nbNodes = face->NbNodes();
      if ( face->IsQuadratic() )
        nbNodes /= 2;

      // find node columns for each node
      vector< const TNodeColumn* > columns( nbNodes );
      for ( int i = 0; i < nbNodes; ++i )
      {
        const SMDS_MeshNode* nIn = face->GetNode( i );
        TNode2ColumnMap::iterator n_col = node2columnMap.find( nIn );
        if ( n_col != node2columnMap.end() ) {
          columns[ i ] = & n_col->second;
        }
        else {
          TNodeNodeMap::iterator nInOut = nodeIn2OutMap.find( nIn );
          if ( nInOut == nodeIn2OutMap.end() )
            RETURN_BAD_RESULT("No matching node for "<< nIn->GetID() <<
                              " in face "<< face->GetID());
          columns[ i ] = makeNodeColumn( node2columnMap, nIn, nInOut->second );
        }
      }

      StdMeshers_Prism_3D::AddPrisms( columns, myHelper );
    }
  } // loop on faces of out shell

  return true;
}