//____________________________________________________________________ // // $Id: Task.cxx,v 1.8 2006-03-22 13:56:17 cholm Exp $ // // A class for grabbing data from scanned graphs // Copyright (C) 2006 Christian Holm Christensen // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License // as published by the Free Software Foundation; either version 2.1 // of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free // Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA // 02111-1307 USA // // This code was heavily inspired by G3Data available from // http://www.frantz.fi/software/g3data.php. The copyright of G3Data // follows because some parts of this code is more or less lifted // directly from there: // // Copyright (C) 2000 Jonas Frantz // // This file is part of g3data. // // g3data is free software; you can redistribute it and/or modify it // under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // g3data is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA // 02111-1307 USA // // /** @file R3Data.C @author Christian Holm Christensen @date Tue Jul 4 01:48:02 2006 @brief Class to lift data points off an image. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; //==================================================================== /** Specialisation of TASImage to facilitate mapping pad coordinates to image coordinates. */ struct R3Image : public TASImage { /** Constructor */ R3Image(const char* filename) : TASImage(filename) {} /** Connstructor */ R3Image(Int_t w, Int_t h) : TASImage(w, h) {} /** Copy an area of the image into @a dst @param dst Destination image @param xsrc X coordinate to start from in this image @param ysrc Y coordinate to start from in this image @param w Width of area to copy @param h Height of area to copy @param xdst X coordinate in @a dst to copy to @param ydst Y coordinate in @a dst to copy to @param gfunc Graphics function. @param chan Colour channel */ void CopyArea(TImage* dst, Int_t xsrc, Int_t ysrc, UInt_t w, UInt_t h, Int_t xdst = 0, Int_t ydst = 0, Int_t gfunc = 3, TImage::EColorChan chan = TImage::kAllChan) { GetArgbArray(); TASImage* adst = ((TASImage*)dst); adst->GetArgbArray(); TASImage::CopyArea(dst, xsrc, ysrc, w, h, xdst, ydst, gfunc, chan); } /** Convert pad coordinates to image coordinates @param px Pad X coordinate @param py Pad Y coordinate @param ix On return, the image X coordinate @param iy On return, the image Y coordinate */ void PadToImage(Int_t px, Int_t py, Int_t& ix, Int_t& iy) { Int_t w = (GetScaledImage() ? GetScaledWidth() : GetWidth()); Int_t h = (GetScaledImage() ? GetScaledHeight() : GetHeight()); Double_t xfact = (GetScaledImage() ? Double_t(w) / fZoomWidth : 1); Double_t yfact = (GetScaledImage() ? Double_t(h) / fZoomHeight : 1); ix = px - gPad->XtoAbsPixel(0); //iy = py - gPad->YtoAbsPixel(1); iy = gPad->YtoAbsPixel(1) - py; iy = h - iy; ix = Int_t(ix / xfact) + fZoomOffX; iy = Int_t(iy / yfact) + fZoomOffY; } /** Execute an event */ void ExecuteEvent(Int_t event, Int_t px, Int_t py) { // Execute mouse events. gPad->SetCursor(kCross); if (!IsEditable()) return; gPad->ExecuteEvent(event, px, py); } ClassDef(R3Image,0); }; //==================================================================== struct R3Store { Int_t fXpixel; Int_t fYpixel; Double_t fXvalue; Double_t fYvalue; TString fXpstr; TString fYpstr; TString fXvstr; TString fYvstr; R3Store(Int_t px=0, Int_t py=0, Double_t x=0, Double_t y=0) : fXpixel(px), fYpixel(py), fXvalue(x), fYvalue(y) {} void SetPixel(Int_t px=0, Int_t py=0) { fXpixel = px; fYpixel = py; fXpstr = Form("%d", px); fYpstr = Form("%d", py); } void SetValue(Double_t x=0, Double_t y=0) { fXvalue = x; fYvalue = y; fXvstr = Form("%g", x); fYvstr = Form("%g", y); } void ClearPixel() { fXpixel = -1; fYpixel = -1; fXpstr = "n/a"; fYpstr = "n/a"; ClearValue(); } void ClearValue() { fXvalue = 0; fYvalue = 0; fXvstr = "n/a"; fYvstr = "n/a"; } }; //==================================================================== /** A specialised marker that has a reference to a List view entry. */ struct R3Point : public TMarker { /** Constructor @param c Container to add to @param n Number */ R3Point(TGLVContainer& c, Int_t n) : TMarker(0,0,25), fPos(0,0), fEX1(0,0), fEX2(0,0), fEY1(0,0), fEY2(0,0), fName(Form("%d", n)), fContainer(c) { SetNDC(); SetMarkerColor(2); SetMarkerSize(0.6); fEntry = new TGLVEntry(&c, fName, fName); fEntry->SetUserData(this); c.AddItem(fEntry); fEX.SetBit(TLine::kLineNDC); fEX.SetLineColor(2); fEY.SetBit(TLine::kLineNDC); fEY.SetLineColor(2); } /** Destructor */ virtual ~R3Point() { gPad->RecursiveRemove(&fEX); gPad->RecursiveRemove(&fEY); fContainer.RemoveItemWithData(this); fContainer.UnSelectAll(); UpdateLV(); } /** Get the name of the marker (number) */ const char* GetName() const { return fName.Data(); } /* MENU */ /** Set the pixel values, and update display @param x X pixel coordinate @param y Y pixel coordinate */ void SetPixels(Int_t x, Int_t y) /* MENU */ { fPos.SetPixel(x, y); fPos.ClearValue(); fEX1.ClearPixel(); fEX2.ClearPixel(); fEY1.ClearPixel(); fEY2.ClearPixel(); gPad->RecursiveRemove(&fEX); gPad->RecursiveRemove(&fEY); UpdateCols(); UpdateLV(); SetX(Double_t(x) / gPad->GetWw()); SetY(1 - Double_t(y) / gPad->GetWh()); Draw(); gPad->Modified(); gPad->Update(); gPad->cd(); } void SetValues(Double_t x, Double_t y) { fPos.SetValue(x, y); UpdateCols(); UpdateLV(); } Bool_t HasXError() const { return (fEX1.fXpixel != fEX2.fXpixel || fEX1.fYpixel != fEX2.fYpixel); } Bool_t HasYError() const { return (fEY1.fXpixel != fEY2.fXpixel || fEY1.fYpixel != fEY2.fYpixel); } /** Update list view entry */ void Update() { if (HasXError()) { fEX.SetX1(double(fEX1.fXpixel) / gPad->GetWw()); fEX.SetX2(double(fEX2.fXpixel) / gPad->GetWw()); fEX.SetY1(1 - double(fEX1.fYpixel) / gPad->GetWh()); fEX.SetY2(1 - double(fEX2.fYpixel) / gPad->GetWh()); fEX.Draw(); } if (HasYError()) { fEY.SetX1(double(fEY1.fXpixel) / gPad->GetWw()); fEY.SetX2(double(fEY2.fXpixel) / gPad->GetWw()); fEY.SetY1(1 - double(fEY1.fYpixel) / gPad->GetWh()); fEY.SetY2(1 - double(fEY2.fYpixel) / gPad->GetWh()); fEY.Draw(); } if (HasXError() || HasYError()) { gPad->Modified(); gPad->Update(); gPad->cd(); } UpdateCols(); UpdateLV(); } /** Clear this point - that is, delete it. @param option Passed on to Delete */ void Clear(Option_t* option="") { Delete(option); } /** Position */ R3Store fPos; /** Lower X error */ R3Store fEX1; /** Upper X error */ R3Store fEX2; /** Lower X error */ R3Store fEY1; /** Upper X error */ R3Store fEY2; /** Graph X coordinate precision */ Double_t fXerr; /** Graph Y coordinate precision */ Double_t fYerr; protected: /** Update the volumns */ void UpdateCols() { fEntry->SetSubnames(fPos.fXpstr.Data(), fPos.fYpstr.Data(), fPos.fXvstr.Data(), fPos.fYvstr.Data(), (HasXError() ? fEX1.fXvstr.Data() : ""), (HasXError() ? fEX2.fXvstr.Data() : ""), (HasYError() ? fEY1.fYvstr.Data() : ""), (HasYError() ? fEY2.fYvstr.Data() : "")); } /** Update the list view */ void UpdateLV() { TGListView* v = fContainer.GetListView(); v->Layout(); v->Resize(v->GetDefaultSize()); } /** Name of entry */ TString fName; /** Container */ TGLVContainer& fContainer; /** List view entry - argh - must be pointer, deleted by container! */ TGLVEntry* fEntry; /** Error line */ TLine fEX; /** Error line */ TLine fEY; ClassDef(R3Point,0); }; struct R3DataMain; //==================================================================== /** A reference point on an axis. */ struct R3Axis : public TMarker, public TQObject { /** Pixel X coordinate */ Int_t fXpixel; /** Pixel Y coordinate */ Int_t fYpixel; /** Constructor @param m Pointer to main frame @param name Name of this @param color Color of this */ R3Axis(TGCompositeFrame& left, const char* name, Int_t color) : TMarker(0,0,28), fXpixel(-1), fYpixel(-1), fName(name), fFrameHints(kLHintsExpandX, 0, 0, 0, 3), fFrame(&left), fButtonHints(kLHintsLeft|kLHintsExpandX|kLHintsExpandY, 0, 3), fButton(&fFrame, fName.Data()), fEntryHints(kLHintsLeft|kLHintsExpandX|kLHintsExpandY, 3, 3, 0, 0), fEntry(&fFrame), fIsSet(kFALSE), fColor(color) { SetMarkerSize(.7); SetMarkerColor(color); SetNDC(); Pixel_t colour; gClient->GetColorByName((color == 3 ? "green" : (color == 4 ? "blue" : (color == 6 ? "magenta" : "cyan"))),colour); fButton.Connect("Clicked()", "R3Axis", this, "HandleButton()"); fButton.AllowStayDown(kTRUE); fEntry.Connect("ValueSet(Long_t)","R3Axis",this,"HandleChanged(Long_t)"); fEntry.SetState(kFALSE); fEntry.GetNumberEntry()->SetTextColor(colour, kTRUE); fFrame.AddFrame(&fButton, &fButtonHints); fFrame.AddFrame(&fEntry, &fEntryHints); left.AddFrame(&fFrame, &fFrameHints); } /** Destructor */ virtual ~R3Axis() { } /** Get the name */ const char* GetName() const { return fName.Data(); } /** Clear this */ void Clear(Option_t* option="") { TMarker::Clear(option); gPad->RecursiveRemove(this); gPad->Modified(); gPad->Update(); gPad->cd(); fIsSet = kFALSE; fXpixel = fYpixel = -1; fEntry.SetNumber(0); fEntry.SetState(kFALSE); } //*MENU* /** Handle when button is pressed. */ void HandleButton() { Define(this); } /** Handle value change */ void HandleChanged(Long_t) { Changed(); } /** Signal to emit */ void Define(R3Axis*) { Long_t args[] = {(Long_t)this, 0}; Emit("Define(R3Axis*)", args); } // *SIGNAL* /** Signal to emit */ void Changed() { Emit("Changed()"); } //*SIGNAL* /** Set the @f$(x,y)@f$ reference point. */ void SetPixels(Int_t x, Int_t y) { fXpixel = x; fYpixel = y; fButton.Toggle(); fEntry.SetState(kTRUE); fIsSet = kTRUE; SetX(Double_t(x) / gPad->GetWw()); SetY(1 - Double_t(y) / gPad->GetWh()); Draw(); gPad->Modified(); gPad->Update(); gPad->cd(); } /** Check if this reference point has been set. */ Bool_t IsSet() const { return fIsSet; } void Delete(Option_t* option="") { Clear(option); } //*MENU* Double_t GetValue() const { return fEntry.GetNumber(); } void Focus() { fEntry.GetNumberEntry()->SetFocus(); fEntry.GetNumberEntry()->SelectAll(); } protected: /** Name */ TString fName; /** Layout hints */ TGLayoutHints fFrameHints; /** Frame */ TGHorizontalFrame fFrame; /** Layout hints */ TGLayoutHints fButtonHints; /** Button */ TGTextButton fButton; /** Layout hints */ TGLayoutHints fEntryHints; /** Number edit entry */ TGNumberEntry fEntry; /** Flag whether we've been set */ Bool_t fIsSet; /** Colour */ Int_t fColor; ClassDef(R3Axis,0); }; //==================================================================== /** Display widget of current coordinate */ struct R3Current : public TGHorizontalFrame { /** Constructor @param f The frame to put this in. @param label Name of this coordinate */ R3Current(TGCompositeFrame& f, const char* label) : TGHorizontalFrame(&f), fSubHints(kLHintsExpandX, 0, 3, 0, 0), fHints(kLHintsExpandX, 0, 0, 3, 0), fLabel(this, label), fValue(this, "") { AddFrame(&fLabel, &fSubHints); AddFrame(&fValue, &fSubHints); f.AddFrame(this, &fHints); fValue.SetEnabled(kFALSE); } /** Set the value to display */ void SetValue(Double_t v) { fValue.SetText(Form("%g", v)); } protected: /** Hints for label and value */ TGLayoutHints fSubHints; /** Hints for widget */ TGLayoutHints fHints; /** Label */ TGLabel fLabel; /** Display of coordinate */ TGTextEntry fValue; ClassDef(R3Current,0); }; //==================================================================== struct R3DataMain : public TGMainFrame { R3DataMain(const char* filename); virtual ~R3DataMain(); /** Open a new image filee */ Bool_t Open(const char* filename); /** Handle mouse in canvas */ void Handle(Int_t e,Int_t x,Int_t y,TObject* s); /** Handle menus */ void HandleMenu(Int_t id); /** Handle selection of point */ void HandleSelect(TGLVEntry* e, Int_t btn); /** Handle request to calculate things */ Bool_t HandleCalc(); /** Define a reference point */ void Define(R3Axis* a) { fCurrent = a; } /** Test if all axis is defined */ Bool_t IsAxisSet() const; /** Calculate the cache variables for speed */ void CalcCommon(); /** Close this window */ void Close(); protected: enum { kOpen, kSave, kSaveAs, kDraw, kPrint, kExit, kAbout, kHelp }; /** Draw the image */ void DrawImage(); /** Draw zoom around @f$ (x,y)@f$ @param e Event @param x X-coordinate @param y Y-coordinate @param s Object. */ void DoZoom(Int_t e,Int_t x,Int_t y,TObject* s); /** Calculate the real coordinate value @f$ (x,y)@f$ corresponding to pixel value @f$ (px,py)@f$ @param px Pixel X coordinate @param py Pixel Y coordinate @param x On return, the graph X coordinate @param y On return, the graph Y coordinate */ void CalcValue(Int_t px, Int_t py, Double_t& x, Double_t& y); /** Calculate the real coordinate value @f$ (x,y)@f$ corresponding to pixel value @f$ (px,py)@f$. This also calculates the precision for each point @param px Pixel X coordinate @param py Pixel Y coordinate @param x On return, the graph X coordinate @param y On return, the graph Y coordinate @param ex On return, the graph X coordinate precision @param ey On return, the graph Y coordinate precision */ void CalcValue(Int_t px, Int_t py, Double_t& x, Double_t& y, Double_t& ex, Double_t& ey); /** Calculate real coordinates relative to @f$(xx,xy)@f$ and @f$(yx,yy)@f$. @param xx X coordinate of X reference point @param xy Y coordinate of X reference point @param yx X coordinate of Y reference point @param yy Y coordinate of Y reference point @param x On return, the graph X coordinate @param y On return, the graph Y coordinate */ void Pixel2Coord(Double_t xx, Double_t xy, Double_t yx, Double_t yy, Double_t& x, Double_t& y); /** Layout Hints */ TGLayoutHints fMenuHints; /** Menu bar */ TGMenuBar fMenu; /** File menu */ TGPopupMenu* fFileMenu; /** Help menu */ TGPopupMenu* fHelpMenu; /** Top frame */ TGHorizontalFrame fTop; /** Layout Hints */ TGLayoutHints fLeftHints; /** Left hand frame */ TGVerticalFrame fLeft; /** Input widget for 1st X reference point */ R3Axis fXLeft; /** Input widget for 2nd X reference point */ R3Axis fXRight; /** Input widget for 1st Y reference point */ R3Axis fYTop; /** Input widget for 2nd Y reference point */ R3Axis fYBottom; /** Display widget to show cursor X value */ R3Current fXCur; /** Display widget to show cursor Y value */ R3Current fYCur; /** Button to initiate calculations */ TGTextButton fCalc; /** Layout Hints */ TGLayoutHints fLogHints; /** Option button of Logarithmic X axis */ TGCheckButton fXLog; /** Option button of Logarithmic Y axis */ TGCheckButton fYLog; /** Layout Hints */ TGLayoutHints fZoomOptHints; /** Frame to hold zoom optons */ TGHorizontalFrame fZoomOpt; /** Layout Hints */ TGLayoutHints fZoomLabelHints; /** Label of zoom leve */ TGLabel fZoomLabel; /** Layout Hints */ TGLayoutHints fZoomEntryHints; /** Zoom level edit entry */ TGNumberEntry fZoomEntry; /** Canvas to show zoom of image */ TRootEmbeddedCanvas fZoom; /** Canvas to show image */ TRootEmbeddedCanvas fCanvas; /** Layout Hints */ TGLayoutHints fViewHints; /** View of points defined */ TGListView fView; /** Container of points defined */ TGLVContainer fPoints; /** Layout Hints */ TGLayoutHints fStatusHints; /** The status bar */ TGStatusBar fStatus; /** The image to read off */ R3Image* fImage; /** Pointer to reference point being defined - if any */ R3Axis* fCurrent; /** The picked entry - if any */ TGLVEntry* fCurPoint; /** Zoom of image */ R3Image* fZoomImg; /** Width of zoom window */ const Int_t fZoomWidth; /** Height of zoom window */ const Int_t fZoomHeight; /** Cached for calculations */ Double_t fXDeltaX; /** Cached for calculations */ Double_t fXDeltaY; /** Cached for calculations */ Double_t fYDeltaX; /** Cached for calculations */ Double_t fYDeltaY; /** Cached for calculations */ Double_t fXPixelLow; /** Cached for calculations */ Double_t fXPixelHigh; /** Cached for calculations */ Double_t fYPixelLow; /** Cached for calculations */ Double_t fYPixelHigh; /** Cached for calculations */ Double_t fXLow; /** Cached for calculations */ Double_t fXHigh; /** Cached for calculations */ Double_t fYLow; /** Cached for calculations */ Double_t fYHigh; /** Cached for calculations */ Bool_t fXIsLog; /** Cached for calculations */ Bool_t fYIsLog; ClassDef(R3DataMain,0); }; //==================================================================== R3DataMain::R3DataMain(const char* filename) : TGMainFrame(gClient->GetRoot(), 1, 1, kVerticalFrame), fMenuHints(kLHintsExpandX, 3, 3, 3, 2), fMenu(this), fTop(this, 100, 100), fLeftHints(kLHintsExpandX, 3, 3, 3, 3), fLeft(&fTop, 200, 200), fXLeft(fLeft, "Define X1", 3), fXRight(fLeft, "Define X2", 4), fYTop(fLeft, "Define Y1", 6), fYBottom(fLeft, "Define Y2", 7), fXCur(fLeft, "Cursor at X:"), fYCur(fLeft, "Cursor at Y:"), fCalc(&fLeft, "Calculate"), fLogHints(kLHintsExpandX, 3, 3, 1, 3), fXLog(&fLeft, "Logarithmic X-axis"), fYLog(&fLeft, "Logarithmic Y-axis"), fZoomOptHints(kLHintsExpandX, 3, 3, 1, 3), fZoomOpt(&fLeft), fZoomLabelHints(kLHintsExpandX), fZoomLabel(&fZoomOpt, "Zoom level:"), fZoomEntryHints(kLHintsExpandX,3), fZoomEntry(&fZoomOpt, 4, 0, -1, TGNumberFormat::kNESInteger, TGNumberFormat::kNEAPositive, TGNumberFormat::kNELLimitMinMax, 2, 8), fZoom("zoom", &fLeft, 200, 200), fCanvas("c", &fTop, 100, 100), fViewHints(kLHintsExpandX, 3, 3, 1, 3), fView(this, 100, 100), fPoints(&fView), fStatusHints(kLHintsExpandX, 0, 0, 0, 3), fStatus(this), fImage(0), fCurrent(0), fCurPoint(0), fZoomImg(0), fZoomWidth(200), fZoomHeight(200) { // Menu bar // fMenu = new TGMenuBar(this); fFileMenu = fMenu.AddPopup("&File"); fFileMenu->AddSeparator(); fFileMenu->AddEntry("&Open", kOpen); fFileMenu->AddEntry("&Save", kSave); fFileMenu->AddEntry("Save &As...", kSaveAs); fFileMenu->AddSeparator(); fFileMenu->AddEntry("&Draw", kDraw); fFileMenu->AddEntry("&Print", kPrint); fFileMenu->AddSeparator(); fFileMenu->AddEntry("E&xit...", kExit); fFileMenu->Connect("Activated(Int_t)", "R3DataMain", this, "HandleMenu(Int_t)"); fHelpMenu = fMenu.AddPopup("&Help"); fHelpMenu->AddEntry("&About ...", kAbout); fHelpMenu->AddEntry("Help ...", kHelp); fHelpMenu->Connect("Activated(Int_t)", "R3DataMain", this, "HandleMenu(Int_t)"); AddFrame(&fMenu, &fMenuHints); // Top frame with canvas and operations. AddFrame(&fTop, new TGLayoutHints(kLHintsExpandX, 3, 3, 3, 2)); fTop.AddFrame(&fLeft, new TGLayoutHints(0, 0, 3, 0, 0)); // Defines fXLeft.Connect("Define(R3Axis*)", "R3DataMain", this, "Define(R3Axis*)"); fXRight.Connect("Define(R3Axis*)", "R3DataMain", this, "Define(R3Axis*)"); fYTop.Connect("Define(R3Axis*)", "R3DataMain", this, "Define(R3Axis*)"); fYBottom.Connect("Define(R3Axis*)", "R3DataMain", this, "Define(R3Axis*)"); fXLeft.Connect("Changed()", "R3DataMain", this, "CalcCommon()"); fXRight.Connect("Changed()", "R3DataMain", this, "CalcCommon()"); fYTop.Connect("Changed()", "R3DataMain", this, "CalcCommon()"); fYBottom.Connect("Changed()", "R3DataMain", this, "CalcCommon()"); // Calculation button fCalc.Connect("Clicked()", "R3DataMain", this, "HandleCalc()"); fCalc.SetEnabled(kFALSE); fLeft.AddFrame(&fCalc, &fLeftHints); // Logarithmic options fXLog.Connect("Clicked()", "R3DataMain", this, "CalcCommon()"); fYLog.Connect("Clicked()", "R3DataMain", this, "CalcCommon()"); fLeft.AddFrame(&fXLog, &fLogHints); fLeft.AddFrame(&fYLog, &fLogHints); // Zoom operations // fZoomOpt = new TGHorizontalFrame(fLeft); fLeft.AddFrame(&fZoomOpt, &fZoomOptHints); fZoomOpt.AddFrame(&fZoomLabel, &fZoomLabelHints); fZoomOpt.AddFrame(&fZoomEntry, &fZoomEntryHints); // Zoom canvas // fZoom = new TRootEmbeddedCanvas("zoom", &fLeft, 200, 200), fZoom.GetCanvas()->SetBit(TPad::kClearAfterCR, kFALSE); fZoom.GetCanvas()->SetBorderMode(0); fLeft.AddFrame(&fZoom); // Canvas to display the image in // fCanvas = new TRootEmbeddedCanvas("c", &fTop, 100, 100), fCanvas.GetCanvas()->SetBit(TPad::kClearAfterCR, kFALSE); fCanvas.GetCanvas()->SetBorderMode(0); fCanvas.GetCanvas()->Connect("ProcessedEvent(Int_t,Int_t,Int_t,TObject*)", "R3DataMain", this, "Handle(Int_t,Int_t,Int_t,TObject*)"); fTop.AddFrame(&fCanvas); // View of points defined fView.SetContainer(&fPoints); fView.SetViewMode(kLVDetails); fView.SetScrolling(TGCanvas::kCanvasScrollVertical); fView.SetHeaders(9); fView.SetHeader("#", kTextLeft, kTextLeft, 0); fView.SetHeader("Xpixel", kTextCenterX, kTextCenterX, 1); fView.SetHeader("Ypixel", kTextCenterX, kTextCenterX, 2); fView.SetHeader("X value", kTextCenterX, kTextCenterX, 3); fView.SetHeader("Y value", kTextCenterX, kTextCenterX, 4); fView.SetHeader("X low error", kTextCenterX, kTextCenterX, 5); fView.SetHeader("X high error", kTextCenterX, kTextCenterX, 6); fView.SetHeader("Y low error", kTextCenterX, kTextCenterX, 7); fView.SetHeader("Y high error", kTextCenterX, kTextCenterX, 8); fView.Connect("Clicked(TGLVEntry*,Int_t)", "R3DataMain", this, "HandleSelect(TGLVEntry*,Int_t)"); fView.Connect("SelectionChanged()", "R3DataMain", this, "HandleSelect(TGLVEntry*,Int_t)"); fPoints.SetViewMode(kLVDetails); fPoints.SetListView(&fView); fPoints.SetLayoutManager(new TGColumnLayout(&fPoints)); fPoints.Associate(&fView); AddFrame(&fView, &fViewHints); Pixel_t white; gClient->GetColorByName("white",white); fPoints.SetBackgroundColor(white); // Status bar fStatus.SetParts(1); fStatus.SetText("Left click to add point"); AddFrame(&fStatus, &fStatusHints); // Connect close button Connect("CloseWindow()", "R3DataMain", this, "Close()"); Open(filename); } //____________________________________________________________________ R3DataMain::~R3DataMain() { fCanvas.GetCanvas()->Clear(); fZoom.GetCanvas()->Clear(); if (fImage) delete fImage; if (fZoomImg) delete fZoomImg; } //____________________________________________________________________ void R3DataMain::Close() { // DontCallClose(); // DestroyWindow(); DontCallClose(); TTimer::SingleShot(150, "R3DataMain", this, "DeleteWindow()"); } //____________________________________________________________________ Bool_t R3DataMain::Open(const char* filename) { // Initialize the images. if (fImage) delete fImage; fCanvas.GetCanvas()->Clear("d"); static const char* types[] = { "Portable Network Graphics", "*.png", "Tagged Image File Format", "*.tif", "Graphics Interchange Format", "*.gif", "Joint Photographic Experts Group", "*.jpg", "Portable Pixmap Format", "*.ppm", "Portable anymap", "*.pnm", "eXperimental Computing Facility", "*.xcf", "Windows Bitmap", "*.bmp", "X BitMap", "*.xbm", "X PixMap", "*.xpm", "Compressed X PixMap", "*.xpm.Z", "GZip Compressed X PixMap", "*.xpm.gz", "Microsoft Icon", "*.ico", "Microsoft Cursor Icon", "*.cur", "Flexible Image Transport System", "*.fits", "Truevision TGA", "*.tga", "XML", "*.xml", "All", "*.*", 0 }; Int_t type = 34; TString fname(filename); while (fname.IsNull()) { TGFileInfo info; info.fFileTypes = types; info.fFileTypeIdx = 0; new TGFileDialog(gClient->GetRoot(), this, kFDOpen, &info); fname = info.fFilename; type = info.fFileTypeIdx; if (fname.IsNull()) { Int_t ret; new TGMsgBox(gClient->GetRoot(), (this ? this : gClient->GetRoot()), "Please specify a file", "No file given, press OK to continue and pick a file", kMBIconAsterisk, kMBOk|kMBCancel, &ret); if (ret == kMBCancel) throw "Arg!"; continue; } } if (type != 34 && !fname.EndsWith(&((types[type + 1])[1]))) fname.Append(&((types[type + 1])[1])); fImage = new R3Image(fname.Data()); if (!fImage) throw "Argh!"; fZoomImg = 0; Int_t iw = fImage->GetWidth(); Int_t ih = fImage->GetHeight(); Int_t tw = iw + fZoomWidth; Int_t vh = 100; Int_t th = ih + vh; // Resize(tw, th); Resize(tw, th); fTop.Resize(tw, ih); fLeft.Resize(fZoomWidth, ih + 10); fView.Resize(tw, vh); fCanvas.Resize(iw, ih); // fPoints->RemoveAll(); // Show it MapSubwindows(); Resize(GetDefaultSize()); Int_t w = int(GetWidth()); Int_t h = int(GetHeight()); SetWMSize(w, h); SetWMSizeHints(w, int(h*.8), 2*w, 2*h, 2, 2); SetWindowName("R3Data"); SetIconName("R3Data"); SetClassHints("R3Data", "R3Data"); MapWindow(); DrawImage(); return kTRUE; } //____________________________________________________________________ Bool_t R3DataMain::IsAxisSet() const { return (fXLeft.IsSet() && fXRight.IsSet() && fYTop.IsSet() && fYBottom.IsSet()); } //____________________________________________________________________ void R3DataMain::DoZoom(Int_t, Int_t x, Int_t y, TObject*) { Int_t ix1, iy1, ix2, iy2; Int_t bs = 2; Int_t zw = fZoomWidth / fZoomEntry.GetIntNumber(); Int_t zh = fZoomHeight / fZoomEntry.GetIntNumber(); fImage->PadToImage(x - zw / 2, y - zh / 2, ix1, iy1); fImage->PadToImage(x + zw / 2, y + zh / 2, ix2, iy2); if (fZoomImg) delete fZoomImg; fZoomImg = new R3Image(zw, zh); fImage->CopyArea(fZoomImg, ix1, iy1, ix2-ix1-1, iy2-iy1-1, 0, 0, 3, TImage::kAllChan); fZoomImg->DrawRectangle((zw - bs) / 2, (zh - bs) / 2, bs, bs, "#ff0000"); fZoomImg->EndPaint(); TCanvas* zoomCanvas = fZoom.GetCanvas(); zoomCanvas->SetLeftMargin(0); zoomCanvas->SetRightMargin(0); zoomCanvas->SetTopMargin(0); zoomCanvas->SetBottomMargin(0); zoomCanvas->Clear(); zoomCanvas->Modified(kTRUE); zoomCanvas->Update(); zoomCanvas->cd(); fZoomImg->Draw(); zoomCanvas->Modified(kTRUE); zoomCanvas->Update(); zoomCanvas->cd(); gClient->NeedRedraw(&fZoom, kTRUE); } // // 389,204 // // //____________________________________________________________________ void R3DataMain::Handle(Int_t e,Int_t x,Int_t y, TObject* o) { static TLine* line = 0; static bool error = false; static Int_t startX, startY, oldX, oldY; DoZoom(e, x, y, o); fCanvas.GetCanvas()->cd(); static bool first = kTRUE; if (IsAxisSet()) { if (first) { CalcCommon(); first = false; } Double_t xx,yy; CalcValue(x,y, xx, yy); fXCur.SetValue(xx); fYCur.SetValue(yy); } else first = false; R3Point* p; switch (e) { case kButton1Down: // gVirtualX->SetLineColor(-1); oldX = startX = x; oldY = startY = y; error = false; break; case kButton1Motion: if (fCurPoint) { Double_t w = gPad->GetWw(); Double_t h = gPad->GetWh(); if (!line) line = new TLine(double(startX)/w, 1-double(startY)/h, double(oldX)/w, 1-double(oldY)/h); line->SetBit(TLine::kLineNDC); oldX = x; oldY = y; line->SetX2(double(oldX)/w); line->SetY2(1-double(oldY)/h); line->SetLineColor(4); fCanvas.GetCanvas()->cd(); line->Draw(); gPad->Modified(); gPad->Update(); gPad->cd(); error = true; } break; case kButton1Up: fCanvas.GetCanvas()->cd(); if (line) { delete line; line = 0; } if (fCurrent) { fCurrent->SetPixels(x, y); fCurrent->Focus(); fCurrent = 0; fCalc.SetEnabled(IsAxisSet()); if (IsAxisSet()) CalcCommon(); return; } if (!error) { if (!fCurPoint) p = new R3Point(fPoints, fPoints.NumItems()); else p = static_cast(fCurPoint->GetUserData()); if (!p) { std::cout << "Argh! no point to make/set" << std::endl; return; } fCanvas.GetCanvas()->cd(); p->SetPixels(x, y); if (!IsAxisSet()) return; Double_t xv, yv; CalcValue(x, y, xv, yv); p->SetValues(xv, yv); } else { if (!fCurPoint) { std::cout << "Argh! no entry selected!" << std::endl; return; } p = static_cast(fCurPoint->GetUserData()); if (!p) { std::cout << "Argh! no point" << std::endl; return; } Double_t dx = TMath::Abs(x - startX); Double_t dy = TMath::Abs(y - startY); R3Store& e1 = (dx > dy ? (startX < x ? p->fEX1 : p->fEX2) : (startY < y ? p->fEY2 : p->fEY1)); R3Store& e2 = (dx > dy ? (startX < x ? p->fEX2 : p->fEX1) : (startY < y ? p->fEY1 : p->fEY2)); e1.SetPixel(startX, startY); e2.SetPixel(x, y); if (!IsAxisSet()) { p->Update(); return; } Double_t xv = p->fPos.fXvalue; Double_t yv = p->fPos.fYvalue; Double_t e1X, e1Y, e2X, e2Y; CalcValue(e1.fXpixel, e1.fYpixel, e1X, e1Y); CalcValue(e2.fXpixel, e2.fYpixel, e2X, e2Y); e1.SetValue(TMath::Abs(xv-e1X), TMath::Abs(yv-e1Y)); e2.SetValue(TMath::Abs(xv-e2X), TMath::Abs(yv-e2Y)); p->Update(); } // gVirtualX->SetLineColor(-2); break; case kButton3Down: break; case kButton3Up: break; } } const char* helpText = "Program to read data from images\n\n" "Define x and y axis by pressing buttons on the left, click in image, and\n" "then fill in the appropriate coordinate value.\n\n" "Define data points by clicking on the image. Use the zoom on the left for\n" "better precision.\n\n" "By selecting entries in the lower panel, the data points can be edited or\n" "removed.\n\n" "To define error bars, select an item in the lower panel, and drag mouse\n" "over error bars while holding down the left mouse button.\n\n" "Choose 'File->Draw' to see the graph. Choose 'File->Save' or\n" "'File->Save As ...' to save to a script or an ASCII file\n"; //____________________________________________________________________ void R3DataMain::HandleMenu(Int_t id) { if (id == kExit) { Close(); // SendCloseMessage(); // delete this; return; } if (id == kOpen) { Open(0); return; } if (id == kAbout) { new TGMsgBox(gClient->GetRoot(), this, "About R3Data", "Program to read data from images\n" "Copyright (C) 2006 Christian Holm\n" "Released under the GPL version 2\n" "Heavily inspired by G3Data"); return; } if (id == kHelp) { TRootHelpDialog* hd = new TRootHelpDialog(this, "Help on R3Data", 600, 400); hd->SetText(helpText); hd->Popup(); return; } if (!HandleCalc()) return; Bool_t hasErrors = kFALSE; TGLVEntry* e = 0; TGFrameElement* fe = 0; R3Point* p = 0; TIter next(fPoints.GetList()); while ((fe = static_cast(next()))) { e = static_cast(fe->fFrame); if (!e) continue; p = static_cast(e->GetUserData()); if (!p) continue; if (p->HasXError() || p->HasYError()) { hasErrors = kTRUE; break; } } TGraph* g = 0; TGraphAsymmErrors* a = 0; if (hasErrors) g = a = new TGraphAsymmErrors; else g = new TGraph; g->SetMarkerStyle(21); g->SetMarkerSize(.6); g->SetName("graph"); g->SetTitle("Data read using R3Data"); Int_t i = 0; next.Reset(); while ((fe = static_cast(next()))) { // (p = static_cast(e->GetUserData())w)) { e = static_cast(fe->fFrame); if (!e) continue; p = static_cast(e->GetUserData()); if (!p) continue; g->SetPoint(i, p->fPos.fXvalue, p->fPos.fYvalue); // g->SetPointError(i, p->fXerr, p->Yerr); if (a && (p->HasXError() || p->HasYError())) a->SetPointError(i, p->fEX1.fXvalue, p->fEX2.fXvalue, p->fEY1.fYvalue, p->fEY2.fYvalue); i++; } if (id == kDraw) { TCanvas* c = new TCanvas("graph", "Graph"); c->SetLogy(fYIsLog); c->SetLogx(fXIsLog); g->Draw("APL"); return; } if (id == kPrint) { g->Print(); return; } static const char* types[] = { "ROOT script", "*.C", "Plain data file", "*.csv", 0 }; TString fname("graph.C"); Int_t type = 0; if (id == kSaveAs) { // We open a file dialog TGFileInfo info; info.fFileTypes = types; // info.fFilename = fname.Data(); new TGFileDialog(gClient->GetRoot(), this, kFDSave, &info); if (!info.fFilename) return; // fname = gSystem->ConcatFileName(info.fIniDir, info.fFilename); fname = info.fFilename; type = info.fFileTypeIdx; } if (fname.IsNull()) { new TGMsgBox(gClient->GetRoot(), this, "Failed", "No file name specified", kMBIconExclamation); return; } if (!fname.EndsWith(&((types[type+1])[1]))) fname.Append(&((types[type+1])[1])); // std::cout << "Saving in " << fname << " ... " << std::flush; std::ofstream out(fname.Data()); if (!out) { new TGMsgBox(gClient->GetRoot(), this, "Failed", Form("Failed to open file \"%s\"", fname.Data()), kMBIconExclamation); return; } if (type == 0) { g->SetDrawOption("apl"); out << "{\n" << " // Generated by R3Data" << std::endl; g->SavePrimitive(out, ""); out << "}\n" << "// EOF" << std::endl; } else { out << "# X\tY\t" << (a ? "EXL\tEXH\tEYL\tEYH" : "") << std::endl << std::scientific; for (i = 0; i < g->GetN(); i++) { Double_t x, y; g->GetPoint(i, x, y); out << x << "\t" << y; if (a) out << "\t" << a->GetErrorXlow(i) << "\t" << a->GetErrorXhigh(i) << "\t" << a->GetErrorYlow(i) << "\t" << a->GetErrorYhigh(i); out << std::endl; } } out.close(); // std::cout << "done" << std::endl; delete g; } //____________________________________________________________________ void R3DataMain::HandleSelect(TGLVEntry* e, Int_t btn) { (void)btn; fCurPoint = e; if (e) fStatus.SetText("Left-click, and drag to define error bars"); else fStatus.SetText("Left-click to add point"); } //____________________________________________________________________ void R3DataMain::CalcCommon() { fStatus.SetText("Click to define an axis point"); if (!IsAxisSet()) return; // Calculate deltax of x axis points - x21 fXDeltaX = double(fXRight.fXpixel - fXLeft.fXpixel); // Calculate deltay of x axis points - y21 fXDeltaY = double(fXRight.fYpixel - fXLeft.fYpixel); // Calculate deltax of y axis points - x43 fYDeltaX = double(fYTop.fXpixel - fYBottom.fXpixel); // Calculate deltay of y axis points - y43 fYDeltaY = double(fYTop.fYpixel - fYBottom.fYpixel); // Get options for log scale fXIsLog = fXLog.IsDown(); fYIsLog = fYLog.IsDown(); // Get the values fXLow = fXLeft.GetValue(); fXHigh = fXRight.GetValue(); fYLow = fYBottom.GetValue(); fYHigh = fYTop.GetValue(); // If x axis is logarithmic, store recalculated values in rlc. // Else store old values in rlc. fXPixelLow = (fXIsLog ? log(fXLow) : fXLow); fXPixelHigh = (fXIsLog ? log(fXHigh) : fXHigh); // If y axis is logarithmic, store recalculated values in rlc. // Else store old values in rlc. fYPixelLow = (fYIsLog ? log(fYLow) : fYLow); fYPixelHigh = (fYIsLog ? log(fYHigh) : fYHigh); } //____________________________________________________________________ void R3DataMain::Pixel2Coord(Double_t xx, Double_t xy, Double_t yx, Double_t yy, Double_t& x, Double_t& y) { double alpha = (xx - xy * (fYDeltaX / fYDeltaY)) / (fXDeltaX - fXDeltaY * fYDeltaX / fYDeltaY); double beta = (yy - yx * (fXDeltaY / fXDeltaX)) / (fYDeltaY - fYDeltaX * fXDeltaY / fXDeltaX); x = -alpha * (fXPixelHigh - fXPixelLow)+ fXPixelLow; if (fXIsLog) x = TMath::Exp(x); y = -beta * (fYPixelHigh - fYPixelLow)+ fYPixelLow; if (fYIsLog) y = TMath::Exp(y); } //____________________________________________________________________ void R3DataMain::CalcValue(Int_t ix, Int_t iy, Double_t& x, Double_t& y) { // Pixel coordinates as doubles Double_t xp = Double_t(ix); Double_t yp = Double_t(iy); // Calculate the coordinates of a pixel picked Pixel2Coord(fXLeft.fXpixel - xp, fXLeft.fYpixel - yp, fYBottom.fXpixel - xp, fYBottom.fYpixel - yp, x, y); } //____________________________________________________________________ void R3DataMain::CalcValue(Int_t ix, Int_t iy, Double_t& x, Double_t& y, Double_t& ex, Double_t& ey) { // Pixel coordinates as doubles Double_t xp = Double_t(ix); Double_t yp = Double_t(iy); // Calculate the coordinates of a pixel picked Pixel2Coord(fXLeft.fXpixel - xp, fXLeft.fYpixel - yp, fYBottom.fXpixel - xp, fYBottom.fYpixel - yp, x, y); // Calculate the coordinates of the pixel over and right to the picked Pixel2Coord(fXLeft.fXpixel - (xp+1), fXLeft.fYpixel - (yp+1), fYBottom.fXpixel - (xp+1), fYBottom.fYpixel - (yp+1), ex, ey); double xe, ye; // Calculate the coordinates of the pixel below and left to the picked Pixel2Coord(fXLeft.fXpixel - (xp-1), fXLeft.fYpixel - (yp-1), fYBottom.fXpixel - (xp-1), fYBottom.fYpixel - (yp-1), xe, ye); // Calculate the distance between pixels ex -= xe; ey -= ye; // Calculate the error ex = TMath::Abs(ex / 4.0); ey = TMath::Abs(ey / 4.0); } //____________________________________________________________________ Bool_t R3DataMain::HandleCalc() { if (!IsAxisSet()) { new TGMsgBox(gClient->GetRoot(), this, "Axis not defined", "Please define axis first", kMBIconExclamation); return kFALSE; } // CalcCommon(); TIter next(fPoints.GetList()); TGLVEntry* e = 0; TGFrameElement* fe = 0; R3Point* p = 0; while ((fe = static_cast(next()))) { // (p = static_cast(e->GetUserData())w)) { e = static_cast(fe->fFrame); if (!e) continue; p = static_cast(e->GetUserData()); if (!p) continue; Double_t xv, yv; CalcValue(p->fPos.fXpixel, p->fPos.fYpixel, xv, yv); p->SetValues(xv, yv); for (size_t i = 0; i < 3; i++) { R3Store& e = (i == 0 ? p->fEX1 : i == 1 ? p->fEX2 : i == 2 ? p->fEY1 : p->fEY2); Double_t eX, eY; CalcValue(e.fXpixel, e.fYpixel, eX, eY); e.SetValue(TMath::Abs(xv-eX), TMath::Abs(yv-eY)); } fCanvas.GetCanvas()->cd(); p->Update(); } return kTRUE; } //____________________________________________________________________ void R3DataMain::DrawImage() { TCanvas* c = fCanvas.GetCanvas(); c->cd(); c->SetLeftMargin(0); c->SetRightMargin(0); c->SetTopMargin(0); c->SetBottomMargin(0); fImage->Draw(); c->Range(0, 0,fImage->GetWidth(),fImage->GetHeight()); DoZoom(0, fImage->GetWidth()/2, fImage->GetHeight()/2, 0); } //____________________________________________________________________ // // EOF //