    // @(#)root/star:$Name:  $:$Id: TVolumeView.cxx,v 1.7 2004/05/12 19:11:45 brun Exp $
    // Author: Valery Fine(fine@bnl.gov)   25/12/98
    // $Id:
    // $Log:

    #include "Riostream.h"
    #include <assert.h>
    #include <stdlib.h>

    #include "TCanvas.h"
    #include "TPad.h"
    #include "TCL.h"
    #include "TBrowser.h"
    #include "TVolumeView.h"
    #include "TVolumeViewIter.h"
    #include "TVolumePosition.h"
    #include "TROOT.h"
    #include "TView.h"
    #include "TPadView3D.h"
    #include "TGeometry.h"
    #include "TVirtualPad.h"
    #include "TObjArray.h"
    //////////////////////////////////////////////////////////////////////////
    //                                                                      //
    // TVolumeView                                                          //
    //                                                                      //
    // TVolumeView class is a special kind of TDataSet with one extra       //
    // pointer to wrap any TObject onto TDataSet object                     //
    //                                                                      //
    //  BE CAREFUL !!!                                                      //
    //  One has to use it carefully no control over that extra object       //
    //  is performed. This means: the object m_Obj data-member points to can//
    //  be destroyed with no this kbject notifying.                         //
    //  There is no tool /protection to check whether m_Obj is till alive.  //
    //  It is one's  code responsilitiy                                     //
    //                                                                      //
    //////////////////////////////////////////////////////////////////////////

    ClassImp(TVolumeView)

    //_____________________________________________________________________________
     TVolumeView::TVolumeView(TVolumeView *viewNode,TVolumePosition *nodePosition)
                : TObjectSet(viewNode->GetName(),(TObject *)nodePosition),fListOfShapes(0)
                //             ,fListOfAttributes(0)
    {
      //
      // This ctor creates a TVolumeView structure from the "marked" nodes
      // of the "viewNode" input structure
      // It re-calculates all positions according of the new topology
      // All new TVolume became UNMARKED though
      //
      if (!gGeometry) new TGeometry;
      if (viewNode)
      {
         SetTitle(viewNode->GetTitle());
         EDataSetPass mode = kContinue;
         TVolumeViewIter next(viewNode,0);
         TVolumeView *nextView = 0;
         while ( (nextView = (TVolumeView *)next(mode)) ){
           mode = kContinue;
           if (nextView->IsMarked()) {
             TVolumePosition *position =next[0];
             if (!position->GetNode()) {
                 Error("TVolumeView ctor","%s %s ",GetName(),nextView->GetName());
             }
             Add(new TVolumeView(nextView,position));
             mode = kPrune;
          }
        }
      }
    }

    //_____________________________________________________________________________
     TVolumeView::TVolumeView(TVolumeView *viewNode,TVolumeView *topNode)
                : TObjectSet(viewNode->GetName(),(TObject *)0),fListOfShapes(0)
                //             ,fListOfAttributes(0)
    {
      //
      // This ctor creates a TVolumeView structure containing:
      //
      //   - viewNode on the top
      //   - skip ALL node from the original viewNode untill topNode found
      //   - include all "marked" node below "topNode" if any
      //     topNode is always included
      //
      // It re-calculates all positions according of the new topology
      //
      if (!gGeometry) new TGeometry;
      if (viewNode && topNode)
      {
         SetTitle(viewNode->GetTitle());
         // define the depth of the "top" Node
         EDataSetPass mode = kContinue;
         TVolumeViewIter next(viewNode,0);
         TVolumeView *nextView = 0;
         while ( (nextView = (TVolumeView *)next(mode)) ){
           mode = kContinue;
          // Skip till  "top Node" found
          if (topNode != nextView) continue;
          TVolumePosition *position = next[0];
          if (!position->GetNode()) {
             Error("TVolumeView ctor","%s %s ",GetName(),nextView->GetName());
          }
          Add(new TVolumeView(nextView,position));
          break;
        }
      }
    }

    //_____________________________________________________________________________
     TVolumeView::TVolumeView(TVolumeView *viewNode,const Char_t *nodeName1,const Char_t *nodeName2)
                : TObjectSet(viewNode->GetName(),(TObject *)0),fListOfShapes(0)
                //             ,fListOfAttributes(0)
    {
      //
      // This ctor creates a TVolumeView structure containing:
      //
      //   - viewNode on the top
      //   - skip ALL node from the original viewNode untill topNodeName found
      //   - include all "marked" node below "topNodename" if any
      //     topNodeName is always included
      //
      // It re-calculates all positions according of the new topology
      //
      const Char_t *foundName[2] = {nodeName1, nodeName2};
      Bool_t found = kFALSE;
      if (!gGeometry) new TGeometry;
      if (viewNode && nodeName1 && nodeName1[0])
      {
         SetTitle(viewNode->GetTitle());
         // define the depth of the "top" Node
         EDataSetPass mode = kContinue;
         TVolumeViewIter next(viewNode,0);
         TVolumeView *nextView = 0;
         while ( (nextView = (TVolumeView *)next(mode)) ){
           mode = kContinue;
          // Skip till  "top Node" found
          Int_t i = 0;
          found = kFALSE;
          for (i=0;i<2;i++) {
            if (foundName[i]) {
                if (strcmp(nextView->GetName(),foundName[i])) continue;
                foundName[i] = 0;
                found = kTRUE;
                break;
            }
          }
          if (!found) continue;
          TVolumePosition *position = next[0];
          if (!position->GetNode()) {
             Error("TVolumeView ctor","%s %s ",GetName(),nextView->GetName());
          }
          Add(new TVolumeView(nextView,position));
          mode = kPrune;
        }
      }
    }

    //_____________________________________________________________________________
     TVolumeView::TVolumeView(TVolumeView *viewNode,const TVolumeView *node1,const TVolumeView *node2)
                : TObjectSet(viewNode->GetName(),(TObject *)0),fListOfShapes(0)
                //             ,fListOfAttributes(0)
    {
      //
      // This ctor creates a TVolumeView structure containing:
      //
      //   - viewNode on the top
      //   - skip ALL node from the original viewNode untill topNodeName found
      //   - include all "marked" node below "topNodename" if any
      //     topNodeName is always included
      //
      // It re-calculates all positions according of the new topology
      //
      const TVolumeView *foundView[2] = {node1, node2};
      const Int_t nViews = sizeof(foundView)/sizeof(const TVolumeView *);
      Bool_t found = kFALSE;
      if (!gGeometry) new TGeometry;
      if (viewNode)
      {
         SetTitle(viewNode->GetTitle());
         // define the depth of the "top" Node
         EDataSetPass mode = kContinue;
         TVolumeViewIter next(viewNode,0);
         TVolumeView *nextView = 0;
         while ( (nextView = (TVolumeView *)next(mode)) ){
           mode = kContinue;
          // Skip till  "top Node" found
          Int_t i = 0;
          found = kFALSE;
          for (i=0;i<nViews;i++) {
            if (foundView[i]) {
                if (nextView != foundView[i]) continue;
                foundView[i] = 0;
                found = kTRUE;
                break;
            }
          }
          if (!found) continue;
          TVolumePosition *position = next[0];
          if (!position->GetNode()) {
             Error("TVolumeView ctor","%s %s ",GetName(),nextView->GetName());
          }
          Add(new TVolumeView(nextView,position));
          mode = kPrune;
        }
      }
    }

    //_____________________________________________________________________________
     TVolumeView::TVolumeView(TVolume &pattern,Int_t maxDepLevel,
                 const TVolumePosition *nodePosition,EDataSetPass iopt, TVolumeView *rootVolume)
                : TObjectSet(pattern.GetName(),(TObject *)nodePosition),fListOfShapes(0)
    {
      //
      // Creates TVolumeView (view) with a topology similar with TVolume *pattern
      //
      //  Parameters:
      //  -----------
      //  pattern        - the pattern dataset
      //  iopt = kStruct - clone only my structural links
      //         kAll    - clone all links
      //         kRefs   - clone only refs
      //         kMarked - clone marked (not implemented yet) only
      //
      //   All new-created sets become the structural ones anyway.
      //
      //  cout << "ctor for " << GetName() << " - " << GetTitle() << endl;
      if (!gGeometry) new TGeometry;
      if (!nodePosition) {
         // Create the trivial position if any
         nodePosition = new TVolumePosition(&pattern);
         SetObject((TObject*)nodePosition);
      }
      if (!rootVolume) {
         rootVolume   = this;
         nodePosition = 0;
      }
      SetTitle(pattern.GetTitle());
      if ( pattern.IsMarked() ) Mark();
      TVolumePosition *position = 0;
      const TList *list = pattern.GetListOfPositions();
      if (!list || maxDepLevel == 1 || maxDepLevel < 0) return;

      TIter nextPosition(list);
      Bool_t optSel    = (iopt == kStruct);
    //  Bool_t optAll    = (iopt == kAll);
      Bool_t optMarked = (iopt == kMarked);

      const TRotMatrix *thisMatrix = 0;
      Double_t thisTranslation[3] = {0,0,0};
      if (nodePosition ) {
         thisMatrix = nodePosition->GetMatrix();
         for (int i =0; i< 3; i++) thisTranslation[i]= nodePosition->GetX(i);
      }
      while ( (position = (TVolumePosition *)nextPosition()) ) {
         // define the the related TVolume
         TVolume *node     = position->GetNode();
         Double_t *positionMatrix = ((TRotMatrix *)position->GetMatrix())->GetMatrix();
         if (node) {
            UInt_t positionId = position->GetId();
            Double_t newTranslation[3] = {position->GetX(),position->GetY(),position->GetZ()};
            Double_t newMatrix[9];
            TRotMatrix currentMatrix;

            if (nodePosition) {
              if (positionMatrix) {
                TGeometry::UpdateTempMatrix(thisTranslation,thisMatrix?((TRotMatrix *)thisMatrix)->GetMatrix():0
                             ,position->GetX(),position->GetY(),position->GetZ(),positionMatrix
                             ,newTranslation,newMatrix);
                currentMatrix.SetMatrix(newMatrix);
              } else {
                TCL::vadd(thisTranslation, newTranslation,newTranslation,3);
                currentMatrix.SetMatrix(((TRotMatrix *)thisMatrix)->GetMatrix());
              }
            } else {
              if (positionMatrix)
                currentMatrix.SetMatrix(positionMatrix);
              else {
                TCL::ucopy(thisTranslation,newTranslation,3);
                currentMatrix.SetMatrix(TVolume::GetIdentity()->GetMatrix());
              }
            }
            TVolumePosition nextPos(node,newTranslation[0],newTranslation[1],
                                         newTranslation[2], &currentMatrix);
            nextPos.SetId(positionId);
            if (optMarked && !node->IsMarked()) {
                TVolumeView fakeView(*node,maxDepLevel,&nextPos,iopt,rootVolume);
                fakeView.DoOwner(kFALSE);
                continue;
            }

            if (optSel) {
               TDataSet *parent = node->GetParent();
               if ( parent && (parent != (TDataSet *)&pattern) ) continue;
            }
            TRotMatrix *newRotation = new TRotMatrix();
            newRotation->SetMatrix(currentMatrix.GetMatrix());
            TVolumePosition *nP = new TVolumePosition(node,newTranslation[0],newTranslation[1],
                                         newTranslation[2], newRotation);
            nP->SetId(positionId);
            rootVolume->Add(new TVolumeView(*node,maxDepLevel?maxDepLevel-1:0,nP,iopt));
         }
         else
           Error("TVolumeView ctor","Position with NO node attached has been supplied");

      }
    }
    //_____________________________________________________________________________
     TVolumeView::TVolumeView(TVolumeView &viewNode):
                 TObjectSet(viewNode.GetName(),(TObject *)viewNode.GetPosition())
                ,TAtt3D()
                ,fListOfShapes(viewNode.GetListOfShapes())
    { if (viewNode.IsOwner()) { viewNode.DoOwner(kFALSE); DoOwner(); } }

    //_____________________________________________________________________________
     TVolumeView::TVolumeView(Double_t *translate, Double_t *rotate, UInt_t positionId, TVolume *topNode,
                             const Char_t *thisNodePath, const Char_t *matrixName, Int_t matrixType)
                // : fListOfAttributes(0)
    {
      // Special ctor to back TVolumeView::SavePrimitive() method
      if (!gGeometry) new TGeometry;
      fListOfShapes     = 0;
      TVolume *thisNode = 0;
      Double_t thisX  = translate[0];
      Double_t thisY  = translate[1];
      Double_t thisZ  = translate[2];

      // Find TVolume by path;
      if (topNode) {
        thisNode =  (TVolume *)topNode->Find(thisNodePath);
        if (!thisNode->InheritsFrom("TVolume")) {
               thisNode = 0;
               fprintf(stderr,"Error wrong node <%s> on path: "%s"n",thisNode->GetName(),thisNodePath);
        }
      }

      TRotMatrix *thisRotMatrix =  0;
      if (matrixName && strlen(matrixName)) thisRotMatrix = gGeometry->GetRotMatrix(matrixName);
      TVolumePosition *thisPosition = 0;
      if (thisRotMatrix)
          thisPosition = new TVolumePosition(thisNode,thisX, thisY, thisZ, matrixName);
      else if (matrixType==2)
          thisPosition = new TVolumePosition(thisNode,thisX, thisY, thisZ);
      else if (rotate) {
          const Char_t *title = "rotation";
          thisRotMatrix = new TRotMatrix((Text_t *)matrixName,(Text_t *)title,rotate);
          thisPosition  = new TVolumePosition(thisNode,thisX, thisY, thisZ, thisRotMatrix);
      }
      else
           Error("TVolumeView"," No rotation matrix is defined");
      thisPosition->SetId(positionId);
      SetObject(thisPosition);
      if (thisNode) {
        SetName(thisNode->GetName());
        SetTitle(thisNode->GetTitle());
      }
    }

    //_____________________________________________________________________________
     TVolumeView::TVolumeView(TVolume *thisNode,TVolumePosition *nodePosition)
                : TObjectSet(thisNode?thisNode->GetName():"",(TObject *)nodePosition),fListOfShapes(0)
    {
      if (!gGeometry) new TGeometry;
      SafeDelete(fListOfShapes);
      if (thisNode)
         SetTitle(thisNode->GetTitle());
    }

    //______________________________________________________________________________
     TVolumeView::~TVolumeView()
    {
    // default dtor (empty for this class)
    }

    //_____________________________________________________________________________
     TVolume *TVolumeView::AddNode(TVolume *node)
    {
      // Add the TVolume in the Tnode data-structure refered
      // by this TVolumeView object
      // Return TVolume * the input TVolume * was attached to

      TVolume *closedNode = 0;
      TVolumePosition *pos ;
      if ( node && (pos = GetPosition() )  && (closedNode = pos->GetNode()) )
             closedNode->Add(node);
      return closedNode;
    }

    //______________________________________________________________________________
     void TVolumeView::Add(TShape *shape, Bool_t IsMaster)
    {
      if (!shape) return;
      if (!fListOfShapes) fListOfShapes = new TList;
      if (IsMaster)
          fListOfShapes->AddFirst(shape);
      else
          fListOfShapes->Add(shape);
    }

    //_____________________________________________________________________________
     void TVolumeView::Browse(TBrowser *b){
      TObjectSet::Browse(b);
    //  TVolumePosition *pos = GetPosition();
    //  if (pos) pos->Browse(b);
    //    b->Add(pos);
    }

    //______________________________________________________________________________
     Int_t TVolumeView::DistancetoPrimitive(Int_t px, Int_t py)
    {
    //*-*-*-*-*-*-*-*-*Compute distance from point px,py to a TVolumeView*-*-*-*-*-*
    //*-*                  ===========================================
    //*-*  Compute the closest distance of approach from point px,py to the position of
    //*-*  this node.
    //*-*  The distance is computed in pixels units.
    //*-*
    //*-*  It is restricted by 2 levels of TVolumes
    //*-*
    //*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*

       const Int_t big = 9999;
       const Int_t inaxis = 7;
       const Int_t maxdist = 5;

       Int_t dist = big;

       Int_t puxmin = gPad->XtoAbsPixel(gPad->GetUxmin());
       Int_t puymin = gPad->YtoAbsPixel(gPad->GetUymin());
       Int_t puxmax = gPad->XtoAbsPixel(gPad->GetUxmax());
       Int_t puymax = gPad->YtoAbsPixel(gPad->GetUymax());

    //*-*- return if point is not in the user area
       if (px < puxmin - inaxis) return big;
       if (py > puymin + inaxis) return big;
       if (px > puxmax + inaxis) return big;
       if (py < puymax - inaxis) return big;

       TView *view =gPad->GetView();
       if (!view) return big;

       TVolumePosition *position = GetPosition();
       TVolume *thisNode  = 0;
       TShape  *thisShape = 0;
       if (position) {
         thisNode = position->GetNode();
         position->UpdatePosition();
         if (thisNode) {
            thisShape    = thisNode->GetShape();
            if (!(thisNode->GetVisibility() & TVolume::kThisUnvisible) &&
                  thisShape && thisShape->GetVisibility())
            {
              dist = thisShape->DistancetoPrimitive(px,py);
              if (dist < maxdist) {
                 gPad->SetSelected(this);
                 return 0;
              }
           }
         }
       }

    //   if ( TestBit(kSonsInvisible) ) return dist;

    //*-*- Loop on all sons
       TSeqCollection *fNodes =  GetCollection();
       Int_t nsons = fNodes?fNodes->GetSize():0;
       Int_t dnode = dist;
       if (nsons) {
          gGeometry->PushLevel();
          TVolume *node;
          TIter  next(fNodes);
          while ((node  = (TVolume *)next())) {
             dnode = node->DistancetoPrimitive(px,py);
             if (dnode <= 0)  break;
             if (dnode < dist) dist = dnode;
             if (gGeometry->GeomLevel() > 2) break;
          }
          gGeometry->PopLevel();
       }

       if (gGeometry->GeomLevel()==0 && dnode > maxdist) {
          gPad->SetSelected(view);
          return 0;
       } else
          return dnode;
    }

    //______________________________________________________________________________
     void TVolumeView::Draw(Option_t *option)
    {
    //*-*-*-*-*-*-*-*-*-*-*-*Draw Referenced node with current parameters*-*-*-*
    //*-*                   =============================================

        TString opt = option;
        opt.ToLower();
    //*-*- Clear pad if option "same" not given
        if (!gPad) {
           if (!gROOT->GetMakeDefCanvas()) return;
           (gROOT->GetMakeDefCanvas())();
        }
        if (!opt.Contains("same")) gPad->Clear();

    //*-*- Draw Referenced node
        gGeometry->SetGeomLevel();
        gGeometry->UpdateTempMatrix();

       // Check geometry level

        Int_t iopt = atoi(option);
        TDataSet *parent = 0;
        char buffer[10];
        if (iopt < 0) {
           sprintf(buffer,"%d",-iopt);
           option = buffer;
           // select parent to draw
           parent = this;
           do parent = parent->GetParent();
           while (parent && ++iopt);
        }
        if (parent) parent->AppendPad(option);
        else        AppendPad(option);
    //*-*- Create a 3-D View
        TView *view = gPad->GetView();
        if (!view) {
           view = new TView(1);
           view->SetAutoRange(kTRUE);
           Paint("range");
           view->SetAutoRange(kFALSE);
        }
    }

    //_____________________________________________________________________________
     TVolume *TVolumeView::GetNode() const {
      TVolumePosition *pos = GetPosition();
      if (pos)
        return pos->GetNode();
      return 0;
    }

    //_____________________________________________________________________________
     Int_t TVolumeView::GetGlobalRange(const TVolumeView *rootNode,Float_t *globalMin,Float_t *globalMax)
    {
      //
      // Calculate the position of the vertrex of the outlined cube in repect
      // of the given TVolumeView object
      //
      if (rootNode)
      {
        SetTitle(rootNode->GetTitle());
        EDataSetPass mode = kContinue;
        TVolumeViewIter next((TVolumeView *)rootNode,0);
        TVolumeView *nextView = 0;
        // Find itself.
        while ( (nextView = (TVolumeView *)next(mode)) && nextView != this ){}
        if (nextView == this) {
          TVolumePosition *position = next[0];
          if (!position->GetNode()) {
              Error("TVolumeView ctor","%s %s ",GetName(),nextView->GetName());
          }
          // Calculate the range of the outlined cube verteces.
          GetLocalRange(globalMin,globalMax);
          Float_t offSet[3] = {position->GetX(),position->GetY(),position->GetZ()};
          for (Int_t i=0;i<3;i++) {
            globalMin[i] += offSet[i];
            globalMax[i] += offSet[i];
          }
        }
        return next.GetDepth();
      }
      else return -1;
    }

    //______________________________________________________________________________
     void TVolumeView::GetLocalRange(Float_t *min, Float_t *max)
    {
      //  GetRange
      //
      //  Calculates the size of 3 box the node occupies.
      //  Return:
      //    two floating point arrays with the bound of box
      //     surroundind all shapes of this TModeView
      //

      TVirtualPad *savePad = gPad;
      //  Create a dummy TPad;
      TCanvas dummyPad("--Dumm--","dum",1,1);
      // Assing 3D TView
      TView view(1);

      gGeometry->SetGeomLevel();
      gGeometry->UpdateTempMatrix();
      view.SetAutoRange(kTRUE);
      Paint("range");
      view.GetRange(&min[0],&max[0]);
      // restore "current pad"
       if (savePad) savePad->cd();
    }

    //______________________________________________________________________________
     Text_t *TVolumeView::GetObjectInfo(Int_t px, Int_t py) const
    {
       if (!gPad) return 0;
       static char info[512];
       Double_t x[3] = {0,0,0.5};
       ((TPad *)gPad)->AbsPixeltoXY(px,py,x[0],x[1]);
       TView *view =gPad->GetView();
       if (view) {
           Double_t min[3], max[3];
           view->GetRange(min,max);
           for (int i =0; i<3;i++) min[i] = (max[i]+min[i])/2;
           view->WCtoNDC(min,max);
           min[0] = x[0]; min[1] = x[1];
           min[2] = max[2];
           view->NDCtoWC(min, x);
       }
       TShape *shape = GetShape();
       if (shape)
         sprintf(info,"%6.2f/%6.2f/%6.2f: %s/%s, shape=%s/%s",x[0],x[1],x[2],GetName(),GetTitle(),shape->GetName(),shape->ClassName());
       else
         sprintf(info,"%6.2f/%6.2f/%6.2f: %s/%s",x[0],x[1],x[2],GetName(),GetTitle());
       return info;
    }

    //______________________________________________________________________________
     TVolumePosition  *TVolumeView::Local2Master(const Char_t *localName, const Char_t *masterName)
    {
      TVolumeView *masterNode = this;
      TVolumePosition *position = 0;
      if (masterName && masterName[0]) masterNode = (TVolumeView *)Find(masterName);
      if (masterNode) {
        TVolumeViewIter transform(masterNode,0);
        if (transform(localName)) position = transform[0];
      }
      return position;
    }

    //______________________________________________________________________________
     TVolumePosition *TVolumeView::Local2Master(const TVolumeView *localNode,const TVolumeView *masterNode)
    {
      TVolumePosition *position = 0;
      if (!masterNode) masterNode = this;
      if (masterNode && localNode) {
        TVolumeViewIter transform((TVolumeView *)masterNode,0);
        TVolumeView *nextNode = 0;
        while ((nextNode = (TVolumeView *)transform()) && nextNode != localNode);
        if (nextNode) position = transform[0];
      }
      return position;
    }

    //______________________________________________________________________________
     Float_t *TVolumeView::Local2Master(const Float_t *local, Float_t *master,
                                       const Char_t *localName, const Char_t *masterName, Int_t nVector)
    {
      //
      // calculate  transformation  master = (M-local->master )*local + (T-local->master )
      //  where
      //     M-local->master - rotation matrix 3 x 3 from the master node to the local node
      //     T-local->master - trasport vector 3 from the master node to the local node
      //
      // returns a "master" pointer if transformation has been found
      //        otherwise 0;
      //
       Float_t *trans = 0;
       TVolumePosition *position = 0;
       TVolumeView *masterNode = this;
       if (masterName && masterName[0]) masterNode = (TVolumeView *)Find(masterName);
       if (masterNode) {
         TVolumeViewIter transform(masterNode,0);
         if (transform(localName) && (position = (TVolumePosition *) transform.GetPosition()) )
             trans = position->Local2Master(local,master,nVector);
       }
       return trans;
    }

    //______________________________________________________________________________
     Float_t *TVolumeView::Local2Master(const Float_t *local, Float_t *master,
                                       const TVolumeView *localNode,
                                       const TVolumeView *masterNode, Int_t nVector)
    {
      //
      // calculate  transformation  master = (M-local->master )*local + (T-local->master )
      //  where
      //     M-local->master - rotation matrix 3 x 3 from the master node to the local node
      //     T-local->master - trasport vector 3 from the master node to the local node
      //
      // returns a "master" pointer if transformation has been found
      //        otherwise 0;
      //
       Float_t *trans = 0;
       TVolumePosition *position = 0;
       if (!masterNode) masterNode = this;
       if (masterNode && localNode) {
         TVolumeViewIter transform((TVolumeView *)masterNode,0);
         TVolumeView *nextNode = 0;
         while ((nextNode = (TVolumeView *)transform()) && nextNode != localNode);
         if (nextNode && (position = (TVolumePosition *) transform.GetPosition()) )
             trans = position->Local2Master(local,master,nVector);
       }
       return trans;
    }

    //______________________________________________________________________________
     void TVolumeView::Paint(Option_t *option)
    {
    //*-*-*-*-*-*-*-*-*-*-*-*Paint Referenced node with current parameters*-*-*-*
    //*-*                   ==============================================
    //*-*
    //*-*  vis = 1  (default) shape is drawn
    //*-*  vis = 0  shape is not drawn but its sons may be not drawn
    //*-*  vis = -1 shape is not drawn. Its sons are not drawn
    //*-*  vis = -2 shape is drawn. Its sons are not drawn
    //*-*
    //*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
    //
    // It draw the TVolumeView layers from the iFirst one (form the zero) till
    // iLast one reached.
    //
    // restrict the levels for "range" option
      Int_t level = gGeometry->GeomLevel();
      if (option && option[0]=='r' && level > 3 ) return;

      Int_t iFirst =  atoi(option);
      Int_t iLast = 0;
      const char *delim = strpbrk( option,":-,");
      if (delim)  iLast = atoi(delim+1);
      if (iLast < iFirst) {
         iLast = iFirst-1;
         iFirst = 0;
      }

      if ( (0 < iLast) && (iLast < level) )  return;

      TPadView3D *view3D = (TPadView3D*)gPad->GetView3D();

      TVolume *thisNode  = 0;
      TVolumePosition *position = GetPosition();

      // UpdatePosition does change the current matrix and it MUST be called FIRST !!!
      if (position) {
         thisNode  = position->GetNode();
         position->UpdatePosition(option);
      }

     // if (option[0] !='r' ) printf(" Level %d first = %d  iLast %d n",level, iFirst, iLast);
      if (level >= iFirst) {
         PaintShape(option);
         if (thisNode)  thisNode->PaintShape(option);
      }
    ////---   if ( thisNode->TestBit(kSonsInvisible) ) return;

    //*-*- Paint all sons
      TSeqCollection *Nodes =  GetCollection();
      Int_t nsons = Nodes?Nodes->GetSize():0;

      if(!nsons) return;

      gGeometry->PushLevel();
      TVolumeView *node;
      TIter  next(Nodes);
      while ((node = (TVolumeView *)next())) {
         if (view3D)  view3D->PushMatrix();

         node->Paint(option);

         if (view3D)  view3D->PopMatrix();
      }
      gGeometry->PopLevel();
    }

    //______________________________________________________________________________
     void TVolumeView::PaintShape(Option_t *option)
    {
      // Paint shape of the node
      // To be called from the TObject::Paint method only
      Bool_t rangeView = option && option[0]=='r';

      TIter nextShape(fListOfShapes);
      TShape *shape = 0;
      while( (shape = (TShape *)nextShape()) ) {
        if (!shape->GetVisibility())   continue;
        if (!rangeView) {
          TPadView3D *view3D = (TPadView3D*)gPad->GetView3D();
          if (view3D)
             view3D->SetLineAttr(shape->GetLineColor(),shape->GetLineWidth(),option);
        }
        shape->Paint(option);
      }
    }

    //______________________________________________________________________________
     TString TVolumeView::PathP() const
    {
     // return the full path of this data set
       TString str;
       TVolumeView *parent = (TVolumeView *)GetParent();
       if (parent) {
           str = parent->PathP();
           str += "/";
       }
       str +=  GetName();
       UInt_t positionId = 0;
       TVolumePosition *p = GetPosition();
       if (p) {
          char buffer[10];
          positionId = p->GetId();
          sprintf(buffer,";%d",p->GetId());
          str +=  buffer;
       }
       return str;
    }

    //_______________________________________________________________________
     void TVolumeView::SavePrimitive(ofstream &out, Option_t *)
    {
    const Char_t *sceleton[] = {
       "TVolumeView *CreateNodeView(TVolume *topNode) {"
      ,"  TString     thisNodePath   = "
      ,"  UInt_t      thisPositionId = "
      ,"  Double_t thisTranslate[3]  = "
      ," "
      ,"  TString        matrixName  = "
      ,"  Int_t          matrixType  = "
      ,"  Double_t     thisMatrix[]  = {  "
      ,"                                  "
      ,"                                  "
      ,"                               };"
      ,"  return = new TVolumeView(thisTranslate, thisMatrix, thisPositionId, topNode,"
      ,"                          thisNodePath.Data(),matrixName.Data(), matrixType);"
      ,"}"
      };
    //------------------- end of sceleton ---------------------
      Int_t sceletonSize = sizeof(sceleton)/4;
      TVolumePosition *thisPosition = GetPosition();
      TVolume *thisFullNode = GetNode();
      TString thisNodePath = thisFullNode ? thisFullNode->Path() : TString("");
      // Define position
      UInt_t thisPositionId = thisPosition ? thisPosition->GetId():0;
      Double_t thisX  = thisPosition ? thisPosition->GetX():0;
      Double_t thisY  = thisPosition ? thisPosition->GetY():0;
      Double_t thisZ  = thisPosition ? thisPosition->GetZ():0;

      const TRotMatrix *matrix = thisPosition ? thisPosition->GetMatrix():0;
      Int_t matrixType = 2;
      TString matrixName = " ";
      Double_t thisMatrix[] = { 0,0,0, 0,0,0, 0,0,0 };
      if (matrix) {
         matrixName = matrix->GetName();
         memcpy(thisMatrix,((TRotMatrix *)matrix)->GetMatrix(),9*sizeof(Double_t));
         matrixType = matrix->GetType();
      }
      Int_t im = 0;
      for (Int_t lineNumber =0; lineNumber < sceletonSize; lineNumber++) {
        out << sceleton[lineNumber];                             // cout << lineNumber << ". " << sceleton[lineNumber];
        switch (lineNumber) {
        case  1:  out  << """ << thisNodePath.Data() << "";" ; // cout  << """ << thisNodePath.Data() << "";" ;
           break;
        case  2:  out   << thisPositionId << ";" ; // cout  << """ << thisNodePath.Data() << "";" ;
           break;
        case  3:  out << "{" << thisX << ", " << thisY << ", "<< thisZ << "};";  // cout << thisX << ";" ;
           break;
        case  5:  out << """ << matrixName << "";" ;           // cout << """ << matrixName << "";" ;
           break;
        case  6:  out <<  matrixType << ";" ;                    // cout <<  matrixType << ";" ;
           break;
        case  7:  out << thisMatrix[im++] << ", " << thisMatrix[im++] << ", " << thisMatrix[im++]  << ", " ;
           break;
        case  8:  out << thisMatrix[im++] << ", " << thisMatrix[im++] << ", " << thisMatrix[im++]  << ", " ;
           break;
        case  9:  out << thisMatrix[im++] << ", " << thisMatrix[im++] << ", " << thisMatrix[im++] ;
           break;
        default:
           break;
       };
    //   cout << " " << endl;
       out << " " << endl;
     }
    }

    //______________________________________________________________________________
     void  TVolumeView::SetLineAttributes()
    {
      TVolume *thisNode = GetNode();
      if (thisNode) thisNode->SetLineAttributes();
    }

    //______________________________________________________________________________
     void TVolumeView::SetVisibility(Int_t vis)
    {
     TVolume *node = GetNode();
     if (node) node->SetVisibility(TVolume::ENodeSEEN(vis));
    }

    //______________________________________________________________________________
     void TVolumeView::Sizeof3D() const
    {
    //*-*-*-*-*-*-*Return total size of this 3-D Node with its attributes*-*-*
    //*-*          ==========================================================

       if (GetListOfShapes()) {
         TIter nextShape(GetListOfShapes());
         TShape *shape = 0;
          while( (shape = (TShape *)nextShape()) ) {
            if (shape->GetVisibility())  shape->Sizeof3D();
         }
       }

       TVolume *thisNode  = GetNode();
       if (thisNode && !(thisNode->GetVisibility()&TVolume::kThisUnvisible) ) {
         TIter nextShape(thisNode->GetListOfShapes());
         TShape *shape = 0;
          while( (shape = (TShape *)nextShape()) ) {
            if (shape->GetVisibility())  shape->Sizeof3D();
         }
       }

    //   if ( TestBit(kSonsInvisible) ) return;

      TVolumeView *node;
      TDataSetIter next((TVolumeView *)this);
      while ((node = (TVolumeView *)next())) node->Sizeof3D();
    }