#include #include #include #include #include #include #include #include "TROOT.h" #include "TFile.h" #include "TClass.h" #include "TApplication.h" #include "TCanvas.h" #include "TRootCanvas.h" #include "TKey.h" #include "TStyle.h" #include "TAttMarker.h" #include "TAttLine.h" #include "TH1.h" #include "TH2.h" #include "TH3.h" #include "TF1.h" #include "TF2.h" #include "THStack.h" #include "TGraph.h" #include "TGraphErrors.h" #include "TGraphAsymmErrors.h" #include "TMultiGraph.h" #include "TProfile.h" #include "TEllipse.h" #include "TMarker.h" #include "TLine.h" #include "TLegend.h" #include "TString.h" #include "TRegexp.h" #include "TText.h" #include "JTools/JRange.hh" #include "JROOT/JStyle.hh" #include "JROOT/JCanvas.hh" #include "JROOT/JMarkerAttributes.hh" #include "JROOT/JLineAttributes.hh" #include "JROOT/JLegend.hh" #include "JGizmo/JRootObjectID.hh" #include "JGizmo/JRootObject.hh" #include "JGizmo/JGizmoToolkit.hh" #include "Jeep/JProperties.hh" #include "Jeep/JPrint.hh" #include "Jeep/JParser.hh" #include "Jeep/JMessage.hh" namespace { using JLANG::JEquals; /** * Set x of object to logarithmic. * * \param object pointer to object * \return true if set; else false */ template inline bool setLogX(TObject* object) { using namespace JPP; T* p = dynamic_cast(object); if (p != NULL) { setLogarithmicX(p); return true; } return false; } /** * Auxiliary data structure for legend options. */ struct JLegend : public JEquals { /** * Default constructor. */ JLegend() : location(), factor (0.0) {} /** * Constructor. * * \param location location * \param factor factor */ JLegend(const std::string& location, const Double_t factor = 1.0) : location(location), factor (factor) {} /** * Check validity. * * \return true if valid; else false */ bool is_valid() const { return (location != "" && factor != 0.0); } /** * Chek equality. * * \param legend legend * \return input stream */ bool equals(const JLegend& legend) const { return (this->location == legend.location); } /** * Read legend from input stream. * * \param in input stream * \param legend legend * \return input stream */ friend inline std::istream& operator>>(std::istream& in, JLegend& legend) { in >> legend.location; if (!(in >> legend.factor)) { legend.factor = 1.0; in.clear(); } return in; } /** * Write legend to output stream. * * \param out output stream * \param legend legend * \return output stream */ friend inline std::ostream& operator<<(std::ostream& out, const JLegend& legend) { return out << legend.location << ' ' << legend.factor; } std::string location; Double_t factor; }; /** * Get width of string. * * \param buffer string * \return width */ inline Ssiz_t getWidth(const TString& buffer) { double w = 0.0; for (Ssiz_t i = 0; i != buffer.Length(); ++i) { if (buffer(i) == ' ') w += 0.5; else if (buffer(i) >= 'A' && buffer(i) <= 'Z') w += 1.0; else if (buffer(i) >= 'a' && buffer(i) <= 'z') w += 0.7; else if (buffer(i) >= '0' && buffer(i) <= '9') w += 0.7; else if (buffer(i) == '.' || buffer(i) == ':' || buffer(i) == ';' || buffer(i) == ',' || buffer(i) == '\'') w += 0.5; else w += 0.0; } return (Ssiz_t) w; } const std::string MASTER = "__H__"; //!< Name of prototype const char* const JName_t = "?"; //!< Draw histogram name as title of plot const char* const JTitle_t = "%"; //!< Draw histogram title as title of plot } /** * \file * General purpose plot program for 1D ROOT objects. * The option -f corresponds to \:\. * \author mdejong */ int main(int argc, char **argv) { using namespace std; using namespace JPP; typedef JRange JRange_t; vector inputFile; string outputFile; JCanvas canvas; JStyle::JParameters parameters; int stats; JLegend legend; JRange_t X; JRange_t Y; JRange_t Z; JCounter logx; bool logy; bool logz; char project; string xLabel; string yLabel; JCounter drawLine; int fillArea; int lineWidth; double markerSize; string option; set grid; bool batch; string title; map Ndivisions; size_t group; int debug; string xTimeFormat; try { JProperties properties = parameters.getProperties(); JParser<> zap("General purpose plot program for 1D ROOT objects."); zap['f'] = make_field(inputFile, ":"); zap['o'] = make_field(outputFile, "graphics output") = ""; zap['w'] = make_field(canvas, "size of canvas x [pixels]") = JCanvas(500, 500); zap['@'] = make_field(properties) = JPARSER::initialised(); zap['s'] = make_field(stats) = -1; zap['L'] = make_field(legend, "position legend e.g. TR [factor]") = JLegend(), JLegend("TL"), JLegend("TR"), JLegend("BR"), JLegend("BL"); zap['x'] = make_field(X, "abscissa range") = JRange_t::DEFAULT_RANGE(); zap['y'] = make_field(Y, "ordinate range") = JRange_t::DEFAULT_RANGE(); zap['z'] = make_field(Z, "ordinate range of projection)") = JRange_t::DEFAULT_RANGE(); zap['X'] = make_field(logx, "logarithmic x-axis (-XX log10 axis)"); zap['Y'] = make_field(logy, "logarithmic y-axis"); zap['Z'] = make_field(logz, "logarithmic y-axis; after projection"); zap['P'] = make_field(project, "projection") = '\0', 'x', 'X', 'y', 'Y'; zap['>'] = make_field(xLabel, "x-axis label") = ""; zap['^'] = make_field(yLabel, "y-axis label") = ""; zap['C'] = make_field(drawLine, "draw line (-C black-and-white -CC colour)"); zap['F'] = make_field(fillArea, "fill area") = 0; zap['l'] = make_field(lineWidth, "line width") = 2; zap['S'] = make_field(markerSize, "marker size") = 1.0; zap['O'] = make_field(option, "plotting option") = ""; zap['G'] = make_field(grid, "grid lines [X][Y]") = JPARSER::initialised(); zap['B'] = make_field(batch, "batch processing"); zap['T'] = make_field(title, "graphics title (" << "\"" << JName_t << "\" -> ROOT name; " << "\"" << JTitle_t << "\" -> ROOT title)") = "KM3NeT preliminary"; zap['N'] = make_field(Ndivisions, "axis divisioning (e.g. \"X 505\")") = JPARSER::initialised(); zap['g'] = make_field(group, "group colour codes of objects") = 1; zap['t'] = make_field(xTimeFormat, "set time format for x-axis, e.g. \%d\\/\%m\\/\\%y%F1970-01-01 00:00:00") = ""; zap['d'] = make_field(debug) = 0; zap(argc, argv); } catch(const exception &error) { FATAL(error.what() << endl); } gROOT->SetBatch(batch); TApplication* tp = new TApplication("user", NULL, NULL); TCanvas* cv = new TCanvas("c1", "c1", canvas.x, canvas.y); if (!batch) { ((TRootCanvas *) cv->GetCanvasImp())->Connect("CloseWindow()", "TApplication", tp, "Terminate()"); } unique_ptr gStyle(new JStyle("gplot", cv->GetWw(), cv->GetWh(), parameters)); if (logy || logz) { gStyle->SetTitleOffset(gStyle->GetTitleOffset() * 1.3, "Y"); } gROOT->SetStyle("gplot"); gROOT->ForceStyle(); cv->SetFillStyle(4000); cv->SetFillColor(kWhite); cv->Divide(1,1); cv->cd(1); JMarkerAttributes::getInstance().setMarkerSize(markerSize); JLineAttributes ::getInstance().setLineWidth (lineWidth); Double_t xmin = numeric_limits::max(); Double_t xmax = numeric_limits::lowest(); Double_t ymin = numeric_limits::max(); Double_t ymax = numeric_limits::lowest(); vector listOfObjects; const bool px = (project == 'x' || project == 'X'); // projection on x-axis of (2|3)D histogram const bool py = (project == 'y' || project == 'Y'); // projection on y-axis of (2|3)D histogram const bool pz = (project == 'z' || project == 'Z'); // projection on z-axis of 3D histogram logy = (logy || logz); if (px) { swap(Y, Z); // Y becomes range in TH2::ProjectionX() and Z becomes y-axis range } if (py) { swap(X, Z); // X becomes range in TH2::ProjectionY() swap(Y, X); // Y becomes x-axis range and Z becomes y-axis range } TH1* master = NULL; for (vector::const_iterator input = inputFile.begin(); input != inputFile.end(); ++input) { DEBUG("Input: " << *input << endl); TDirectory* dir = getDirectory(*input); if (dir == NULL) { ERROR("File: " << input->getFullFilename() << " not opened." << endl); continue; } const TRegexp regexp(input->getObjectName()); TIter iter(dir->GetListOfKeys()); for (TKey* key; (key = (TKey*) iter.Next()) != NULL; ) { const TString tag(key->GetName()); DEBUG("Key: " << tag << " match = " << tag.Contains(regexp) << endl); // option match if (tag.Contains(regexp) && isTObject(key)) { if (title == JName_t) { title = key->GetName(); } else if (title == JTitle_t) { title = key->GetTitle(); } JRootObject object(key->ReadObj()); TAttMarker marker = JMarkerAttributes::getInstance().get(0); TAttLine line = JLineAttributes ::getInstance().get(0); if (group != 0) { marker.SetMarkerColor(JMarkerAttributes::getInstance().get(listOfObjects.size()/group).GetMarkerColor()); } if (drawLine == 1) line = JLineAttributes::getInstance().get(listOfObjects.size()); else line.SetLineColor(marker.GetMarkerColor()); // projections try { TProfile& h1 = dynamic_cast(*object); object = h1.ProjectionX(); } catch(exception&) {} try { TH2& h2 = dynamic_cast(*object); if (px) { if (Z.is_valid()) object = h2.ProjectionX(MAKE_CSTRING(h2.GetName() << "_px" << LABEL_TERMINATOR << listOfObjects.size()), h2.GetYaxis()->FindBin(Z.getLowerLimit()), h2.GetYaxis()->FindBin(Z.getUpperLimit()) - 1); else object = h2.ProjectionX(MAKE_CSTRING(h2.GetName() << "_px" << LABEL_TERMINATOR << listOfObjects.size()), 1, h2.GetYaxis()->GetNbins()); } else if (py) { if (Z.is_valid()) object = h2.ProjectionY(MAKE_CSTRING(h2.GetName() << "_py" << LABEL_TERMINATOR << listOfObjects.size()), h2.GetXaxis()->FindBin(Z.getLowerLimit()), h2.GetXaxis()->FindBin(Z.getUpperLimit()) - 1); else object = h2.ProjectionY(MAKE_CSTRING(h2.GetName() << "_py" << LABEL_TERMINATOR << listOfObjects.size()), 1, h2.GetXaxis()->GetNbins()); } else { ERROR("For 2D histograms, use option option -P for projections or use JPlot2D" << endl); continue; } } catch(exception&) {} try { TH3& h3 = dynamic_cast(*object); if (px) { object = h3.ProjectionX(MAKE_CSTRING(h3.GetName() << "_px" << LABEL_TERMINATOR << listOfObjects.size())); } else if (py) { object = h3.ProjectionY(MAKE_CSTRING(h3.GetName() << "_py" << LABEL_TERMINATOR << listOfObjects.size())); } else if (pz) { object = h3.ProjectionZ(MAKE_CSTRING(h3.GetName() << "_pz" << LABEL_TERMINATOR << listOfObjects.size())); } else { ERROR("For 3D histograms, use option option -P for projections or use JPlot2D -P " << endl); continue; } } catch(exception&) {} // colouring try { if (dynamic_cast(object.get()) == NULL) { dynamic_cast(*object) = marker; } } catch(exception&) {} try { if (dynamic_cast(object.get()) == NULL) { dynamic_cast (*object) = line; } } catch(exception&) {} if (fillArea != 0) { try { TAttFill& fill = dynamic_cast(*object); if (!isTAttLine(object) || fillArea < 0) { fill.SetFillColor(marker.GetMarkerColor()); fill.SetFillStyle(abs(fillArea)); } } catch(exception&) {} } // set errors if (drawLine) { try { TH1& h1 = dynamic_cast(*object); for (int i = 1; i <= h1.GetNbinsX(); ++i) { h1.SetBinError(i, 0.0); } } catch(exception&) {} try { TGraphErrors& g1 = dynamic_cast(*object); for (Int_t i = 0; i != g1.GetN(); ++i) { g1.GetEX()[i] = 0.0; g1.GetEY()[i] = 0.0; } } catch(exception&) {} } // min-max try { TH1& h1 = dynamic_cast(*object); h1.SetStats(stats != -1); xmin = min(xmin, h1.GetXaxis()->GetXmin()); xmax = max(xmax, h1.GetXaxis()->GetXmax()); ymin = min(ymin, logy ? h1.GetMinimum(0.0) : h1.GetMinimum()); ymax = max(ymax, h1.GetMaximum()); if (!logy && h1.GetListOfFunctions() != NULL) { for (unique_ptr iterator(h1.GetListOfFunctions()->MakeIterator()); TF1* f1 = (TF1*) iterator->Next(); ) { ymin = min(ymin, f1->GetMinimum(h1.GetXaxis()->GetXmin(), h1.GetXaxis()->GetXmax())); ymax = max(ymax, f1->GetMaximum(h1.GetXaxis()->GetXmin(), h1.GetXaxis()->GetXmax())); } } } catch(exception&) {} try { TGraph& g1 = dynamic_cast(*object); for (Int_t i = 0; i != g1.GetN(); ++i) { xmin = min(xmin, g1.GetX()[i]); xmax = max(xmax, g1.GetX()[i]); if (!logy || g1.GetY()[i] > 0.0) { ymin = min(ymin, g1.GetY()[i]); ymax = max(ymax, g1.GetY()[i]); } } } catch(exception&) {} try { TGraphErrors& g1 = dynamic_cast(*object); for (Int_t i = 0; i != g1.GetN(); ++i) { if (!logy || g1.GetY()[i] - g1.GetEY()[i] > 0.0) { ymin = min(ymin, g1.GetY()[i] - g1.GetEY()[i]); } if (!logy || g1.GetY()[i] + g1.GetEY()[i] > 0.0) { ymax = max(ymax, g1.GetY()[i] + g1.GetEY()[i]); } } } catch(exception&) {} try { TMultiGraph& m1 = dynamic_cast(*object); for (TIter i1(m1.GetListOfGraphs()); TGraph* g1 = dynamic_cast(i1()); ) { for (Int_t i = 0; i != g1->GetN(); ++i) { xmin = min(xmin, g1->GetX()[i]); xmax = max(xmax, g1->GetX()[i]); if (!logy || g1->GetY()[i] > 0.0) { ymin = min(ymin, g1->GetY()[i]); ymax = max(ymax, g1->GetY()[i]); } } } } catch(exception&) {} try { TF2& f2 = dynamic_cast(*object); TF1* f1 = NULL; TString formula = f2.GetExpFormula(); TString _z_ = TString::Format("%f", 0.5 * (Z.getLowerLimit() + Z.getUpperLimit())); double __xmin; double __xmax; double __ymin; double __ymax; f2.GetRange(__xmin, __ymin, __xmax, __ymax); if (px) { formula.ReplaceAll("y", _z_); f1 = new TF1(MAKE_CSTRING(f2.GetName() << "_px" << LABEL_TERMINATOR << listOfObjects.size()), formula); f1->SetRange(__xmin, __xmax); } else if (py) { formula.ReplaceAll("x", _z_); formula.ReplaceAll("y", "x"); f1 = new TF1(MAKE_CSTRING(f2.GetName() << "_py" << LABEL_TERMINATOR << listOfObjects.size()), formula); f1->SetRange(__ymin, __ymax); } else { ERROR("For 2D functions, use option option -P for projections or use JPlot2D" << endl); continue; } DEBUG("TF1: " << f1->GetExpFormula() << endl); f1->SetParameters(f2.GetParameters()); object = f1; } catch(exception&) {} try { TF1& f1 = dynamic_cast(*object); double __xmin; double __xmax; f1.GetRange(__xmin, __xmax); xmin = min(xmin, __xmin); xmax = max(xmax, __xmax); ymin = min(ymin, f1.GetMinimum()); ymax = max(ymax, f1.GetMaximum()); } catch(exception&) {} try { THStack& hs = dynamic_cast(*object); NOTICE("THStack" << endl); unique_ptr iterator(hs.GetHists()->MakeIterator()); for (size_t index = 1; TObject* i = iterator->Next(); ++index) { TH1& h1 = dynamic_cast(*i); NOTICE("TH1[" << index << "] " << h1.GetName() << endl); xmin = min(xmin, h1.GetXaxis()->GetXmin()); xmax = max(xmax, h1.GetXaxis()->GetXmax()); ymin = min(ymin, logy ? h1.GetMinimum(0.0) : h1.GetMinimum()); ymax = max(ymax, h1.GetMaximum()); h1.SetLineWidth(1); h1.SetLineColor(kBlack); h1.SetFillColor(JMarkerAttributes::getInstance().get(index).GetMarkerColor()); } } catch(exception&) {} try { TLine& h1 = dynamic_cast(*object); xmin = min(xmin, h1.GetX1()); xmax = max(xmax, h1.GetX2()); ymin = min(ymin, h1.GetY1()); ymax = max(ymax, h1.GetY2()); } catch(exception&) {} // label for (TString buffer[] = { object.getLabel(), input->getFilename().c_str(), "" }, *i = buffer; *i != ""; ++i) { *i = (*i)(TRegexp("\\[.*\\]")); if (i->Length() > 2) { object.setLabel((*i)(1, i->Length() - 2)); } } DEBUG("Add object: " << tag << " with label <" << object.getLabel() << ">" << endl); if (master == NULL) { master = dynamic_cast(object.get()); } listOfObjects.push_back(object); } } } if (listOfObjects.empty()) { ERROR("Nothing to draw." << endl); } for (vector::iterator i = listOfObjects.begin(); i != listOfObjects.end(); ++i) { // set line attributes of associated functions // for single objects, change colour if same range else change style // for multiple objecs, keep colour and change style TH1* h1 = dynamic_cast (i->get()); TGraph* g1 = dynamic_cast (i->get()); TMultiGraph* m1 = dynamic_cast(i->get()); TAttLine* ls = dynamic_cast (i->get()); TList list; unique_ptr iterator; if (h1 != NULL) { iterator.reset(h1->GetListOfFunctions()->MakeIterator()); } else if (g1 != NULL) { iterator.reset(g1->GetListOfFunctions()->MakeIterator()); } else if (m1 != NULL) { for (TIter i1(m1->GetListOfGraphs()); TGraph* gi = dynamic_cast(i1()); ) { for (TIter i2(gi->GetListOfFunctions()); TF1* fi = dynamic_cast(i2()); ) { list.Add(fi); } } iterator.reset(list.MakeIterator()); } if (iterator != NULL) { Double_t x1[] = { numeric_limits::max(), numeric_limits::max() }; Double_t x2[] = { numeric_limits::lowest(), numeric_limits::lowest() }; for (int nf = 0, ns = 0, nc = 1; TF1* f1 = (TF1*) iterator->Next(); ++nf) { f1->GetRange(x1[1], x2[1]); f1->SetNpx(5000); dynamic_cast(*f1) = JLineAttributes::getInstance().get(0); if (listOfObjects.size() == 1) { if (x1[0] == x1[1] && x2[0] == x2[1]) ++nc; // change colour else if (nf != 0) ++ns; // change style f1->SetLineStyle(JLineAttributes ::getInstance().get(ns).GetLineStyle()); f1->SetLineColor(JMarkerAttributes::getInstance().get(nc).GetMarkerColor()); } else { // keep colour of base object and accordingly modify line style. f1->SetLineColor(ls->GetLineColor()); f1->SetLineStyle(JLineAttributes::getInstance().get(ns++).GetLineStyle()); } x1[0] = x1[1]; x2[0] = x2[1]; // set limits /* double __xmin; double __xmax; f1->GetRange(__xmin, __xmax); ymin = min(ymin, f1->GetMinimum(__xmin, __xmax)); ymax = max(ymax, f1->GetMaximum(__xmin, __xmax)); */ } } } // plot frame if (X.is_valid()) { xmin = X.getLowerLimit(); xmax = X.getUpperLimit(); } if (Y.is_valid()) { ymin = Y.getLowerLimit(); ymax = Y.getUpperLimit(); } else if (ymax > ymin) { setRange(ymin, ymax, logy); } cv->cd(1); if (!listOfObjects.empty()) { if (master == NULL) { master = new TH1D(MASTER.c_str(), NULL, 1000, xmin, xmax); master->SetStats(kFALSE); for (Int_t i = 1; i <= master->GetXaxis()->GetNbins(); ++i) { master->SetBinContent(i, ymin); } } } if (master == NULL) { TText* p = new TText(0.5, 0.5, MAKE_CSTRING("No data")); p->SetTextAlign(21); p->SetTextAngle(45); p->Draw(); } else { if (logx) { gPad->SetLogx(); } if (logy) { gPad->SetLogy(); } master->SetTitle(title.c_str()); master->GetXaxis()->SetRangeUser(xmin, xmax); if (logx > 1) { setLogarithmicX(master); } if (logx > 2) { master->GetXaxis()->SetNoExponent(); } master->SetMinimum(ymin); master->SetMaximum(ymax); if (xLabel != "") { master->GetXaxis()->SetTitle(xLabel.c_str()); master->GetXaxis()->CenterTitle(true); } if (yLabel != "") { master->GetYaxis()->SetTitle(yLabel.c_str()); master->GetYaxis()->CenterTitle(true); } master->GetXaxis()->SetMoreLogLabels((logx == 1 && log10(xmax/xmin) < 2) || (logx > 1 && (xmax-xmin) < 2)); master->GetYaxis()->SetMoreLogLabels( logy && log10(ymax/ymin) < 2); master->GetYaxis()->SetNoExponent ( logy && log10(ymax/ymin) < 2); for (map::const_iterator i = Ndivisions.begin(); i != Ndivisions.end(); ++i) { master->SetNdivisions(i->second, i->first.c_str()); } if (xTimeFormat != "") { master->GetXaxis()->SetTimeDisplay(1); if (xTimeFormat == "utc") { master->GetXaxis()->SetTimeFormat("#splitline{}{#splitline{%d-%m-%y}{ %H:%M}}"); master->GetXaxis()->SetTimeOffset(0.0, "gmt"); } else if (xTimeFormat == "UTC") { master->GetXaxis()->SetTimeFormat("%d-%m-%y"); master->GetXaxis()->SetTimeOffset(0.0, "gmt"); } else { master->GetXaxis()->SetTimeFormat(xTimeFormat.c_str()); } } master->Draw(option.c_str()); } if (logx > 1) { for (vector::iterator i = listOfObjects.begin(); i != listOfObjects.end(); ++i) { if (dynamic_cast(i->get()) != master) { if (setLogX (i->get())) {} else if (setLogX (i->get())) {} else if (setLogX(i->get())) {} else if (setLogX (i->get())) {} else if (setLogX (i->get())) {} else if (setLogX (i->get())) {} else if (setLogX (i->get())) {} } } } if (grid.count('x') || grid.count('X')) { gPad->SetGridx(); } if (grid.count('y') || grid.count('Y')) { gPad->SetGridy(); } if (stats != -1) gStyle->SetOptStat(stats); else gStyle->SetOptFit(kFALSE); for (vector::const_iterator i = listOfObjects.begin(); i != listOfObjects.end(); ++i) { DEBUG("Draw " << (*i)->GetName() << ' ' << (*i)->GetTitle() << endl); string buffer(option); //if (!dynamic_cast(i->get())) { // buffer += "SAMES"; //} buffer += "SAME"; TF1* f1 = dynamic_cast (i->get()); TGraph* g1 = dynamic_cast (i->get()); TMultiGraph* q1 = dynamic_cast(i->get()); if (f1 != NULL) { f1->SetNpx(5000); } if (g1 != NULL) { if (g1->GetN() > 1 && drawLine) buffer += "L"; // drawing cut line else if (fillArea == 0 || (dynamic_cast (g1) == NULL && dynamic_cast(g1) == NULL)) buffer += "P"; // drawing point(s) } if (q1 != NULL) { for (TIter i1(q1->GetListOfGraphs()); TGraph* gi = dynamic_cast(i1()); ) { string zbuf = buffer; if (gi->GetN() > 1 && drawLine) zbuf += "L"; // drawing cut line else zbuf += "P"; // drawing point(s) gi->Draw(zbuf.c_str()); } } else { (*i).Draw(buffer.c_str()); } } //gPad->RedrawAxis(); if (legend.is_valid()) { if (group == 0) { group = listOfObjects.size(); } Ssiz_t height = listOfObjects.size()/group; Ssiz_t width = 1; for (vector::const_iterator i = listOfObjects.begin(); i != listOfObjects.end(); ++i) { width = max(width, getWidth(i->getLabel())); } TLegend* lg = getLegend(width, height, legend.location, legend.factor); for (size_t i = 0; i < listOfObjects.size(); i += group) { const JRootObject& object = listOfObjects[i]; if (dynamic_cast(object.get()) == NULL && dynamic_cast (object.get()) == NULL && dynamic_cast (object.get()) == NULL) { lg->AddEntry(object, " " + object.getLabel(), isTAttLine(object) ? "L" : isTAttFill(object) && fillArea != 0 ? "F" : "P"); } } lg->Draw(); } cv->Update(); if (outputFile != "") { cv->SaveAs(outputFile.c_str()); } if (!batch) { tp->Run(); } return (master != NULL ? 0 : 1); }