#include "DPlotDrawer.h" /*********************************************************** INITIALIZATION ************************************************************/ ClassImp(DPlotDrawer) DPlotDrawer::DPlotDrawer() { dDebugFlag = false; dSavePlotsFlag = false; dCommonGraphRange_OnlyErrorsFlag = false; dResultAxisLogScale = false; dSaveFileExtensions.push_back("png"); dDefaultCanvasInstructions = new DCanvasInstructions("Default"); } /************************************************************ DRAW: ONE PAD ************************************************************/ TCanvas* DPlotDrawer::Draw_Object(TObject* locPlot, const DCanvasInstructions* locCanvasInstructions) { TObjArray* locPadArray = new TObjArray(1); locPadArray->AddAt(locPlot, 0); return Draw_Superimpose(locPadArray, locCanvasInstructions); } //TObjArray of objects: Superimpose all on one pad TCanvas* DPlotDrawer::Draw_Superimpose(TObjArray* locObjectArray, const DCanvasInstructions* locCanvasInstructions) { if(locCanvasInstructions == NULL) locCanvasInstructions = dDefaultCanvasInstructions; TObjArray* locPlotSetArray = Build_PlotSets_Superimpose(locObjectArray, locCanvasInstructions->dPadInstructions[0]); TObjArray* locCanvasArray = Draw_PlotSetArray(locPlotSetArray, locCanvasInstructions); return (TCanvas*)locCanvasArray->At(0); } TObjArray* DPlotDrawer::Build_PlotSets_Superimpose(TObjArray* locObjectArray, const DPadInstructions* locPadInstructions) { int locNumObjects = locObjectArray->GetEntriesFast(); TObjArray* locPlotSetArray = new TObjArray(); for(int loc_i = 0; loc_i < locNumObjects; ++loc_i) { TObject* locObject = locObjectArray->At(loc_i); locPlotSetArray->AddLast(new DPlotSet(locObject, locPadInstructions, loc_i, locNumObjects)); } return locPlotSetArray; } /************************************************************* DRAW: ARRAY *************************************************************/ //TObjArray of objects: 1 per pad TObjArray* DPlotDrawer::Draw_Array(TObjArray* locObjectArray, const DCanvasInstructions* locCanvasInstructions) { if(locCanvasInstructions == NULL) locCanvasInstructions = dDefaultCanvasInstructions; TObjArray* locPlotSetArray = new TObjArray(); DPlotSet* locPlotSet = new DPlotSet(locObjectArray, locCanvasInstructions->dPadInstructions[0], 0, 1); locPlotSetArray->AddLast(locPlotSet); return Draw_PlotSetArray(locPlotSetArray, locCanvasInstructions); } //array dimensions: plot type, pad TObjArray* DPlotDrawer::Draw_2DArray(TObjArray* loc2DArray, const DCanvasInstructions* locCanvasInstructions) { if(locCanvasInstructions == NULL) locCanvasInstructions = dDefaultCanvasInstructions; int locNumObjects = loc2DArray->GetEntriesFast(); TObjArray* locPlotSetArray = new TObjArray(); for(int loc_i = 0; loc_i < locNumObjects; ++loc_i) { TObjArray* locObjectArray = (TObjArray*)loc2DArray->At(loc_i); locPlotSetArray->AddLast(new DPlotSet(locObjectArray, locCanvasInstructions->Get_PadInstructions(loc_i), loc_i, locNumObjects)); } return Draw_PlotSetArray(locPlotSetArray, locCanvasInstructions); } /*********************************************************** DRAW: PLOTSETS ************************************************************/ TObjArray* DPlotDrawer::Draw_PlotSet(DPlotSet* locPlotSet, const DCanvasInstructions* locCanvasInstructions) { TObjArray* locPlotSetArray = new TObjArray(1); locPlotSetArray->AddAt(locPlotSet, 0); return Draw_PlotSetArray(locPlotSetArray, locCanvasInstructions); } TObjArray* DPlotDrawer::Draw_PlotSetArray(TObjArray* locPlotSetArray, const DCanvasInstructions* locCanvasInstructions) { //Set Canvas Instructions if(locCanvasInstructions == NULL) locCanvasInstructions = dDefaultCanvasInstructions; locCanvasInstructions->dStyle->cd(); dCommonGraphRange_OnlyErrorsFlag = locCanvasInstructions->dCommonGraphRange_OnlyErrorsFlag; //Calculate Canvas Info int locTotalNumPads = 0; for(int loc_i = 0; loc_i < locPlotSetArray->GetEntriesFast(); ++loc_i) { DPlotSet* locPlotSet = (DPlotSet*)locPlotSetArray->At(loc_i); int locSetNumPads = locPlotSet->Get_MaxPlotIndex() + 1; if(locSetNumPads > locTotalNumPads) locTotalNumPads = locSetNumPads; } int locNumRows = 1, locNumColumns = 1; int locNumPadsPerCanvas = Design_Canvases(locTotalNumPads, locCanvasInstructions, locNumRows, locNumColumns); int locNumCanvases = locTotalNumPads/locNumPadsPerCanvas; if(locTotalNumPads % locNumPadsPerCanvas != 0) ++locNumCanvases; //save sizes prior to scaling // double locOrigMarkerSize = gStyle->GetMarkerSize(); double locOrigPadRightMargin = gStyle->GetPadRightMargin(); double locOrigPadTopMargin = gStyle->GetPadTopMargin(); //Loop over input pads until done drawing TObjArray* locCanvasArray = new TObjArray(0); int locOverallPadIndex = 0; for(int loc_i = 0; loc_i < locNumCanvases; ++loc_i) { //Create canvas int locNumPadsThisCanvas = 1; TObjArray* locPadArray = Create_Canvas(locPlotSetArray, locTotalNumPads, locCanvasArray, locCanvasInstructions, locNumPadsPerCanvas, locOverallPadIndex, locNumPadsThisCanvas, locNumRows, locNumColumns); //determine common range for canvas (if needed) pair locCombinedRangePair(1.0, -1.0); //default = don't use if(locCanvasInstructions->dCommonRangeFlag) locCombinedRangePair = Find_CombinedRange_PlotSets(locPlotSetArray, locCanvasInstructions, locOverallPadIndex, locNumPadsThisCanvas); //Loop over pads TCanvas* locCanvas = (TCanvas*)locCanvasArray->Last(); for(int loc_j = 0; loc_j < locNumPadsThisCanvas; ++loc_j) { //if new row, adjacent plots, and NOT common range for the whole canvas, determine new common if(locCanvasInstructions->Get_AdjacentPlotsFlag() && !locCanvasInstructions->dCommonRangeFlag && (loc_j % locNumColumns == 0)) { //get num pads this row size_t locNumPadsThisRow = locNumColumns; if(loc_j >= ((locNumRows - 1)*locNumColumns)) //bottom row locNumPadsThisRow = locNumPadsThisCanvas - (locNumRows - 1)*locNumColumns; //find the range locCombinedRangePair = Find_CombinedRange_PlotSets(locPlotSetArray, locCanvasInstructions, locOverallPadIndex, locNumPadsThisRow); } //select pad if(locPadArray == NULL) locCanvas->cd(loc_j + 1); else ((TPad*)locPadArray->At(loc_j))->cd(); //Get pad info & objects const DPadInstructions* locPadInstructions = locCanvasInstructions->Get_PadInstructions(locOverallPadIndex); //Get edge plot bits bitset<5> locEdgePlotBits = Find_EdgePlotBits(locCanvasInstructions, locNumColumns, locNumRows, loc_j + 1, locTotalNumPads, locOverallPadIndex); //Call Draw func based on type TObject* locFirstObject = ((DPlotSet*)locPlotSetArray->At(0))->Get_FirstPlot(); if(locFirstObject->IsA()->InheritsFrom(TH1::Class())) Draw_Pad_Hist(locPlotSetArray, locOverallPadIndex, locPadInstructions, locEdgePlotBits, locCombinedRangePair); else if(locFirstObject->IsA()->InheritsFrom(TGraph::Class())) Draw_Pad_Graph(locPlotSetArray, locOverallPadIndex, locPadInstructions, locEdgePlotBits, locCombinedRangePair); TVirtualPad::Pad()->Update(); ++locOverallPadIndex; } //Save plot if(dSavePlotsFlag) Save_Canvas(locCanvas, locCanvasInstructions); //restore margins & sizes gStyle->SetPadRightMargin(locOrigPadRightMargin); gStyle->SetPadTopMargin(locOrigPadTopMargin); // gStyle->SetMarkerSize(locOrigMarkerSize); } return locCanvasArray; } /*********************************************************** CANVAS CREATION ***********************************************************/ int DPlotDrawer::Design_Canvases(int locNumPads, const DCanvasInstructions* locCanvasInstructions, int& locNumRows, int& locNumColumns) { if(locNumPads == 1) { locNumRows = 1; locNumColumns = 1; return 1; } int locMaxNumPadsPerCanvas = Calc_MaxNumPadsPerCanvas(locCanvasInstructions); int locNumCanvases = Calc_NumCanvases(locCanvasInstructions, locNumPads, locMaxNumPadsPerCanvas); float locAvgNumPadsPerCanvas = float(locNumPads)/float(locNumCanvases); if(dDebugFlag) cout << "num pads, max per, num canvsases, avg = " << locNumPads << ", " << locMaxNumPadsPerCanvas << ", " << locNumCanvases << ", " << locAvgNumPadsPerCanvas << endl; bool locTargetIsMinNumPads = (locNumCanvases == 1); int locNumPadsPerCanvas = Calc_PadLayout(locCanvasInstructions, locAvgNumPadsPerCanvas, locTargetIsMinNumPads, locNumRows, locNumColumns); if(dDebugFlag) cout << "num per canvas, num rows, num columns = " << locNumPadsPerCanvas << ", " << locNumRows << ", " << locNumColumns << endl; return locNumPadsPerCanvas; } TObjArray* DPlotDrawer::Create_Canvas(TObjArray* locPlotSetArray, int locTotalNumPads, TObjArray* locCanvasArray, const DCanvasInstructions* locCanvasInstructions, int locNumPadsPerCanvas, int locOverallPadIndex, int& locNumPadsThisCanvas, int& locNumRows, int& locNumColumns) { bool locIsLastCanvasFlag = (locOverallPadIndex + locNumPadsPerCanvas >= locTotalNumPads); int locNumPadsRemaining = locTotalNumPads - locOverallPadIndex; locNumPadsThisCanvas = locIsLastCanvasFlag ? locNumPadsRemaining : locNumPadsPerCanvas; int locLastPadIndexThisCanvas = locIsLastCanvasFlag ? locTotalNumPads - 1 : locOverallPadIndex + locNumPadsPerCanvas - 1; //Set pad right margin size bool locIsPadRightMarginLarge = Check_IsPadRightMarginLarge(locCanvasInstructions, locPlotSetArray, locOverallPadIndex, locLastPadIndexThisCanvas); if(locIsPadRightMarginLarge) gStyle->SetPadRightMargin(locCanvasInstructions->dPadLargeRightMargin); string locCanvasBaseName = ((DPlotSet*)locPlotSetArray->At(0))->Get_FirstPlot()->GetName(); if(locNumPadsThisCanvas == 1) { string locCanvasName = Get_CanvasName(locCanvasBaseName, locCanvasArray->GetEntriesFast()); TCanvas* locCanvas = new TCanvas(locCanvasName.c_str(), locCanvasName.c_str()); //size is auto-grabbed from gStyle locCanvasArray->AddLast(locCanvas); return NULL; } //Get pad layout if(locNumPadsRemaining < locNumPadsPerCanvas) //true if last canvas & # pads not perfectly divisible: calc new layout Calc_PadLayout(locCanvasInstructions, locNumPadsRemaining, true, locNumRows, locNumColumns); /* //Calc and apply marker scale factor: locOrigMarkerSize -> locOrigMarkerSize/2.0 double locOrigMarkerSize = gStyle->GetMarkerSize(); double locMarkerScaleFactor = (locNumColumns > 4) ? locOrigMarkerSize/2.0 : locOrigMarkerSize*(1.0 - (locNumColumns - 1)/6.0); gStyle->SetMarkerSize(locOrigMarkerSize*locMarkerScaleFactor); */ //if adjacent & more than 1 row, change top margin if(locCanvasInstructions->Get_AdjacentPlotsFlag() && (locNumRows > 1)) gStyle->SetPadTopMargin(locCanvasInstructions->dAdjacentPadTopMargin); //Make new canvas string locCanvasName = Get_CanvasName(locCanvasBaseName, locCanvasArray->GetEntriesFast()); int locCanvasWidth = 0, locCanvasHeight = 0; Calc_CanvasSize(locCanvasInstructions, locNumRows, locNumColumns, locCanvasWidth, locCanvasHeight); TCanvas* locCanvas = new TCanvas(locCanvasName.c_str(), locCanvasName.c_str(), locCanvasWidth, locCanvasHeight); //Divide canvas TObjArray* locPadArray = NULL; if(locCanvasInstructions->Get_AdjacentPlotsFlag()) locPadArray = Create_AdjacentPads(locCanvas, locNumColumns, locNumRows); else locCanvas->Divide(locNumColumns, locNumRows); locCanvasArray->AddLast(locCanvas); return locPadArray; } int DPlotDrawer::Calc_NumCanvases(const DCanvasInstructions* locCanvasInstructions, int locNumObjects, int locMaxNumPadsPerCanvas) { int locNumCanvases = locNumObjects/locMaxNumPadsPerCanvas; if(locNumCanvases == 0) ++locNumCanvases; else if(locNumObjects % locMaxNumPadsPerCanvas > 0) ++locNumCanvases; return locNumCanvases; } int DPlotDrawer::Calc_MaxNumPadsPerCanvas(const DCanvasInstructions* locCanvasInstructions) { //assume canvas size is fixed //get info int locCanvasWidth = locCanvasInstructions->Get_TargetCanvasWidth(); int locCanvasHeight = locCanvasInstructions->Get_TargetCanvasHeight(); int locMinPadWidth = locCanvasInstructions->dMinPadWidth; float locTargetPadAspectRatio = locCanvasInstructions->dTargetPadAspectRatio; if(dDebugFlag) cout << "Canvas w/h, min pad width, pad ratio = " << locCanvasWidth << ", " << locCanvasHeight << ", " << locMinPadWidth << ", " << locTargetPadAspectRatio << endl; //columns int locMaxNumColumns = int(float(locCanvasWidth)/float(locMinPadWidth)); //round down int locPadWidth = locCanvasWidth/locMaxNumColumns; //rows int locIdealPadHeight = int(float(locPadWidth)/locTargetPadAspectRatio); int locMaxNumRows = int(float(locCanvasHeight)/float(locIdealPadHeight) + 0.5); //round to nearest integer if(dDebugFlag) cout << "max cols, pad width, ideal pad height, max rows = " << locMaxNumColumns << ", " << locPadWidth << ", " << locIdealPadHeight << ", " << locMaxNumRows << endl; return locMaxNumRows*locMaxNumColumns; } int DPlotDrawer::Calc_PadLayout(const DCanvasInstructions* locCanvasInstructions, float locTargetNumPads, bool locTargetIsMinNumPads, int& locNumRows, int& locNumColumns) { //try to divide up canvas to fit "locTargetNumPads" pads per canvas //try to do it in such a way that remains as close to "DCanvasInstructions::dTargetPadAspectRatio" as possible //if locTargetIsMinNumPads is true, then canvas will contain at least "int(locTargetNumPads + 0.001)" pads //assume canvas size is fixed //get info int locCanvasHeight = locCanvasInstructions->Get_TargetCanvasHeight(); int locCanvasWidth = locCanvasInstructions->Get_TargetCanvasWidth(); int locMinPadWidth = locCanvasInstructions->dMinPadWidth; float locTargetPadAspectRatio = locCanvasInstructions->dTargetPadAspectRatio; if(dDebugFlag) cout << "canvas w/h, min pad width, target ratio = " << locCanvasWidth << ", " << locCanvasHeight << ", " << locMinPadWidth << ", " << locTargetPadAspectRatio << endl; //#columns, respecting min pad width float locTargetCanvasAspectRatio = locCanvasInstructions->Get_TargetCanvasAspectRatio(); float locNumColumns_Float = sqrt(locTargetCanvasAspectRatio*locTargetNumPads/locTargetPadAspectRatio); locNumColumns = int(locNumColumns_Float + 0.5); int locMaxNumColumns = int(float(locCanvasWidth)/float(locMinPadWidth)); //round to nearest integer if(locNumColumns > locMaxNumColumns) --locNumColumns; //and don't increment rows int locPadWidth = locCanvasWidth/locNumColumns; if(dDebugFlag) cout << "canv aspect, num columns f/i, max cols, pad width = " << locTargetCanvasAspectRatio << ", " << locNumColumns_Float << ", " << locNumColumns << ", " << locMaxNumColumns << ", " << locPadWidth << endl; //#rows, trying to reach target aspect ratio int locIdealPadHeight = int(float(locPadWidth)/locTargetPadAspectRatio); float locNumRows_Float = float(locCanvasHeight)/float(locIdealPadHeight); locNumRows = int(locNumRows_Float + 0.5); //round to nearest integer int locPadHeight = locCanvasHeight/locNumRows; if(dDebugFlag) cout << "ideal height, num rows F/I, pad height = " << locIdealPadHeight << ", " << locNumRows_Float << ", " << locNumRows << ", " << locPadHeight << endl; //check if target pads is min pads, and whether it's satisfied while(locTargetIsMinNumPads && (locNumRows*locNumColumns < int(locTargetNumPads + 0.001))) { if(locNumColumns == locMaxNumColumns) ++locNumRows; else { locPadWidth = locCanvasWidth/locNumColumns; locPadHeight = locCanvasHeight/locNumRows; double locCurrentAspectRatio = float(locPadWidth)/float(locPadHeight); if(locCurrentAspectRatio > locTargetPadAspectRatio) ++locNumRows; else ++locNumColumns; } } return locNumRows*locNumColumns; } void DPlotDrawer::Calc_CanvasSize(const DCanvasInstructions* locCanvasInstructions, int locNumRows, int locNumColumns, int& locCanvasWidth, int& locCanvasHeight) { float locTargetPadAspectRatio = locCanvasInstructions->dTargetPadAspectRatio; int locTargetCanvasHeight = locCanvasInstructions->Get_TargetCanvasHeight(); int locTargetCanvasWidth = locCanvasInstructions->Get_TargetCanvasWidth(); //assume the user doesn't want a canvas larger than their input canvas size: e.g. larger than the screen, difficult to see //try: say you stretch the pad width to meet the canvas width //use the pad aspect ratio to get the canvas height float locPadWidth = float(locTargetCanvasWidth)/float(locNumColumns); float locPadHeight = locPadWidth/locTargetPadAspectRatio; locCanvasWidth = locTargetCanvasWidth; locCanvasHeight = int(locPadHeight*float(locNumRows) + 0.00001); if(dDebugFlag) cout << "first-attempt pad w/h, first-attempt canvas w/h = " << locPadWidth << ", " << locPadHeight << ", " << locCanvasWidth << ", " << locCanvasHeight << endl; if(locCanvasHeight <= locTargetCanvasHeight) return; //good //wrong stretch! //say you stretch the pad height to meet the canvas height //use the pad aspect ratio to get the canvas width locPadHeight = float(locTargetCanvasHeight)/float(locNumRows); locPadWidth = locTargetPadAspectRatio*locPadHeight; locCanvasHeight = locTargetCanvasHeight; locCanvasWidth = int(locPadWidth*float(locNumColumns) + 0.00001); if(dDebugFlag) cout << "second-attempt pad w/h, first-attempt canvas w/h = " << locPadWidth << ", " << locPadHeight << ", " << locCanvasWidth << ", " << locCanvasHeight << endl; } string DPlotDrawer::Get_CanvasName(string locCanvasBaseName, int locCanvasIndex) { ostringstream locCanvasName; locCanvasName << locCanvasBaseName << "_Canvas_" << locCanvasIndex; while(gROOT->GetListOfCanvases()->FindObject(locCanvasName.str().c_str()) != NULL) locCanvasName << "_0"; return locCanvasName.str(); } TObjArray* DPlotDrawer::Create_AdjacentPads(TCanvas* locCanvas, int locNumColumns, int locNumRows) { //create adjacent pads on the canvas, with no space between them //however, there will be a border around the outer edges of the pads: whatever the style margins are double locLeftFraction = gStyle->GetPadLeftMargin(); double locRightFraction = gStyle->GetPadRightMargin(); double locTopFraction = gStyle->GetPadTopMargin(); double locBottomFraction = gStyle->GetPadBottomMargin(); //Figure out what the size of the content (e.g. hist) should be (fraction) //This is done taking into account the # of pixels the margins will take //Where the margin fraction is the fraction of the given sub-pad, assuming all 4 margins were present (e.g. not adjacent) //e.g. Fox x-content: //with N hists: //canvas_width = num_columns*hist_width + left_margin + right_margin //left_margin = one_pad_width*left_margin_frac //right_margin = one_pad_width*right_margin_frac //canvas_width = one_pad_width*(left_margin_frac + right_margin_frac) + num_columns*hist_width //OK, what is "one_pad_width"?? Well, if only one hist: //one_pad_width = left_margin + right_margin + hist_width //left_margin = one_pad_width*left_margin_frac //right_margin = one_pad_width*right_margin_frac //one_pad_width = one_pad_width*left_margin_frac + one_pad_width*right_margin_frac + hist_width //one_pad_width = hist_width/(1.0 - left_margin_frac - right_margin_frac) //Plugging into canvas_width gives: //canvas_width = num_columns*hist_width + hist_width*(left_margin_frac + right_margin_frac)/(1.0 - left_margin_frac - right_margin_frac) //let's rename the frac ratio: //margin_ratio = (left_margin_frac + right_margin_frac)/(1.0 - left_margin_frac - right_margin_frac) //giving: //canvas_width = num_columns*hist_width + hist_width*margin_ratio //now, solving for hist_width: //hist_width = canvas_width/(num_columns + margin_ratio) //and finally, converting from pixels to pad fraction: //locContentXSize = hist_width_fraction = 1.0/(num_columns + margin_ratio) double locCanvasHeight = locCanvas->GetWh(); double locCanvasWidth = locCanvas->GetWw(); double locXMarginRatio = (locLeftFraction + locRightFraction)/(1.0 - locLeftFraction - locRightFraction); double locYMarginRatio = (locTopFraction + locBottomFraction)/(1.0 - locTopFraction - locBottomFraction); double locContentXSize = 1.0/(locNumColumns + locXMarginRatio); double locContentYSize = 1.0/(locNumRows + locYMarginRatio); //loop through y //top to bottom double locYUp = 1.0; TObjArray* locPadArray = new TObjArray(); locCanvas->cd(); for(int loc_i = 0; loc_i < locNumRows; ++loc_i) { //calc y-low double locYLow = locYUp - locContentYSize; if(loc_i == (locNumRows - 1)) locYLow = 0.000001; //bottom, but up a smidge to avoid float comparison errors else { if(loc_i == 0) //adjust for top margin locYLow -= locTopFraction*locContentYSize/(1.0 - locTopFraction - locBottomFraction); //one_pad_height*top_margin_frac //make sure the fraction yields an exact pixel number to prevent extra spacing between pads //convert to pixels, round, then convert back to fraction //https://root.cern.ch/root/html534/src/TPad.cxx.html#QvrhxE int locYLowPixel = int(0.00005 + locCanvasHeight * locYLow); //0.00005 "rounding" hard-coded in ROOT source code locYLow = (double(locYLowPixel) - 0.00005)/locCanvasHeight + 0.000000001/locCanvasHeight; //back calculate and increase a smidge (since float) } //get y margins //need to convert from fractional area of canvas to fractional area of the SUB-PAD /* double locEdgePixels = locTopFraction*locContentYSize/(1.0 - locTopFraction - locBottomFraction); double locPadTopMargin = locEdgePixels/(locEdgePixels + locContentYSize); double locPadTopMargin = 1.0/(1.0 + locContentYSize/locEdgePixels); locContentYSize/locEdgePixels = 1.0/(locTopFraction/(1.0 - locTopFraction - locBottomFraction)); locContentYSize/locEdgePixels = (1.0 - locTopFraction - locBottomFraction)/locTopFraction; double locPadTopMargin = 1.0/(1.0 + (1.0 - locTopFraction - locBottomFraction)/locTopFraction); double locPadTopMargin = locTopFraction/(locTopFraction + 1.0 - locTopFraction - locBottomFraction); double locPadTopMargin = locTopFraction/(1.0 - locBottomFraction); */ double locPadTopMargin = (loc_i == 0) ? locTopFraction/(1.0 - locBottomFraction) : 0.0; double locPadBottomMargin = (loc_i == (locNumRows - 1)) ? locBottomFraction/(1.0 - locTopFraction) : 0.0; if(locNumRows == 1) //special case { locPadTopMargin = locTopFraction; locPadBottomMargin = locBottomFraction; } //loop through x //left to right double locXLow = 0.0; for(int loc_j = 0; loc_j < locNumColumns; ++loc_j) { //calc x-up double locXUp = locXLow + locContentXSize; if(loc_j == (locNumColumns - 1)) locXUp = 0.9999999; //down a smidge to avoid float comparison errors else { if(loc_j == 0) locXUp += locLeftFraction*locContentXSize/(1.0 - locLeftFraction - locRightFraction); //one_pad_width*left_margin_frac //make sure the fraction yields an exact pixel number to prevent extra spacing between pads //convert to pixels, round, then convert back to fraction //https://root.cern.ch/root/html534/src/TPad.cxx.html#QvrhxE int locXUpPixel = int(0.00005 + locCanvasWidth * locXUp); //0.00005 "rounding" hard-coded in ROOT source code locXUp = (double(locXUpPixel) - 0.00005)/locCanvasWidth + 0.000000001/locCanvasWidth; //back calculate and increase a smidge (since float) } if(dDebugFlag) { cout << "i, j: " << loc_i << ", " << loc_j << endl; cout << "xlow/up, ylow/up = " << locXLow << ", " << locYLow << ", " << locXUp << ", " << locYUp << endl; } //get x margins double locPadLeftMargin = (loc_j == 0) ? locLeftFraction/(1.0 - locRightFraction) : 0.0; double locPadRightMargin = (loc_j == (locNumColumns - 1)) ? locRightFraction/(1.0 - locLeftFraction) : 0.0; if(locNumColumns == 1) //special case { locPadLeftMargin = locLeftFraction; locPadRightMargin = locRightFraction; } //create pad ostringstream locPadName; locPadName << locCanvas->GetName() << "_" << loc_i << "_" << loc_j; TPad* locPad = new TPad(locPadName.str().c_str(), locPadName.str().c_str(), locXLow, locYLow, locXUp, locYUp); //set pad margins locPad->SetMargin(locPadLeftMargin, locPadRightMargin, locPadBottomMargin, locPadTopMargin); //draw, save locPad->Draw(); locPadArray->AddLast(locPad); //update x-low locXLow = locXUp; } //update y-low locYUp = locYLow; } return locPadArray; } bitset<5> DPlotDrawer::Find_EdgePlotBits(const DCanvasInstructions* locCanvasInstructions, int locNumColumns, int locNumRows, int locCanvasPadIndex, int locNumPads, int locOverallPadIndex) { if(!locCanvasInstructions->Get_AdjacentPlotsFlag()) return bitset<5>(0xF); if(locNumPads == 1) return bitset<5>(0x1F); //Find edge bits bitset<5> locEdgePlotBits(0x10); if(locCanvasPadIndex <= locNumColumns) locEdgePlotBits.set(d_TopEdge); if(locCanvasPadIndex > ((locNumRows - 1)*locNumColumns)) locEdgePlotBits.set(d_BottomEdge); if(locCanvasPadIndex % locNumColumns == 1) locEdgePlotBits.set(d_LeftEdge); if((locCanvasPadIndex % locNumColumns == 0) || (locOverallPadIndex == (locNumPads - 1))) locEdgePlotBits.set(d_RightEdge); if(dDebugFlag) cout << "index, bits = " << locCanvasPadIndex << ", " << locEdgePlotBits[d_LeftEdge] << ", " << locEdgePlotBits[d_RightEdge] << ", " << locEdgePlotBits[d_TopEdge] << ", " << locEdgePlotBits[d_BottomEdge] << endl; return locEdgePlotBits; } void DPlotDrawer::Save_Canvas(TCanvas *locCanvas, const DCanvasInstructions* locCanvasInstructions) { for(size_t loc_i = 0; loc_i < dSaveFileExtensions.size(); ++loc_i) { string locSaveName = string(locCanvas->GetName()) + string(".") + dSaveFileExtensions[loc_i]; locCanvas->Print(locSaveName.c_str()); } } /**************************************************************** DRAW *****************************************************************/ void DPlotDrawer::Draw_Pad_Hist(const TObjArray* locPlotSetArray, int locOverallPadIndex, const DPadInstructions* locPadInstructions, bitset<5> locEdgePlotBits, pair locCombinedRangePair) { size_t locNumPlotSets = locPlotSetArray->GetEntriesFast(); TObject* locFirstPlot = ((DPlotSet*)locPlotSetArray->At(0))->Get_FirstPlot(); //get histogram type int locNumDimensions = Get_NumDimensions(locFirstPlot); bool locScaleFlag = locPadInstructions->dScaleFlag; bool locScaleByMaxFlag = locPadInstructions->dScaleByMaxFlag; bool locScaleAxisFlag = locPadInstructions->dScaleAxisFlag; //create hist stack, if desired bool locHistStackFlag = locPadInstructions->dHistStackFlag; THStack* locHistStack = NULL; if(locHistStackFlag) { TH1* locHist = (TH1*)locFirstPlot; string locHistName = string(locHist->GetName()) + string("_Stack"); string locHistTitle = string(locHist->GetTitle()) + string(";") + string(locHist->GetXaxis()->GetTitle()) + string(";"); locHistTitle += string(locHist->GetYaxis()->GetTitle()); locHistStack = new THStack(locHistName.c_str(), locHistTitle.c_str()); } //is result-axis log scale? if((locNumDimensions == 1) && locPadInstructions->dLogYFlag) dResultAxisLogScale = true; else if((locNumDimensions == 2) && locPadInstructions->dLogZFlag) dResultAxisLogScale = true; else dResultAxisLogScale = false; //draw double locScaleReference = 0.0; pair locRightAxisMinMax(1.0, -1.0); TObjArray* locDrawnArray = new TObjArray(); //may be different than input if scaled (cloned!) for(size_t loc_i = 0; loc_i < locNumPlotSets; ++loc_i) { //Init DPlotSet* locPlotSet = (DPlotSet*)locPlotSetArray->At(loc_i); TH1* locHist = (TH1*)locPlotSet->Get_Plot(locOverallPadIndex); if(locHist == NULL) continue; //stat box if((locNumPlotSets > 1) || ((gStyle->GetOptStat() == 0) && (gStyle->GetOptFit() == 0))) locHist->SetStats(kFALSE); //don't draw stats if more than 1 hist (meaningless) //Scaling if(locScaleFlag) { if(loc_i == 0) //Get reference from first hist locScaleReference = locScaleByMaxFlag ? locHist->GetMaximum() : locHist->Integral(); else //Scale subsequent histograms to the first histogram locRightAxisMinMax = Scale_Histogram(locHist, locScaleByMaxFlag, locScaleReference); } //Set fill, line, marker color/style Set_HistStyle(locHist, locPlotSet); //Draw Option string locDrawOption = locPlotSet->dDrawOption; if(loc_i >= 1) locDrawOption += "SAME"; //add to stack or draw if((locNumDimensions == 1) && locPadInstructions->dHistStackFlag) locHistStack->Add(locHist); else locHist->Draw(locDrawOption.c_str()); locDrawnArray->AddLast(locHist); } //Draw Hist Stack if((locNumDimensions == 1) && locPadInstructions->dHistStackFlag) { string locDrawOption = locPadInstructions->dDrawOptions.empty() ? "" : locPadInstructions->dDrawOptions[0]; locHistStack->Draw(locDrawOption.c_str()); } //Tweak plots: Apply axis style & ranges, adjacent plot tweaks, etc. Tweak_PadPlots(locDrawnArray, locPadInstructions, locEdgePlotBits, locCombinedRangePair, NULL); //Redraw the tickmarks (axis) in case drawn over if(locNumPlotSets > 1) locFirstPlot->Draw("SAMEAXIS"); //"axis": draw only axis //draw an axis on the right side if: 1D, scaling, & EXACTLY 2 histograms if((locNumDimensions == 1) && locScaleFlag && locScaleAxisFlag && (locNumPlotSets == 2)) Draw_ScaleAxis_Hist(locPlotSetArray, locOverallPadIndex, locRightAxisMinMax); delete locDrawnArray; } void DPlotDrawer::Draw_Pad_Graph(const TObjArray* locPlotSetArray, int locOverallPadIndex, const DPadInstructions* locPadInstructions, bitset<5> locEdgePlotBits, pair locCombinedRangePair) { size_t locNumPlotSets = locPlotSetArray->GetEntriesFast(); TGraph* locFirstGraph = NULL; for(size_t loc_i = 0; loc_i < locNumPlotSets; ++loc_i) { DPlotSet* locPlotSet = (DPlotSet*)locPlotSetArray->At(loc_i); locFirstGraph = (TGraph*)locPlotSet->Get_Plot(locOverallPadIndex); if(locFirstGraph != NULL) break; } if(locFirstGraph == NULL) return; //scaling bool locScaleFlag = locPadInstructions->dScaleFlag; bool locScaleByMaxFlag = locPadInstructions->dScaleByMaxFlag; bool locScaleAxisFlag = locPadInstructions->dScaleAxisFlag; //create multigraph if > 1 graph TMultiGraph* locMultiGraph = NULL; string locMultiGraphDrawOption = locPadInstructions->Get_DefaultDrawOption("TMultiGraph"); if(locNumPlotSets > 1) { string locGraphName = string(locFirstGraph->GetName()) + string("_Multigraph"); string locGraphTitle = string(locFirstGraph->GetTitle()) + string(";") + string(locFirstGraph->GetXaxis()->GetTitle()); locGraphTitle += string(";") + string(locFirstGraph->GetYaxis()->GetTitle()); locMultiGraph = new TMultiGraph(locGraphName.c_str(), locGraphTitle.c_str()); } //is result-axis log scale? TObject* locFirstPlot = ((DPlotSet*)locPlotSetArray->At(0))->Get_FirstPlot(); int locNumDimensions = Get_NumDimensions(locFirstPlot); if((locNumDimensions == 1) && locPadInstructions->dLogYFlag) dResultAxisLogScale = true; else if((locNumDimensions == 2) && locPadInstructions->dLogZFlag) dResultAxisLogScale = true; else dResultAxisLogScale = false; //draw double locScaleReference = 0.0; pair locRightAxisMinMax(1.0, -1.0); TObjArray* locDrawnArray = new TObjArray(); //may be different than input if scaled (cloned!) for(size_t loc_i = 0; loc_i < locNumPlotSets; ++loc_i) { //Init DPlotSet* locPlotSet = (DPlotSet*)locPlotSetArray->At(loc_i); TGraph* locGraph = (TGraph*)locPlotSet->Get_Plot(locOverallPadIndex); if(locGraph == NULL) continue; TGraphErrors* locGraphErrors = dynamic_cast(locGraph); //Draw Option string locDrawOption = locPlotSet->dDrawOption; if(locNumPlotSets > 1) { //manually suppress drawing of axis ("a"): should draw with multigraph if desired TString locTStringDrawOption(locDrawOption.c_str()); locTStringDrawOption.ReplaceAll("a", ""); locTStringDrawOption.ReplaceAll("A", ""); locDrawOption = (const char*)locTStringDrawOption; } //if tgrapherrors but errors are zero, draw line instead of points if((locGraphErrors != NULL) && !(locGraphErrors->GetEY()[0] > 0.0)) { //draw line instead of points TString locTStringDrawOption(locDrawOption.c_str()); locTStringDrawOption.ReplaceAll("p", "l"); locTStringDrawOption.ReplaceAll("P", "L"); locDrawOption = (const char*)locTStringDrawOption; } //Set graph style Set_GraphStyle(locGraph, locPlotSet); //Scale if(locScaleFlag) { if(loc_i == 0) //get scale reference locScaleReference = locScaleByMaxFlag ? Find_GraphMinMax(locGraph).second : Calc_GraphMeanY(locGraph); else //scale locRightAxisMinMax = Scale_Graph(locGraph, locScaleByMaxFlag, locScaleReference); } //add to multi-graph or draw if(locNumPlotSets > 1) locMultiGraph->Add(locGraph, locDrawOption.c_str()); else locGraph->Draw(locDrawOption.c_str()); locDrawnArray->AddLast(locGraph); } if(locDrawnArray->GetEntriesFast() == 0) { delete locDrawnArray; return; } //Draw multigraph, else finalize axis range if(locNumPlotSets > 1) locMultiGraph->Draw(locMultiGraphDrawOption.c_str()); //TVirtualPad::Pad()->Update(); //Tweak plots: Apply axis style & ranges, adjacent plot tweaks, etc. map locPadAxisMap = (locNumPlotSets > 1) ? Build_AxisMap(locMultiGraph) : map(); Tweak_PadPlots(locDrawnArray, locPadInstructions, locEdgePlotBits, locCombinedRangePair, locMultiGraph); //draw an axis on the right side if supported if(locScaleFlag && locScaleAxisFlag && (locNumPlotSets == 2)) Draw_ScaleAxis_Graph(locPlotSetArray, locOverallPadIndex, locRightAxisMinMax); delete locDrawnArray; } void DPlotDrawer::Tweak_PadPlots(TObjArray* locObjectArray, const DPadInstructions* locPadInstructions, bitset<5> locEdgePlotBits, pair locCombinedRangePair, TObject* locCollectionObject) { int locNumDimensions = Get_NumDimensions(locObjectArray->At(0)); TObjArray locCollectionArray; locCollectionArray.AddLast(locCollectionObject); TObjArray* locLoopArray = (locCollectionObject == NULL) ? locObjectArray : &locCollectionArray; for(size_t loc_i = 0; loc_i < locLoopArray->GetEntriesFast(); ++loc_i) { //build axis map TObject* locObject = locLoopArray->At(loc_i); bool locIsHistFlag = locObject->IsA()->InheritsFrom(TH1::Class()); map locAxisMap = Build_AxisMap(locObject); //Apply axis style Apply_AxisStyle(locAxisMap); //if adjacent plots, disable axes and center the ones that are present if(locEdgePlotBits[d_AdjacentFlag]) DisableOrAlign_Axes(locEdgePlotBits, locAxisMap); //Fix label/title size (adjacent plots) if(locEdgePlotBits[d_AdjacentFlag]) Fix_AxisTextSizes(locEdgePlotBits, locAxisMap); //Apply user-defined axis ranges Apply_AxisRanges(locAxisMap, locPadInstructions, locIsHistFlag); //Apply value range (if defined) Set_ValueRange(locLoopArray, (locNumDimensions == 1), locPadInstructions, locCombinedRangePair); //Adjust range for adjacent plots pair locValueMinMax = (locCombinedRangePair.second > locCombinedRangePair.first) ? locCombinedRangePair : Find_ValueMinMax(locObject, locPadInstructions->Get_AxisRange('X')); Adjust_AxisRanges((locNumDimensions == 2), locAxisMap, locEdgePlotBits, locValueMinMax); } //Fix pad title position Fix_PadTitlePosition(locEdgePlotBits); //Modify Palette //label size & adjacent plots Modify_Palette(locObjectArray->At(0), locPadInstructions, locEdgePlotBits); //Log Set_Logs(locPadInstructions); //Legend if(locObjectArray->GetEntriesFast() > 1) Draw_Legend(locObjectArray, locPadInstructions); } /***************************************************** DRAW UTILITIES: HISTOGRAMS ******************************************************/ pair DPlotDrawer::Scale_Histogram(TH1*& locHist, bool locScaleByMaxFlag, double locScaleReference) { //if locScaleByMaxFlag is false, scale by integral instead //locScaleReference: max-height if locScaleByMaxFlag = true, integral if the flag is false //Clone first so don't disrupt original! string locHistName = locHist->GetName() + string("_Clone"); locHist = (TH1*)locHist->Clone(locHistName.c_str()); //Get min/max for right-side axis pair locMinMaxPair; locMinMaxPair.first = locHist->GetBinContent(locHist->GetMinimumBin()); locMinMaxPair.second = locHist->GetBinContent(locHist->GetMaximumBin()); //Get scale factor double locScaleFactor = locScaleReference; if(locScaleByMaxFlag) locScaleFactor /= locMinMaxPair.second; else //scale by height locScaleFactor /= locHist->Integral(); //Scale locHist->Scale(locScaleFactor); //Finalize right-side axis range & return Finalize_MinMax(locMinMaxPair, true); return locMinMaxPair; } void DPlotDrawer::Draw_ScaleAxis_Hist(const TObjArray* locPlotSetArray, int locOverallPadIndex, pair locRightAxisMinMax) { //get hists TH1* locReferenceHist = (TH1*)((DPlotSet*)locPlotSetArray->At(0))->Get_Plot(locOverallPadIndex); DPlotSet* locScalePlotSet = (DPlotSet*)locPlotSetArray->At(1); TH1* locScaleHist = (TH1*)locScalePlotSet->Get_Plot(locOverallPadIndex); //get title & fonts string locAxisTitle = locScaleHist->GetYaxis()->GetTitle(); int locLabelFont = locReferenceHist->GetYaxis()->GetLabelFont(); int locTitleFont = locReferenceHist->GetYaxis()->GetTitleFont(); //get axis color int locAxisColor = locScalePlotSet->dHistFillColor; if(locScaleHist->GetSumw2N()) //if true: has error bars, will draw markers: use marker color locAxisColor = locScalePlotSet->dMarkerColor; else if(!locScalePlotSet->dHistFillColorFlag) //not filling: use line color locAxisColor = locScalePlotSet->dLineColor; //Draw axis Draw_ScaleAxis(locRightAxisMinMax, locAxisTitle, locAxisColor, locLabelFont, locTitleFont); } void DPlotDrawer::Set_HistStyle(TH1* locHist, DPlotSet* locPlotSet) { //Fill, line, marker color/style if(locHist->GetSumw2N()) //error bars: use marker color { locHist->SetFillColor(0); locHist->SetLineColor(locPlotSet->dMarkerColor); } else if(locPlotSet->dHistFillColorFlag) //filling and no error bars: use fill color { locHist->SetFillColor(locPlotSet->dHistFillColor); locHist->SetLineColor(locPlotSet->dLineColor); } else //not filling and no error bars: will use line color { locHist->SetFillColor(0); locHist->SetLineColor(locPlotSet->dLineColor); } //line settings locHist->SetLineWidth(locPlotSet->dLineWidth); locHist->SetLineStyle(locPlotSet->dLineStyle); //for when there are error bars locHist->SetMarkerColor(locPlotSet->dMarkerColor); locHist->SetMarkerStyle(locPlotSet->dMarkerStyle); if(locPlotSet->dMarkerSize > 0.0) locHist->SetMarkerSize(locPlotSet->dMarkerSize); else locHist->SetMarkerSize(gStyle->GetMarkerSize()); } /******************************************************* DRAW UTILITIES: GRAPHS ********************************************************/ double DPlotDrawer::Calc_GraphMeanY(TGraph* locGraph) { double locSum = 0.0; double* locY = locGraph->GetY(); for(int loc_q = 0; loc_q < locGraph->GetN(); ++loc_q) locSum += locY[loc_q]; return locSum/locGraph->GetN(); } pair DPlotDrawer::Find_GraphMinMax(TGraph* locGraph, double locRangeMin, double locRangeMax) { //first in pair is min, 2nd is max double* locX = locGraph->GetX(); double* locY = locGraph->GetY(); pair locMinMaxPair(9.9E99, -9.9E99); TGraphErrors* locGraphErrors = dynamic_cast(locGraph); double* locEY = (locGraphErrors != NULL) ? locGraphErrors->GetEY() : NULL; for(int loc_i = 0; loc_i < locGraph->GetN(); ++loc_i) { if((locX[loc_i] < locRangeMin) && (locRangeMax > locRangeMin)) continue; if((locX[loc_i] > locRangeMax) && (locRangeMax > locRangeMin)) continue; double locMinValue = (locEY == NULL) ? locY[loc_i] : locY[loc_i] - locEY[loc_i]; if(dResultAxisLogScale && (locMinValue <= 0.0)) locMinValue = 0.5*locY[loc_i]; double locMaxValue = (locEY == NULL) ? locY[loc_i] : locY[loc_i] + locEY[loc_i]; if(locMinValue < locMinMaxPair.first) locMinMaxPair.first = locMinValue; if(locMaxValue > locMinMaxPair.second) locMinMaxPair.second = locMaxValue; } //cout << "graph min/max = " << locMinMaxPair.first << ", " << locMinMaxPair.second << endl; return locMinMaxPair; } void DPlotDrawer::Draw_ScaleAxis_Graph(const TObjArray* locPlotSetArray, int locOverallPadIndex, pair locRightAxisMinMax) { //get graphs TGraph* locReferenceGraph = (TGraph*)((DPlotSet*)locPlotSetArray->At(0))->Get_Plot(locOverallPadIndex); DPlotSet* locScalePlotSet = (DPlotSet*)locPlotSetArray->At(1); TGraph* locScaleGraph = (TGraph*)locScalePlotSet->Get_Plot(locOverallPadIndex); //get title, fonts, color string locAxisTitle = locScaleGraph->GetYaxis()->GetTitle(); int locAxisColor = locScalePlotSet->dMarkerColor; int locLabelFont = locReferenceGraph->GetYaxis()->GetLabelFont(); int locTitleFont = locReferenceGraph->GetYaxis()->GetTitleFont(); //Draw axis Draw_ScaleAxis(locRightAxisMinMax, locAxisTitle, locAxisColor, locLabelFont, locTitleFont); } pair DPlotDrawer::Scale_Graph(TGraph*& locGraph, bool locScaleByMaxFlag, double locScaleReference) { //if locScaleByMaxFlag is false, scale by mean instead //Clone first so don't disrupt original! string locGraphName = locGraph->GetName() + string("_Clone"); locGraph = (TGraph*)locGraph->Clone(locGraphName.c_str()); //Get min/max for right-side axis pair locGraphMinMax = Find_GraphMinMax(locGraph); //Get scale factor double locScaleFactor = locScaleReference; if(locScaleByMaxFlag) locScaleFactor /= locGraphMinMax.second; else //scale by mean locScaleFactor /= Calc_GraphMeanY(locGraph); //only changes y & ey: assumes x & n are the same! double* locY = locGraph->GetY(); double* locEY = locGraph->GetEY(); for(int loc_i = 0; loc_i < locGraph->GetN(); ++loc_i) { locY[loc_i] *= locScaleFactor; locEY[loc_i] *= locScaleFactor; } //Finalize right-side axis range & return Finalize_MinMax(locGraphMinMax, false); return locGraphMinMax; } void DPlotDrawer::Set_GraphStyle(TGraph* locGraph, DPlotSet* locPlotSet) { //Line style, width locGraph->SetLineWidth(locPlotSet->dLineWidth); locGraph->SetLineStyle(locPlotSet->dLineStyle); locGraph->SetLineColor(locPlotSet->dLineColor); //Marker color, style, size locGraph->SetMarkerColor(locPlotSet->dMarkerColor); locGraph->SetMarkerStyle(locPlotSet->dMarkerStyle); if(locPlotSet->dMarkerSize > 0.0) locGraph->SetMarkerSize(locPlotSet->dMarkerSize); else locGraph->SetMarkerSize(gStyle->GetMarkerSize()); } /************************************************** DRAW UTILITIES: ALL OBJECTS: AXES **************************************************/ map DPlotDrawer::Build_AxisMap(TObject* locObject) { map locAxisMap; if(locObject->IsA()->InheritsFrom(TH1::Class())) //histogram { TH1* locHist = static_cast(locObject); locAxisMap['X'] = locHist->GetXaxis(); locAxisMap['Y'] = locHist->GetYaxis(); locAxisMap['Z'] = locHist->GetZaxis(); } else if(locObject->IsA()->InheritsFrom(TGraph::Class())) { TGraph* locGraph = static_cast(locObject); locAxisMap['X'] = locGraph->GetXaxis(); locAxisMap['Y'] = locGraph->GetYaxis(); } else if(locObject->IsA()->InheritsFrom(TMultiGraph::Class())) { TMultiGraph* locMultiGraph = static_cast(locObject); locAxisMap['X'] = locMultiGraph->GetXaxis(); locAxisMap['Y'] = locMultiGraph->GetYaxis(); } else if(locObject->IsA()->InheritsFrom(TGraph2D::Class())) { TGraph2D* loc2DGraph = static_cast(locObject); locAxisMap['X'] = loc2DGraph->GetXaxis(); locAxisMap['Y'] = loc2DGraph->GetYaxis(); locAxisMap['Z'] = loc2DGraph->GetZaxis(); } return locAxisMap; } void DPlotDrawer::Apply_AxisRanges(map& locAxisMap, const DPadInstructions* locPadInstructions, bool locIsHistFlag) { //excluding hist min/max!!! for(auto& locAxisPair : locAxisMap) { char locAxisChar = locAxisPair.first; TAxis* locAxis = locAxisPair.second; if(locPadInstructions->Is_AxisRangeSet(locAxisChar)) { auto locAxisRangePair = locPadInstructions->Get_AxisRange(locAxisChar); if(locIsHistFlag) locAxis->SetRangeUser(locAxisRangePair.first, locAxisRangePair.second); else locAxis->SetLimits(locAxisRangePair.first, locAxisRangePair.second); } } } void DPlotDrawer::Set_ValueRange(const TObjArray* locObjectArray, bool locIs1DFlag, const DPadInstructions* locPadInstructions, pair locValueRangePair) { //after drawing the pad content: //first, apply user-input ranges //then, if adjacent or many hists on pad, FIND combined range //then, if adjacent, ADJUST ranges to avoid pad-edge overlaps //1D: Y-axis if(locIs1DFlag && !locPadInstructions->Is_AxisRangeSet('Y')) //1d, user y-range not specified { if(!locPadInstructions->dScaleFlag && (locValueRangePair.first >= locValueRangePair.second)) //not scaling, and range not set externally locValueRangePair = Find_CombinedRange_Pad(locObjectArray, locPadInstructions); //find the range if(locValueRangePair.second > locValueRangePair.first) //set the range for all objects { for(size_t loc_j = 0; loc_j < locObjectArray->GetEntriesFast(); ++loc_j) { TObject* locObject = locObjectArray->At(loc_j); map locAxisMap = Build_AxisMap(locObject); //cout << "set y-range: " << locValueRangePair.first << ", " << locValueRangePair.second << endl; locAxisMap['Y']->SetRangeUser(locValueRangePair.first, locValueRangePair.second); locAxisMap['Y']->SetLimits(locValueRangePair.first, locValueRangePair.second); //if is graph, MUST set min/max if(locObject->IsA()->InheritsFrom(TGraph::Class())) { TGraph* locGraph = (TGraph*)locObject; locGraph->SetMinimum(locValueRangePair.first); locGraph->SetMaximum(locValueRangePair.second); } else if(locObject->IsA()->InheritsFrom(TMultiGraph::Class())) { TMultiGraph* locMultiGraph = (TMultiGraph*)locObject; locMultiGraph->SetMinimum(locValueRangePair.first); locMultiGraph->SetMaximum(locValueRangePair.second); } } } } //2D/3D: Min/max if(locPadInstructions->Is_AxisRangeSet('M')) { auto locAxisRangePair = locPadInstructions->Get_AxisRange('M'); locValueRangePair.first = locAxisRangePair.first; locValueRangePair.second = locAxisRangePair.second; } else if(!locIs1DFlag) //not-1D, user min/max not specified { if(!locPadInstructions->dScaleFlag && (locValueRangePair.first >= locValueRangePair.second)) //not scaling, and range not set externally locValueRangePair = Find_CombinedRange_Pad(locObjectArray, locPadInstructions); //find the range } else return; if(locValueRangePair.first >= locValueRangePair.second) //set the range for all objects return; //set the range for all objects for(size_t loc_j = 0; loc_j < locObjectArray->GetEntriesFast(); ++loc_j) //loop over objects in pad { TObject* locObject = locObjectArray->At(loc_j); if(locObject->IsA()->InheritsFrom(TH1::Class())) //histogram { TH1* locHist = (TH1*)locObject; locHist->SetMinimum(locValueRangePair.first); locHist->SetMaximum(locValueRangePair.second); } else if(locObject->IsA()->InheritsFrom(TGraph::Class())) { TGraph* locGraph = (TGraph*)locObject; locGraph->SetMinimum(locValueRangePair.first); locGraph->SetMaximum(locValueRangePair.second); } else if(locObject->IsA()->InheritsFrom(TMultiGraph::Class())) { TMultiGraph* locMultiGraph = (TMultiGraph*)locObject; locMultiGraph->SetMinimum(locValueRangePair.first); locMultiGraph->SetMaximum(locValueRangePair.second); } else //e.g. TGraph2D, not implemented yet continue; } } void DPlotDrawer::Apply_AxisStyle(map& locAxisMap) { //ROOT is inconsistent, and sometimes doesn't recognize/set the gStyle title/label options for(auto& locAxisPair : locAxisMap) { string locAxisString(1, locAxisPair.first); const char* locAxisChar = locAxisString.c_str(); TAxis* locAxis = locAxisPair.second; locAxis->SetTitleSize(gStyle->GetTitleSize(locAxisChar)); locAxis->SetTitleOffset(gStyle->GetTitleOffset(locAxisChar)); locAxis->SetTitleFont(gStyle->GetTitleFont(locAxisChar)); locAxis->SetTitleColor(gStyle->GetTitleColor(locAxisChar)); locAxis->SetLabelSize(gStyle->GetLabelSize(locAxisChar)); locAxis->SetLabelOffset(gStyle->GetLabelOffset(locAxisChar)); locAxis->SetLabelFont(gStyle->GetLabelFont(locAxisChar)); locAxis->SetLabelColor(gStyle->GetLabelColor(locAxisChar)); locAxis->SetNdivisions(gStyle->GetNdivisions(locAxisChar)); } } /********************************************* DRAW UTILITIES: ALL OBJECTS: AXES: ADJACENT *********************************************/ void DPlotDrawer::DisableOrAlign_Axes(bitset<5> locEdgePlotBits, map& locAxisMap) { //Turn off axes if not an edge plot //Also, for titles that are present, center them on the axes if(!locEdgePlotBits[d_BottomEdge]) { locAxisMap['X']->SetTitleSize(0.0); locAxisMap['X']->SetLabelSize(0.0); } if(!locEdgePlotBits[d_LeftEdge]) { locAxisMap['Y']->SetTitleSize(0.0); locAxisMap['Y']->SetLabelSize(0.0); } if(!locEdgePlotBits[d_RightEdge] && (locAxisMap.find('Z') != locAxisMap.end())) { locAxisMap['Z']->SetTitleSize(0.0); locAxisMap['Z']->SetLabelSize(0.0); } //if it's an adjacent plot, center all axis titles if(locEdgePlotBits[d_AdjacentFlag]) { locAxisMap['X']->CenterTitle(); locAxisMap['Y']->CenterTitle(); if(locAxisMap.find('Z') != locAxisMap.end()) locAxisMap['Z']->CenterTitle(); } } void DPlotDrawer::Adjust_AxisRanges(bool locIs2DFlag, map& locAxisMap, bitset<5> locEdgePlotBits, pair locValueMinMax) { //Adjust range for adjacent plots //At the edge of the plot, the axis labels for one plot are cut off at the edge of the pad //There is no way (that I know of) to disable drawing the labels at the edges, but not the rest //Therefore, slightly adjust the axis range so that it's not at a "round" number: won't print there //For x-axis, assume the user input a "round" range. So, shift inwards by 1 bin each (if not on edge!): //For y-axis, if 2-D, do same as x-axis //For y-axis, if 1-D, assume that the bottom number is "round," so shift it if not on the bottom. //For y-axis, if 1-D, assume that the top # is random, and thus there's no label (don't change) //For z-axis: Do nothing (view is 3d) //x-axis if(!locEdgePlotBits[d_LeftEdge] || !locEdgePlotBits[d_RightEdge]) { TAxis* locAxis = locAxisMap['X']; int locLeftEdgeBin = locEdgePlotBits[d_LeftEdge] ? locAxis->GetFirst() : locAxis->GetFirst() + 1; int locRightEdgeBin = locEdgePlotBits[d_RightEdge] ? locAxis->GetLast() : locAxis->GetLast() - 1; locAxis->SetRange(locLeftEdgeBin, locRightEdgeBin); } //y-axis if(!locEdgePlotBits[d_TopEdge] || !locEdgePlotBits[d_BottomEdge]) { TAxis* locAxis = locAxisMap['Y']; if(locIs2DFlag) { int locBottomEdgeBin = locEdgePlotBits[d_BottomEdge] ? locAxis->GetFirst() : locAxis->GetFirst() + 1; int locTopEdgeBin = locEdgePlotBits[d_TopEdge] ? locAxis->GetLast() : locAxis->GetLast() - 1; locAxis->SetRange(locBottomEdgeBin, locTopEdgeBin); } else if(!locEdgePlotBits[d_BottomEdge]) //1D, adjust bottom edge only, not log scale { if(!dResultAxisLogScale) locValueMinMax.first += (locValueMinMax.second - locValueMinMax.first)*0.0001; //increase by 0.01% to avoid label at floor else { //minimum { int locExponent = int(TMath::Log10(locValueMinMax.first)); if(locValueMinMax.first < 1) --locExponent; //(int) rounds the wrong direction (for this) for negative numbers double locMultiplier = locValueMinMax.first/pow(10.0, locExponent); if(locMultiplier > 7.5) locValueMinMax.first = 7.5*pow(10.0, locExponent); //lower it } //maximum { int locExponent = int(TMath::Log10(locValueMinMax.second)); if(locValueMinMax.second < 1) --locExponent; //(int) rounds the wrong direction (for this) for negative numbers double locMultiplier = locValueMinMax.second/pow(10.0, locExponent); if(locMultiplier < 1.3) locValueMinMax.second = 1.3*pow(10.0, locExponent); //raise it } } locAxis->SetRangeUser(locValueMinMax.first, locValueMinMax.second); locAxis->SetLimits(locValueMinMax.first, locValueMinMax.second); } } } void DPlotDrawer::Fix_AxisTextSizes(bitset<5> locEdgePlotBits, map& locAxisMap) { if(!locEdgePlotBits[d_AdjacentFlag]) return; //nothing to change: no adjacent plots //Fix label/title size //is fraction of pad size, so is inconsistent if pad size changes (margins for adjacent plots) //adjust assuming input label/title size was size for pad that had all margins //convert text size to pixels, then draw all sizes with the same pixel size //get pad size term double locPadWidth = double(gPad->GetWw())*gPad->GetAbsWNDC(); //canvas width * pad fractional size double locPadHeight = double(gPad->GetWh())*gPad->GetAbsHNDC(); //canvas height * pad fractional size double locPadSizeTerm = (gPad->GetWw() > gPad->GetWh()) ? locPadHeight : locPadWidth; //size multiplier double locMultiplier = 1.0; if(gPad->GetWw() > gPad->GetWh()) //use pad height info { if((!locEdgePlotBits[d_TopEdge]) && (!locEdgePlotBits[d_BottomEdge])) //must convert from middle to full locMultiplier = 1.0/(1.0 - gStyle->GetPadTopMargin() - gStyle->GetPadBottomMargin()); else if(!locEdgePlotBits[d_BottomEdge]) //must convert from top to full locMultiplier = 1.0/(1.0 - gStyle->GetPadBottomMargin()); else if(!locEdgePlotBits[d_TopEdge]) //must convert from bottom to full locMultiplier = 1.0/(1.0 - gStyle->GetPadTopMargin()); } else //use pad width info { if((!locEdgePlotBits[d_LeftEdge]) && (!locEdgePlotBits[d_RightEdge])) //must convert from middle to full locMultiplier = 1.0/(1.0 - gStyle->GetPadLeftMargin() - gStyle->GetPadRightMargin()); else if(!locEdgePlotBits[d_RightEdge]) //must convert from left to full locMultiplier = 1.0/(1.0 - gStyle->GetPadRightMargin()); else if(!locEdgePlotBits[d_LeftEdge]) //must convert from right to full locMultiplier = 1.0/(1.0 - gStyle->GetPadLeftMargin()); } //above multipliers aren't perfectly correct: rounded to nearest pixel when creating pads in the first place //close enough for now though //set x-axis sizes double locPixelSize = locMultiplier*locPadSizeTerm*gStyle->GetTitleSize("X"); //cout << "w>h?, multiplier, pad size term, pixel size = " << (gPad->GetWw() > gPad->GetWh()) << ", " << locMultiplier << ", " << locPadSizeTerm << ", " << locPixelSize << endl; locAxisMap['X']->SetTitleFont(gStyle->GetTitleFont("X")); locAxisMap['X']->SetTitleSize(locPixelSize); locPixelSize = locMultiplier*locPadSizeTerm*gStyle->GetLabelSize("X"); locAxisMap['X']->SetLabelFont(gStyle->GetLabelFont("X")); locAxisMap['X']->SetLabelSize(locPixelSize); //set y-axis sizes locPixelSize = locMultiplier*locPadSizeTerm*gStyle->GetTitleSize("Y"); locAxisMap['Y']->SetTitleFont(gStyle->GetTitleFont("Y")); locAxisMap['Y']->SetTitleSize(locPixelSize); locPixelSize = locMultiplier*locPadSizeTerm*gStyle->GetLabelSize("Y"); locAxisMap['Y']->SetLabelFont(gStyle->GetLabelFont("Y")); locAxisMap['Y']->SetLabelSize(locPixelSize); //set z-axis sizes if(locAxisMap.find('Z') != locAxisMap.end()) { locPixelSize = locMultiplier*locPadSizeTerm*gStyle->GetTitleSize("Z"); locAxisMap['Z']->SetTitleFont(gStyle->GetTitleFont("Z")); locAxisMap['Z']->SetTitleSize(locPixelSize); locPixelSize = locMultiplier*locPadSizeTerm*gStyle->GetLabelSize("Z"); locAxisMap['Z']->SetLabelFont(gStyle->GetLabelFont("Z")); locAxisMap['Z']->SetLabelSize(locPixelSize); } } /************************************************** DRAW UTILITIES: ALL OBJECTS: MISC **************************************************/ void DPlotDrawer::Set_Logs(const DPadInstructions* locPadInstructions) { TVirtualPad::Pad()->GetLogy(); //this is required because root will crash without it if(locPadInstructions->dLogXFlag) TVirtualPad::Pad()->SetLogx(); if(locPadInstructions->dLogYFlag) TVirtualPad::Pad()->SetLogy(); if(locPadInstructions->dLogZFlag) TVirtualPad::Pad()->SetLogz(); TVirtualPad::Pad()->Update(); } void DPlotDrawer::Modify_Palette(TObject* locObject, const DPadInstructions* locPadInstructions, bitset<5> locEdgePlotBits) { //Color Palette doesn't exist until drawn TVirtualPad::Pad()->Update(); //Get palette object TObject* locPaletteObject = NULL; if(locObject->IsA()->InheritsFrom(TH1::Class())) //histogram { TH1* locHist = (TH1*)locObject; locPaletteObject = locHist->GetListOfFunctions()->FindObject("palette"); } else if(locObject->IsA()->InheritsFrom(TGraph::Class())) { TGraph* locGraph = (TGraph*)locObject; locPaletteObject = locGraph->GetListOfFunctions()->FindObject("palette"); } if(locPaletteObject == NULL) return; TPaletteAxis* locPaletteAxis = static_cast(locPaletteObject); Modify_Palette(locPaletteAxis, locPadInstructions, locEdgePlotBits); } void DPlotDrawer::Modify_Palette(TPaletteAxis* locPaletteAxis, const DPadInstructions* locPadInstructions, bitset<5> locEdgePlotBits) { if(!locEdgePlotBits[d_RightEdge]) //turn it off! { locPaletteAxis->SetLabelSize(0.0); locPaletteAxis->SetLineWidth(0.0); locPaletteAxis->SetTitleSize(0.0); } else if(locPadInstructions->dPaletteLabelSize >= 0.0) locPaletteAxis->SetLabelSize(locPadInstructions->dPaletteLabelSize); //should scale this!! TVirtualPad::Pad()->Update(); } void DPlotDrawer::Draw_ScaleAxis(pair locRightAxisMinMax, string locAxisTitle, int locAxisColor, int locLabelFont, int locTitleFont) { TGaxis *locAxis = new TGaxis(TVirtualPad::Pad()->GetUxmax(), TVirtualPad::Pad()->GetUymin(), TVirtualPad::Pad()->GetUxmax(), TVirtualPad::Pad()->GetUymax(), locRightAxisMinMax.first, locRightAxisMinMax.second, 510, "+L"); locAxis->SetTitle(locAxisTitle.c_str()); locAxis->SetLineColor(locAxisColor); locAxis->SetLabelColor(locAxisColor); locAxis->SetTextColor(locAxisColor); locAxis->SetTitleSize(gStyle->GetTitleSize("Y")); locAxis->SetTitleOffset(gStyle->GetTitleYOffset()); locAxis->SetLabelSize(gStyle->GetLabelSize("Y")); locAxis->SetLabelFont(locLabelFont); locAxis->SetTitleFont(locTitleFont); locAxis->Draw("SAME"); } void DPlotDrawer::Fix_PadTitlePosition(bitset<5> locEdgePlotBits) { //Fix pad title location: //Set so that x-centered above the content, not centered in the pad (difference: margins) //Also, when adjacent pads, put pad title within the content itself, 2% from the top (target distance) TVirtualPad::Pad()->Update(); //y-position TPaveText* locPaveText = (TPaveText*)gPad->FindObject("title"); if(locPaveText == NULL) return; if(locEdgePlotBits[d_AdjacentFlag]) { double locPadTopMargin = gStyle->GetPadTopMargin(); double locTargetOffset = 0.02; double locYMultiplier = 1.0; if((!locEdgePlotBits[d_TopEdge]) && (!locEdgePlotBits[d_BottomEdge])) //must convert from middle to full { locYMultiplier = 1.0/(1.0 - gStyle->GetPadTopMargin() - gStyle->GetPadBottomMargin()); locPadTopMargin = 0.0; } else if(!locEdgePlotBits[d_BottomEdge]) //must convert from top to full { locYMultiplier = 1.0/(1.0 - gStyle->GetPadBottomMargin()); locPadTopMargin = gStyle->GetPadTopMargin()/(1.0 - gStyle->GetPadBottomMargin()); } else if(!locEdgePlotBits[d_TopEdge]) //must convert from bottom to full { locYMultiplier = 1.0/(1.0 - gStyle->GetPadTopMargin()); locPadTopMargin = 0.0; } double locTitleYUp = 1.0 - locPadTopMargin - locTargetOffset*locYMultiplier; //fractional distance double locTitleHeight = gStyle->GetTitleSize(); if((TString(locPaveText->GetLine(0)->GetTitle())).Contains("#splitline")) locTitleYUp -= 0.5*locTitleHeight; //cout << "top margin, yup = " << locPadTopMargin << ", " << locTitleYUp << endl; locPaveText->SetY1NDC(locTitleYUp - locTitleHeight); //ylow locPaveText->SetY2NDC(locTitleYUp); //yup } //x-position double locContentFraction = 1.0 - gStyle->GetPadLeftMargin() - gStyle->GetPadRightMargin(); double locXMultiplier = 1.0; double locTextCenter = gStyle->GetPadLeftMargin() + 0.5*locContentFraction; if((!locEdgePlotBits[d_LeftEdge]) && (!locEdgePlotBits[d_RightEdge])) //must convert from middle to full { locTextCenter = 0.5; locContentFraction = 1.0; locXMultiplier = 1.0/(1.0 - gStyle->GetPadLeftMargin() - gStyle->GetPadRightMargin()); } else if(!locEdgePlotBits[d_RightEdge]) //must convert from Left to full { double locPadLeftMargin = gStyle->GetPadLeftMargin()/(1.0 - gStyle->GetPadRightMargin()); locContentFraction = 1.0 - locPadLeftMargin; locTextCenter = 0.5*locContentFraction + locPadLeftMargin; locXMultiplier = 1.0/(1.0 - gStyle->GetPadRightMargin()); } else if(!locEdgePlotBits[d_LeftEdge]) //must convert from Right to full { double locPadRightMargin = gStyle->GetPadRightMargin()/(1.0 - gStyle->GetPadLeftMargin()); locContentFraction = 1.0 - locPadRightMargin; locTextCenter = 0.5*locContentFraction; locXMultiplier = 1.0/(1.0 - gStyle->GetPadLeftMargin()); } double locPadWidth = gPad->GetWw()*gPad->GetAbsWNDC(); if(locEdgePlotBits[d_AdjacentFlag]) { double locPixelSize = locXMultiplier*locPadWidth*gStyle->GetTitleSize("T"); //cout << "pad width, title size, pixel size = " << locPadWidth << ", " << gStyle->GetTitleSize("T") << ", " << locPixelSize << endl; locPaveText->SetTextFont(gStyle->GetTitleFont("T")); locPaveText->SetTextSize(locPixelSize); } double locTextWidth = 0.9*locContentFraction; //text won't necessarily take up this much space double locTitleXLow = locTextCenter - 0.5*locTextWidth; double locTitleXUp = locTextCenter + 0.5*locTextWidth; //cout << "text center/width, xlow, xup = " << locTextCenter << ", " << locTextWidth << ", " << locTitleXLow << ", " << locTitleXUp << endl; locPaveText->SetX1NDC(locTitleXLow); //xlow locPaveText->SetX2NDC(locTitleXUp); //xup gPad->Modified(); TVirtualPad::Pad()->Update(); } void DPlotDrawer::Draw_Legend(const TObjArray* locObjectArray, const DPadInstructions* locPadInstructions) { //setup legend if(locPadInstructions->dLegendNames.empty()) return; int locNumObjects = locObjectArray->GetEntriesFast(); size_t locMaxStringLength = 0; for(int loc_i = 0; loc_i < locNumObjects; ++loc_i) { size_t locStringLength = 0; locStringLength = locPadInstructions->dLegendNames[loc_i].size(); if(locStringLength > locMaxStringLength) locMaxStringLength = locStringLength; } double locPadWidth = gPad->GetWw()*gPad->GetAbsWNDC(); //Calc legibility scale factors: 1.0, 1.333, 1.667, 2.0 For sizes: 1200, 900, 600, 300 double locLegibilityScaleFactor = 2.0 + 1.0/3.0 - locPadWidth/900.0; if(locLegibilityScaleFactor < 1.0) locLegibilityScaleFactor = 1.0; else if(locLegibilityScaleFactor > 2.0) locLegibilityScaleFactor = 2.0; double locTopY = 1.0 - gStyle->GetPadTopMargin(); double locRightX = 1.0 - gStyle->GetPadRightMargin(); double locBottomY = locTopY - 0.06*locLegibilityScaleFactor*double(locPadInstructions->dLegendNames.size()); double locLeftX = locRightX - (0.15/12.0)*locLegibilityScaleFactor*double(locMaxStringLength); //12 chars -> 0.15 if(locBottomY < locPadInstructions->dLegendMinBottomY) locBottomY = locPadInstructions->dLegendMinBottomY; if(locLeftX < locPadInstructions->dLegendMinLeftX) locLeftX = locPadInstructions->dLegendMinLeftX; TLegend *locLegend = new TLegend(locLeftX, locBottomY, locRightX, locTopY); //botleft x/y, topright x/y locLegend->SetHeader("Legend"); for(int loc_i = 0; loc_i < locNumObjects; ++loc_i) { string locDrawOption = ""; bool locIsHistFlag = locObjectArray->At(loc_i)->IsA()->InheritsFrom(TH1::Class()); if(locIsHistFlag) { TH1* locHist = static_cast(locObjectArray->At(loc_i)); locDrawOption = (locHist->GetSumw2()->GetSize() > 0) ? "EL" : "F"; } else { bool locIsGraphErrorsFlag = locObjectArray->At(loc_i)->IsA()->InheritsFrom(TGraphErrors::Class()); locDrawOption = locIsGraphErrorsFlag ? "EP" : "P"; } locLegend->AddEntry(locObjectArray->At(loc_i), locPadInstructions->dLegendNames[loc_i].c_str(), locDrawOption.c_str()); } locLegend->Draw(); } /*********************************************************** MISC: UTILITIES ***********************************************************/ pair DPlotDrawer::Find_CombinedRange_PlotSets(const TObjArray* locPlotSetArray, const DCanvasInstructions* locCanvasInstructions, int locOverallPadIndex, int locNumPads) { pair locCombinedRangeMinMaxPair(9.9e99, -9.9e99); for(size_t loc_i = 0; loc_i < locNumPads; ++loc_i) //loop over pads in range { size_t locPadIndex = loc_i + locOverallPadIndex; const DPadInstructions* locPadInstructions = locCanvasInstructions->Get_PadInstructions(locPadIndex); //loop over plotsets, build plot array TObjArray locPadArray; for(size_t loc_j = 0; loc_j < locPlotSetArray->GetEntriesFast(); ++loc_j) { DPlotSet* locPlotSet = (DPlotSet*)locPlotSetArray->At(loc_j); TObject* locPlot = locPlotSet->Get_Plot(locPadIndex); if(locPlot != NULL) locPadArray.AddLast(locPlot); } //find range within pad pair locPadMinMaxPair = Find_CombinedRange_Pad(&locPadArray, locPadInstructions); if(locPadMinMaxPair.first < locCombinedRangeMinMaxPair.first) locCombinedRangeMinMaxPair.first = locPadMinMaxPair.first; if(locPadMinMaxPair.second > locCombinedRangeMinMaxPair.second) locCombinedRangeMinMaxPair.second = locPadMinMaxPair.second; } return locCombinedRangeMinMaxPair; } pair DPlotDrawer::Find_CombinedRange_Pad(const TObjArray* locPadArray, const DPadInstructions* locPadInstructions) { pair locPadMinMaxPair(9.9e99, -9.9e99); //determine x-range over which to evaluate y values: pair locAxisRangePair(9.9e99, -9.9e99); if(dCommonGraphRange_OnlyErrorsFlag) { //find range over which there's data that have errors for(size_t loc_j = 0; loc_j < locPadArray->GetEntriesFast(); ++loc_j) //loop over objects in pad { TObject* locObject = locPadArray->At(loc_j); if(locObject->IsA()->InheritsFrom(TH1::Class())) //histogram: full x-range { TH1* locHist = (TH1*)locObject; if(locHist->GetXaxis()->GetXmin() < locAxisRangePair.first) locAxisRangePair.first = locHist->GetXaxis()->GetXmin(); if(locHist->GetXaxis()->GetXmax() > locAxisRangePair.second) locAxisRangePair.second = locHist->GetXaxis()->GetXmax(); } else if(locObject->IsA()->InheritsFrom(TGraphErrors::Class())) { TGraphErrors* locGraphErrors = (TGraphErrors*)locObject; if(!((locGraphErrors->GetEY())[0] > 0.0)) continue; if((locGraphErrors->GetX())[0] < locAxisRangePair.first) locAxisRangePair.first = (locGraphErrors->GetX())[0]; if((locGraphErrors->GetX())[locGraphErrors->GetN() - 1] > locAxisRangePair.second) locAxisRangePair.second = (locGraphErrors->GetX())[locGraphErrors->GetN() - 1]; } } locAxisRangePair.first *= (locAxisRangePair.first > 0.0) ? 0.99 : 1.01; locAxisRangePair.second *= (locAxisRangePair.second > 0.0) ? 1.01 : 0.99; //use the narrower of the data & the user request if(locPadInstructions->Is_AxisRangeSet('X')) { auto locUserAxisRangePair = locPadInstructions->Get_AxisRange('X'); if(locUserAxisRangePair.first > locAxisRangePair.first) locAxisRangePair.first = locUserAxisRangePair.first; if(locUserAxisRangePair.second < locAxisRangePair.second) locAxisRangePair.second = locUserAxisRangePair.second; } } else locAxisRangePair = locPadInstructions->Get_AxisRange('X'); cout << "axis range = " << locAxisRangePair.first << ", " << locAxisRangePair.second << endl; for(size_t loc_j = 0; loc_j < locPadArray->GetEntriesFast(); ++loc_j) //loop over objects in pad { TObject* locObject = locPadArray->At(loc_j); bool locIsHistFlag = locObject->IsA()->InheritsFrom(TH1::Class()); int locNumDimensions = Get_NumDimensions(locObject); if((locNumDimensions == 1) && locPadInstructions->dLogYFlag) dResultAxisLogScale = true; else if((locNumDimensions == 2) && locPadInstructions->dLogZFlag) dResultAxisLogScale = true; else dResultAxisLogScale = false; //apply axis ranges map locAxisMap = Build_AxisMap(locObject); Apply_AxisRanges(locAxisMap, locPadInstructions, locIsHistFlag); pair locMinMaxPair = Find_ValueMinMax(locObject, locAxisRangePair); cout << "graph min max = " << locMinMaxPair.first << ", " << locMinMaxPair.second << endl; if(locMinMaxPair.first < locPadMinMaxPair.first) locPadMinMaxPair.first = locMinMaxPair.first; if(locMinMaxPair.second > locPadMinMaxPair.second) locPadMinMaxPair.second = locMinMaxPair.second; } cout << "pad min max = " << locPadMinMaxPair.first << ", " << locPadMinMaxPair.second << endl; return locPadMinMaxPair; } pair DPlotDrawer::Find_ValueMinMax(const TObject* locObject, pair locAxisRangePair) { pair locMinMaxPair(9.9e99, -9.9e99); if(locObject->IsA()->InheritsFrom(TH1::Class())) //histogram { TH1* locHist = (TH1*)locObject; locMinMaxPair.first = locHist->GetBinContent(locHist->GetMinimumBin()); locMinMaxPair.second = locHist->GetBinContent(locHist->GetMaximumBin()); Finalize_MinMax(locMinMaxPair, true); } else if(locObject->IsA()->InheritsFrom(TGraph::Class())) { TGraph* locGraph = (TGraph*)locObject; locMinMaxPair = Find_GraphMinMax(locGraph, locAxisRangePair.first, locAxisRangePair.second); Finalize_MinMax(locMinMaxPair, false); } else if(locObject->IsA()->InheritsFrom(TMultiGraph::Class())) { TMultiGraph* locMultiGraph = (TMultiGraph*)locObject; TList* locGraphList = locMultiGraph->GetListOfGraphs(); for(int loc_i = 0; loc_i < locGraphList->GetSize(); ++loc_i) { TGraph* locGraph = (TGraph*)locGraphList->At(loc_i); pair locGraphMinMax = Find_ValueMinMax(locGraph, locAxisRangePair); if(locGraphMinMax.first < locMinMaxPair.first) locMinMaxPair.first = locGraphMinMax.first; if(locGraphMinMax.second > locMinMaxPair.second) locMinMaxPair.second = locGraphMinMax.second; } } return locMinMaxPair; } void DPlotDrawer::Finalize_MinMax(pair& locMinMaxPair, bool locIsHistFlag) { if(!std::isfinite(locMinMaxPair.first) || !std::isfinite(locMinMaxPair.second)) { locMinMaxPair.first = 1.0; locMinMaxPair.second = 0.0; return; } //Code converted from end of THistPainter::PaintInit() if(dResultAxisLogScale) { if(locMinMaxPair.first <= 0.0) return; //cout << "input min/max: " << locMinMaxPair.first << ", " << locMinMaxPair.second << endl; //10^locMinMaxPair.first = 10^[TMath::Log10(locMinMaxPair.first) + TMath::Log10(0.5)]; //locMinMaxPair.first *= 0.5; //is equivalent of function { int locExponent = int(TMath::Log10(locMinMaxPair.first)); if(locMinMaxPair.first < 1) --locExponent; //(int) rounds the wrong direction (for this) for negative numbers double locMultiplier = locMinMaxPair.first/pow(10.0, locExponent); if(locMultiplier > 1.0) locMinMaxPair.first = (floor(locMultiplier) - 0.15)*pow(10.0, locExponent); else locMinMaxPair.first *= 0.5; /* if(locMultiplier > 2.0) locMinMaxPair.first -= pow(10.0, locExponent); //shift by a tick mark else locMinMaxPair.first *= 0.5; */ } //10^locMinMaxPair.second = 10^[TMath::Log10(locMinMaxPair.second) + TMath::Log10(2.0*(0.9/0.95))]; { int locExponent = int(TMath::Log10(locMinMaxPair.second)); if(locMinMaxPair.second < 1) --locExponent; //(int) rounds the wrong direction (for this) for negative numbers double locMultiplier = locMinMaxPair.second/pow(10.0, locExponent); locMinMaxPair.second = (floor(locMultiplier) + 1.15)*pow(10.0, locExponent); /* if(locMultiplier < 3.0) // locMinMaxPair.second += pow(10.0, locExponent); //shift by a tick mark locMinMaxPair.second = (floor(locMultiplier) + 0.1)*pow(10.0, locExponent); else locMinMaxPair.second *= 2.0*(0.9/0.95); */ } //cout << "output min/max: " << locMinMaxPair.first << ", " << locMinMaxPair.second << endl; return; } //Set minimum Double_t locDYMin = gStyle->GetHistTopMargin()*(locMinMaxPair.second - locMinMaxPair.first); if(gStyle->GetHistMinimumZero() && locIsHistFlag) { if(locMinMaxPair.first >= 0) locMinMaxPair.first = 0; else locMinMaxPair.first -= locDYMin; } else { if((locMinMaxPair.first >= 0.0) && ((locMinMaxPair.first - locDYMin) <= 0.0)) locMinMaxPair.first = 0.0; else locMinMaxPair.first -= locDYMin; } //Set maximum locMinMaxPair.second += gStyle->GetHistTopMargin()*(locMinMaxPair.second - locMinMaxPair.first); } bool DPlotDrawer::Check_IsPadRightMarginLarge(const DCanvasInstructions* locCanvasInstructions, TObjArray* locPlotSetArray, int locFirstPadIndex, int locLastPadIndex) { //loop through the canvas pads for(Int_t locPadIndex = locFirstPadIndex; locPadIndex <= locLastPadIndex; ++locPadIndex) { //loop through the plotsets, and get the objects for this pad TObjArray locPadArray; for(int loc_i = 0; loc_i < locPlotSetArray->GetEntriesFast(); ++loc_i) { DPlotSet* locPlotSet = (DPlotSet*)locPlotSetArray->At(loc_i); TObject* locObject = locPlotSet->Get_Plot(locPadIndex); if(locObject == NULL) continue; if(Get_NumDimensions(locObject) == 2) return true; locPadArray.AddLast(locObject); } //See if will scale (need room for right-axis) int locNumPadObjects = locPadArray.GetEntriesFast(); DPadInstructions* locPadInstructions = locCanvasInstructions->Get_PadInstructions(locPadIndex); if(locPadInstructions->dScaleFlag && locPadInstructions->dScaleAxisFlag && (locNumPadObjects > 1)) return true; } return false; } int DPlotDrawer::Get_NumDimensions(TObject* locObject) { if(locObject->IsA()->InheritsFrom(TH1::Class())) //histogram { if(locObject->IsA()->InheritsFrom(TH3::Class())) return 3; if(locObject->IsA()->InheritsFrom(TH2::Class())) return 2; return 1; } else if(locObject->IsA()->InheritsFrom(TGraph::Class())) return 1; //2d not supported yet! return 0; }