ユーザ用ツール

サイト用ツール


サイドバー

Menu

Latest

study:software:root:graphic

Graphic

  • rootで作成されたヒストグラムなどのオブジェクトは作っただけでは画面には表示されず、TCanvasに描くことで初めて見ることができる

グラフィック関係の基本知識

TPadの座標系について

TPadにはuser coordinate,Normalized Coordinate(NDC),Pixel Coordinateの三種類の座標系が用意されており、座標を指定してオブジェクトを描くことができる。

  • user座標は、rootでは一番基本的な座標系で、そのTPadに描かれたヒストグラムの軸を基準とした座標系である。

    つまりuser座標ではヒストグラムの原点の位置を(0,0)とし、ヒストグラムの軸の目盛りを使って座標を指定する。目盛りの最大、最小を超えた値にすると枠の外も指定できる。
  • NDCはパッドの左下端の点を(0,0)、右上端の点を(1,1)とした座標系である。
  • ピクセル座標は左上を(0,0)とした、ピクセル座標?(たぶん普通の画像ソフトで使う座標系と同じ)。これを使用することはほとんどないとのこと。

ユーザー座標をNDCに変換

まず、Pad上でのヒストグラムの位置をNDCでどうなっているかを調べる。

[ ] gStyle->GetPadTopMargin()
(const Float_t)1.00000001490116119e-01
[ ] gStyle->GetPadRightMargin()
(const Float_t)1.00000001490116119e-01
[ ] gStyle->GetPadBottomMargin()
(const Float_t)1.00000001490116119e-01
[ ] gStyle->GetPadLeftMargin()
(const Float_t)1.00000001490116119e-01

デフォルト値では例えばユーザ座標の原点はNDCでは(0.1,0.1)になる。

で、さらに軸のメモリの最大値と最小値の情報が必要なので以下で取得する。

[ ] hist->GetXaxis()->GetXmax();
[ ] hist->GetXaxis()->GetXmin();
[ ] hist->GetYaxis()->GetXmax();
[ ] hist->GetYaxis()->GetXmin();

これがわかれば比率でUser Coordinate to NDCができる。

[ ] double l = gStyle->GetPadLeftMargin();
[ ] double r = 1 - gStyle->GetPadRightMargin();
[ ] double xm = hist->GetXaxis()->GetXmin();
[ ] double xM = hist->GetXaxis()->GetXmax();
[ ] double p = l + (r - l) * (x - xm) / (xM - xm); //User CoordinateでxにあたるNDCでの値
[ ]
[ ] double b = gStyle->GetPadBottomMargin();
[ ] double t = 1 - gStyle->GetPadTopMargin();
[ ] double ym = hist->GetYaxis()->GetXmin();
[ ] double yM = hist->GetYaxis()->GetXmax();
[ ] double q = b + (t - b) * (y - ym) / (yM - ym); //User CoordinateでyにあたるNDCでの値

上の方法はたぶん失敗します。原因は調べていない

TStyle

gStyleはTStyleのグローバル変数。

以下のクラスの見た目(style)のデフォルト値を変更することができる。

   - Canvas
   - Pad
   - Histogram axis
   - Lines
   - Fill areas
   - Text
   - Markers
   - Functions
   - Histogram Statistics and Titles

Styleの変更とModifiedとUpdateとDrawの関係

ROOTを使っていてこんな経験はないだろうか?

  • gStyleの設定を変えているのに見た目が変わらない
  • オブジェクトの設定を変えているのに見た目が変わらない
  • Canvasをクリックしたら突如見た目が変化した
  • 画像を保存したら絵が変わっていた
  • コマンドラインとマクロで挙動が違う

これらの疑問はたぶんここを読めば解決します。


まずgStyleの仕組みについて。

gStyleとはオブジェクトのデフォルト値の設定をするためのグローバル変数。

例えばヒストグラムなどのオブジェクトを生成すると、そのオブジェクトはgStyleの現在の設定をコピーする。

よって、オブジェクトの生成→gStyleの設定変更という順番で行っても、オブジェクトにその設定は反映されない。

この状態でgStyleの設定を反映させたい場合はオブジェクトのUseCurrentStyleメソッドを使う必要がある。

gStyle->SetHogehoge();
obj->UseCurrentStyle();


次にオブジェクトが画面に描画されるタイミングについて。

ROOTでDrawされたオブジェクトはすぐさま画面出力(Update)されるわけではなくて、以下のタイミングで出力されるようになっている。

  • CINTでスクリプトの実行が終了した時
  • CINTのコマンドラインでDrawメソッドを実行した時
  • マウスで画面上のCanvasのウィンドウをクリックした時
  • PrintメソッドなどでCanvasを保存する時
  • TCanvas::Update()をコールした時


ここで注意することは画面上の表示が更新されるためにはCanvasに含まれる各Padの少なくとも一つがModifiedのフラッグが立っていないといけないこと。

PadはオブジェクトがDrawされたときModifiedされ、この後にUpdateされると画面の表示が更新される。ひとたび、UpdateされるとCanvas中のModifiedされているPadに含まれた各オブジェクトはPaintをCallされ、PadはModifiedのフラッグがおろされる。

逆にオブジェクトに何らかの変化があっても、それが描かれているPadがModifiedのフラッグが立っていない場合、Updateされても画面の表示は変わらない。

obj→Draw()とはカレントパッドのTListにそのオブジェクトを加えるメソッドで、実際には画面表示を行う動作とDrawは無関係。

画面表示を行っているのはobj→Paint()で、Paintメソッドはcanvas→Update()が実行された時にcallされる。このとき初めて画面表示が行われる。

ただし、コマンドラインではDrawした時にUpdateがされるため、Paintも同時にCallされる。

例えば、コマンドラインで以下の様な順番で、コマンドすると、SetFillColorの操作は画面には反映されない。

[ ] hist->Draw(); //canvasの動作としてはModified -> Update -> Modifiedのフラッグを下ろす
[ ] hist->SetFillColor(kYellow); //ここではcanvasはModifiedされない
[ ] canvas->Update(); //canvasはModifiedされたことを知らないので、変化なし

このような場合は、一度CanvasをModifiedしてから、Updateを再度行う必要がある。

canvas->Modified();
canvas->Update();

ややこしいところは、これはコマンドライン独特の現象であるということ。どういうことかというと、以下のようなマクロの場合、ヒストグラムの設定は反映される。

hist->Draw(); //マクロの実行中はModifiedされるだけで、Updateされない。
hist->SetFillColor(kYellow); //ここではcanvasはModifiedされない
//マクロが終了したときにUpdateがCallされる。Modifiedされているため、SetFillColorがきちんと反映される

この挙動の差はかなり理解しにくいと思う。

TAttFill/TAttLine/TAttMarker/TAttText/etc

基底クラスについて

rootには TAttFill, TAttLine, TAttMarker, TAttTextt といったグラフィックの性質を司るベースクラスがあり、様々なクラスがこれを継承している。

例えば、TH1は前3つを継承しているし、TLineはTAttLineを継承している。

(rootに限らずクラス継承そのもののメリットだけど)これによって、グラフィックの性質は統一的にその設定を変えることができる。

色の種類

基本の色

TCanvas *c = new TCanvas("c","colors",0,0,500,200);
c->DrawColorTable();

色の調整

TColorWheel *w = new TColorWheel();
w->Draw();

上に書かれているkRedとかkBlueとかはrootで最初から定義されている色である。その正体は下のように

enum EColor { kWhite =0,   kBlack =1,   kGray=920,
              kRed   =632, kGreen =416, kBlue=600, kYellow=400, kMagenta=616, kCyan=432,
              kOrange=800, kSpring=820, kTeal=840, kAzure =860, kViolet =880, kPink=900 };

$ROOTSYS/include/Rtypes.hで定義された、列挙型である。

例えば、

myObject.SetFillColor(kRed);
myObject.SetFillColor(kYellow-10);
myLine.SetLineColor(kMagenta+2);

のように明暗などを調整できる。

markerの種類

TCanvas *c = new TCanvas("c","Marker types",0,0,500,200);
TMarker marker;
marker.DisplayMarkerTypes();

8-20はなんなんだろう。

デフォルトは1になっていると思う。なぜかは知らないが、1はMarkerSizeをかえても大きさが変わらない

lineの種類

{
   TCanvas *Lw = new TCanvas("Lw","test",500,200);
   TText  t;
   t.SetTextAlign(32);
   t.SetTextSize(0.08);
   Int_t i=1;
   for (float s=0.1; s<1.0 ; s+=0.092) {
      TLine *lh = new TLine(0.15,s,.85,s);
      lh->SetLineWidth(i);
      t.DrawText(0.1,s,Form("%d",i++));
      lh->Draw();
   }
   return Lw;
}

背景を透明にする

TAttFillで背景を透明にしたい場合。

obj->SetFillStyle(0)

TCanvas/TPad

  • TCanvasはrootの様々なオブジェクトを描く(ディスプレイに出力する)ためのクラス
  • TCanvasはTPadを継承しており、TCanvasの中には複数のTPadをもつことができる

TCanvasとそれに描くオブジェクトはヒープ領域に作る(newして作る)こと。 スコープから外れてインスタンスが消去されたときにTCanvasに描かれている絵も消えてしまう。

TCanvasの自動生成について

オブジェクトをディスプレイに出したり、画像として保存するときはTCanvas型のオブジェクトを作るのは必須である。

しかし、TH1やTTreeのDrawを使うと勝手にc1という名前でTCanvasが生成されるので、自分で宣言する必要はない。これを確認しておく。

[ ] TH1F *h1 = new TH1F("h1","",100,0,10);
[ ] h1->Draw();
Info in <TCanvas::MakeDefCanvas>:  created default TCanvas with name c1

下のようにして、このc1にアクセスして設定を変えることもできる。

[ ] c1->SetLogy();

もちろんc1はオブジェクトの名前でポインタではない

宣言とサイズについて

TCanvas *c1 = new TCanvas("name","title",form);
TCanvas *c1 = new TCanvas("name","title",width,height);

form = 1 700x500 at 10,10 (set by TStyle::SetCanvasDefH,W,X,Y)
form = 2 500x500 at 20,20
form = 3 500x500 at 30,30
form = 4 500x500 at 40,40
form = 5 500x500 at 50,50

TCanvasの大きさはメニューなどを含んだ大きさに設定されるため、画像としては宣言した大きさよりも若干小さくなるという謎仕様がある。

具体的に言うと、width4,height28だけ小さくなる(環境によって変わるかもしれない)。

root [] TCanvas *c1 = new TCanvas("name","title"); //デフォルト値は700×500なのだが…
root [] c1->GetWw()
(const unsigned int)696
root [] c1->GetWh()
(const unsigned int)472

キャンバスを分割する、または複数のキャンバスにそれぞれ描く

TCanvasにもカレントディレクトリみたいにカレントキャンバスというべき概念がある。キャンバスを変えないで、ヒストグラムを描こうとすると前のオブジェクトは上書きされて消えてしまう(ただし、上書きして消去するのはヒストグラムの場合のみ)。

複数のオブジェクトを描く方法としては、TCanvasを分割するか、TCanvasを複数宣言すればいい。

canvas->Divide(n,m); //n行,m列にcanvasを分割する
canvas->cd(i); //i番目の区画に移動する
hist->Draw();

区画の番号はDivide(4,3)なら下のようになる。

1 2 3 4
5 6 7 8
9 10 11 12

下のようにPad同士のマージンを調整することもできる(デフォルト値は0.01)。

canvas->Divide(n,m,0.02,0.02); 

cd()またはcd(0)は親キャンバスを指す。


Divideせずに別々の画像として保存したい場合は下のようにして、複数宣言したCanvasを行き来することもできる。

TCanvas *c1 = new TCanvas("c1","c1");
TCanvas *c2 = new TCanvas("c2","c2");
c1->cd();
h1->Draw();
c2->cd();
h2->Draw();

上の例ではわざと手間のかかる方法をとったが、本当はTCanvasを宣言すると自動的にそのキャンバスに移動するので、下のようにすればできる。

TCanvas *c1 = new TCanvas("c1","c1");
h1->Draw();
TCanvas *c2 = new TCanvas("c2","c2");
h2->Draw();

TCanvasはDivideした各区画一つ一つは実はTPad。また、そのTPad自体も複数のTPadに分割することが出来る。

Drawメソッドは正確には、TCanvasではなくて、TCanvasに含まれたTPadに描くというのが正しい(と思う)。 cdで内部的に行われているのはつまり、オブジェクトを描く対象のTPadを選択するということ。

ちなみにgPadはカレントパッドを指しているグローバル変数である。

キャンバスの任意の場所にTPadを描く

Divideを使った場合は各区画の大きさは同じのものしか作れない。

もしも不均一な大きさで絵を描きたい場合はTPadを自分で適当な大きさで作る必要がある。

そもそもDivideの内部的な動作は複数のTPadを定義してTCanvasに均等な大きさで重ならないように並べているだけである。

でも初心者向けにTPadの説明はされないと思うので、使い方を知らない場合も多いと思う。

TPad *pad = new TPad("name", "title",xlow,ylow,xup,yup,color, bordersize,bordermode);
pad->Draw();
pad->SetNumber(n);

  • 位置はNDCで指定する
  • SetNumberはcd(n)で移動できるTPadの番号を決める
bordermode = -1 box looks as it is behind the screen
bordermode = 0 no special effects
bordermode = 1 box looks as it is in front of the screen

log scaleにする

c1->SetLogy();
c1->SetLogy(0);// もとに戻す

canvasを分割している場合は

c1->GetPad(i)->SetLogy();
c1->cd(i)->SetLogy();//移動しつつ、せっても変える

で出来る。

画像を保存する

c1->Print("title.ps"); //psじゃなくても、png,pdf,gifなども作れる
c1->SaveAs("title.ps"); //psじゃなくても、png,pdf,gifなども作れる

  • Printは内部的にはSaveAsと同じ。

複数のキャンバスから一つのpdfファイルを生成する

c1->Print("title.pdf(")
c2->Print("title.pdf")
c3->Print("title.pdf)")

  • eps、psでも可能
  • (じゃなくても[でも可能

SetGrid・グリッドを描く

c1->SetGrid();
c1->SetGridx();
c1->SetGridy();

Gridのlineのtypesは下のように変えることができる。TAttLineのtypeが使える。

gStyle->SetGridStyle(1); //defaultは3

Clear・キャンバスをClearする

c1->Clear();

TCanvasをディスプレイに出さないようにする

rootでは一度TCanvasにDrawしないと、画像出力できない。ところでGUIが使えないor使いたくない場合、TCanvasが画面に出て欲しくないときもある。

そういうときはマクロを読み込むときに

$ root -b macro.cxx

とするか

gROOT->SetBatch();

として、バッチモードで動かせばいい。なんとなく動作も軽い気がする。

TLine

TLine line=new TLine(0,0,3,5);
line->SetLineColor(3);
line->SetLineWidth(5);
line->SetLineStyle(3);//1=line,2=broken,3=dotted,4=broken-dot,5=long-broken-dot
line->Draw();

study/software/root/graphic.txt · 最終更新: 2015/08/15 06:47 by kamo

ページ用ツール