# 11 Graphics and the Graphical User Interface

Graphical capabilities of ROOT range from 2D objects (lines, polygons, arrows) to various plots, histograms, and 3D graphical objects. In this chapter, we are going to focus on principals of graphics and 2D objects. Plots and histograms are discussed in a chapter of their own.

## 11.1 Drawing Objects

In ROOT, most objects derive from a base class TObject. This class has a virtual method Draw() so all objects are supposed to be able to be “drawn”. The basic whiteboard on which an object is drawn is called a canvas (defined by the class TCanvas). If several canvases are defined, there is only one active at a time. One draws an object in the active canvas by using the statement:

object.Draw()

This instructs the object “object” to draw itself. If no canvas is opened, a default one (named “c1”) is instantiated and is drawn.

root[] TLine a(0.1,0.1,0.6,0.6)
root[] a.Draw()
<TCanvas::MakeDefCanvas>: created default TCanvas with name c1

The first statement defines a line and the second one draws it. A default canvas is drawn since there was no opened one.

## 11.2 Interacting with Graphical Objects

When an object is drawn, one can interact with it. For example, the line drawn in the previous paragraph may be moved or transformed. One very important characteristic of ROOT is that transforming an object on the screen will also transform it in memory. One actually interacts with the real object, not with a copy of it on the screen. You can try for instance to look at the starting X coordinate of the line:

root[] a.GetX1()
(double)1.000000000e-1

X1 is the x value of the starting coordinate given in the definition above. Now move it interactively by clicking with the left mouse button in the line’s middle and try to do again:

root[] a.GetX1()
(Double_t)1.31175468483816005e-01

You do not obtain the same result as before, the coordinates of ‘a’ have changed. As said, interacting with an object on the screen changes the object in memory.

### 11.2.1 Moving, Resizing and Modifying Objects

Changing the graphic objects attributes can be done with the GUI or programmatically. First, let’s see how it is done in the GUI.

#### 11.2.1.1 The Left Mouse Button

As was just seen moving or resizing an object is done with the left mouse button. The cursor changes its shape to indicate what may be done:

Point the object or one part of it:

Rotate:

Resize (exists also for the other directions):

Enlarge (used for text):

Move:

Here are some examples of:

Moving: Resizing:

Rotating:

#### 11.2.1.2 With C++ Statements (Programmatically)

How would one move an object in a script? Since there is a tight correspondence between what is seen on the screen and the object in memory, changing the object changes it on the screen. For example, try to do:

root[] a.SetX1(0.9)

This should change one of the coordinates of our line, but nothing happens on the screen. Why is that? In short, the canvas is not updated with each change for performance reasons. See “Updating the Pad”.

### 11.2.2 Selecting Objects

#### 11.2.2.1 The Middle Mouse Button

Objects in a canvas, as well as in a pad, are stacked on top of each other in the order they were drawn. Some objects may become “active” objects, which mean they are reordered to be on top of the others. To interactively make an object “active”, you can use the middle mouse button. In case of canvases or pads, the border becomes highlighted when it is active.

#### 11.2.2.2 With C++ Statements (Programmatically)

Frequently we want to draw in different canvases or pads. By default, the objects are drawn in the active canvas. To activate a canvas you can use the TPad::cd() method.

root[] c1->cd()

### 11.2.3 Context Menus: the Right Mouse Button

The context menus are a way to interactively call certain methods of an object. When designing a class, the programmer can add methods to the context menu of the object by making minor changes to the header file.

On a ROOT canvas, you can right-click on any object and see the context menu for it. The script hsimple.C draws a histogram. The image below shows the context menus for some of the objects on the canvas. Next picture shows that drawing a simple histogram involves as many as seven objects. When selecting a method from the context menu and that method has options, the user will be asked for numerical values or strings to fill in the option. For example, TAxis::SetTitle will prompt you for a string to use for the axis title.

#### 11.2.3.2 Structure of the Context Menus

The curious reader will have noticed that each entry in the context menu corresponds to a method of the class. Look for example to the menu named TAxis::xaxis. xaxis is the name of the object and TAxis the name of its class. If we look at the list of TAxis methods, for example in http://root.cern.ch/root/htmldoc/TAxis.html, we see the methods SetTimeDisplay() andUnZoom(), which appear also in the context menu.

There are several divisions in the context menu, separated by lines. The top division is a list of the class methods; the second division is a list of the parent class methods. The subsequent divisions are the methods other parent classes in case of multiple inheritance. For example, see the TPaveText::title context menu. A TPaveText inherits from TAttLine, which has the method SetLineAttributes().

For a method to appear in the context menu of the object it has to be marked by // *MENU* in the header file. Below is the line from TAttLine.h that adds the SetLineAttribute method to the context menu.

Nothing else is needed, since Cling knows the classes and their methods. It takes advantage of that to create the context menu on the fly when the object is clicking on. If you click on an axis, ROOT will ask the interpreter what are the methods of the TAxis and which ones are set for being displayed in a context menu.

Now, how does the interpreter know this? Remember, when you build a class that you want to use in the ROOT environment, you use rootcling that builds the so-called stub functions and the dictionary. These functions and the dictionary contain the knowledge of the used classes. To do this, rootcling parses all the header files. ROOT has defined some special syntax to inform Cling of certain things, this is done in the comments so that the code still compiles with a C++ compiler.

For example, you have a class with a Draw() method, which will display itself. You would like a context menu to appear when on clicks on the image of an object of this class. The recipe is the following:

• The class has to contain the ClassDef/ClassImp macros

• For each method you want to appear in the context menu, put a comment after the declaration containing *MENU* or *TOGGLE* depending on the behavior you expect. One usually uses Set methods (setters). The *TOGGLE* comment is used to toggle a boolean data field. In that case, it is safe to call the data field fMyBool where MyBool is the name of the setter SetMyBool. Replace MyBool with your own boolean variable.

• You can specify arguments and the data members in which to store the arguments.

For example:

class MyClass : public TObject {
private:
int      fV1;   // first variable
double   fV2;   // second variable
public:
int    GetV1() {return fV1;}
double GetV2() {return fV2;}
void   SetV1(int x1) { fV1 = x1;}     // *MENU*
void   SetV2(double d2) { fV2 = d2;}  // *MENU*
void   SetBoth(int x1, double d2) {fV1 = x1; fV2 = d2;}

ClassDef (MyClass,1)
}

To specify arguments:

void SetXXX(Int_t x1, Float_t y2); //*MENU* *ARGS={x1=>fV1}

This statement is in the comment field, after the *MENU*. If there is more than one argument, these arguments are separated by commas, where fX1 and fY2 are data fields in the same class.

void SetXXX(Int_t x1, Float_t y2); //*MENU* *ARGS={x1=>fX1,y2=>fY2}

If the arguments statement is present, the option dialog displayed when selecting SetXXX field will show the values of variables. We indicate to the system which argument corresponds to which data member of the class.

### 11.2.4 Executing Events when a Cursor Passes on Top of an Object

This paragraph is for class designers. When a class is designed, it is often desirable to include drawing methods for it. We will have a more extensive discussion about this, but drawing an object in a canvas or a pad consists in “attaching” the object to that pad. When one uses object.Draw(), the object is NOT painted at this moment. It is only attached to the active pad or canvas.

Another method should be provided for the object to be painted, the Paint() method. This is all explained in the next paragraph. As well as Draw() and Paint(), other methods may be provided by the designer of the class. When the mouse is moved or a button pressed/released, the TCanvas function named HandleInput() scans the list of objects in all it’s pads and for each object calls some standard methods to make the object react to the event (mouse movement, click or whatever).

The second one is DistanceToPrimitive(px,py). This function computes a “distance” to an object from the mouse position at the pixel position (px, py, see definition at the end of this paragraph) and returns this distance in pixel units. The selected object will be the one with the shortest computed distance. To see how this works, select the “Event Status” item in the canvas “Options” menu. ROOT will display one status line showing the picked object. If the picked object is, for example, a histogram, the status line indicates the name of the histogram, the position x,y in histogram coordinates, the channel number and the channel content.

It is nice for the canvas to know what the closest object from the mouse is, but it’s even nicer to be able to make this object react. The third standard method to be provided is ExecuteEvent(). This method actually does the event reaction. Its prototype is where px and py are the coordinates at which the event occurred, except if the event is a key press, in which case px contains the key code.

void ExecuteEvent(Int_t event, Int_t px, Int_t py);

Where event is the event that occurs and is one of the following (defined in Buttons.h):

kNoEvent,          kButton1Down,      kButton2Down,
kButton3Down,      kKeyDown,          kButton1Up,
kButton2Up,        kButton3Up,        kButton1Motion,
kButton2Motion,    kButton3Motion,    kKeyPress,
kButton1Locate,    kButton2Locate,    kButton3Locate,
kKeyUp,            kButton1Double,    kButton2Double,
kButton3Double,    kMouseMotion,      kMouseEnter,
kMouseLeave

We hope the names are self-explanatory.

Designing an ExecuteEvent method is not very easy, except if one wants very basic treatment. We will not go into that and let the reader refer to the sources of classes like TLine or TBox. Go and look at their ExecuteEvent method! We can nevertheless give some reference to the various actions that may be performed. For example, one often wants to change the shape of the cursor when passing on top of an object. This is done with the SetCursor method:

The argument cursor is the type of cursor. It may be:

kBottomLeft,  kBottomRight,  kTopLeft,
kTopRight,    kBottomSide,   kLeftSide,
kTopSide,     kRightSide,    kMove,
kCross,       kArrowHor,     kArrowVer,
kHand,        kRotate,       kPointer,
kArrowRight,  kCaret,        kWatch

They are defined in TVirtualX.h and again we hope the names are self-explanatory. If not, try them by designing a small class. It may derive from something already known like TLine.

Note that the ExecuteEvent() functions may in turn; invoke such functions for other objects, in case an object is drawn using other objects. You can also exploit at best the virtues of inheritance. See for example how the class TArrow (derived from TLine) use or redefine the picking functions in its base class.

The last comment is that mouse position is always given in pixel units in all these standard functions. px=0 and py=0 corresponds to the top-left corner of the canvas. Here, we have followed the standard convention in windowing systems. Note that user coordinates in a canvas (pad) have the origin at the bottom-left corner of the canvas (pad). This is all explained in the paragraph “The Coordinate Systems of a Pad”.

## 11.3 Graphical Containers: Canvas and Pad

Drawing an object is nothing more than adding its pointer to this list. Look for example at the code of TH1::Draw(). It is merely ten lines of code. The last statement is AppendPad(). This statement calls method of TObject that just adds the pointer of the object, here a histogram, to the list of objects attached to the current pad. Since this is a TObject’s method, every object may be “drawn”, which means attached to a pad.

When is the painting done then ? The answer is: when needed. Every object that derives from TObject has a Paint() method. It may be empty, but for graphical objects, this routine contains all the instructions to paint effectively it in the active pad. Since a Pad has the list of objects it owns, it will call successively the Paint() method of each object, thus re-painting the whole pad on the screen. If the object is a sub-pad, its Paint() method will call the Paint() method of the objects attached, recursively calling Paint() for all the objects.

In some cases a pad need to be painted during a macro execution. To force the pad painting gPad->Update() (see next section) should be performed.

The list of primitives stored in the pad is also used to pick objects and to interact with them.

When an object is drawn, it is always in the so-called active pad. For every day use, it is comfortable to be able to access the active pad, whatever it is. For that purpose, there is a global pointer, called gPad. It is always pointing to the active pad. If you want to change the fill color of the active pad to blue but you do not know its name, do this.

To get the list of colors, go to the paragraph “Color and color palettes” or if you have an opened canvas, click on the View menu, selecting the Colors item.

#### 11.3.1.1 Finding an Object in a Pad

Now that we have a pointer to the active pad, gPad and that we know this pad contains some objects, it is sometimes interesting to access one of those objects. The method GetPrimitive() of TPad, i.e. TPad::GetPrimitive(const char* name) does exactly this. Since most of the objects that a pad contains derive from TObject, they have a name. The following statement will return a pointer to the object myobjectname and put that pointer into the variable obj. As you can see, the type of returned pointer is TObject*.

(class TObject*)0x1063cba8

Even if your object is something more complicated, like a histogram TH1F, this is normal. A function cannot return more than one type. So the one chosen was the lowest common denominator to all possible classes, the class from which everything derives, TObject. How do we get the right pointer then? Simply do a cast of the function output that will transform the output (pointer) into the right type. For example if the object is a TPaveLabel:

(class TPaveLabel*)0x1063cba8

This works for all objects deriving from TObject. However, a question remains. An object has a name if it derives from TNamed, not from TObject. For example, an arrow (TArrow) doesn’t have a name. In that case, the “name” is the name of the class. To know the name of an object, just click with the right button on it. The name appears at the top of the context menu. In case of multiple unnamed objects, a call to GetPrimitive("className") returns the instance of the class that was first created. To retrieve a later instance you can use GetListOfPrimitives(), which returns a list of all the objects on the pad. From the list you can select the object you need.

#### 11.3.1.2 Hiding an Object

Hiding an object in a pad can be made by removing it from the list of objects owned by that pad. This list is accessible by the GetListOfPrimitives() method of TPad. This method returns a pointer to a TList. Suppose we get the pointer to the object, we want to hide, call it obj (see paragraph above). We get the pointer to the list:

Then remove the object from this list:

root[] li->Remove(obj)

The object will disappear from the pad as soon as the pad is updated (try to resize it for example). If one wants to make the object reappear:

root[] obj->Draw()

Caution, this will not work with composed objects, for example many histograms drawn on the same plot (with the option “same”). There are other ways! Try to use the method described here for simple objects.

### 11.3.2 The Coordinate Systems of a Pad

There are coordinate systems in a TPad: user coordinates, normalized coordinates (NDC), and pixel coordinates.

#### 11.3.2.1 The User Coordinate System

The most common is the user coordinate system. Most methods of TPad use the user coordinates, and all graphic primitives have their parameters defined in terms of user coordinates. By default, when an empty pad is drawn, the user coordinates are set to a range from 0 to 1 starting at the lower left corner. At this point they are equivalent of the NDC coordinates (see below). If you draw a high level graphical object, such as a histogram or a function, the user coordinates are set to the coordinates of the histogram. Therefore, when you set a point it will be in the histogram coordinates.

For a newly created blank pad, one may use TPad::Range to set the user coordinate system. This function is defined as:

void Range(float x1,float y1,float x2,float y2)

The arguments x1, x2 defines the new range in the x direction, and the y1, y2 define the new range in the y-direction.

root[] TCanvas MyCanvas ("MyCanvas")

This will set the active pad to have both coordinates to go from -100 to 100, with the center of the pad at (0,0). You can visually check the coordinates by viewing the status bar in the canvas. To display the status bar select Event Status entry in the View canvas menu.

#### 11.3.2.2 The Normalized Coordinate System (NDC)

Normalized coordinates are independent of the window size and of the user system. The coordinates range from 0 to 1 and (0, 0) corresponds to the bottom-left corner of the pad. Several internal ROOT functions use the NDC system (3D primitives, PostScript, log scale mapping to linear scale). You may want to use this system if the user coordinates are not known ahead of time.

#### 11.3.2.3 The Pixel Coordinate System

The least common is the pixel coordinate system, used by functions such as DistanceToPrimitive() and ExecuteEvent(). Its primary use is for cursor position, which is always given in pixel coordinates. If (px,py) is the cursor position, px=0 and py=0 corresponds to the top-left corner of the pad, which is the standard convention in windowing systems.

#### 11.3.2.4 Using NDC for a particular Object

Most of the time, you will be using the user coordinate system. But sometimes, you will want to use NDC. For example, if you want to draw text always at the same place over a histogram, no matter what the histogram coordinates are. There are two ways to do this. You can set the NDC for one object or may convert NDC to user coordinates. Most graphical objects offer an option to be drawn in NDC. For instance, a line (TLine) may be drawn in NDC by using DrawLineNDC(). A latex formula or a text may use TText::SetNDC() to be drawn in NDC coordinates.

### 11.3.3 Converting between Coordinate Systems

There are a few utility functions in TPad to convert from one system of coordinates to another. In the following table, a point is defined by (px,py) in pixel coordinates, (ux,uy) in user coordinates, (ndcx,ndcy) in normalized coordinates, (apx, apy) are in absolute pixel coordinates.

 Conversion TPad’s Methods Returns NDC to Pixel UtoPixel(ndcx) VtoPixel(ndcy) Int_t Int_t Pixel to User PixeltoX(px) PixeltoY(py) PixeltoXY(px,py,&ux,&uy) Double_t Double_t Double_t ux,uy User to Pixel XtoPixel(ux) YtoPixel(uy) XYtoPixel(ux,uy,&px,&py) Int_t Int_t Int_t px,py User to absolute pixel XtoAbsPixel(ux) YtoAbsPixel(uy) XYtoAbsPixel(ux,uy,&apx,&apy) Int_t Int_t Int_t apx,apy Absolute pixel to user AbsPixeltoX(apx) AbsPixeltoY(apy) AbsPixeltoXY(apx,apy,&ux,&uy) Double_t Double_t Double_t ux,uy

Note: all the pixel conversion functions along the Y axis consider that py=0 is at the top of the pad except PixeltoY() which assume that the position py=0 is at the bottom of the pad. To make PixeltoY() converting the same way as the other conversion functions, it should be used the following way (p is a pointer to a TPad):

p->PixeltoY(py - p->GetWh());

Dividing a pad into sub pads in order for instance to draw a few histograms, may be done in two ways. The first is to build pad objects and to draw them into a parent pad, which may be a canvas. The second is to automatically divide a pad into horizontal and vertical sub pads.

#### 11.3.4.1 Creating a Single Sub-pad

The simplest way to divide a pad is to build sub-pads in it. However, this forces the user to explicitly indicate the size and position of those sub-pads. Suppose we want to build a sub-pad in the active pad (pointed by gPad). First, we build it, using a TPad constructor:

One gives the coordinates of the lower left point (0.1, 0.1) and of the upper right one (0.5, 0.5). These coordinates are in NDC. This means that they are independent of the user coordinates system, in particular if you have already drawn for example a histogram in the mother pad. The only thing left is to draw the pad:

If you want more sub-pads, you have to repeat this procedure as many times as necessary.

#### 11.3.4.2 Dividing a Canvas into Sub-Pads

The manual way of dividing a pad into sub-pads is sometimes very tedious. There is a way to automatically generate horizontal and vertical sub-pads inside a given pad.

One question remains. In case one does an automatic divide, how one can set the default margins between pads? This is done by adding two parameters to Divide(), which are the margins in x and y:

The margins are here set to 10% of the parent pad width.

For performance reasons, a pad is not updated with every change. For example, changing the coordinates of the pad does not automatically redraw it. Instead, the pad has a “bit-modified” that triggers a redraw. This bit is automatically set by:

• Touching the pad with the mouse - for example resizing it with the mouse.

• Finishing the execution of a script.

• Adding a new primitive or modifying some primitives for example the name and title of an object.

• You can also set the “bit-modified” explicitly with the Modified method:

// recursively update all modified pads:
root[] c1->Update()

A subsequent call to TCanvas::Update() scans the list of sub-pads and repaints the pads declared modified.

### 11.3.6 Making a Pad Transparent

As we will see in the paragraph “Fill Attributes”, a fill style (type of hatching) may be set for a pad.

This is done with the SetFillStyle method where istyle is a style number, defined in “Fill Attributes”. A special set of styles allows handling of various levels of transparency. These are styles number 4000 to 4100, 4000 being fully transparent and 4100 fully opaque. So, suppose you have an existing canvas with several pads. You create a new pad (transparent) covering for example the entire canvas. Then you draw your primitives in this pad. The same can be achieved with the graphics editor. For example:

root[] .x tutorials/hist/h1draw.C
root[] // create some primitives, etc

### 11.3.7 Setting the Log Scale

Setting the scale to logarithmic or linear is an attribute of the pad, not the axis or the histogram. The scale is an attribute of the pad because you may want to draw the same histogram in linear scale in one pad and in log scale in another pad. Frequently, we see several histograms on top of each other in the same pad. It would be very inconvenient to set the scale attribute for each histogram in a pad.

Furthermore, if the logic was set in the histogram class (or each object) the scale setting in each Paint method of all objects should be tested.

If you have a pad with a histogram, a right-click on the pad, outside of the histograms frame will convince you. The SetLogx(), SetLogy() and SetLogz() methods are there. As you see, TPad defines log scale for the two directions x and y plus z if you want to draw a 3D representation of some function or histogram.

The way to set log scale in the x direction for the active pad is:

If you have a divided pad, you need to set the scale on each of the sub-pads. Setting it on the containing pad does not automatically propagate to the sub-pads. Here is an example of how to set the log scale for the x-axis on a canvas with four sub-pads:

root[] TCanvas MyCanvas("MyCanvas","My Canvas")
root[] MyCanvas->Divide(2,2)
root[] MyCanvas->cd(1)
root[] MyCanvas->cd(2)
root[] MyCanvas->cd(3)

### 11.3.8 WaitPrimitive method

When the TPad::WaitPrimitive() method is called with no arguments, it will wait until a double click or any key pressed is executed in the canvas. A call to gSystem->Sleep(10) has been added in the loop to avoid consuming at all the CPU. This new option is convenient when executing a macro. By adding statements like:

canvas->WaitPrimitive();

You can monitor the progress of a running macro, stop it at convenient places with the possibility to interact with the canvas and resume the execution with a double click or a key press.

You can make the TPad non-editable. Then no new objects can be added, and the existing objects and the pad can not be changed with the mouse or programmatically. By default the TPad is editable.

## 11.4 Graphical Objects

In this paragraph, we describe the various simple 2D graphical objects defined in ROOT. Usually, one defines these objects with their constructor and draws them with their Draw() method. Therefore, the examples will be very brief. Most graphical objects have line and fill attributes (color, width) that will be described in “Graphical objects attributes”. If the user wants more information, the class names are given and they may refer to the online developer documentation. This is especially true for functions and methods that set and get internal values of the objects described here. By default 2D graphical objects are created in User Coordinates with (0, 0) in the lower left corner.

### 11.4.1 Lines, Arrows and Polylines

The simplest graphical object is a line. It is implemented in the TLine class. The line constructor is:

TLine(Double_t x1,Double_t y1,Double_t x2,Double_t y2)

The arguments x1, y1, x2, y2 are the coordinates of the first and second point. It can be used:

root[] l = new TLine(0.2,0.2,0.8,0.3)
root[] l->Draw()

The arrow constructor is:

TArrow(Double_t x1, Double_t y1,
Double_t x2, Double_t y2,
Float_t arrowsize, Option_t *option)

It defines an arrow between points x1,y1 and x2,y2. The arrow size is in percentage of the pad height. The option parameter has the following meanings:

“>”

“<|”

“<”

“|>”

“<>”

“<|>”

Once an arrow is drawn on the screen, one can:

• click on one of the edges and move this edge.

• click on any other arrow part to move the entire arrow.

If FillColor is 0, an open triangle is drawn; else a full triangle is filled with the set fill color. If ar is an arrow object, fill color is set with:

ar.SetFillColor(icolor);

Where icolor is the color defined in “Color and Color Palettes”.

The default-opening angle between the two sides of the arrow is 60 degrees. It can be changed with the method ar->SetAngle(angle), where angle is expressed in degrees.

A poly-line is a set of joint segments. It is defined by a set of N points in a 2D space. Its constructor is:

TPolyLine(Int_t n,Double_t* x,Double_t* y,Option_t* option)

Where n is the number of points, and x and y are arrays of n elements with the coordinates of the points. TPolyLine can be used by it self, but is also a base class for other objects, such as curly arcs.

### 11.4.2 Circles and Ellipses

An ellipse can be truncated and rotated. It is defined by its center (x1,y1) and two radii r1 and r2. A minimum and maximum angle may be specified (phimin,phimax). The ellipse may be rotated with an angle theta. All these angles are in degrees. The attributes of the outline line are set via TAttLine, of the fill area - via TAttFill class. They are described in “Graphical Objects Attributes”.

When an ellipse sector is drawn only, the lines between the center and the end points of the sector are drawn by default. By specifying the drawn option “only”, these lines can be avoided. Alternatively, the method SetNoEdges() can be called. To remove completely the ellipse outline, specify zero (0) as a line style.

The TEllipse constructor is:

TEllipse(Double_t x1, Double_t y1, Double_t r1, Double_t r2,
Double_t phimin, Double_t phimax, Double_t theta)

An ellipse may be created with:

root[] e = new TEllipse(0.2,0.2,0.8,0.3)
root[] e->Draw()

### 11.4.3 Rectangles

The class TBox defines a rectangle. It is a base class for many different higher-level graphical primitives. Its bottom left coordinates x1, y1 and its top right coordinates x2, y2, defines a box. The constructor is:

TBox(Double_t x1,Double_t y1,Double_t x2,Double_t y2)

It may be used as in:

root[] b = new TBox(0.2,0.2,0.8,0.3)
root[] b->SetFillColor(5)
root[] b->Draw()

A TWbox is a rectangle (TBox) with a border size and a border mode. The attributes of the outline line and of the fill area are described in “Graphical Objects Attributes”

### 11.4.4 Markers

A marker is a point with a fancy shape! The possible markers are shown in the next figure.

The marker constructor is:

TMarker(Double_t x,Double_t y,Int_t marker)

The parameters x and y are the marker coordinates and marker is the marker type, shown in the previous figure. Suppose the pointer ma is a valid marker. The marker size is set via ma->SetMarkerSize(size), where size is the desired size. Note, that the marker types 1, 6 and 7 (the dots) cannot be scaled. They are always drawn with the same number of pixels. SetMarkerSize does not apply on them. To have a “scalable dot” a circle shape should be used instead, for example, the marker type 20. The default marker type is 1, if SetMarkerStyle is not specified. It is the most common one to draw scatter plots.

The user interface for changing the marker color, style and size looks like shown in this picture. It takes place in the editor frame anytime the selected object inherits the class TAttMarker.

Non-symmetric symbols should be used carefully in plotting. The next two graphs show how the misleading a careless use of symbols can be. The two plots represent the same data sets but because of a bad symbol choice, the two on the top appear further apart from the next example.

A TPolyMaker is defined by an array on N points in a 2D space. At each point x[i], y[i] a marker is drawn. The list of marker types is shown in the previous paragraph. The marker attributes are managed by the class TAttMarker and are described in “Graphical Objects Attributes”. The TPolyMarker constructor is:

TPolyMarker(Int_t n,Double_t *x,Double_t *y,Option_t *option)

Where x and y are arrays of coordinates for the n points that form the poly-marker.

### 11.4.5 Curly and Wavy Lines for Feynman Diagrams

This is a peculiarity of particle physics, but we do need sometimes to draw Feynman diagrams. Our friends working in banking can skip this part. A set of classes implements curly or wavy poly-lines typically used to draw Feynman diagrams. Amplitudes and wavelengths may be specified in the constructors, via commands or interactively from context menus. These classes are TCurlyLine and TCurlyArc. These classes make use of TPolyLine by inheritance; ExecuteEvent methods are highly inspired from the methods used in TPolyLine and TArc.

The TCurlyLine constructor is:

TCurlyLine(Double_t x1, Double_t y1, Double_t x2, Double_t y2,
Double_t wavelength, Double_t amplitude)

The coordinates (x1, y1) define the starting point, (x2, y2) - the end-point. The wavelength and the amplitude are given in percent of the pad height.

The TCurlyArc constructor is:

TCurlyArc(Double_t x1, Double_t y1, Double_t rad,
Double_t phimin, Double_t phimax,
Double_t wavelength, Double_t amplitude)

The curly arc center is (x1, y1) and the radius is rad. The wavelength and the amplitude are given in percent of the line length. The parameters phimin and phimax are the starting and ending angle of the arc (given in degrees). Refer to $ROOTSYS/tutorials/graphics/feynman.C for the script that built the figure above. ### 11.4.6 Text and Latex Mathematical Expressions Text displayed in a pad may be embedded into boxes, called paves (TPaveLabel), or titles of graphs or many other objects but it can live a life of its own. All text displayed in ROOT graphics is an object of class TText. For a physicist, it will be most of the time a TLatex expression (which derives from TText). TLatex has been conceived to draw mathematical formulas or equations. Its syntax is very similar to the Latex in mathematical mode. #### 11.4.6.1 Subscripts and Superscripts Subscripts and superscripts are made with the _ and ^ commands. These commands can be combined to make complex subscript and superscript expressions. You may choose how to display subscripts and superscripts using the 2 functions SetIndiceSize(Double_t) and SetLimitIndiceSize(Int_t). Examples of what can be obtained using subscripts and superscripts:  The expression Gives The expression Gives The expression Gives x^{2y} $$x^{2y}$$ x^{y^{2}} $$x^{y^{2}}$$ x_{1}^{y_{1}} $$x_{1}^{y_{1}}$$ x_{2y} $$x_{2y}$$ x^{y_{1}} $$x^{y_{1}}$$ x_{1}^{y} $$x_{1}^{y}$$ #### 11.4.6.2 Fractions Fractions denoted by the / symbol are made in the obvious way. The #frac command is used for large fractions in displayed formula; it has two arguments: the numerator and the denominator. For example, the equation x = y + z 2 y 2 + 1 is obtained by following expression x=#frac{y+z/2}{y^{2}+1}. #### 11.4.6.3 Roots The #sqrt command produces the square ROOT of its argument; it has an optional first argument for other roots. Example: #sqrt{10} #sqrt[3]{10} #### 11.4.6.4 Delimiters You can produce three kinds of proportional delimiters. #[]{....} or “à la” Latex #left[.....#right]big square brackets #{}{....} or #left{.....#right}big curly brackets #||{....} or #left|.....#right|big absolute value symbol #(){....} or #left(.....#right)big parenthesis #### 11.4.6.5 Changing Style in Math Mode You can change the font and the text color at any moment using: #font[font-number]{...} and #color[color-number]{...} #### 11.4.6.6 Line Splitting A TLatex string may be split in two with the following command: #splitline{top}{bottom}. TAxis and TGaxis objects can take advantage of this feature. For example, the date and time could be shown in the time axis over two lines with: #splitline{21 April 2003}{14:23:56} ### 11.4.7 Greek Letters The command to produce a lowercase Greek letter is obtained by adding # to the name of the letter. For an uppercase Greek letter, just capitalize the first letter of the command name. #alpha #beta #chi #delta #varepsilon #phi #gamma #eta #iota #varphi #kappa #lambda #mu #nu #omicron #pi #theta #rho #sigma #tau #upsilon #varomega #omega #xi #psi #zeta #Alpha #Beta #Chi #Delta #Epsilon #Phi #Gamma #Eta #Iota #Kappa #vartheta #Lambda #Mu #Nu #Omicron #Pi #Theta #Rho #Sigma #Tau #Upsilon #Omega #varsigma #Xi #Psi #epsilon #varUpsilon #Zeta ### 11.4.8 Mathematical Symbols TLatex can make mathematical and other symbols. A few of them, such as + and >, are produced by typing the corresponding keyboard character. Others are obtained with the commands as shown in the table above. #### 11.4.8.1 Accents, Arrows and Bars Symbols in a formula are sometimes placed one above another. TLatex provides special commands for that. #hat{a} =hat #check =inverted hat #acute =acute #grave =accent grave #dot =derivative #ddot =double derivative #tilde =tilde #slash =special sign. Draw a slash on top of the text between brackets for example #slash{E}_{T}generates “Missing ET” a _ is obtained with #bar{a} a -> is obtained with #vec{a} #### 11.4.8.2 Example 1 The script$ROOTSYS/tutorials/graphics/latex.C:

{
TCanvas c1("c1","Latex",600,700);
TLatex l;
l.SetTextAlign(12);
l.SetTextSize(0.04);

#int^{x}_{0}cos(#frac{#pi}{2}t^{2})dt");
#int^{x}cos(#frac{#pi}{2}t^{2})dt");
l.DrawLatex(0.1,0.4,"3) R = |A|^{2} =
#frac{1}{2}(#[]{#frac{1}{2}+C(V)}^{2}+
#[]{#frac{1}{2}+S(V)}^{2})");
l.DrawLatex(0.1,0.2,"4) F(t) = #sum_{i=
-#infty}^{#infty}A(i)cos#[]{#frac{i}{t+i}}");
}

The script $ROOTSYS/tutorials/graphics/latex2.C: { TCanvas c1("c1","Latex",600,700); TLatex l; l.SetTextAlign(23); l.SetTextSize(0.1); l.DrawLatex(0.5,0.95,"e^{+}e^{-}#rightarrowZ^{0} #rightarrowI#bar{I}, q#bar{q}"); l.DrawLatex(0.5,0.75,"|#vec{a}#bullet#vec{b}|= #Sigmaa^{i}_{jk}+b^{bj}_{i}"); l.DrawLatex(0.5,0.5,"i(#partial_{#mu}#bar{#psi}#gamma^{#mu} +m#bar{#psi}=0 #Leftrightarrow(#Box+m^{2})#psi=0"); l.DrawLatex(0.5,0.3,"L_{em}=eJ^{#mu}_{em}A_{#mu} , J^{#mu}_{em}=#bar{I}#gamma_{#mu}I M^{j}_{i}=#SigmaA_{#alpha}#tau^{#alphaj}_{i}"); } #### 11.4.8.4 Example 3 The script$ROOTSYS/tutorials/graphics/latex3.C:

{
TCanvas c1("c1");
TPaveText pt(.1,.5,.9,.9);
#frac{d#sigma}{dcos#theta} (e^{+}e^{-}
#rightarrow f#bar{f} ) = ");
(1+cos^{2}#theta");
pt.AddText("+ 4 Re #left{ #frac{2}{1 - #Delta#alpha} #chi(s)
#[]{#hat{g}_{#nu}^{e}#hat{g}_{#nu}^{f}
(1 + cos^{2}#theta) + 2 #hat{g}_{a}^{e}
#hat{g}_{a}^{f} cos#theta) } #right}");
pt.SetLabel("Born equation");
pt.Draw();
}

### 11.4.9 Text in a Pad

Text displayed in a pad may be embedded into boxes, called paves, or may be drawn alone. In any case, it is recommended to use a Latex expression, which is covered in the previous paragraph. Using TLatex is valid whether the text is embedded or not. In fact, you will use Latex expressions without knowing it since it is the standard for all the embedded text. A pave is just a box with a border size and a shadow option. The options common to all types of paves and used when building those objects are the following:

option = "T" top frame

option = "B" bottom frame

option = "R" right frame

option = "L" left frame

option = "NDC" x1,y1,x2,y2 are given in NDC

option = "ARC" corners are rounded

We will see the practical use of these options in the description of the more functional objects like TPaveLabels. There are several categories of paves containing text: TPaveLabel, TPaveText and TPavesText. TPaveLabels are panels containing one line of text. They are used for labeling.

TPaveLabel(Double_t x1, Double_t y1, Double_t x2, Double_t y2,
const char *label, Option_t *option)

Where (x1, y1) are the coordinates of the bottom left corner, (x2,y2) - coordinates of the upper right corner. “label” is the text to be displayed and “option” is the drawing option, described above. By default, the border size is 5 and the option is “br”. If one wants to set the border size to some other value, one may use the method SetBorderSize(). For example, suppose we have a histogram, which limits are (-100,100) in the x direction and (0, 1000) in the y direction. The following lines will draw a label in the center of the histogram, with no border. If one wants the label position to be independent of the histogram coordinates, or user coordinates, one can use the option “NDC”. See “The Coordinate Systems of a Pad”.

root[] pl = new TPaveLabel(-50,0,50,200,"Some text")
root[] pl->SetBorderSize(0)
root[] pl->Draw()

A TPaveLabel can contain only one line of text. A TPaveText may contain several lines. This is the only difference. This picture illustrates and explains some of the points of TPaveText. Once a TPaveText is drawn, a line can be added or removed by brining up the context menu with the mouse.

A TPavesText is a stack of text panels (see TPaveText). One can set the number of stacked panels at building time. It has the following constructor: By default, the number of stacked panels is 5, option=br”.

TPavesText(Double_t x1, Double_t y1, Double_t x2, Double_t y2,
Int_t npaves, Option_t* option)

### 11.4.10 The TeX Processor TMathText

TMathText’s purpose is to write mathematical equations, exactly as TeX would do it. The syntax is the same as the TeX’s one.

You can manipulate the viewer via the GUI or via the base TGLViewer object behind the interface. These are detailed below - see also ROOTSYS/tutorials/gl/glViewerExercise.C. #### 11.13.2.1 Projections Modes (Cameras) The GL Viewer supports two basic types of camera, which affect how the 3D world is projected onto the 2D render area: • Perspective: Objects are drawn with characteristic ‘foreshortening’ effect, where distant objects appear smaller than near ones. This is useful for obtaining a ‘real world’ views. The degree of foreshortening is affected by the current camera field of view (focal length of its ‘lens’) - see “Adjusting Cameras”. • Orthographic: Distance from camera does not affect object size. These projections are useful for measurement or checking alignments, as the sizes and angles between objects are preserved. You can select the active camera from the viewer’s Camera menu on the top menu bar. There are three perspective camera choices: • Perspective (Floor XOZ) Default • Perspective (Floor YOZ) • Perspective (Floor XOY) In each case the perspective camera is constrained to keep the chosen floor plane, defined by a pair of world axes, appearing level at all times - i.e. there is no banking of the ‘horizon’ that you experience when a plane rolls. There are also three orthographic camera choices: • Orthographic (XOY) • Orthographic (XOZ) • Orthographic (ZOY) Orthographic projections are generally constrained to look down one of the global axes of the world, with the other two axes lying horizontal/vertical on the viewer window. Therefore, XOY has the X-axis horizontal, the Y-axis vertical. You can always confirm the orientation and constraints of the camera in the world by enabling axis drawing in the “Guides” tab - see sections “Guides” and “Clipping” below. For orthographic camera a ruler-depicting current scene units is also available. You can also pick the current camera by obtaining a handle to the GL Viewer object behind the interface: TGLViewer * v = (TGLViewer *)gPad->GetViewer3D(); calling the method TGLViewer::SetCurrentCamera with one of the TGLViewer::ECameraType types: v->SetCurrentCamera(TGLViewer::kCameraPerspXOZ); See alsoROOTSYS/tutorials/gl/glViewerExercise.C.

The interactions with the camera are summarized above. In each case the interaction is listed, along with description and user actions required to achieve it. For all cameras you can reset the original default view, framing the entire scene, by double clicking any mouse button.

For the Zoom interaction you can use the following modifiers combinations to adjust the sensitivity:

• Shiftx 10

• Ctrlx 0.1

• Shift + Ctrlx 0.01

The modifiers must be applied after the zoom action has started (right mouse button is down).

Note for orthographic cameras:

• There is no field of view of view/focal length - dollying and zooming producing an identical scaling action.

• There is a fixed eye direction - so the ‘Orbit’ action is disabled.

Note for perspective cameras:

• Dollying (moving the camera backwards/forwards) and zooming are often confused, and may appear very similar.

• When you dolly the camera the lens focal length does not change, hence the distortions associated with the projections are unaffected. However the movement can result in objects coming ‘through the front’ of the camera and disappearing.

• When you zoom, the camera does not move - hence clipping of near objects is unaffected. However with extremely small zooms (FOV large/focal length short) noticeable distortions, causing straight lines to become curved, can be seen with objects near the camera - the ‘fisheye’ lens effect.

• Generally dollying is more ‘natural’, but you may need to use both to achieve the desired perspective and eye position - particularly when you are working inside or very close to 3D objects.

Configure the camera by calling the methods SetPerspectiveCamera() or SetOrthographicCamera() of TGLViewer:

TGLViewer * v = (TGLViewer *)gPad->GetViewer3D();
v->SetOrthoCamera(TGLViewer::kCameraOrthoXOY,
left,right,top,bottom);
...
v->SetPerspectiveCamera (camera,fov,dolly,center,hRotate,vRotate);

Note - you can configure any of the six cameras in the viewer at any time, but you will not see the result until the camera is made current.

#### 11.13.2.3 Draw Styles

The GL Viewer supports three different rendering modes, which are applied to all the objects in your scene, but not Clip Shapes and Guides (See “Clipping” and “Manipulators”). These are shown below, along with the key used to activate the style.

Filled Polygons Wireframe Outline Enable with ‘r’ key Enable with ‘w’ key Enable with ‘t’ key Solid polygons, with hidden surface Object edges in color, with Combination of Filled Polygons removal, color surface materials, no surface filling/hiding. and Outline styles. Solid opacity, specular reflection etc. shapes with edges. Black background. Black background. White background.

Call method TGLViewer::SetStyle with one of TGLRnrCtx::EDrawStyleflags kFill, kOutline, kWireFrame:

v->SetStyle(TGLRnrCtx::kFill);

#### 11.13.2.4 Lighting / Style

The GL viewer creates five diffuse lights (left, right, top, bottom, and front) arranged around the 3D scene. These lights are carried with the camera - that is they are always in same position relative to your eye - the left light always shines from the left.

Light controls are located: Viewer Controls Pane ‘Style’.

Each light has a checkbox to enable/disable it. Set lights on/off with TGLLightSet::SetLight e.g.

v->GetLightSet()->SetLight(TGLLightSet::kLightBottom, kFALSE);

#### 11.13.2.5 Clipping

The GL viewer supports interactive clipping, enabling you to remove sections of your 3D scene and the shapes, revealing internal details.

The controls for clipping can be found under: Viewer Controls Pane ‘Clipping’ tab.

Two clipping ‘shapes’ are currently supported:

• Single plane

• Box

Pick the type from the radio buttons - only one (or none) may be active at one time.

The clip object can be adjusted by:

• Adjusting the values in the properties panel GUI

• Directly manipulating the clip object in the viewer

To show and/or directly manipulate the object check the ‘Show / Edit in Viewer’ checkbox. The clip object is drawn in semi-transparent light brown. The current manipulator is attached to it, allowing you direct control over its position, scale and rotation. See “Manipulators” section below for details on using viewer manipulators.

The clip plane is described by the standard plane equation: ax+by+cz+d=0, where the factors a, b, c, d are entered into the edit boxes, and applied using the ‘Apply’ button.

The clip box is described by its center position, entered in the ‘Center X’, ‘Center Y’ and ‘Center Z’ edit boxes, and its lengths (extents) entered in the ‘Length X’, ‘Length Y’ and ‘Length Z’ edit boxes.

This clipping is achieved using OpenGL clip plane support; as such, there are certain limitations:

• Solid shapes are not capped - they appear hollow.

• Only shapes, which can be described with combination of planes, can be rendered in this fashion - e.g. a clipping tube is not possible.

• Each additional clipping plane requires an additional render pass - so the more active planes the more time the render will take.

Set the current clip object with TGLClipSet::SetClipType

v->GetClipSet()->SetClipType(TGLClipSet::kClipPlane);

Configure the clip object with TGLClipSet::SetClipState

Double_t planeEq[4] = {0.5,1.0,-1.0, 2.0};
v->GetClipSet()->SetClipState(TGLClipSet::kClipPlane, planeEq);

As with cameras, any clip can be configured at any time, but you must set the clip current to see the effect.

#### 11.13.2.6 Manipulators

Manipulators are GUI ‘widgets’ or controls attached to a 3D object in the viewer, allowing a direct manipulation of the object’s geometry. There are three manipulators for the three basic geometries transformations. In each case, the manipulator consists of three components, one for each local axis of the object, shown in standard colors: red (X), green (Y) and blue (Z).

Activate the manipulator by moving the mouse over one of these components (which turns yellow to indicate active state). Click with left mouse and drag this active component to perform the manipulation. Toggle between the manipulator types using the ‘x’, ‘c’, ‘v’ keys while the mouse cursoris above the manipulator. Note: Manipulators cannot be controlled via the API at present.

#### 11.13.2.7 Guides

Guides are visual aids drawn into the viewer world. Controls for these are under the “Guides” tab:

Viewer Controls Pane Guides Tab

Axes show the world (global) frame coordinatedirections: X (red), Y (green) and Z (blue). The negative portion of the axis line is shown in dark color, the positive in bright. The axis name and minimum / maximum values are labeled in the same color. There are three options for axes drawing - selected by radio buttons:

• None - not drawn (default).

• Edge - draw axes on the (minimum) edge of the scene extents box.

• Origin - drawn axes through the origin.

For edge axes, the zero value for each axis is marked on the axis line with a colored sphere. For origin axes, a single white sphere is shown at the origin.

Edge axes are depth clipped - i.e. are obscured by 3D objects in front of them. Origin axes (which generally pass through the middle of the 3D scene) are not depth clipped - so always visible.

A single orange sphere of fixed view port (window) size can be shown at any arbitrary position. Enable / disable the drawing with ‘Show’ checkbox. Enter X/Y/Z position in the edit boxes to set position. Initial position is at the center of the scene.

Set the guides using TGLViewer::SetGuideState e.g. to enable edge axes, and enable a reference marker at world position 50, 60, 100:

Double_t refPos[3] = {50.0,60.0,100.0};
v->SetGuideState(TGLUtil::kAxesEdge, kTRUE, refPos);

#### 11.13.2.8 Selecting Scene Shapes

You can select a single shape from your scene by pressing ‘Shift’ key, pointing and left clicking anywhere on the shape in the viewer. Selection is currently shown by drawing the shape-bounding box (not depth clipped) in white (polygon or wire frame render styles) or red (outline render style). Manipulators supported by the shape are drawn in red, green and blue while the non-supported ones are drawn in grey. To deselect a shape, either select another, or shift/click anywhere on the background (empty space) in the viewer. You cannot select Manipulators or Guides (Axes / Reference Marker).

#### 11.13.2.9 Editing Shapes

When a shape is selected, the viewer’s control pane shows the user interface that allows you to review and adjust the color and geometry properties of the shape.

Note: At present modifications to the shapes are local to the viewer - they are not propagated back to external objects/client that published to the viewer. The changes are preserved only until the viewer is closed. In some cases, this will never be feasible as there is not a one-to-one correspondence between a shape in the viewer and a single external object in which the modification could be stored.

#### 11.13.2.10 Colors / Style

Viewer Controls Pane ‘Style’ tab.

A full description of OpenGL materials, colors and lighting is beyond the scope of this document. You should refer to the OpenGL programming manual (Red Book) for a full discussion. In most cases adjustment of the Diffuse color material + Opacity/Shine properties is sufficient to achieve desired results.

A shape has four-color materials (components):

• Diffuse

• Ambient

• Specular

• Emissive

For each of these you can select the component via the radio buttons. Each component can have the red, green and blue values for the component adjusted via the sliders. You can apply this adjustment to the shape itself, or to all shapes sharing a common ‘family’. Shapes of the same family have external objects with the same TObject name string. You can also adjust the ‘Opacity’ and ‘Shine’ for the shapes materials via the sliders.

#### 11.13.2.11 Geometry

Viewer Controls Pane ‘Geometry’ tab.

Review and modify the shapes X/Y/Z center and scaling factors via the edit boxes. Selection and editing of shapes is not available via the API at present.

#### 11.13.2.12 Outputting Viewer Contents

The current viewer rendering can be output to an external EPS or PDF, using the options under the ‘File’ menu on the top menu bar. The file is named ‘viewer.eps’ or ‘viewer.pdf’ and written to the current ROOT directory.

### 11.13.3 The X3D Viewer

The X3D viewer is a fairly simple and limited viewer, capable of showing basic lines and polygons. It lacks the quality, performance and more advanced features of the GL Viewer, and additionally is not supported on Windows. It is not actively developed and you are encouraged to use the GL Viewer out of preference. The below table presents the main interactions - these are repeated in the Help dialog of the viewer.

Action KeyActionKey

Wireframe Mode wRotate about xx a

Hidden Line Mode eRotate about yy b

Hidden Surface Mode rRotate about zz c

Move object down uAuto-rotate about x1 2 3

Move object up iAuto-rotate about y4 5 6

Move object left lAuto-rotate about z7 8 9

Move object right hToggle controls styleo

Move object forward jToggle stereo displays

Move object backward kToggle blue stereo viewd

Adjust focus (stereo mode) [ ] { }Toggle double bufferf

Rotate object Left mouse button down + move.

### 11.13.4 Common 3D Viewer Architecture

The 3D Viewer Architecture provides a common mechanism for viewer clients to publish 3D objects to it. It enables:

• Decoupling of producers (geometry packages etc) who model collection of 3D objects from consumers (viewers) which display them.

• Producer code free of explicit drawing commands & viewer specific branching.

• Support differing viewers and clients capabilities, e.g.

• Mix of native (in viewer) shapes and generic client side tessellation.

• Local/global frame object description

• Bounding boxes

• Placing copies sharing common geometry (logical/physical shapes).

The architecture consists of:

• TVirtualViewer3D interface: An abstract handle to the viewer, allowing client to add objects, test preferences etc.

• TBuffer3D class hierarchy: Used to describe 3D objects (“shapes”) - filled /added by negotiation with viewer via TVirtualViewer3D.

A typical interaction between viewer and client using these, taken from TGeoPainter is:

// Does viewer prefer local frame positions?
Bool_t localFrame = viewer->PreferLocalFrame();
// Perform first fetch of buffer from the shape and try adding it to the viewer
const TBuffer3D &buffer = shape.GetBuffer3D(TBuffer3D::kCore |
TBuffer3D::kBoundingBox |
TBuffer3D::kShapeSpecific,
localFrame);

// If the viewer requires additional sections fetch from the shape
// (if possible) and add again
if (reqSections != TBuffer3D::kNone)
shape.GetBuffer3D(reqSections, localFrame);

Together these allow clients to publish objects to any one of the 3D viewers free of viewer specific drawing code. They allow our simple x3d viewer, and considerably more sophisticated OpenGL one to both work with both geometry libraries (g3d and geom) efficiently.

In addition to external viewers, created in separate windows, this architecture is also used by internal TPad drawing when it requires 3D projections. Publishing to a viewer consists of the following steps:

1- Create / obtain viewer handle.

2- Begin scene on viewer.

3- Fill mandatory parts of TBuffer3D describing object.

5- Fill optional parts of TBuffer3D as requested by viewer.

[ …. repeat 3/4/5 as required for other/child objects]

6- End scene on viewer.

You should attach the top-level node of your external geometry (or the manager) to a TPad object using TObject::Draw(), and perform the publishing to the viewer in your object’s TObject::Paint() overloaded method. See “Scene Rebuilds”, and example scripts, for more details.

#### 11.13.4.1 Creating / Obtaining Viewer Handle

External viewers are bound to a TPad object (this may be removed as a requirement in the future). You can create or obtain the current viewer handle via the method:

Here the “type” string defines the viewer type - currently one of:

• ogl” : External GL viewer

• x3d”: External X3D viewer

If no type is passed (null string), and there is no current viewer, then the type is defaulted to “pad”. If no type is passed and there is a current viewer, then this is returned - hence once a viewer is created it can be obtained elsewhere by:

#### 11.13.4.2 Opening / Closing Scenes

Objects must be added to viewer between BeginScene() and EndScene() calls e.g.

viewer->BeginScene();
viewer ->EndScene();

These calls enable the viewer to suspend redraws, and perform internal caching/setup. If the object you attach to the pad derives from TAtt3D, then the pad will take responsibility for calling BeginScene() and EndScene() for you. You can always test if the scene is already open for object addition with:

viewer->BuildingScene();

Note: the x3d viewer does not support rebuilding of scenes - objects added after the first Open/Close Scene pair will be ignored.

#### 11.13.4.3 Describing Objects - Filling TBuffer3D

The viewers behind the TVirtualViewer3D interface differ greatly in their capabilities e.g.

• Some support native shape (e.g. spheres/tubes in OpenGL) and can draw these based on an abstract description. Others always require a tessellation description based on TBuffer3D’s kRaw / kRawSizes points/lines/segments sections.

• Some need the 3D object positions in the master (world) frame, others can cope with local frames and a translation matrix to place the object.

• Some require bounding boxes for objects - others do not.

Similarly some viewer clients are only capable of providing positions in master frame, cannot provide bounding boxes etc. Additionally we do not want to incur the cost of expensive tessellation operations if the viewer does not require them. To cope with these variations the TBuffer3D objects are filled by negotiation with the viewer.

TBuffer3D classes are conceptually divided into enumerated sections: kCore, kBoundingBox, kRaw - see the class diagram and the file TBuffer3D.h for more details. The TBuffer3D methods SectionsValid(), SetSectionsValid(), ClearSectionsValid() are used to test, set, clear these section validity flags e.g.

buffer.SetSectionsValid(TBuffer3D::kShapeSpecific);
...
if (buffer.SectionsValid(TBuffer3D:: kShapeSpecific)) {
...
}

The sections found in the base TBuffer3D (kCore/kBoundingBox/kRawSizes/kRaw) are sufficient to describe any tessellated shape in a generic fashion. An additional kShapeSpecific section is added in TBuffer3D derived classes, allowing a more abstract shape description (“a sphere of inner radius x, outer radius y”). This enables a viewer, which knows how to draw (tessellate) the shape itself to do so, while providing a generic fallback suitable for all viewers. The rules for client negotiation with the viewer are:

• If suitable specialized TBuffer3D class exists, use it, otherwise use TBuffer3D.

• Complete the mandatory kCore section.

• Complete the kShapeSpecific section if applicable.

• Complete the kBoundingBox if you can.

• Pass this buffer to the viewer using one of the TBuffer3D::AddObject() methods.

If the viewer requires more sections to be completed (kRaw/kRawSizes) TBuffer3D::AddObject() will return flags indicating which ones, otherwise it returns kNone. If requested, you must fill the buffer, mark these sections valid, and call TBuffer3D::AddObject again, to complete adding the object. For example, in out TGeo geometry package, in TGeoPainter::PaintShape, we perform the negotiation with viewer:

if (shape.IsA() != TGeoCompositeShape::Class()) {
// Does viewer prefer local frame positions?
Bool_t localFrame = viewer->PreferLocalFrame();
// Perform first fetch of buffer from the shape and adding
// it to the viewer
const TBuffer3D &buffer = shape.GetBuffer3D(TBuffer3D::kCore |
TBuffer3D::kBoundingBox |
TBuffer3D::kShapeSpecific, localFrame);
// If the viewer requires additional sections fetch from the
// shape (if possible) and add again
if (reqSections != TBuffer3D::kNone) {
shape.GetBuffer3D(reqSections, localFrame);
}
}

The buffer is supplied/filled by the appropriate TShape::GetBuffer3D() and TShape::FillBuffer3D overloads e.g. for a sphere in TGeoSphere.

const TBuffer3D &TGeoSphere::GetBuffer3D(Int_t reqSections,
Bool_t localFrame) const {
// Fills a static 3D buffer and returns a reference.
static TBuffer3DSphere buffer;
// Filling of kBoundingBox is defered to TGeoBBox, and
// kCore on up to TGeoShape
TGeoBBox::FillBuffer3D(buffer, reqSections, localFrame);
// Complete kShapeSpecific section for sphere
if (reqSections & TBuffer3D::kShapeSpecific) {
...
buffer.SetSectionsValid(TBuffer3D::kShapeSpecific);
}
// Complete kRawSizes section
if (reqSections & TBuffer3D::kRawSizes) {
...
buffer.SetSectionsValid(TBuffer3D::kRawSizes);
}
}
// Complete kRaw tesselation section
if ((reqSections & TBuffer3D::kRaw) &&
buffer.SectionsValid(TBuffer3D::kRawSizes)) {
SetPoints(buffer.fPnts);
// Transform points to master frame if viewer requires it
// The fLocalFrame flag and translation matrix will have
// already been set in TGeoShape::FillBuffer3D() as required
if (!buffer.fLocalFrame)
TransformPoints(buffer.fPnts, buffer.NbPnts());
SetSegsAndPols(buffer);
buffer.SetSectionsValid(TBuffer3D::kRaw);
}
return buffer;
}

Note:

• we use a static TBuffer3D derived object for efficiency - once the object is added the buffer can be reused.

• kRawSize (the calculation of tessellation sizing required in buffer) and kRaw (the actual filling of tessellation) is split, as the X3D viewer requires two publication passes - one to establish the full tessellation capacity for all shapes, and another to actually add them. Splitting avoids having to do the expensive tessellation on the first pass.

#### 11.13.4.4 Shape Specific TBuffer3D Derived Classes

Currently we provide the following shape specific classes, which the GL Viewer can take advantage of (see TBuffer3D.h and TBuffer3DTypes.h)

• TBuffer3DSphere - solid, hollow and cut spheres (GL Viewer only supports solid spheres at present - cut / hollow ones will be requested as tessellated objects by client.)

• TBuffer3DTube - basic tube with inner/outer radius and length.

• TBuffer3DTubeSeg - angle tube segment.

• TBuffer3DCutTube - angle tube segment with plane cut ends.

See the above example from TGeoSphere::GetBuffer3D and also equivalent functions in TGeoTube, TGeoTubeSeg and TGeoCtub. Anyone is free to add new TBuffer3D classes, but it should be clear that one or more viewers will require updating to be able to take advantage of them. Hence we only provide classes which existing viewers can benefit from. The number of native shapes in GL Viewer will be expanded in the future.

#### 11.13.4.5 Master / Local Reference Frames

The Core section of TBuffer3D contains two members relating to reference frames:

• fLocalFrame: indicates if any positions in the buffer (bounding box and tessellation vertexes) are in local or master (world frame).

• fLocalMaster: is a standard 4x4 translation matrix (OpenGL column major ordering) for placing the object into the 3D master frame.

If fLocalFrame is false, fLocalMaster should contain an identity matrix. This is set by default, and can be reset using the TBuffer3D::SetLocalMasterIdentity() method.

#### 11.13.4.6 Bounding Boxes

You are not obliged to complete the kBoundingBox section, as any viewer requiring one internally (GL Viewer) will build it if you do not provide. However to do this the viewer will force you to provide the (expensive) raw tessellation, and the resulting box will be axis aligned with the overall scene, which is non-ideal for rotated shapes. As we need to support orientated (rotated) bounding boxes, TBuffer3D requires the 6 vertices of the box. We also provide a convenience function, TBuffer::SetAABoundingBox(), for simpler case of setting an axis aligned bounding box. The bounding box should be filled in same frame (local / master) as the rest of the TBuffer3D, and inaccordance with fLocalFrame flag.

A typical example from TGeoBBox::FillBuffer3D:

if (reqSections & TBuffer3D::kBoundingBox) {
Double_t halfLengths[3] = { fDX, fDY, fDZ };
buffer.SetAABoundingBox(fOrigin, halfLengths);
if (!buffer.fLocalFrame) {
TransformPoints(buffer.fBBVertex[0], 8);
}
buffer.SetSectionsValid(TBuffer3D::kBoundingBox);
}

#### 11.13.4.7 Logical and Physical Objects

Some viewers can support two types of object placement:

• Add object as a single independent entity in the world reference frame - e.g. a sphere, radius r, at x, y, z.

• Repeated placement (copying) in world frame of this locally unique piece of geometry (described in local reference frame) e.g. define a sphere S (radius r), place copy at x1, y1, z1, another copy at x2, y2, z2 etc.

The second case is very typical in geometry packages, e.g. ROOT’s TGeo package, GEANT4 etc, where we have very large number repeated placements of relatively few unique “shapes”.

Some viewers (GL Viewer only at present) are able to take advantage of this by identifying unique logical shapes from the fID logical ID member of TBuffer3D. If repeated addition of the same fID is found, the shape is cached already - and the costly tessellation does not need to be sent again. The viewer can also perform internal GL specific caching (display lists) with considerable performance gains in these cases. For this to work correctly the logical object in must be described in TBuffer3D in the local reference frame, complete with the local/master translation. In some cases you will not have a real object you can reasonably set TBuffer3D::fID to, or the object is recycled or temporary. To suppress internal caching in the GL Viewer in these cases, set TBuffer3D::fID to 0 (null).

The viewer indicates it can support local frame objects through the TVirtualViewer3D interface method: PreferLocalFrame(). If this returns kTRUE you can make repeated calls to AddObject(), with TBuffer3D containing the same fID, and different fLocalMaster placements.

For viewers supporting logical/physical objects, the TBuffer3D content refers to the properties of the logical object, with the exception of:

• fLocalMaster transform

• fColor

• fTransparency

attributes, which can be varied for each physical object.

As a minimum requirement all clients must be capable of filling the raw tessellation of the object buffer, in the master reference frame. Conversely viewers must always be capable of displaying the object described by this buffer. If either does not meet this requirement the object may not be displayed.

#### 11.13.4.8 Scene Rebuilds

TBuffer3D::AddObject is not an explicit command to the viewer - it may for various reasons decide to ignore it:

• It already has the object internally cached.

• The object falls outside some ‘interest’ limits of the viewer camera.

• The object is too small to be worth drawing.

In all these cases TBuffer3D::AddObject() returns kNone, as it does for successful addition, indicating it does not require further information about this object. Hence you should not try to make any assumptions about what the viewer did with the object. The viewer may decide to force the client to rebuild (republish) the scene, obtaining a different collection of objects, if the internal viewer state changes .e.g. significant camera move. It does this presently by forcing a repaint on the attached TPad object - hence you should attach you master geometry object to the pad (via TObject::Draw()), and perform the publishing to the viewer in response to TObject::Paint().

#### 11.13.4.9 Physical IDs

TVirtualViewer3D provides for two methods of object addition:

const TBuffer3D & buffer,

If you use the first (simple) case a viewer using logical/physical pairs will generate sequential IDs for each physical object internally. Scene rebuilds will require destruction and recreation of all physical objects. For the second you can specify an identifier from the client side, which must be unique and stable - i.e. the IDs of a published object is consistent, regardless of changes in termination of contained child geometry branches. In this case the viewer can safely cache the physical objects across scene rebuilds, discarding those no longer of interest.

#### 11.13.4.10 Child Objects

In many geometries there is a rigid containment hierarchy, and so if the viewer is not interested in a certain object due to limits/size then it will also not be interest in any of the contained branch of siblings. Both TBuffer3D::AddObject() methods have an addChildren return parameter. The viewer will complete this (if passed) indicating if children of the object just sent are worth sending.

#### 11.13.4.11 Recycling TBuffer3D

Once add TBuffer3D::AddObject() has been called, the contents are copied to the viewer’s internal data structures. You are free to destroy this TBuffer3D, or recycle it for the next object if suitable.

#### 11.13.4.12 Examples

For an example of a simple geometry, working in master reference frame examine the code under $ROOTSYS/g3d. For a more complex example, which works in both master and local frames, and uses logical/physical division of shape geometry and placement, examine the code under$ROOTSYS/geom - in particular TGeoShape hierarchy, and the painter object TGeoPainter (under geopainter) where the negotiation with the viewer is performed.