diff --git a/scratch/make_pretty.C b/scratch/make_pretty.C new file mode 100644 index 0000000..829012d --- /dev/null +++ b/scratch/make_pretty.C @@ -0,0 +1,169 @@ +// ============================================================================= +// make_pretty.C +// +// Feed it a ROOT file and a histogram name, get a publication-quality PNG. +// +// Usage: +// root -l -b -q 'make_pretty.C("myfile.root", "histName")' +// root -l -b -q 'make_pretty.C("myfile.root", "histName", "x label", "y label")' +// +// Supports TH1F, TH1D, TH2F, TH2D — type is detected automatically. +// Output PNG is written to the same directory as the input file. +// ============================================================================= + +#include "TFile.h" +#include "TH1.h" +#include "TH2.h" +#include "TCanvas.h" +#include "TStyle.h" +#include "TGaxis.h" +#include "TLatex.h" +#include "TSystem.h" +#include "TROOT.h" + +#include +#include + +// --------------------------------------------------------------------------- +// Style — called once before anything is drawn +// --------------------------------------------------------------------------- +void SetStyle() { + gROOT->SetStyle("Plain"); + gStyle->SetOptStat(0); + gStyle->SetOptTitle(0); + + gStyle->SetTextFont(42); + gStyle->SetLabelFont(42, "xyz"); + gStyle->SetTitleFont(42, "xyz"); + + gStyle->SetLabelSize(0.045, "xyz"); + gStyle->SetTitleSize(0.050, "xyz"); + gStyle->SetTitleOffset(1.15, "x"); + gStyle->SetTitleOffset(1.20, "y"); + + gStyle->SetTickLength(0.025, "xy"); + gStyle->SetNdivisions(510, "xy"); + TGaxis::SetMaxDigits(4); + + gStyle->SetCanvasColor(0); + gStyle->SetPadColor(0); + gStyle->SetFrameLineWidth(2); + gStyle->SetHistLineWidth(2); + + gStyle->SetPadLeftMargin(0.14); + gStyle->SetPadRightMargin(0.04); + gStyle->SetPadBottomMargin(0.13); + gStyle->SetPadTopMargin(0.06); + gStyle->SetPadTickX(1); + gStyle->SetPadTickY(1); + + // kBird: perceptually uniform, grayscale-safe, colorblind-friendly + gStyle->SetPalette(kBird); + gStyle->SetNumberContours(255); +} + +// --------------------------------------------------------------------------- +// make_pretty() +// --------------------------------------------------------------------------- +void make_pretty(const char* rootFile, + const char* histName, + const char* xlabel = "", + const char* ylabel = "") { + + SetStyle(); + + // --- Open file ---------------------------------------------------------- + TFile* f = TFile::Open(rootFile, "READ"); + if (!f || f->IsZombie()) { + std::cerr << "ERROR: Cannot open " << rootFile << "\n"; + return; + } + + TObject* obj = f->Get(histName); + if (!obj) { + std::cerr << "ERROR: '" << histName << "' not found in " << rootFile << "\n"; + f->Close(); + return; + } + + // --- Clone and detach BEFORE closing the file --------------------------- + // ROOT ties every histogram to the TDirectory of the file it came from. + // When f->Close() is called, ROOT deletes all objects in that directory — + // including the clone — unless SetDirectory(0) is called first to detach + // it. Drawing a deleted object causes the segfault you are seeing. + TObject* clone = obj->Clone(Form("%s_clone", histName)); + if (!clone) { + std::cerr << "ERROR: Clone failed for '" << histName << "'\n"; + f->Close(); + return; + } + if (clone->InheritsFrom(TH1::Class())) + ((TH1*)clone)->SetDirectory(0); // detach — survives file close + f->Close(); + + // --- Detect dimension --------------------------------------------------- + bool is2D = clone->InheritsFrom(TH2::Class()); + + // --- Canvas: 2100 px wide = 7 in at 300 DPI ----------------------------- + int canvasW = 2100; + int canvasH = is2D ? 1800 : 1575; // square-ish for 2D, 4:3 for 1D + + TCanvas c("c", "", 0, 0, canvasW, canvasH); + c.cd(); + + // Widen right margin for the colz palette bar + if (is2D) gPad->SetRightMargin(0.13); + + // --- Draw --------------------------------------------------------------- + if (is2D) { + TH2* h = (TH2*)clone; + h->SetStats(0); + h->GetXaxis()->SetTitle(""); + h->GetYaxis()->SetTitle(""); + h->Draw("colz"); + } else { + TH1* h = (TH1*)clone; + h->SetStats(0); + h->SetLineColor(kBlue+1); + h->SetFillColorAlpha(kBlue+1, 0.25); + h->SetFillStyle(1001); + h->GetXaxis()->SetTitle(""); + h->GetYaxis()->SetTitle(""); + h->Draw("hist"); + } + + // --- Axis labels via TLatex (full typographic control) ------------------ + TLatex tex; + tex.SetNDC(); + tex.SetTextFont(42); + tex.SetTextSize(0.050); + + // X: y=0.06 sits just below the tick numbers inside the bottom margin (0.13). + // Y: x=0.08 sits just left of the tick numbers inside the left margin (0.14). + // Increase either value to push the label further from the axis. + if (strlen(xlabel) > 0) + tex.DrawLatex(0.46, 0.05, xlabel); + + if (strlen(ylabel) > 0) { + tex.SetTextAngle(90); + tex.DrawLatex(0.08, 0.40, ylabel); + tex.SetTextAngle(0); + } + + // --- Save --------------------------------------------------------------- + // Build output path: same directory as input file, named after the histogram + std::string inPath(rootFile); + std::string dir = inPath.substr(0, inPath.find_last_of("/\\")); + if (dir == inPath) dir = "."; // no directory component — use cwd + + std::string outPath = dir + "/" + std::string(histName) + ".png"; + // Replace any "/" inside histName (e.g. "folder/hist") with "_" + for (char& ch : outPath) + if (ch == '/') ch = '_'; + + c.Modified(); + c.Update(); + c.SaveAs(outPath.c_str()); + + std::cout << "Saved: " << outPath << "\n"; +} \ No newline at end of file