Improved mouse-wheeling for histograms und graphs

Hey ROOTers,

Olivier told me a couple of days ago about this crazy feature in root, I never realized before: the mouse-wheel.

I started to wheel everywhere :slight_smile: Makes a lot of fun, but I wasn’t satisfied. What I want to present you here is an improved mechanism that allows to zoom in and out as it is realized in many known applications, such as gle earth, Autoc EA*LE, and many more.

Its detect the position of the mouse cursor and zooms in and out with respect to it. I tried it on a large col-plot - its really fantastic.

Please modify these lines in the function ExecuteEvent(Int_t event, Int_t px, Int_t py) of THistPainter (the originals are outcommented):

 case kWheelUp:

      if (dimension ==2) {
		  /*
         bin1 = xaxis->GetFirst()+1;
         bin2 = xaxis->GetLast()-1;
         bin1 = TMath::Max(bin1, 1);
         bin2 = TMath::Min(bin2, xaxis->GetNbins());
         if (bin2>bin1) xaxis->SetRange(bin1,bin2);
         bin1 = yaxis->GetFirst()+1;
         bin2 = yaxis->GetLast()-1;
         bin1 = TMath::Max(bin1, 1);
         bin2 = TMath::Min(bin2, yaxis->GetNbins());
         if (bin2>bin1) yaxis->SetRange(bin1,bin2);
         */
         
         
         const Double_t scaleFactor = 0.05;
         Double_t x1, x2;
         Double_t mouseX, mouseY;
         Double_t relPosX1, relPosX2;
         mouseX = gPad->AbsPixeltoX(px);
         mouseY = gPad->AbsPixeltoY(py);
         x1 = xaxis->GetBinLowEdge(xaxis->GetFirst());
         x2 = xaxis->GetBinUpEdge(xaxis->GetLast());
         relPosX1 = mouseX-x1;
         relPosX2 = x2-mouseX;
         relPosX1 *= (1-scaleFactor);
         relPosX2 *= (1-scaleFactor);
         xaxis->SetRangeUser(mouseX-relPosX1,mouseX+relPosX2);
         
         Double_t y1, y2;
         Double_t relPosY1, relPosY2;
         y1 = yaxis->GetBinLowEdge(yaxis->GetFirst());
         y2 = yaxis->GetBinUpEdge(yaxis->GetLast());
         relPosY1 = mouseY-y1;
         relPosY2 = y2-mouseY;
         relPosY1 *= (1-scaleFactor);
         relPosY2 *= (1-scaleFactor);
         yaxis->SetRangeUser(mouseY-relPosY1,mouseY+relPosY2);
      }
      gPad->Modified();
      gPad->Update();

      break;

   case kWheelDown:

      if (dimension == 2) {
         /*bin1 = xaxis->GetFirst()-1;
         bin2 = xaxis->GetLast()+1;
         bin1 = TMath::Max(bin1, 1);
         bin2 = TMath::Min(bin2, xaxis->GetNbins());
         if (bin2>bin1) xaxis->SetRange(bin1,bin2);
         bin1 = yaxis->GetFirst()-1;
         bin2 = yaxis->GetLast()+1;
         bin1 = TMath::Max(bin1, 1);
         bin2 = TMath::Min(bin2, yaxis->GetNbins());
         if (bin2>bin1) yaxis->SetRange(bin1,bin2);
         * */
         const Double_t scaleFactor = 0.05;
         Double_t x1, x2;
         Double_t mouseX, mouseY;
         Double_t relPosX1, relPosX2;
         mouseX = gPad->AbsPixeltoX(px);
         mouseY = gPad->AbsPixeltoY(py);
         x1 = xaxis->GetBinLowEdge(xaxis->GetFirst());
         x2 = xaxis->GetBinUpEdge(xaxis->GetLast());
         relPosX1 = mouseX-x1;
         relPosX2 = x2-mouseX;
         relPosX1 *= (1+scaleFactor);
         relPosX2 *= (1+scaleFactor);
         xaxis->SetRangeUser(mouseX-relPosX1,mouseX+relPosX2);
         
         Double_t y1, y2;
         Double_t relPosY1, relPosY2;
         y1 = yaxis->GetBinLowEdge(yaxis->GetFirst());
         y2 = yaxis->GetBinUpEdge(yaxis->GetLast());
         relPosY1 = mouseY-y1;
         relPosY2 = y2-mouseY;
         relPosY1 *= (1+scaleFactor);
         relPosY2 *= (1+scaleFactor);
         yaxis->SetRangeUser(mouseY-relPosY1,mouseY+relPosY2);
      }
      gPad->Modified();
      gPad->Update();

      break;

And these lines in ExecuteEventAxis(Int_t event, Int_t px, Int_t py, TAxis *axis) of TPad.cxx

case kWheelUp:
 
   
   /*
      bin1 = axis->GetFirst()+1;
      bin2 = axis->GetLast()-1;
      bin1 = TMath::Max(bin1, 1);
      bin2 = TMath::Min(bin2, axis->GetNbins());
      if (bin2>bin1) {
         axis->SetRange(bin1,bin2);
         gPad->Modified();
         gPad->Update();
      }
     */
      
		{
		const Double_t scaleFactor = 0.05;
		Double_t x1, x2;
		Double_t mouse;
		Double_t relPos1, relPos2;
		ratio2 = (AbsPixeltoX(px) - GetUxmin())/(GetUxmax() - GetUxmin());
		mouse = (GetUxmin()+ratio2*(GetUxmax() - GetUxmin()));
		x1 = axis->GetBinLowEdge(axis->GetFirst());
		x2 = axis->GetBinUpEdge(axis->GetLast());
		relPos1 = mouse-x1;
		relPos2 = x2-mouse;
		relPos1 *= (1-scaleFactor);
		relPos2 *= (1-scaleFactor);
		axis->SetRangeUser(mouse-relPos1,mouse+relPos2);
		gPad->Modified();
		gPad->Update();
		}
         
   break;


   case kWheelDown:
		/*
      bin1 = axis->GetFirst()-1;
      bin2 = axis->GetLast()+1;
      bin1 = TMath::Max(bin1, 1);
      bin2 = TMath::Min(bin2, axis->GetNbins());
      if (bin2>bin1) {
         axis->SetRange(bin1,bin2);
         gPad->Modified();
         gPad->Update();
      }
      */
      {
		const Double_t scaleFactor = 0.05;
		Double_t x1, x2;
		Double_t mouse;
		Double_t relPos1, relPos2;
		ratio2 = (AbsPixeltoX(px) - GetUxmin())/(GetUxmax() - GetUxmin());
		mouse = (GetUxmin()+ratio2*(GetUxmax() - GetUxmin()));
		x1 = axis->GetBinLowEdge(axis->GetFirst());
		x2 = axis->GetBinUpEdge(axis->GetLast());
		relPos1 = mouse-x1;
		relPos2 = x2-mouse;
		relPos1 *= (1+scaleFactor);
		relPos2 *= (1+scaleFactor);
		axis->SetRangeUser(mouse-relPos1,mouse+relPos2);
		gPad->Modified();
		gPad->Update();
		}
   break;

Become inspired by the new wheeling :slight_smile:

Don’t hesitate to contact me.

Georg

Hi Georg,

I just tried your mods in THisPainter. It does not work very well for me. It does not zoom a far as the previous version and changing the position of the mouse to zoom elsewhere ends up blocking sometimes … I do not put it in THistPainter for the time being.

Olivier

Hi,
I just tried if I can reproduce, what you said, Olivier,
Yes, it’s no zooming as far as the original, this is because of the too small scale factor. If you have only 10 bins and you want to zoom in 5% this will not work.

This implementation (and also the behaviour) would be much simpler, if these SetRangeUser would allow to zoom on parts of bins. What is the reason that this is not possible?

Therefore, the histograms jitter around when zooming, currently.

Maybe we can find a solution to implement this in a better way. I do not want to say its not innovative, but its intuitive

BTW, I was no able to provoke that it makes other things than expected, what did you do exactly?

Georg