TMarker Disappearing

Hello all,

I have a query regarding the disappearance of TMarkers I have plotted. I have created a class for some of my work called Skymap, which contains various plotting and output routines using Root classes. However, I am a bit puzzled as to the behaviour of TMarkers.

My class is defined by the following (I’ve cut it right down so only the basics are here):

[code]class Skymap {
public:

Skymap();
~Skymap();

void PlotSinglePoint(const float&,const float&);
void Print(const std::string&);

void DrawAll();

private:
TCanvas mCanvas;
std::vector mMarkers;
}; [/code]

with the source as follows:

[code]Skymap::Skymap()
: mCanvas(“c1”,"",0,0,800,800)
{
mCanvas.Range(-30,-30,30,30);
}

Skymap::~Skymap()
{
}

void Skymap::Print(const std::string& filepth)
{
mCanvas.Print(filepth.c_str());
}

void Skymap::PlotSinglePoint(const float& x,const float& y)
{
mMarkers.push_back(TMarker(x,y,7));
mMarkers.back().Draw();
}[/code]

Now, if I repeatedly call PlotSinglePoint, only the marker corresponding to the most recent function call is shown - all others are invisible upon printing the canvas to a file. If, however, I remove the Draw() command from PlotSinglePoint and, instead, have a function that draws all the TMarkers contained in mMarkers at once:

void Skymap::DrawAll() { for(std::vector<TMarker>::iterator vIt=mMarkers.begin();vIt!=mMarkers.end();++vIt){ vIt->Draw(); } }

all of the TMarkers in mMarkers are present in the image I save.

Can someone please explain why this happens? I’m guessing it’s something to do with the TMarkers going out of scsope and being deleted from mCanvas, but my C++ is not good enough to tell me where that is occurring.

Thanks in advance for any help.

May be something to do with the fact that in one case you are not using pointers and that you are in the other…

I had wondered the same thing myself. However, if I define the DrawAll() function as:

for(int i=0;i<mMarkers.size();++i) { mMarkers[i].Draw(); }

then the same thing happens. As far as I can tell, the only difference is drawing through repeated calls to the same function compared to drawing all TMarkers in the same function through successive calls to Draw(). I don’t understand why that would cause this behaviour.

EDIT: If anyone would like to test this out, here’s the code for the main function:


Just comment out both PlotSinglePoint() function calls OR the DrawAll() function call to see the different behaviour.
#include <cstdlib>
#include <iostream>

#include "Skymap.h"

int main(){
  Skymap test;
  float x1=-10.,y1=15.;
  test.PlotSinglePoint(x1,y1);

  float x2=10.,y2=20.;
  test.PlotSinglePoint(x2,y2);

  test.DrawAll();
  test.Print("test.eps");
}

can you make a self contained small file we can run in CINT ?
that would help to investigate.

I have attached Skymap.C, which exhibits the behaviour. If you run it as is, you should find that only the most recent TMarker is shown in the image that is created. Commenting out the Draw command in the PlotSinglePoint function (I’ve pointed out where it is) and uncommenting the DrawAll function call in the main section of the code should give you an image with both dots displayed.

Thanks for your assistance.
Skymap.C (744 Bytes)

I made it work this way:

#include <vector>

class Skymap {
 public:

  Skymap()
  {
    mCanvas = new TCanvas("c1","",0,0,800,800);
    mCanvas->Range(-30,-30,30,30);
  }

  void PlotSinglePoint(const float& x,const float& y)
  {
    mMarkers.push_back(TMarker(x,y,7));
    mMarkers.back().DrawMarker(x,y); //Comment this out before using DrawAll()
    mCanvas->ls();
  }
  
  void Print(const std::string& filepth)
  {
    mCanvas->Print(filepth.c_str());
  }
   
  void DrawAll()
  {
    for(int i=0;i<mMarkers.size();++i){
      mMarkers[i].Draw();
    }
  }


 private:
  TCanvas *mCanvas;
  std::vector<TMarker> mMarkers;
};

void Skymap(){
  Skymap test;
  float x1=-10.,y1=15.;
  test.PlotSinglePoint(x1,y1);

  float x2=10.,y2=20.;
  test.PlotSinglePoint(x2,y2);

  //test.DrawAll();
  test.Print("test.pdf");
}

Thanks very much for that! The crucial part seems to be changing Draw() to DrawMarker() - if I make only that change, I still manage to get all of the markers appearing.

I guess it has something to do with the details of how the two functions update the canvas or store objects?

Again, thanks for your help!

Yes you are right the other changes were just some tries I did but they are not crucial.
In the version you had before you were drawing always the same marker. Therefore the canvas was cleaned
each time you redraw it. DrawMarker create a new instance of the marker in the current canvas therefore it is not clean up each time it is drawn as it is a new marker in the canvas . ls() shows you the list of objects in the canvas

I still do not quite understand why, in the original code, I was drawing the same marker. Each time a new point was desired, the vector mMarkers was increased to contain a new TMarker which was then drawn. This Draw command was being called on a different TMarker object to the previous time the function call was made.

I’m sorry for my ignorance - I’m sure I’m missing something simple, but it is not clear to me! (And this may help me avoid making similar mistakes in the future).

Yes… you are right … I do not have the answer yet… I was just looking at TCanvas::ls() and saw that only the last marker was there. May be a side effect of std vector ?

EDIT: My mistake. That seems quite strange that the canvas only recognises a single TMarker

In your “Skymap.C”, replace:
mMarkers.back().Draw();
with:
(new TMarker(mMarkers.back()))->Draw();

In Olivier’s code, replace:
mMarkers[i].Draw()
with:
(new TMarker(mMarkers[i]))->Draw();

I hope it’s clear why.

Thanks, I would normally have gone with something like your suggestion. However, I’m trying to avoid the use of pointers and dynamically allocated memory within my class in order to reduce the potential for memory leaks etc. (as the class will be used as a part of some more extensive code).

However, I must admit that I do not clearly understand the reason for pointers working when the objects themselves don’t. Using the new command creates a set of pointers to distinct TMarker objects, for which the Draw() command is invoked, which works differently to invoking the Draw() command on the objects directly.

I still fail to see why the latter does not work.

The line:
mMarkers.push_back(TMarker(x,y,7));
increases the vector size by one, which triggers a reallocation of the internal allocated storage (if the vector size was equal to the vector capacity before the call, as is the case here). Reallocations invalidate all previously obtained references and pointers (and iterators).
That means that the references / pointers to ALL previously drawn TMarker objects are invalidated (the “old” objects will be deleted, I think) -> as a result they disappear from the TCanvas -> so, you will ONLY get the very last added “mMarkers.back()” object drawn correctly by your “PlotSinglePoint” method -> hence, you always see just a single TMarker.
So, you need to make a “new TMarker” copy of each TMarker object in your vector in your “PlotSinglePoint” method -> addresses of these copies will never change.
When you use the “DrawAll” method, you do not resize your vector any more, so all references and pointers are correctly passed to your TCanvas by the “Draw” method.

So why did I say you should modify Olivier’s “DrawAll” method?
Well, you do not draw the TCanvas in your “Skymap.C” on the screen, you just save it in a file.
Olivier, however, draws the TCanvas on the screen. The problem is then that as soon as the “Skymap()” function finishes, the “Skymap test;” object gets destroyed and so the “mMarkers” vector disappears, too … but the TCanvas stays displayed … just all TMarker objects are gone.
Again, the solution is to make a “new TMarker” copy of each TMarker object in your vector -> these copies will not be deleted -> you will see them in the TCanvas on the screen afterwards.

Thanks, I really appreciate the detailed explanation. That makes a lot of sense, as I hadn’t considered what was happening at each call to push_back().

Once again, thanks a lot for the help from both of you. That will help me avoid similar mistakes in the future too!

Actually, you could change your class to use:
std::vector<TMarker*> mMarkers;
and then in the “PlotSinglePoint”:
mMarkers.push_back(new TMarker(x,y,7));
mMarkers.back()->Draw();
and in the “DrawAll”:
mMarkers[i]->Draw();
Well, you might need to add a destructor, though. Maybe something like:

~Skymap() { mCanvas.Clear(); // in Skymap.C ... "delete mCanvas;" in Olivier's code for (std::vector<TMarker*>::iterator vIt = mMarkers.begin(); vIt < mMarkers.end(); vIt++) delete (*vIt); }