From f84173faaa4e0b5976ecd66bb0b4246f45993d53 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Thu, 7 May 2026 09:44:16 +0200 Subject: [PATCH 01/26] Fix TText methods in case of NDC DistanceToPrimitive, GetBoundingBox and ExecuteEvent have to use absolute pixel coordinates. --- graf2d/graf/src/TText.cxx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/graf2d/graf/src/TText.cxx b/graf2d/graf/src/TText.cxx index ad0410d198e5f..bf646c21e6a37 100644 --- a/graf2d/graf/src/TText.cxx +++ b/graf2d/graf/src/TText.cxx @@ -145,8 +145,8 @@ Int_t TText::DistancetoPrimitive(Int_t px, Int_t py) TAttText::Modify(); // change text attributes only if necessary if (TestBit(kTextNDC)) { - ptx = gPad->UtoPixel(fX); - pty = gPad->VtoPixel(fY); + ptx = gPad->UtoAbsPixel(fX); + pty = gPad->VtoAbsPixel(fY); } else { ptx = gPad->XtoAbsPixel(gPad->XtoPad(fX)); pty = gPad->YtoAbsPixel(gPad->YtoPad(fY)); @@ -254,8 +254,8 @@ void TText::ExecuteEvent(Int_t event, Int_t px, Int_t py) case kMouseMotion: if (TestBit(kTextNDC)) { - px1 = gPad->UtoPixel(fX); - py1 = gPad->VtoPixel(fY); + px1 = gPad->UtoAbsPixel(fX); + py1 = gPad->VtoAbsPixel(fY); } else { px1 = gPad->XtoAbsPixel(gPad->XtoPad(fX)); py1 = gPad->YtoAbsPixel(gPad->YtoPad(fY)); @@ -481,8 +481,8 @@ void TText::GetBoundingBox(UInt_t &w, UInt_t &h, Bool_t angle) Int_t cBoxX[4], cBoxY[4]; Int_t ptx, pty; if (TestBit(kTextNDC)) { - ptx = gPad->UtoPixel(fX); - pty = gPad->VtoPixel(fY); + ptx = gPad->UtoAbsPixel(fX); + pty = gPad->VtoAbsPixel(fY); } else { ptx = gPad->XtoAbsPixel(gPad->XtoPad(fX)); pty = gPad->YtoAbsPixel(gPad->YtoPad(fY)); From ad36ae2495e82aa7174f95f3d56342f05c4b25fc Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Thu, 7 May 2026 10:15:17 +0200 Subject: [PATCH 02/26] Exclude gVirtualX from TText For non-opaque moving rectangle and marker are drawn. --- graf2d/graf/src/TText.cxx | 105 ++++++++++++++++++++------------------ 1 file changed, 56 insertions(+), 49 deletions(-) diff --git a/graf2d/graf/src/TText.cxx b/graf2d/graf/src/TText.cxx index bf646c21e6a37..a79549514311e 100644 --- a/graf2d/graf/src/TText.cxx +++ b/graf2d/graf/src/TText.cxx @@ -15,8 +15,9 @@ #include "TBuffer.h" #include "TVirtualPad.h" #include "TVirtualPadPainter.h" -#include "TVirtualX.h" +#include "TCanvasImp.h" #include "TMath.h" +#include "TAttMarker.h" #include "TPoint.h" #include @@ -220,14 +221,17 @@ TText *TText::DrawTextNDC(Double_t x, Double_t y, const wchar_t *text) void TText::ExecuteEvent(Int_t event, Int_t px, Int_t py) { - if (!gPad) return; + if (!gPad || !gPad->IsEditable()) + return; + + auto &parent = *gPad; static Int_t px1, py1, pxold, pyold, Size, height, width; static Bool_t resize,turn; Int_t dx, dy; const char *text = GetTitle(); Int_t len = strlen(text); - Double_t sizetowin = gPad->GetAbsHNDC()*Double_t(gPad->GetWh()); + Double_t sizetowin = parent.GetAbsHNDC()*Double_t(parent.GetWh()); Double_t fh = (fTextSize*sizetowin); Int_t h = Int_t(fh/2); Int_t w = h*len; @@ -242,9 +246,8 @@ void TText::ExecuteEvent(Int_t event, Int_t px, Int_t py) Double_t dpx,dpy,xp1,yp1; Int_t cBoxX[4], cBoxY[4], part; Double_t div = 0; - Bool_t opaque = gPad->OpaqueMoving(); + Bool_t opaque = parent.OpaqueMoving(); - if (!gPad->IsEditable()) return; switch (event) { case kArrowKeyPress: @@ -254,11 +257,11 @@ void TText::ExecuteEvent(Int_t event, Int_t px, Int_t py) case kMouseMotion: if (TestBit(kTextNDC)) { - px1 = gPad->UtoAbsPixel(fX); - py1 = gPad->VtoAbsPixel(fY); + px1 = parent.UtoAbsPixel(fX); + py1 = parent.VtoAbsPixel(fY); } else { - px1 = gPad->XtoAbsPixel(gPad->XtoPad(fX)); - py1 = gPad->YtoAbsPixel(gPad->YtoPad(fY)); + px1 = parent.XtoAbsPixel(parent.XtoPad(fX)); + py1 = parent.YtoAbsPixel(parent.YtoPad(fY)); } theta = fTextAngle; Size = 0; @@ -277,27 +280,27 @@ void TText::ExecuteEvent(Int_t event, Int_t px, Int_t py) if (halign == 3) { turn = kTRUE; right = kTRUE; - gPad->SetCursor(kRotate); + parent.SetCursor(kRotate); } else { resize = kTRUE; height = valign; width = halign; - gPad->SetCursor(kArrowVer); + parent.SetCursor(kArrowVer); } break; case 1: - gPad->SetCursor(kMove); + parent.SetCursor(kMove); break; case 2: if (halign == 3) { resize = kTRUE; height = valign; width = halign; - gPad->SetCursor(kArrowVer); + parent.SetCursor(kArrowVer); } else { turn = kTRUE; right = kFALSE; - gPad->SetCursor(kRotate); + parent.SetCursor(kRotate); } } break; @@ -357,14 +360,14 @@ void TText::ExecuteEvent(Int_t event, Int_t px, Int_t py) } if (opaque) { if (ndcsav) this->SetNDC(kFALSE); - this->SetX(gPad->PadtoX(gPad->AbsPixeltoX(px1))); - this->SetY(gPad->PadtoY(gPad->AbsPixeltoY(py1))); - if (resize) gPad->ShowGuidelines(this, event, 't', false); - if ((!resize)&&(!turn)) gPad->ShowGuidelines(this, event, 'i', true); - gPad->ShowGuidelines(this, event, !resize&!turn); - this->SetTextAngle(theta); - gPad->Modified(kTRUE); - gPad->Update(); + SetX(parent.PadtoX(parent.AbsPixeltoX(px1))); + SetY(parent.PadtoY(parent.AbsPixeltoY(py1))); + if (resize) parent.ShowGuidelines(this, event, 't', false); + if (!resize && !turn) parent.ShowGuidelines(this, event, 'i', true); + parent.ShowGuidelines(this, event, !resize&!turn); + SetTextAngle(theta); + parent.Modified(kTRUE); + parent.Update(); } if (!opaque) PaintControlBox(px1, py1, -theta); pxold = px; pyold = py; @@ -373,26 +376,26 @@ void TText::ExecuteEvent(Int_t event, Int_t px, Int_t py) case kButton1Up: if (opaque) { if (ndcsav && !this->TestBit(kTextNDC)) { - this->SetX((fX - gPad->GetX1())/(gPad->GetX2()-gPad->GetX1())); - this->SetY((fY - gPad->GetY1())/(gPad->GetY2()-gPad->GetY1())); - this->SetNDC(); + SetX((fX - parent.GetX1())/(parent.GetX2()-parent.GetX1())); + SetY((fY - parent.GetY1())/(parent.GetY2()-parent.GetY1())); + SetNDC(); } - gPad->ShowGuidelines(this, event, !resize&!turn); + parent.ShowGuidelines(this, event, !resize&!turn); } else { if (TestBit(kTextNDC)) { - dpx = gPad->GetX2() - gPad->GetX1(); - dpy = gPad->GetY2() - gPad->GetY1(); - xp1 = gPad->GetX1(); - yp1 = gPad->GetY1(); - fX = (gPad->AbsPixeltoX(px1)-xp1)/dpx; - fY = (gPad->AbsPixeltoY(py1)-yp1)/dpy; + dpx = parent.GetX2() - parent.GetX1(); + dpy = parent.GetY2() - parent.GetY1(); + xp1 = parent.GetX1(); + yp1 = parent.GetY1(); + fX = (parent.AbsPixeltoX(px1)-xp1)/dpx; + fY = (parent.AbsPixeltoY(py1)-yp1)/dpy; } else { - fX = gPad->PadtoX(gPad->AbsPixeltoX(px1)); - fY = gPad->PadtoY(gPad->AbsPixeltoY(py1)); + fX = parent.PadtoX(parent.AbsPixeltoX(px1)); + fY = parent.PadtoY(parent.AbsPixeltoY(py1)); } fTextAngle = theta; } - gPad->Modified(kTRUE); + parent.Modified(kTRUE); break; case kButton1Locate: @@ -400,7 +403,7 @@ void TText::ExecuteEvent(Int_t event, Int_t px, Int_t py) while (1) { px = py = 0; - event = gVirtualX->RequestLocator(1, 1, px, py); + event = parent.GetCanvasImp()->RequestLocator(px, py); ExecuteEvent(kButton1Motion, px, py); @@ -625,14 +628,20 @@ void TText::PaintControlBox(Int_t x, Int_t y, Double_t theta) Short_t valign = fTextAlign - 10*halign; // vertical alignment GetControlBox(x, y, theta, cBoxX, cBoxY); + + auto pp = gPad->GetPainter(); + if (!pp) + return; + // Draw the text control box outline - gVirtualX->SetLineStyle((Style_t)1); - gVirtualX->SetLineWidth(1); - gVirtualX->SetLineColor(1); - gVirtualX->DrawLine(cBoxX[0], cBoxY[0], cBoxX[1], cBoxY[1]); - gVirtualX->DrawLine(cBoxX[1], cBoxY[1], cBoxX[2], cBoxY[2]); - gVirtualX->DrawLine(cBoxX[2], cBoxY[2], cBoxX[3], cBoxY[3]); - gVirtualX->DrawLine(cBoxX[3], cBoxY[3], cBoxX[0], cBoxY[0]); + pp->SetAttLine({(Style_t)1, 1, 1}); + for (int p1 = 0; p1 < 4; ++p1) { + int p2 = (p1 + 1) % 4; + pp->DrawLine(gPad->AbsPixeltoX(cBoxX[p1]), + gPad->AbsPixeltoY(cBoxY[p1]), + gPad->AbsPixeltoX(cBoxX[p2]), + gPad->AbsPixeltoY(cBoxY[p2])); + } // Draw a symbol at the text starting point TPoint p; @@ -660,12 +669,10 @@ void TText::PaintControlBox(Int_t x, Int_t y, Double_t theta) } break; } - p.fX = (cBoxX[ix]+cBoxX[iy])/2; - p.fY = (cBoxY[ix]+cBoxY[iy])/2; - gVirtualX->SetMarkerColor(1); - gVirtualX->SetMarkerStyle(24); - gVirtualX->SetMarkerSize(0.7); - gVirtualX->DrawPolyMarker(1, &p); + Double_t mX = gPad->AbsPixeltoX((cBoxX[ix]+cBoxX[iy])/2); + Double_t mY = gPad->AbsPixeltoY((cBoxY[ix]+cBoxY[iy])/2); + pp->SetAttMarker({(Color_t)1, 24, 0.7}); + pp->DrawPolyMarker(1, &mX, &mY); } //////////////////////////////////////////////////////////////////////////////// From 0501fe5a9d870b637f9059c6b4f3a01bd1eb9939 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Thu, 7 May 2026 10:38:39 +0200 Subject: [PATCH 03/26] Adjust TText::BBox methods Use provided base-class methods to recalculate pixels to text coordinates --- graf2d/graf/inc/TText.h | 9 ++-- graf2d/graf/src/TText.cxx | 91 +++++++++++++-------------------------- 2 files changed, 33 insertions(+), 67 deletions(-) diff --git a/graf2d/graf/inc/TText.h b/graf2d/graf/inc/TText.h index e4dcc853aefba..78a5ce4c861ce 100644 --- a/graf2d/graf/inc/TText.h +++ b/graf2d/graf/inc/TText.h @@ -78,13 +78,12 @@ class TText : public TNamed, public TAttText, public TAttBBox2D { Rectangle_t GetBBox() override; TPoint GetBBoxCenter() override; - void SetBBoxCenter(const TPoint &p) override; void SetBBoxCenterX(const Int_t x) override; void SetBBoxCenterY(const Int_t y) override; - void SetBBoxX1(const Int_t) override; //Not Implemented - void SetBBoxX2(const Int_t) override; //Not Implemented - void SetBBoxY1(const Int_t) override; //Not Implemented - void SetBBoxY2(const Int_t) override; //Not Implemented + void SetBBoxX1(const Int_t) override {} //Not Implemented + void SetBBoxX2(const Int_t) override {} //Not Implemented + void SetBBoxY1(const Int_t) override {} //Not Implemented + void SetBBoxY2(const Int_t) override {} //Not Implemented ClassDefOverride(TText,3) //Text }; diff --git a/graf2d/graf/src/TText.cxx b/graf2d/graf/src/TText.cxx index a79549514311e..a51860a3afbab 100644 --- a/graf2d/graf/src/TText.cxx +++ b/graf2d/graf/src/TText.cxx @@ -425,10 +425,15 @@ void TText::GetControlBox(Int_t x, Int_t y, Double_t theta, { Short_t halign = fTextAlign/10; // horizontal alignment Short_t valign = fTextAlign - 10*halign; // vertical alignment - UInt_t cBoxW, cBoxH; // control box width and heigh + UInt_t cBoxW = 0, cBoxH = 0; // control box width and heigh UInt_t Dx = 0, Dy = 0; // delta along x and y to align the box - GetBoundingBox(cBoxW, cBoxH); + if (gPad) { + Double_t tsize = GetTextSizePixels(*gPad); + auto pp = gPad->GetPainter(); + if (pp) + pp->GetTextExtent(GetTextFont(), tsize, cBoxW, cBoxH, GetTitle()); + } // compute the translations (Dx, Dy) required by the alignments switch (halign) { @@ -501,8 +506,8 @@ void TText::GetBoundingBox(UInt_t &w, UInt_t &h, Bool_t angle) if (cBoxY[i] < y1) y1 = cBoxY[i]; if (cBoxY[i] > y2) y2 = cBoxY[i]; } - w = x2-x1; - h = y2-y1; + w = x2 - x1; + h = y2 - y1; } else { Double_t tsize = GetTextSizePixels(*gPad); auto pp = gPad->GetPainter(); @@ -794,7 +799,7 @@ void TText::Streamer(TBuffer &R__b) Rectangle_t TText::GetBBox() { - Rectangle_t BBox{0, 0, 0, 0}; + Rectangle_t bbox{0, 0, 0, 0}; if (gPad) { UInt_t w, h; Int_t Dx = 0, Dy = 0; @@ -814,13 +819,17 @@ Rectangle_t TText::GetBBox() case 2: Dy = h / 2; break; case 3: Dy = 0; break; } - - BBox.fX = gPad->XtoPixel(fX) - Dx; - BBox.fY = gPad->YtoPixel(fY) - Dy; - BBox.fWidth = w; - BBox.fHeight = h; + if (TestBit(kTextNDC)) { + bbox.fX = gPad->UtoPixel(GetX()) - Dx; + bbox.fY = gPad->VtoPixel(GetY()) - Dy; + } else { + bbox.fX = gPad->XtoPixel(gPad->XtoPad(GetX())) - Dx; + bbox.fY = gPad->YtoPixel(gPad->YtoPad(GetY())) - Dy; + } + bbox.fWidth = w; + bbox.fHeight = h; } - return BBox; + return bbox; } //////////////////////////////////////////////////////////////////////////////// @@ -830,29 +839,23 @@ TPoint TText::GetBBoxCenter() { TPoint p(0, 0); if (gPad) { - p.SetX(gPad->XtoPixel(fX)); - p.SetY(gPad->YtoPixel(fY)); + if (TestBit(kTextNDC)) { + p.SetX(gPad->UtoPixel(GetX())); + p.SetY(gPad->VtoPixel(GetY())); + } else { + p.SetX(gPad->XtoPixel(gPad->XtoPad(GetX()))); + p.SetY(gPad->YtoPixel(gPad->YtoPad(GetY()))); + } } return p; } -//////////////////////////////////////////////////////////////////////////////// -/// Set the point given by Alignment as 'center' - -void TText::SetBBoxCenter(const TPoint &p) -{ - if (!gPad) return; - this->SetX(gPad->PixeltoX(p.GetX())); - this->SetY(gPad->PixeltoY(p.GetY()-gPad->VtoPixel(0))); -} - //////////////////////////////////////////////////////////////////////////////// /// Set X coordinate of the point given by Alignment as 'center' void TText::SetBBoxCenterX(const Int_t x) { - if (!gPad) return; - this->SetX(gPad->PixeltoX(x)); + SetX(GetXCoord(x, TestBit(kTextNDC))); } //////////////////////////////////////////////////////////////////////////////// @@ -860,41 +863,5 @@ void TText::SetBBoxCenterX(const Int_t x) void TText::SetBBoxCenterY(const Int_t y) { - if (!gPad) return; - this->SetY(gPad->PixeltoY(y - gPad->VtoPixel(0))); -} - -//////////////////////////////////////////////////////////////////////////////// -/// Set left hand side of BoundingBox to a value -/// (resize in x direction on left) - -void TText::SetBBoxX1(const Int_t /*x*/) -{ - //NOT IMPLEMENTED -} - -//////////////////////////////////////////////////////////////////////////////// -/// Set right hand side of BoundingBox to a value -/// (resize in x direction on right) - -void TText::SetBBoxX2(const Int_t /*x*/) -{ - //NOT IMPLEMENTED -} - -//////////////////////////////////////////////////////////////////////////////// -/// Set top of BoundingBox to a value (resize in y direction on top) - -void TText::SetBBoxY1(const Int_t /*y*/) -{ - //NOT IMPLEMENTED -} - -//////////////////////////////////////////////////////////////////////////////// -/// Set bottom of BoundingBox to a value -/// (resize in y direction on bottom) - -void TText::SetBBoxY2(const Int_t /*y*/) -{ - //NOT IMPLEMENTED + SetY(GetYCoord(y, TestBit(kTextNDC))); } From 785443c508871131d36081aacf76982258297ed2 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Thu, 7 May 2026 11:20:24 +0200 Subject: [PATCH 04/26] Support abs coordinates in TAttBBox methods There are several places where such method are used. First is BBox handling, second is ExecuteEvent handling --- core/base/inc/TAttBBox2D.h | 4 ++-- core/base/src/TAttBBox2D.cxx | 28 +++++++++++++++++++++------- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/core/base/inc/TAttBBox2D.h b/core/base/inc/TAttBBox2D.h index f4f36e26be86c..4ca16c776b618 100644 --- a/core/base/inc/TAttBBox2D.h +++ b/core/base/inc/TAttBBox2D.h @@ -20,8 +20,8 @@ class TAttBBox2D { protected: - Double_t GetXCoord(const Int_t x, Bool_t is_ndc = kFALSE); - Double_t GetYCoord(const Int_t y, Bool_t is_ndc = kFALSE); + Double_t GetXCoord(const Int_t x, Bool_t is_ndc = kFALSE, Bool_t is_absolute = kFALSE); + Double_t GetYCoord(const Int_t y, Bool_t is_ndc = kFALSE, Bool_t is_absolute = kFALSE); public: virtual ~TAttBBox2D(); diff --git a/core/base/src/TAttBBox2D.cxx b/core/base/src/TAttBBox2D.cxx index 95b95f02e074f..874eb290f06df 100644 --- a/core/base/src/TAttBBox2D.cxx +++ b/core/base/src/TAttBBox2D.cxx @@ -54,34 +54,48 @@ void TAttBBox2D::SetBBoxCenter(const TPoint &p) } //////////////////////////////////////////////////////////////////////////////// -// Return user X coordinate for pixel X value -// Used in derived classes to implement SetBBox... methods +/// Return user X coordinate for pixel X value +/// Can return ndc or normal values +/// Also one can specify to use absolute coordinates for input parameter x +/// Used in derived classes to implement SetBBox... methods -Double_t TAttBBox2D::GetXCoord(const Int_t x, Bool_t is_ndc) +Double_t TAttBBox2D::GetXCoord(const Int_t x, Bool_t is_ndc, Bool_t is_absolute) { if (!gPad) return 0.; if (!is_ndc) - return gPad->PadtoX(gPad->PixeltoX(x)); + return gPad->PadtoX(is_absolute ? gPad->AbsPixeltoX(x) : gPad->PixeltoX(x)); + if (is_absolute) { + Double_t ww = gPad->GetWw(); + Double_t wndc = gPad->GetAbsWNDC(); + return ww > 0 && wndc > 0 ? (x / ww - gPad->GetAbsXlowNDC()) / wndc : 0.; + } Int_t pw = gPad->GetPadWidth(); return pw > 0 ? 1. * x / pw : 0.; } //////////////////////////////////////////////////////////////////////////////// // Return user Y coordinate for pixel Y value +/// Can return ndc or normal values +/// Also one can specify to use absolute coordinates for input parameter x // Used in derived classes to implement SetBBox... methods -Double_t TAttBBox2D::GetYCoord(const Int_t y, Bool_t is_ndc) +Double_t TAttBBox2D::GetYCoord(const Int_t y, Bool_t is_ndc, Bool_t is_absolute) { if (!gPad) return 0.; if (!is_ndc) - return gPad->PadtoY(gPad->PixeltoY(y - gPad->VtoPixel(0))); + return gPad->PadtoY(is_absolute ? gPad->AbsPixeltoY(y) : gPad->PixeltoY(y - gPad->VtoPixel(0))); - Int_t ph = gPad->GetPadHeight(); + if (is_absolute) { + Double_t wh = gPad->GetWh(); + Double_t hndc = gPad->GetAbsHNDC(); + return wh > 0 && hndc > 0 ? ((1. - y / wh) - gPad->GetAbsYlowNDC()) / hndc : 0.; + } + Int_t ph = gPad->GetPadHeight(); return ph > 0 ? 1. - 1. * y / ph : 0.; } From 81c8982852286602ae35f57de5015435e8bc916e Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Thu, 7 May 2026 11:20:54 +0200 Subject: [PATCH 05/26] Improve TText::executeEvent Allow to keep usage of NDC. Use new methods for coordinates calculation --- graf2d/graf/src/TText.cxx | 78 ++++++++++++++++----------------------- 1 file changed, 32 insertions(+), 46 deletions(-) diff --git a/graf2d/graf/src/TText.cxx b/graf2d/graf/src/TText.cxx index a51860a3afbab..b736c94e91af8 100644 --- a/graf2d/graf/src/TText.cxx +++ b/graf2d/graf/src/TText.cxx @@ -143,8 +143,6 @@ Int_t TText::DistancetoPrimitive(Int_t px, Int_t py) if (!gPad) return 9999; Int_t ptx, pty; - TAttText::Modify(); // change text attributes only if necessary - if (TestBit(kTextNDC)) { ptx = gPad->UtoAbsPixel(fX); pty = gPad->VtoAbsPixel(fY); @@ -238,12 +236,10 @@ void TText::ExecuteEvent(Int_t event, Int_t px, Int_t py) Short_t halign = fTextAlign/10; Short_t valign = fTextAlign - 10*halign; Double_t co, si, dtheta, norm; - static Bool_t right, ndcsav; + static Bool_t right; static Double_t theta; - Int_t ax, ay, bx, by, cx, cy; - ax = ay = 0; + Int_t ax = 0, ay = 0, bx, by, cx, cy; Double_t lambda, x2,y2; - Double_t dpx,dpy,xp1,yp1; Int_t cBoxX[4], cBoxY[4], part; Double_t div = 0; Bool_t opaque = parent.OpaqueMoving(); @@ -252,9 +248,6 @@ void TText::ExecuteEvent(Int_t event, Int_t px, Int_t py) case kArrowKeyPress: case kButton1Down: - ndcsav = TestBit(kTextNDC); - // No break !!! - case kMouseMotion: if (TestBit(kTextNDC)) { px1 = parent.UtoAbsPixel(fX); @@ -263,18 +256,20 @@ void TText::ExecuteEvent(Int_t event, Int_t px, Int_t py) px1 = parent.XtoAbsPixel(parent.XtoPad(fX)); py1 = parent.YtoAbsPixel(parent.YtoPad(fY)); } - theta = fTextAngle; + theta = GetTextAngle(); Size = 0; pxold = px; pyold = py; - co = TMath::Cos(fTextAngle*0.017453293); - si = TMath::Sin(fTextAngle*0.017453293); + co = TMath::Cos(theta*0.017453293); + si = TMath::Sin(theta*0.017453293); resize = kFALSE; turn = kFALSE; GetControlBox(px1, py1, -theta, cBoxX, cBoxY); div = ((cBoxX[3]-cBoxX[0])*co-(cBoxY[3]-cBoxY[0])*si); - if (TMath::Abs(div) > 1e-8) part = (Int_t)(3*((px-cBoxX[0])*co-(py-cBoxY[0])*si)/ div); - else part = 0; + if (TMath::Abs(div) > 1e-8) + part = (Int_t)(3*((px-cBoxX[0])*co-(py-cBoxY[0])*si)/ div); + else + part = 0; switch (part) { case 0: if (halign == 3) { @@ -307,7 +302,8 @@ void TText::ExecuteEvent(Int_t event, Int_t px, Int_t py) case kArrowKeyRelease: case kButton1Motion: - if (!opaque) PaintControlBox(px1, py1, -theta); + if (!opaque) + PaintControlBox(px1, py1, -theta); if (turn) { norm = TMath::Sqrt(Double_t((py-py1)*(py-py1)+(px-px1)*(px-px1))); if (norm>0) { @@ -319,7 +315,6 @@ void TText::ExecuteEvent(Int_t event, Int_t px, Int_t py) if (right) {theta = theta+180; if (theta>=360) theta -= 360;} } } else if (resize) { - co = TMath::Cos(fTextAngle*0.017453293); si = TMath::Sin(fTextAngle*0.017453293); if (width == 1) { @@ -343,8 +338,13 @@ void TText::ExecuteEvent(Int_t event, Int_t px, Int_t py) case 3 : ax = px1-Int_t(co*w+si*h*3/2); ay = py1+Int_t(si*w+co*h*3/2); break; } } - if (height == 3) {bx = ax-Int_t(si*h); by = ay-Int_t(co*h);} - else {bx = ax; by = ay;} + if (height == 3) { + bx = ax-Int_t(si*h); + by = ay-Int_t(co*h); + } else { + bx = ax; + by = ay; + } cx = bx+Int_t(co*w); cy = by-Int_t(si*w); lambda = Double_t(((px-bx)*(cx-bx)+(py-by)*(cy-by)))/Double_t(((cx-bx)*(cx-bx)+(cy-by)*(cy-by))); x2 = Double_t(px) - lambda*Double_t(cx-bx)-Double_t(bx); @@ -353,47 +353,33 @@ void TText::ExecuteEvent(Int_t event, Int_t px, Int_t py) if (Size<4) Size = 4; SetTextSize(Size/sizetowin); - TAttText::Modify(); } else { dx = px - pxold; px1 += dx; dy = py - pyold; py1 += dy; } if (opaque) { - if (ndcsav) this->SetNDC(kFALSE); - SetX(parent.PadtoX(parent.AbsPixeltoX(px1))); - SetY(parent.PadtoY(parent.AbsPixeltoY(py1))); - if (resize) parent.ShowGuidelines(this, event, 't', false); - if (!resize && !turn) parent.ShowGuidelines(this, event, 'i', true); - parent.ShowGuidelines(this, event, !resize&!turn); + SetX(GetXCoord(px1, TestBit(kTextNDC), kTRUE)); + SetY(GetYCoord(py1, TestBit(kTextNDC), kTRUE)); + if (resize) + parent.ShowGuidelines(this, event, 't', false); + if (!resize && !turn) + parent.ShowGuidelines(this, event, 'i', true); + parent.ShowGuidelines(this, event, !resize && !turn); SetTextAngle(theta); - parent.Modified(kTRUE); - parent.Update(); + parent.ModifiedUpdate(); } - if (!opaque) PaintControlBox(px1, py1, -theta); + if (!opaque) + PaintControlBox(px1, py1, -theta); pxold = px; pyold = py; break; case kButton1Up: if (opaque) { - if (ndcsav && !this->TestBit(kTextNDC)) { - SetX((fX - parent.GetX1())/(parent.GetX2()-parent.GetX1())); - SetY((fY - parent.GetY1())/(parent.GetY2()-parent.GetY1())); - SetNDC(); - } - parent.ShowGuidelines(this, event, !resize&!turn); + parent.ShowGuidelines(this, event, !resize && !turn); } else { - if (TestBit(kTextNDC)) { - dpx = parent.GetX2() - parent.GetX1(); - dpy = parent.GetY2() - parent.GetY1(); - xp1 = parent.GetX1(); - yp1 = parent.GetY1(); - fX = (parent.AbsPixeltoX(px1)-xp1)/dpx; - fY = (parent.AbsPixeltoY(py1)-yp1)/dpy; - } else { - fX = parent.PadtoX(parent.AbsPixeltoX(px1)); - fY = parent.PadtoY(parent.AbsPixeltoY(py1)); - } - fTextAngle = theta; + SetX(GetXCoord(px1, TestBit(kTextNDC), kTRUE)); + SetY(GetYCoord(py1, TestBit(kTextNDC), kTRUE)); + SetTextAngle(theta); } parent.Modified(kTRUE); break; From 00496145478a2925c0764ffaa3c0cc9e09a2d33a Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Thu, 7 May 2026 12:20:15 +0200 Subject: [PATCH 06/26] Use new method in TLine::ExecuteEvent --- graf2d/graf/src/TLine.cxx | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/graf2d/graf/src/TLine.cxx b/graf2d/graf/src/TLine.cxx index b0401e31abe3e..ade905fd26d50 100644 --- a/graf2d/graf/src/TLine.cxx +++ b/graf2d/graf/src/TLine.cxx @@ -143,35 +143,22 @@ void TLine::ExecuteEvent(Int_t event, Int_t px, Int_t py) Bool_t opaque = parent.OpaqueMoving(); auto action = [this, &parent](Int_t code, Int_t _x1, Int_t _y1, Int_t _x2 = 0, Int_t _y2 = 0) { - Double_t x1, y1, x2, y2; Bool_t isndc = TestBit(kLineNDC); - if (isndc) { - x1 = (1. * _x1 / parent.GetWw() - parent.GetAbsXlowNDC()) / parent.GetAbsWNDC(); - y1 = ((1 - 1. * _y1 / parent.GetWh()) - parent.GetAbsYlowNDC()) / parent.GetAbsHNDC(); - x2 = (1. * _x2 / parent.GetWw() - parent.GetAbsXlowNDC()) / parent.GetAbsWNDC(); - y2 = ((1 - 1. * _y2 / parent.GetWh()) - parent.GetAbsYlowNDC()) / parent.GetAbsHNDC(); - } else { - x1 = parent.AbsPixeltoX(_x1); - y1 = parent.AbsPixeltoY(_y1); - x2 = parent.AbsPixeltoX(_x2); - y2 = parent.AbsPixeltoY(_y2); - } + Double_t x1 = GetXCoord(_x1, isndc, kTRUE); + Double_t y1 = GetYCoord(_y1, isndc, kTRUE); + Double_t x2 = GetXCoord(_x2, isndc, kTRUE); + Double_t y2 = GetYCoord(_y2, isndc, kTRUE); + if (code == 0) { auto pp = parent.GetPainter(); pp->SetAttLine(*this); if (isndc) pp->DrawLineNDC(x1, y1, x2, y2); else - pp->DrawLine(x1, y1, x2, y2); + pp->DrawLine(parent.XtoPad(x1), parent.YtoPad(y1), parent.XtoPad(x2), parent.YtoPad(y2)); } else { - if (!isndc) { - x1 = parent.PadtoX(x1); - x2 = parent.PadtoX(x2); - y1 = parent.PadtoY(y1); - y2 = parent.PadtoY(y2); - } if (code & 1) { SetX1(x1); From 8c329850e3dfdcfec034b236f2cc41a5754cb806 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Thu, 7 May 2026 13:05:36 +0200 Subject: [PATCH 07/26] Adjust TEllipse BBox and ExecuteEvent Use new methods for coordinate conversion --- graf2d/graf/inc/TEllipse.h | 1 - graf2d/graf/src/TEllipse.cxx | 104 ++++++++++++++--------------------- 2 files changed, 41 insertions(+), 64 deletions(-) diff --git a/graf2d/graf/inc/TEllipse.h b/graf2d/graf/inc/TEllipse.h index f54f54336b3cf..56e681bdc4b10 100644 --- a/graf2d/graf/inc/TEllipse.h +++ b/graf2d/graf/inc/TEllipse.h @@ -69,7 +69,6 @@ class TEllipse : public TObject, public TAttLine, public TAttFill, public TAttBB virtual void SetY1(Double_t y1) {fY1=y1;} // *MENU* Rectangle_t GetBBox() override; TPoint GetBBoxCenter() override; - void SetBBoxCenter(const TPoint &p) override; void SetBBoxCenterX(const Int_t x) override; void SetBBoxCenterY(const Int_t y) override; void SetBBoxX1(const Int_t x) override; diff --git a/graf2d/graf/src/TEllipse.cxx b/graf2d/graf/src/TEllipse.cxx index 2002b0f5b3025..3c0646e4b05d6 100644 --- a/graf2d/graf/src/TEllipse.cxx +++ b/graf2d/graf/src/TEllipse.cxx @@ -199,11 +199,11 @@ TEllipse *TEllipse::DrawEllipse(Double_t x1, Double_t y1,Double_t r1,Double_t r2 void TEllipse::ExecuteEvent(Int_t event, Int_t px, Int_t py) { - if (!gPad) return; + if (!gPad || !gPad->IsEditable()) return; Int_t kMaxDiff = 10; - Int_t i, dpx, dpy; + Int_t dpx, dpy; Double_t angle,dx,dy,dphi,ct,st,fTy,fBy,fLx,fRx; static Int_t px1,py1,npe,r1,r2,sav1,sav2; const Int_t kMinSize = 25; @@ -218,8 +218,6 @@ void TEllipse::ExecuteEvent(Int_t event, Int_t px, Int_t py) Bool_t opaque = gPad->OpaqueMoving(); - if (!gPad->IsEditable()) return; - switch (event) { case kArrowKeyPress: @@ -231,7 +229,7 @@ void TEllipse::ExecuteEvent(Int_t event, Int_t px, Int_t py) dphi = (fPhimax-fPhimin)*kPI/(180*np); ct = TMath::Cos(kPI*fTheta/180); st = TMath::Sin(kPI*fTheta/180); - for (i=0;iDrawLine(px1+4, pTy-4, px1+4, pTy+4); } else { - sdx = this->GetX1()-gPad->AbsPixeltoX(px); - sdy = this->GetY1()-gPad->AbsPixeltoY(py); + sdx = GetX1() - gPad->AbsPixeltoX(px); + sdy = GetY1() - gPad->AbsPixeltoY(py); } // No break !!! @@ -344,7 +342,7 @@ void TEllipse::ExecuteEvent(Int_t event, Int_t px, Int_t py) gVirtualX->DrawLine(px1-4, pTy+4, px1-4, pTy-4); gVirtualX->DrawLine(px1-4, pTy-4, px1+4, pTy-4); gVirtualX->DrawLine(px1+4, pTy-4, px1+4, pTy+4); - for (i=0;iDrawLine(x[i], y[i], x[i+1], y[i+1]); + for (Int_t i=0;iDrawLine(x[i], y[i], x[i+1], y[i+1]); } if (pTop) { sav1 = py1; @@ -395,7 +393,7 @@ void TEllipse::ExecuteEvent(Int_t event, Int_t px, Int_t py) dphi = (fPhimax-fPhimin)*kPI/(180*np); ct = TMath::Cos(kPI*fTheta/180); st = TMath::Sin(kPI*fTheta/180); - for (i=0;iSetLineColor(-1); TAttLine::Modify(); - for (i=0;iDrawLine(x[i], y[i], x[i+1], y[i+1]); } else { - this->SetX1(gPad->AbsPixeltoX(px1)); - this->SetY1(gPad->AbsPixeltoY(py1)); - this->SetR1(TMath::Abs(gPad->AbsPixeltoX(px1-r1)-gPad->AbsPixeltoX(px1+r1))/2); - this->SetR2(TMath::Abs(gPad->AbsPixeltoY(py1-r2)-gPad->AbsPixeltoY(py1+r2))/2); + SetX1(GetXCoord(px1, kFALSE, kTRUE)); + SetY1(GetYCoord(py1, kFALSE, kTRUE)); + SetR1(TMath::Abs(GetXCoord(px1+r1, kFALSE, kTRUE) - GetXCoord(px1-r1, kFALSE, kTRUE)) / 2); + SetR2(TMath::Abs(GetYCoord(py1-r2, kFALSE, kTRUE) - GetYCoord(py1+r2, kFALSE, kTRUE)) / 2); if (pTop) gPad->ShowGuidelines(this, event, 't', true); if (pBot) gPad->ShowGuidelines(this, event, 'b', true); if (pL) gPad->ShowGuidelines(this, event, 'l', true); if (pR) gPad->ShowGuidelines(this, event, 'r', true); - gPad->Modified(kTRUE); - gPad->Update(); + gPad->ModifiedUpdate(); } } if (pINSIDE) { if (!opaque){ dpx = px-pxold; dpy = py-pyold; px1 += dpx; py1 += dpy; - for (i=0;i<=npe;i++) { x[i] += dpx; y[i] += dpy;} - for (i=0;iDrawLine(x[i], y[i], x[i+1], y[i+1]); + for (Int_t i=0;i<=npe;i++) { x[i] += dpx; y[i] += dpy;} + for (Int_t i=0;iDrawLine(x[i], y[i], x[i+1], y[i+1]); } else { - this->SetX1(gPad->AbsPixeltoX(px)+sdx); - this->SetY1(gPad->AbsPixeltoY(py)+sdy); + SetX1(gPad->AbsPixeltoX(px)+sdx); + SetY1(gPad->AbsPixeltoY(py)+sdy); gPad->ShowGuidelines(this, event, 'i', true); - gPad->Modified(kTRUE); - gPad->Update(); + gPad->ModifiedUpdate(); } } if (!opaque){ @@ -480,12 +476,11 @@ void TEllipse::ExecuteEvent(Int_t event, Int_t px, Int_t py) gROOT->SetEscape(kFALSE); if (opaque) { gPad->ShowGuidelines(this, event); - this->SetX1(oldX1); - this->SetY1(oldY1); - this->SetR1(oldR1); - this->SetR2(oldR2); - gPad->Modified(kTRUE); - gPad->Update(); + SetX1(oldX1); + SetY1(oldY1); + SetR1(oldR1); + SetR2(oldR2); + gPad->ModifiedUpdate(); } break; } @@ -716,23 +711,12 @@ TPoint TEllipse::GetBBoxCenter() return p; } -//////////////////////////////////////////////////////////////////////////////// -/// Set center of the Ellipse - -void TEllipse::SetBBoxCenter(const TPoint &p) -{ - if (!gPad) return; - fX1 = gPad->PixeltoX(p.GetX()); - fY1 = gPad->PixeltoY(p.GetY()-gPad->VtoPixel(0)); -} - //////////////////////////////////////////////////////////////////////////////// /// Set X coordinate of the center of the Ellipse void TEllipse::SetBBoxCenterX(const Int_t x) { - if (!gPad) return; - fX1 = gPad->PixeltoX(x); + SetX1(GetXCoord(x)); } //////////////////////////////////////////////////////////////////////////////// @@ -740,8 +724,7 @@ void TEllipse::SetBBoxCenterX(const Int_t x) void TEllipse::SetBBoxCenterY(const Int_t y) { - if (!gPad) return; - fY1 = gPad->PixeltoY(y-gPad->VtoPixel(0)); + SetY1(GetYCoord(y)); } //////////////////////////////////////////////////////////////////////////////// @@ -750,12 +733,11 @@ void TEllipse::SetBBoxCenterY(const Int_t y) void TEllipse::SetBBoxX1(const Int_t x) { - if (!gPad) return; - Double_t x1 = gPad->PixeltoX(x); - if (x1>fX1+fR1) return; + Double_t x1 = GetXCoord(x); + if (x1 > fX1+fR1) return; - fR1 = (fX1+fR1-x1)*0.5; - fX1 = x1 + fR1; + SetR1((fX1+fR1-x1)*0.5); + SetX1(x1 + fR1); } //////////////////////////////////////////////////////////////////////////////// @@ -764,12 +746,11 @@ void TEllipse::SetBBoxX1(const Int_t x) void TEllipse::SetBBoxX2(const Int_t x) { - if (!gPad) return; - Double_t x2 = gPad->PixeltoX(x); - if (x2PixeltoY(y-gPad->VtoPixel(0)); - if (y1PixeltoY(y-gPad->VtoPixel(0)); - - if (y2>fY1+fR2) return; + Double_t y2 = GetYCoord(y); + if (y2 > fY1+fR2) return; - fR2 = (fY1+fR2-y2)*0.5; - fY1 = y2+fR2; + SetR2((fY1+fR2-y2)*0.5); + SetY1(y2 + fR2); } From a5891f1745f4133c8fc3396ed4a8b066d123b01c Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Thu, 7 May 2026 13:35:50 +0200 Subject: [PATCH 08/26] Optmize TEllipse painting First fill points for painting in the vectors and then perform painting. This clarify paint procedure and let reuse same code in other place --- graf2d/graf/inc/TEllipse.h | 4 ++ graf2d/graf/src/TEllipse.cxx | 85 ++++++++++++++++++++++-------------- 2 files changed, 56 insertions(+), 33 deletions(-) diff --git a/graf2d/graf/inc/TEllipse.h b/graf2d/graf/inc/TEllipse.h index 56e681bdc4b10..9de88ee3f9f19 100644 --- a/graf2d/graf/inc/TEllipse.h +++ b/graf2d/graf/inc/TEllipse.h @@ -17,6 +17,7 @@ #include "TAttLine.h" #include "TAttFill.h" #include "TAttBBox2D.h" +#include class TPoint; @@ -31,6 +32,9 @@ class TEllipse : public TObject, public TAttLine, public TAttFill, public TAttBB Double_t fPhimax; ///< Maximum angle (degrees) Double_t fTheta; ///< Rotation angle (degrees) + Bool_t FillPoints(TVirtualPad &pad, std::vector &x, std::vector &y, + Double_t x1, Double_t y1, Double_t r1, Double_t r2, Double_t phimin, Double_t phimax, Double_t theta); + public: // TEllipse status bits enum { diff --git a/graf2d/graf/src/TEllipse.cxx b/graf2d/graf/src/TEllipse.cxx index 3c0646e4b05d6..045cb4623c821 100644 --- a/graf2d/graf/src/TEllipse.cxx +++ b/graf2d/graf/src/TEllipse.cxx @@ -548,56 +548,75 @@ void TEllipse::ls(Option_t *) const void TEllipse::Paint(Option_t *option) { - PaintEllipse(fX1,fY1,fR1,fR2,fPhimin,fPhimax,fTheta,option); + PaintEllipse(fX1, fY1, fR1, fR2, fPhimin, fPhimax, fTheta, option); } //////////////////////////////////////////////////////////////////////////////// -/// Draw this ellipse with new coordinates. +/// Fill points which can be used for the painting +/// Return true if full 360 ellipse is created -void TEllipse::PaintEllipse(Double_t x1, Double_t y1, Double_t r1, Double_t r2, - Double_t phimin, Double_t phimax, Double_t theta, - Option_t *option) +Bool_t TEllipse::FillPoints(TVirtualPad &pad, std::vector &x, std::vector &y, + Double_t x1, Double_t y1, Double_t r1, Double_t r2, Double_t phimin, Double_t phimax, Double_t theta) { - if (!gPad) return; const Int_t np = 200; - static Double_t x[np+3], y[np+3]; - TAttLine::Modify(); //Change line attributes only if necessary - TAttFill::Modify(); //Change fill attributes only if necessary - Double_t phi1 = TMath::Min(phimin,phimax); - Double_t phi2 = TMath::Max(phimin,phimax); + Double_t phi1 = TMath::Min(phimin, phimax); + Double_t phi2 = TMath::Max(phimin, phimax); //set number of points approximatively proportional to the ellipse circumference Double_t circ = kPI*(r1+r2)*(phi2-phi1)/360; - Int_t n = (Int_t)(np*circ/((gPad->GetX2()-gPad->GetX1())+(gPad->GetY2()-gPad->GetY1()))); - if (n < 8) n= 8; + Int_t n = (Int_t)(np*circ/((pad.GetX2() - pad.GetX1())+(pad.GetY2()-pad.GetY1()))); + if (n < 8) n = 8; if (n > np) n = np; - Double_t angle,dx,dy; + Bool_t full_circle = phi2-phi1 >= 360; + + x.resize(n + (full_circle ? 1 : 3)); + y.resize(n + (full_circle ? 1 : 3)); + Double_t dphi = (phi2-phi1)*kPI/(180*n); Double_t ct = TMath::Cos(kPI*theta/180); Double_t st = TMath::Sin(kPI*theta/180); - for (Int_t i=0;i<=n;i++) { - angle = phi1*kPI/180 + Double_t(i)*dphi; - dx = r1*TMath::Cos(angle); - dy = r2*TMath::Sin(angle); - x[i] = gPad->XtoPad(x1 + dx*ct - dy*st); - y[i] = gPad->YtoPad(y1 + dx*st + dy*ct); + for (Int_t i = 0; i <= n; i++) { + Double_t angle = phi1*kPI/180 + i*dphi; + Double_t dx = r1*TMath::Cos(angle); + Double_t dy = r2*TMath::Sin(angle); + x[i] = pad.XtoPad(x1 + dx*ct - dy*st); + y[i] = pad.YtoPad(y1 + dx*st + dy*ct); } - TString opt = option; - opt.ToLower(); - if (phi2-phi1 >= 360 ) { - if (GetFillStyle()) gPad->PaintFillArea(n,x,y); - if (GetLineStyle()) gPad->PaintPolyLine(n+1,x,y); - } else { - x[n+1] = gPad->XtoPad(x1); - y[n+1] = gPad->YtoPad(y1); + if (!full_circle) { + x[n+1] = pad.XtoPad(x1); + y[n+1] = pad.YtoPad(y1); x[n+2] = x[0]; y[n+2] = y[0]; - if (GetFillStyle()) gPad->PaintFillArea(n+2,x,y); - if (GetLineStyle()) { - if (TestBit(kNoEdges) || opt.Contains("only")) gPad->PaintPolyLine(n+1,x,y); - else gPad->PaintPolyLine(n+3,x,y); - } + } + + return full_circle; +} + + +//////////////////////////////////////////////////////////////////////////////// +/// Draw this ellipse with new coordinates. + +void TEllipse::PaintEllipse(Double_t x1, Double_t y1, Double_t r1, Double_t r2, + Double_t phimin, Double_t phimax, Double_t theta, + Option_t *option) +{ + if (!gPad) return; + + std::vector x, y; + Bool_t full_circle = FillPoints(*gPad, x, y, x1, y1, r1, r2, phimin, phimax, theta); + + TAttFill::ModifyOn(*gPad); //Change fill attributes only if necessary + TAttLine::ModifyOn(*gPad); //Change line attributes only if necessary + + if (GetFillStyle() > 0) + gPad->PaintFillArea(x.size() - 1, x.data(), y.data()); + + if (GetLineStyle() > 0) { + TString opt = option; + opt.ToLower(); + Bool_t less_points = !full_circle && (TestBit(kNoEdges) || opt.Contains("only")); + gPad->PaintPolyLine(x.size() - (less_points ? 2 : 0), x.data(), y.data()); } } From b37cccf202d6518ad7d81135f01a3b4387a3ee94 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Thu, 7 May 2026 15:27:19 +0200 Subject: [PATCH 09/26] Reimplement TEllipse::ExecuteEvent Significantly reduce number of static variables, use markers and FillPoints method to produce painting points for non-opaque paint. Fully avoid gVirtualX --- graf2d/graf/src/TEllipse.cxx | 358 ++++++++++++----------------------- 1 file changed, 123 insertions(+), 235 deletions(-) diff --git a/graf2d/graf/src/TEllipse.cxx b/graf2d/graf/src/TEllipse.cxx index 045cb4623c821..3e477cc722d9a 100644 --- a/graf2d/graf/src/TEllipse.cxx +++ b/graf2d/graf/src/TEllipse.cxx @@ -16,6 +16,7 @@ #include "TBuffer.h" #include "TEllipse.h" #include "TVirtualPad.h" +#include "TVirtualPadPainter.h" #include "TMath.h" #include "TPoint.h" #include "TVirtualX.h" @@ -201,271 +202,167 @@ void TEllipse::ExecuteEvent(Int_t event, Int_t px, Int_t py) { if (!gPad || !gPad->IsEditable()) return; + auto &parent = *gPad; + + auto pp = parent.GetPainter(); + Int_t kMaxDiff = 10; - Int_t dpx, dpy; - Double_t angle,dx,dy,dphi,ct,st,fTy,fBy,fLx,fRx; - static Int_t px1,py1,npe,r1,r2,sav1,sav2; + // Double_t angle,dphi,ct,st,fTy,fBy,fLx,fRx; + static Int_t px1,py1,r1,r2, pTy, pBy, pLx, pRx; + const Int_t kMinSize = 25; - const Int_t np = 40; - static Bool_t pTop, pL, pR, pBot, pINSIDE; - static Int_t pTx,pTy,pLx,pLy,pRx,pRy,pBx,pBy; - static Int_t x[np+2], y[np+2]; + static enum { pNone, pTop, pL, pR, pBot, pINSIDE } mode = pNone; static Int_t pxold, pyold; - static Int_t sig,impair; - static Double_t sdx, sdy; + static Int_t impair = 0; + static Int_t sdx, sdy; static Double_t oldX1, oldY1, oldR1, oldR2; - Bool_t opaque = gPad->OpaqueMoving(); + auto paint_marker = [this,&parent,pp](Int_t dx, Int_t dy) { + Double_t x = GetX1() + dx * GetR1(); + Double_t y = GetY1() + dy * GetR2(); + pp->SetAttMarker({GetLineColor(), 25, 2}); + pp->DrawPolyMarker(1, &x, &y); + }; + + Bool_t opaque = parent.OpaqueMoving(); switch (event) { case kArrowKeyPress: case kButton1Down: - oldX1 = fX1; - oldY1 = fY1; - oldR1 = fR1; - oldR2 = fR2; - dphi = (fPhimax-fPhimin)*kPI/(180*np); - ct = TMath::Cos(kPI*fTheta/180); - st = TMath::Sin(kPI*fTheta/180); - for (Int_t i=0; iXtoAbsPixel(fX1 + dx*ct - dy*st); - y[i] = gPad->YtoAbsPixel(fY1 + dx*st + dy*ct); - } - if (fPhimax-fPhimin >= 360 ) { - x[np] = x[0]; - y[np] = y[0]; - npe = np; - } else { - x[np] = gPad->XtoAbsPixel(fX1); - y[np] = gPad->YtoAbsPixel(fY1); - x[np+1] = x[0]; - y[np+1] = y[0]; - npe = np + 1; - } - impair = 0; - px1 = gPad->XtoAbsPixel(fX1); - py1 = gPad->YtoAbsPixel(fY1); - pTx = pBx = px1; - pLy = pRy = py1; - pTy = gPad->YtoAbsPixel(fR2+fY1); - pBy = gPad->YtoAbsPixel(-fR2+fY1); - pLx = gPad->XtoAbsPixel(-fR1+fX1); - pRx = gPad->XtoAbsPixel(fR1+fX1); - r2 = (pBy-pTy)/2; - r1 = (pRx-pLx)/2; - if (!opaque) { - gVirtualX->SetLineColor(-1); - TAttLine::Modify(); - gVirtualX->DrawLine(pRx+4, py1+4, pRx-4, py1+4); - gVirtualX->DrawLine(pRx-4, py1+4, pRx-4, py1-4); - gVirtualX->DrawLine(pRx-4, py1-4, pRx+4, py1-4); - gVirtualX->DrawLine(pRx+4, py1-4, pRx+4, py1+4); - gVirtualX->DrawLine(pLx+4, py1+4, pLx-4, py1+4); - gVirtualX->DrawLine(pLx-4, py1+4, pLx-4, py1-4); - gVirtualX->DrawLine(pLx-4, py1-4, pLx+4, py1-4); - gVirtualX->DrawLine(pLx+4, py1-4, pLx+4, py1+4); - gVirtualX->DrawLine(px1+4, pBy+4, px1-4, pBy+4); - gVirtualX->DrawLine(px1-4, pBy+4, px1-4, pBy-4); - gVirtualX->DrawLine(px1-4, pBy-4, px1+4, pBy-4); - gVirtualX->DrawLine(px1+4, pBy-4, px1+4, pBy+4); - gVirtualX->DrawLine(px1+4, pTy+4, px1-4, pTy+4); - gVirtualX->DrawLine(px1-4, pTy+4, px1-4, pTy-4); - gVirtualX->DrawLine(px1-4, pTy-4, px1+4, pTy-4); - gVirtualX->DrawLine(px1+4, pTy-4, px1+4, pTy+4); - } - else { - sdx = GetX1() - gPad->AbsPixeltoX(px); - sdy = GetY1() - gPad->AbsPixeltoY(py); - } + oldX1 = fX1; + oldY1 = fY1; + oldR1 = fR1; + oldR2 = fR2; + impair = 0; + + pxold = px1 = parent.XtoAbsPixel(parent.XtoPad(GetX1())); + pyold = py1 = parent.YtoAbsPixel(parent.YtoPad(GetY1())); + sdx = px1 - px; + sdy = py1 - py; + + paint_marker(-1, 0); + paint_marker( 1, 0); + paint_marker( 0, -1); + paint_marker( 0, 1); + // No break !!! case kMouseMotion: - px1 = gPad->XtoAbsPixel(fX1); - py1 = gPad->YtoAbsPixel(fY1); - pTx = pBx = px1; - pLy = pRy = py1; - pTy = gPad->YtoAbsPixel(fR2+fY1); - pBy = gPad->YtoAbsPixel(-fR2+fY1); - pLx = gPad->XtoAbsPixel(-fR1+fX1); - pRx = gPad->XtoAbsPixel(fR1+fX1); - pTop = pL = pR = pBot = pINSIDE = kFALSE; - if ((TMath::Abs(px - pTx) < kMaxDiff) && - (TMath::Abs(py - pTy) < kMaxDiff)) { // top edge - pTop = kTRUE; - gPad->SetCursor(kTopSide); - } - else - if ((TMath::Abs(px - pBx) < kMaxDiff) && - (TMath::Abs(py - pBy) < kMaxDiff)) { // bottom edge - pBot = kTRUE; - gPad->SetCursor(kBottomSide); - } - else - if ((TMath::Abs(py - pLy) < kMaxDiff) && - (TMath::Abs(px - pLx) < kMaxDiff)) { // left edge - pL = kTRUE; - gPad->SetCursor(kLeftSide); - } - else - if ((TMath::Abs(py - pRy) < kMaxDiff) && - (TMath::Abs(px - pRx) < kMaxDiff)) { // right edge - pR = kTRUE; - gPad->SetCursor(kRightSide); + px1 = parent.XtoAbsPixel(parent.XtoPad(GetX1())); + py1 = parent.YtoAbsPixel(parent.YtoPad(GetY1())); + pTy = parent.YtoAbsPixel(parent.YtoPad(GetY1() + GetR2())); + pBy = parent.YtoAbsPixel(parent.YtoPad(GetY1() - GetR2())); + pLx = parent.XtoAbsPixel(parent.XtoPad(GetX1() - GetR1())); + pRx = parent.XtoAbsPixel(parent.XtoPad(GetX1() + GetR1())); + r1 = (pRx - pLx) / 2; + r2 = (pBy - pTy) / 2; + mode = pNone; + if ((TMath::Abs(px - px1) < kMaxDiff) && (TMath::Abs(py - pTy) < kMaxDiff)) { + mode = pTop; // top edge + parent.SetCursor(kTopSide); + } else if ((TMath::Abs(px - px1) < kMaxDiff) && (TMath::Abs(py - pBy) < kMaxDiff)) { + mode = pBot; // bottom edge + parent.SetCursor(kBottomSide); + } else if ((TMath::Abs(py - py1) < kMaxDiff) && (TMath::Abs(px - pLx) < kMaxDiff)) { + mode = pL; // left edge + parent.SetCursor(kLeftSide); + } else if ((TMath::Abs(py - py1) < kMaxDiff) && (TMath::Abs(px - pRx) < kMaxDiff)) { + mode = pR; // right edge + parent.SetCursor(kRightSide); + } else { + mode = pINSIDE; + parent.SetCursor(kMove); } - else {pINSIDE= kTRUE; gPad->SetCursor(kMove); } - pxold = px; pyold = py; + pxold = px; + pyold = py; break; case kArrowKeyRelease: case kButton1Motion: - if (!opaque) - { - gVirtualX->DrawLine(pRx+4, py1+4, pRx-4, py1+4); - gVirtualX->DrawLine(pRx-4, py1+4, pRx-4, py1-4); - gVirtualX->DrawLine(pRx-4, py1-4, pRx+4, py1-4); - gVirtualX->DrawLine(pRx+4, py1-4, pRx+4, py1+4); - gVirtualX->DrawLine(pLx+4, py1+4, pLx-4, py1+4); - gVirtualX->DrawLine(pLx-4, py1+4, pLx-4, py1-4); - gVirtualX->DrawLine(pLx-4, py1-4, pLx+4, py1-4); - gVirtualX->DrawLine(pLx+4, py1-4, pLx+4, py1+4); - gVirtualX->DrawLine(px1+4, pBy+4, px1-4, pBy+4); - gVirtualX->DrawLine(px1-4, pBy+4, px1-4, pBy-4); - gVirtualX->DrawLine(px1-4, pBy-4, px1+4, pBy-4); - gVirtualX->DrawLine(px1+4, pBy-4, px1+4, pBy+4); - gVirtualX->DrawLine(px1+4, pTy+4, px1-4, pTy+4); - gVirtualX->DrawLine(px1-4, pTy+4, px1-4, pTy-4); - gVirtualX->DrawLine(px1-4, pTy-4, px1+4, pTy-4); - gVirtualX->DrawLine(px1+4, pTy-4, px1+4, pTy+4); - for (Int_t i=0;iDrawLine(x[i], y[i], x[i+1], y[i+1]); + if (!opaque) { + pp->SetAttLine(*this); + std::vector x, y; + FillPoints(parent, x, y, fX1, fY1, fR1, fR2, fPhimin, fPhimax, fTheta); + pp->DrawPolyLine(x.size(), x.data(), y.data()); + + paint_marker(-1, 0); + paint_marker( 1, 0); + paint_marker( 0, -1); + paint_marker( 0, 1); } - if (pTop) { - sav1 = py1; - sav2 = r2; + if (mode == pTop) { + Int_t sav1 = py1; + Int_t sav2 = r2; py1 += (py - pyold)/2; r2 -= (py - pyold)/2; if (TMath::Abs(pyold-py)%2==1) impair++; - if (py-pyold>0) sig=+1; - else sig=-1; + Int_t sig = py - pyold > 0 ? 1 : -1; if (impair==2) { impair = 0; py1 += sig; r2 -= sig;} if (py1 > pBy-kMinSize) {py1 = sav1; r2 = sav2; py = pyold;} - } - if (pBot) { - sav1 = py1; - sav2 = r2; + } else if (mode == pBot) { + Int_t sav1 = py1; + Int_t sav2 = r2; py1 += (py - pyold)/2; r2 += (py - pyold)/2; if (TMath::Abs(pyold-py)%2==1) impair++; - if (py-pyold>0) sig=+1; - else sig=-1; + Int_t sig = py - pyold > 0 ? 1 : -1; if (impair==2) { impair = 0; py1 += sig; r2 += sig;} if (py1 < pTy+kMinSize) {py1 = sav1; r2 = sav2; py = pyold;} - } - if (pL) { - sav1 = px1; - sav2 = r1; + } else if (mode == pL) { + Int_t sav1 = px1; + Int_t sav2 = r1; px1 += (px - pxold)/2; r1 -= (px - pxold)/2; if (TMath::Abs(pxold-px)%2==1) impair++; - if (px-pxold>0) sig=+1; - else sig=-1; + Int_t sig = px - pxold > 0 ? 1 : -1; if (impair==2) { impair = 0; px1 += sig; r1 -= sig;} if (px1 > pRx-kMinSize) {px1 = sav1; r1 = sav2; px = pxold;} - } - if (pR) { - sav1 = px1; - sav2 = r1; + } else if (mode == pR) { + Int_t sav1 = px1; + Int_t sav2 = r1; px1 += (px - pxold)/2; r1 += (px - pxold)/2; if (TMath::Abs(pxold-px)%2==1) impair++; - if (px-pxold>0) sig=+1; - else sig=-1; + Int_t sig = px - pxold > 0 ? 1 : -1; if (impair==2) { impair = 0; px1 += sig; r1 += sig;} if (px1 < pLx+kMinSize) {px1 = sav1; r1 = sav2; px = pxold;} } - if (pTop || pBot || pL || pR) { - if (!opaque) { - dphi = (fPhimax-fPhimin)*kPI/(180*np); - ct = TMath::Cos(kPI*fTheta/180); - st = TMath::Sin(kPI*fTheta/180); - for (Int_t i = 0; i < np; i++) { - angle = fPhimin*kPI/180 + Double_t(i)*dphi; - dx = r1*TMath::Cos(angle); - dy = r2*TMath::Sin(angle); - x[i] = px1 + Int_t(dx*ct - dy*st); - y[i] = py1 + Int_t(dx*st + dy*ct); - } - if (fPhimax-fPhimin >= 360 ) { - x[np] = x[0]; - y[np] = y[0]; - npe = np; - } else { - x[np] = px1; - y[np] = py1; - x[np+1] = x[0]; - y[np+1] = y[0]; - npe = np + 1; - } - gVirtualX->SetLineColor(-1); - TAttLine::Modify(); - for (Int_t i=0;iDrawLine(x[i], y[i], x[i+1], y[i+1]); - } - else - { - SetX1(GetXCoord(px1, kFALSE, kTRUE)); - SetY1(GetYCoord(py1, kFALSE, kTRUE)); - SetR1(TMath::Abs(GetXCoord(px1+r1, kFALSE, kTRUE) - GetXCoord(px1-r1, kFALSE, kTRUE)) / 2); - SetR2(TMath::Abs(GetYCoord(py1-r2, kFALSE, kTRUE) - GetYCoord(py1+r2, kFALSE, kTRUE)) / 2); - if (pTop) gPad->ShowGuidelines(this, event, 't', true); - if (pBot) gPad->ShowGuidelines(this, event, 'b', true); - if (pL) gPad->ShowGuidelines(this, event, 'l', true); - if (pR) gPad->ShowGuidelines(this, event, 'r', true); - gPad->ModifiedUpdate(); - } - } - if (pINSIDE) { - if (!opaque){ - dpx = px-pxold; dpy = py-pyold; - px1 += dpx; py1 += dpy; - for (Int_t i=0;i<=npe;i++) { x[i] += dpx; y[i] += dpy;} - for (Int_t i=0;iDrawLine(x[i], y[i], x[i+1], y[i+1]); + if (mode == pTop || mode == pBot || mode == pL || mode == pR) { + SetX1(GetXCoord(px1, kFALSE, kTRUE)); + SetY1(GetYCoord(py1, kFALSE, kTRUE)); + SetR1(TMath::Abs(GetXCoord(px1+r1, kFALSE, kTRUE) - GetXCoord(px1-r1, kFALSE, kTRUE)) / 2); + SetR2(TMath::Abs(GetYCoord(py1-r2, kFALSE, kTRUE) - GetYCoord(py1+r2, kFALSE, kTRUE)) / 2); + + if (opaque) { + if (mode == pTop) parent.ShowGuidelines(this, event, 't', true); + if (mode == pBot) parent.ShowGuidelines(this, event, 'b', true); + if (mode == pL) parent.ShowGuidelines(this, event, 'l', true); + if (mode == pR) parent.ShowGuidelines(this, event, 'r', true); + parent.ModifiedUpdate(); } - else { - SetX1(gPad->AbsPixeltoX(px)+sdx); - SetY1(gPad->AbsPixeltoY(py)+sdy); - gPad->ShowGuidelines(this, event, 'i', true); - gPad->ModifiedUpdate(); + } else if (mode == pINSIDE) { + px1 = px + sdx; + py1 = py + sdy; + SetX1(parent.AbsPixeltoX(px1)); + SetY1(parent.AbsPixeltoY(py1)); + if (opaque){ + parent.ShowGuidelines(this, event, 'i', true); + parent.ModifiedUpdate(); } } if (!opaque){ - pTx = pBx = px1; - pRx = px1+r1; - pLx = px1-r1; - pRy = pLy = py1; - pTy = py1-r2; - pBy = py1+r2; - gVirtualX->DrawLine(pRx+4, py1+4, pRx-4, py1+4); - gVirtualX->DrawLine(pRx-4, py1+4, pRx-4, py1-4); - gVirtualX->DrawLine(pRx-4, py1-4, pRx+4, py1-4); - gVirtualX->DrawLine(pRx+4, py1-4, pRx+4, py1+4); - gVirtualX->DrawLine(pLx+4, py1+4, pLx-4, py1+4); - gVirtualX->DrawLine(pLx-4, py1+4, pLx-4, py1-4); - gVirtualX->DrawLine(pLx-4, py1-4, pLx+4, py1-4); - gVirtualX->DrawLine(pLx+4, py1-4, pLx+4, py1+4); - gVirtualX->DrawLine(px1+4, pBy+4, px1-4, pBy+4); - gVirtualX->DrawLine(px1-4, pBy+4, px1-4, pBy-4); - gVirtualX->DrawLine(px1-4, pBy-4, px1+4, pBy-4); - gVirtualX->DrawLine(px1+4, pBy-4, px1+4, pBy+4); - gVirtualX->DrawLine(px1+4, pTy+4, px1-4, pTy+4); - gVirtualX->DrawLine(px1-4, pTy+4, px1-4, pTy-4); - gVirtualX->DrawLine(px1-4, pTy-4, px1+4, pTy-4); - gVirtualX->DrawLine(px1+4, pTy-4, px1+4, pTy+4); + pp->SetAttLine(*this); + std::vector x, y; + FillPoints(parent, x, y, fX1, fY1, fR1, fR2, fPhimin, fPhimax, fTheta); + pp->DrawPolyLine(x.size(), x.data(), y.data()); + + paint_marker(-1, 0); + paint_marker( 1, 0); + paint_marker( 0, -1); + paint_marker( 0, 1); } pxold = px; pyold = py; @@ -475,30 +372,21 @@ void TEllipse::ExecuteEvent(Int_t event, Int_t px, Int_t py) if (gROOT->IsEscaped()) { gROOT->SetEscape(kFALSE); if (opaque) { - gPad->ShowGuidelines(this, event); + parent.ShowGuidelines(this, event); SetX1(oldX1); SetY1(oldY1); SetR1(oldR1); SetR2(oldR2); - gPad->ModifiedUpdate(); + parent.ModifiedUpdate(); } break; } - if (opaque) { - gPad->ShowGuidelines(this, event); - } else { - fX1 = gPad->AbsPixeltoX(px1); - fY1 = gPad->AbsPixeltoY(py1); - fBy = gPad->AbsPixeltoY(py1+r2); - fTy = gPad->AbsPixeltoY(py1-r2); - fLx = gPad->AbsPixeltoX(px1+r1); - fRx = gPad->AbsPixeltoX(px1-r1); - fR1 = TMath::Abs(fRx-fLx)/2; - fR2 = TMath::Abs(fTy-fBy)/2; - gPad->Modified(kTRUE); - gVirtualX->SetLineColor(-1); - } + if (opaque) + parent.ShowGuidelines(this, event); + else + parent.Modified(kTRUE); + mode = pNone; } } From 1eaf64ddcf1c79ae0713aab86d60075a3d6b50ae Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Mon, 11 May 2026 11:19:31 +0200 Subject: [PATCH 10/26] [padpainter] suppot polyline abs coordinates in non-opaque mode It is used not by TEllipse, can be used in other classes --- graf2d/gpad/src/TPadPainter.cxx | 37 +++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/graf2d/gpad/src/TPadPainter.cxx b/graf2d/gpad/src/TPadPainter.cxx index f39bcb95fe2b3..827104b5a74e6 100644 --- a/graf2d/gpad/src/TPadPainter.cxx +++ b/graf2d/gpad/src/TPadPainter.cxx @@ -31,7 +31,7 @@ using size_type = std::vector::size_type; template void ConvertPoints(TVirtualPad *pad, unsigned nPoints, const T *xs, const T *ys, - std::vector &dst); + std::vector &dst, Bool_t absCoord = kFALSE); inline void MergePointsX(std::vector &points, unsigned nMerged, SCoord_t yMin, SCoord_t yMax, SCoord_t yLast); @@ -50,7 +50,7 @@ template void DrawFillAreaAux(TVirtualPad *pad, WinContext_t cont, Int_t nPoints, const T *xs, const T *ys, Bool_t add_first_point); template -void DrawPolyLineAux(TVirtualPad *pad, WinContext_t cont, unsigned nPoints, const T *xs, const T *ys); +void DrawPolyLineAux(TVirtualPad *pad, WinContext_t cont, Bool_t absCoord, unsigned nPoints, const T *xs, const T *ys); template void DrawPolyMarkerAux(TVirtualPad *pad, WinContext_t cont, Bool_t double_buffer, unsigned nPoints, const T *xs, const T *ys); @@ -365,7 +365,7 @@ void TPadPainter::DrawPolyLine(Int_t n, const Double_t *xs, const Double_t *ys) return; } - DrawPolyLineAux(gPad, fWinContext, n, xs, ys); + DrawPolyLineAux(gPad, fWinContext, !fDoubleBuffer, n, xs, ys); } @@ -382,7 +382,7 @@ void TPadPainter::DrawPolyLine(Int_t n, const Float_t *xs, const Float_t *ys) return; } - DrawPolyLineAux(gPad, fWinContext, n, xs, ys); + DrawPolyLineAux(gPad, fWinContext, !fDoubleBuffer, n, xs, ys); } @@ -628,16 +628,23 @@ namespace { template void ConvertPoints(TVirtualPad *pad, unsigned nPoints, const T *x, const T *y, - std::vector &dst) + std::vector &dst, Bool_t absCoord) { if (!nPoints) return; dst.resize(nPoints); - for (unsigned i = 0; i < nPoints; ++i) { - dst[i].fX = (SCoord_t)pad->XtoPixel(x[i]); - dst[i].fY = (SCoord_t)pad->YtoPixel(y[i]); + if (absCoord) { + for (unsigned i = 0; i < nPoints; ++i) { + dst[i].fX = (SCoord_t) pad->XtoAbsPixel(x[i]); + dst[i].fY = (SCoord_t) pad->YtoAbsPixel(y[i]); + } + } else { + for (unsigned i = 0; i < nPoints; ++i) { + dst[i].fX = (SCoord_t) pad->XtoPixel(x[i]); + dst[i].fY = (SCoord_t) pad->YtoPixel(y[i]); + } } } @@ -846,27 +853,25 @@ void DrawFillAreaAux(TVirtualPad *pad, WinContext_t cont, Int_t nPoints, const T //////////////////////////////////////////////////////////////////////////////// -template -void DrawPolyLineAux(TVirtualPad *pad, WinContext_t cont, unsigned nPoints, const T *xs, const T *ys) +template +void DrawPolyLineAux(TVirtualPad *pad, WinContext_t cont, Bool_t absCoord, unsigned nPoints, const T *xs, const T *ys) { std::vector xy; - const Int_t threshold = Int_t(TMath::Min(pad->GetWw() * pad->GetAbsWNDC(), - pad->GetWh() * pad->GetAbsHNDC())) * 2; + const UInt_t threshold = TMath::Min(pad->GetPadWidth(), pad->GetPadHeight()) * 2; - if (threshold <= 0) {//Ooops, pad is invisible or something really bad and stupid happened. + if (threshold == 0) {//Ooops, pad is invisible or something really bad and stupid happened. ::Error("DrawPolyLineAux", "invalid pad's geometry"); return; } - if (nPoints < (unsigned)threshold) - ConvertPoints(pad, nPoints, xs, ys, xy); + if (absCoord || (nPoints < threshold)) + ConvertPoints(pad, nPoints, xs, ys, xy, absCoord); else ConvertPointsAndMerge(pad, threshold, nPoints, xs, ys, xy); if (xy.size() > 1) gVirtualX->DrawPolyLineW(cont, xy.size(), &xy[0]); - } //////////////////////////////////////////////////////////////////////////////// From b369cb828068a8f38dc03170bc49c0761f2feade Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Mon, 11 May 2026 11:30:02 +0200 Subject: [PATCH 11/26] [glpainter] support polymaker in interactive mode Provide helper method to draw polymarkers in both interactive and normal mode --- graf3d/gl/inc/TGLPadPainter.h | 8 +- graf3d/gl/src/TGLPadPainter.cxx | 157 +++++++++++++++----------------- 2 files changed, 80 insertions(+), 85 deletions(-) diff --git a/graf3d/gl/inc/TGLPadPainter.h b/graf3d/gl/inc/TGLPadPainter.h index b77c8a6178baf..4497c5a1103af 100644 --- a/graf3d/gl/inc/TGLPadPainter.h +++ b/graf3d/gl/inc/TGLPadPainter.h @@ -43,13 +43,17 @@ class TGLPadPainter : public TPadPainterBase { Int_t fVp[4]; - std::vector fPoly; Bool_t fIsHollowArea; Bool_t fLocked; + Bool_t IsInteractiveMode(); + void SelectGLFont(Font_t font, Float_t size); + template + void DrawPolyMarkerHelper(Int_t n, const ValueType *x, const ValueType *y); + template void DrawTextHelper(Double_t x, Double_t y, const Char_t *text, ETextMode mode); @@ -144,8 +148,6 @@ class TGLPadPainter : public TPadPainterBase { void SaveViewport(); void RestoreViewport(); - void DrawPolyMarker(); - //Aux. functions for a gradient and solid fill: void DrawPolygonWithGradient(Int_t n, const Double_t *x, const Double_t *y); // diff --git a/graf3d/gl/src/TGLPadPainter.cxx b/graf3d/gl/src/TGLPadPainter.cxx index 83e4ebca52504..26c7268928f7b 100644 --- a/graf3d/gl/src/TGLPadPainter.cxx +++ b/graf3d/gl/src/TGLPadPainter.cxx @@ -597,47 +597,45 @@ void TGLPadPainter::DrawPolyLineNDC(Int_t n, const Double_t *u, const Double_t * glEnd(); } -namespace { - -//Aux. function. -template -void ConvertMarkerPoints(Int_t n, const ValueType *x, const ValueType *y, std::vector & dst); - -} - //////////////////////////////////////////////////////////////////////////////// -///Poly-marker. +/// Returns true when invert mode is configured and painter in locked state +/// Used when non-opaque of objects moving is involved -void TGLPadPainter::DrawPolyMarker(Int_t n, const Double_t *x, const Double_t *y) +Bool_t TGLPadPainter::IsInteractiveMode() { - if (fLocked) return; - - ConvertMarkerPoints(n, x, y, fPoly); - DrawPolyMarker(); + return fLocked && fWinContext && (gVirtualX->GetDrawModeW(fWinContext) == TVirtualX::kInvert); } //////////////////////////////////////////////////////////////////////////////// -///Poly-marker. +///Poly-marker drawing -void TGLPadPainter::DrawPolyMarker(Int_t n, const Float_t *x, const Float_t *y) +template +void TGLPadPainter::DrawPolyMarkerHelper(Int_t n, const ValueType *x, const ValueType *y) { - if (fLocked) return; + std::vector poly(n); - ConvertMarkerPoints(n, x, y, fPoly); - DrawPolyMarker(); -} + if (fLocked) { + if (!IsInteractiveMode()) + return; + for (Int_t i = 0; i < n; ++i) { + poly[i].fX = gPad->XtoAbsPixel(x[i]); + poly[i].fY = gPad->YtoAbsPixel(y[i]); + } + gVirtualX->DrawPolyMarkerW(fWinContext, poly.size(), poly.data()); + return; + } -//////////////////////////////////////////////////////////////////////////////// -///Poly-marker. + const UInt_t padH = gPad->GetPadHeight(); -void TGLPadPainter::DrawPolyMarker() -{ - if (fLocked) return; + for (Int_t i = 0; i < n; ++i) { + poly[i].fX = gPad->XtoPixel(x[i]); + poly[i].fY = padH - gPad->YtoPixel(y[i]); + } SaveProjectionMatrix(); glLoadIdentity(); // - glOrtho(0, gPad->GetAbsWNDC() * gPad->GetWw(), 0, gPad->GetAbsHNDC() * gPad->GetWh(), -10., 10.); + glOrtho(0, gPad->GetPadWidth(), 0, gPad->GetPadHeight(), -10., 10.); // glMatrixMode(GL_MODELVIEW); // @@ -648,127 +646,124 @@ void TGLPadPainter::DrawPolyMarker() glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glColor4fv(rgba); - const Width_t w = TMath::Max(1, Int_t(TAttMarker::GetMarkerLineWidth(GetAttMarker().GetMarkerStyle()))); glLineWidth(w > fLimits.GetMaxLineWidth() ? fLimits.GetMaxLineWidth() : !w ? 1.f : w); fMarker.SetMarkerSizeWidth(GetAttMarker().GetMarkerSize(), w); - const TPoint *xy = &fPoly[0]; const Style_t markerStyle = TAttMarker::GetMarkerStyleBase(GetAttMarker().GetMarkerStyle()); - const UInt_t n = UInt_t(fPoly.size()); switch (markerStyle) { case kDot: - fMarker.DrawDot(n, xy); + fMarker.DrawDot(n, poly.data()); break; case kPlus: - fMarker.DrawPlus(n, xy); + fMarker.DrawPlus(n, poly.data()); break; case kStar: case 31: - fMarker.DrawStar(n, xy); + fMarker.DrawStar(n, poly.data()); break; case kCircle: case kOpenCircle: - fMarker.DrawCircle(n, xy); + fMarker.DrawCircle(n, poly.data()); break; case kMultiply: - fMarker.DrawX(n, xy); + fMarker.DrawX(n, poly.data()); break; case kFullDotSmall://"Full dot small" - fMarker.DrawFullDotSmall(n, xy); + fMarker.DrawFullDotSmall(n, poly.data()); break; case kFullDotMedium: - fMarker.DrawFullDotMedium(n, xy); + fMarker.DrawFullDotMedium(n, poly.data()); break; case kFullDotLarge: case kFullCircle: - fMarker.DrawFullDotLarge(n, xy); + fMarker.DrawFullDotLarge(n, poly.data()); break; case kFullSquare: - fMarker.DrawFullSquare(n, xy); + fMarker.DrawFullSquare(n, poly.data()); break; case kFullTriangleUp: - fMarker.DrawFullTrianlgeUp(n, xy); + fMarker.DrawFullTrianlgeUp(n, poly.data()); break; case kFullTriangleDown: - fMarker.DrawFullTrianlgeDown(n, xy); + fMarker.DrawFullTrianlgeDown(n, poly.data()); break; case kOpenSquare: glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - fMarker.DrawFullSquare(n, xy); + fMarker.DrawFullSquare(n, poly.data()); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); break; case kOpenTriangleUp: glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - fMarker.DrawFullTrianlgeUp(n, xy); + fMarker.DrawFullTrianlgeUp(n, poly.data()); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); break; case kOpenDiamond: - fMarker.DrawDiamond(n, xy); + fMarker.DrawDiamond(n, poly.data()); break; case kOpenCross: - fMarker.DrawOpenCross(n, xy); + fMarker.DrawOpenCross(n, poly.data()); break; case kFullStar: - fMarker.DrawFullStar(n, xy); + fMarker.DrawFullStar(n, poly.data()); break; case kOpenStar: - fMarker.DrawOpenStar(n, xy); + fMarker.DrawOpenStar(n, poly.data()); break; case kOpenTriangleDown: - fMarker.DrawOpenTrianlgeDown(n, xy); + fMarker.DrawOpenTrianlgeDown(n, poly.data()); break; case kFullDiamond: - fMarker.DrawFullDiamond(n, xy); + fMarker.DrawFullDiamond(n, poly.data()); break; case kFullCross: - fMarker.DrawFullCross(n, xy); + fMarker.DrawFullCross(n, poly.data()); break; case kOpenDiamondCross: - fMarker.DrawOpenDiamondCross(n, xy); + fMarker.DrawOpenDiamondCross(n, poly.data()); break; case kOpenSquareDiagonal: - fMarker.DrawOpenSquareDiagonal(n, xy); + fMarker.DrawOpenSquareDiagonal(n, poly.data()); break; case kOpenThreeTriangles: - fMarker.DrawOpenThreeTriangles(n, xy); + fMarker.DrawOpenThreeTriangles(n, poly.data()); break; case kOctagonCross: - fMarker.DrawOctagonCross(n, xy); + fMarker.DrawOctagonCross(n, poly.data()); break; case kFullThreeTriangles: - fMarker.DrawFullThreeTriangles(n, xy); + fMarker.DrawFullThreeTriangles(n, poly.data()); break; case kOpenFourTrianglesX: - fMarker.DrawOpenFourTrianglesX(n, xy); + fMarker.DrawOpenFourTrianglesX(n, poly.data()); break; case kFullFourTrianglesX: - fMarker.DrawFullFourTrianglesX(n, xy); + fMarker.DrawFullFourTrianglesX(n, poly.data()); break; case kOpenDoubleDiamond: - fMarker.DrawOpenDoubleDiamond(n, xy); + fMarker.DrawOpenDoubleDiamond(n, poly.data()); break; case kFullDoubleDiamond: - fMarker.DrawFullDoubleDiamond(n, xy); + fMarker.DrawFullDoubleDiamond(n, poly.data()); break; case kOpenFourTrianglesPlus: - fMarker.DrawOpenFourTrianglesPlus(n, xy); + fMarker.DrawOpenFourTrianglesPlus(n, poly.data()); break; case kFullFourTrianglesPlus: - fMarker.DrawFullFourTrianglesPlus(n, xy); + fMarker.DrawFullFourTrianglesPlus(n, poly.data()); break; case kOpenCrossX: - fMarker.DrawOpenCrossX(n, xy); + fMarker.DrawOpenCrossX(n, poly.data()); break; case kFullCrossX: - fMarker.DrawFullCrossX(n, xy); + fMarker.DrawFullCrossX(n, poly.data()); break; case kFourSquaresX: - fMarker.DrawFourSquaresX(n, xy); + fMarker.DrawFourSquaresX(n, poly.data()); break; case kFourSquaresPlus: - fMarker.DrawFourSquaresPlus(n, xy); + fMarker.DrawFourSquaresPlus(n, poly.data()); break; } @@ -777,6 +772,22 @@ void TGLPadPainter::DrawPolyMarker() glLineWidth(1.f); } +//////////////////////////////////////////////////////////////////////////////// +///Poly-marker. + +void TGLPadPainter::DrawPolyMarker(Int_t n, const Double_t *x, const Double_t *y) +{ + DrawPolyMarkerHelper(n, x, y); +} + +//////////////////////////////////////////////////////////////////////////////// +///Poly-marker. + +void TGLPadPainter::DrawPolyMarker(Int_t n, const Float_t *x, const Float_t *y) +{ + DrawPolyMarkerHelper(n, x, y); +} + //////////////////////////////////////////////////////////////////////////////// /// Select specified font/size @@ -1540,21 +1551,3 @@ void TGLPadPainter::DrawTesselation(Int_t n, const Double_t *x, const Double_t * gluEndPolygon(t); } - -//Aux. functions. -namespace { - -template -void ConvertMarkerPoints(Int_t n, const ValueType *x, const ValueType *y, std::vector & dst) -{ - const UInt_t padH = UInt_t(gPad->GetAbsHNDC() * gPad->GetWh()); - - dst.resize(n); - for (Int_t i = 0; i < n; ++i) { - dst[i].fX = gPad->XtoPixel(x[i]); - dst[i].fY = padH - gPad->YtoPixel(y[i]); - } -} - -} - From 6a200c44925bfd2a577f8ae82d4e1221d1c45b29 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Mon, 11 May 2026 12:43:31 +0200 Subject: [PATCH 12/26] [glpainter] support polyline in invert mode Used now by some classes like TEllipse in ExecuteEvent --- graf3d/gl/inc/TGLPadPainter.h | 5 ++- graf3d/gl/src/TGLPadPainter.cxx | 67 +++++++++++++++++---------------- 2 files changed, 38 insertions(+), 34 deletions(-) diff --git a/graf3d/gl/inc/TGLPadPainter.h b/graf3d/gl/inc/TGLPadPainter.h index 4497c5a1103af..082534627c988 100644 --- a/graf3d/gl/inc/TGLPadPainter.h +++ b/graf3d/gl/inc/TGLPadPainter.h @@ -47,13 +47,16 @@ class TGLPadPainter : public TPadPainterBase { Bool_t fLocked; - Bool_t IsInteractiveMode(); + Bool_t IsInvertMode(); void SelectGLFont(Font_t font, Float_t size); template void DrawPolyMarkerHelper(Int_t n, const ValueType *x, const ValueType *y); + template + void DrawPolyLineHelper(Int_t n, const ValueType *x, const ValueType *y); + template void DrawTextHelper(Double_t x, Double_t y, const Char_t *text, ETextMode mode); diff --git a/graf3d/gl/src/TGLPadPainter.cxx b/graf3d/gl/src/TGLPadPainter.cxx index 26c7268928f7b..7d4bf07608428 100644 --- a/graf3d/gl/src/TGLPadPainter.cxx +++ b/graf3d/gl/src/TGLPadPainter.cxx @@ -355,11 +355,10 @@ void TGLPadPainter::DrawLine(Double_t x1, Double_t y1, Double_t x2, Double_t y2) //that TView3D wants to draw itself in a XOR mode, via //gVirtualX. // TODO: only here set line attributes to virtual x - if (fWinContext && (gVirtualX->GetDrawModeW(fWinContext) == TVirtualX::kInvert)) { + if (IsInvertMode()) gVirtualX->DrawLineW(fWinContext, gPad->XtoAbsPixel(x1), gPad->YtoAbsPixel(y1), gPad->XtoAbsPixel(x2), gPad->YtoAbsPixel(y2)); - } return; } @@ -395,13 +394,11 @@ void TGLPadPainter::DrawLine(Double_t x1, Double_t y1, Double_t x2, Double_t y2) void TGLPadPainter::DrawLineNDC(Double_t u1, Double_t v1, Double_t u2, Double_t v2) { if (fLocked) { - // this code used when crosshair cursor is drawn - if (fWinContext && (gVirtualX->GetDrawModeW(fWinContext) == TVirtualX::kInvert)) { - // TODO: only here set line attributes to virtual x + // this code used when crosshair cursor is drawn or interactive objects move + if (IsInvertMode()) gVirtualX->DrawLineW(fWinContext, gPad->UtoAbsPixel(u1), gPad->VtoAbsPixel(v1), gPad->UtoAbsPixel(u2), gPad->VtoAbsPixel(v2)); - } return; } @@ -427,12 +424,11 @@ void TGLPadPainter::DrawBox(Double_t x1, Double_t y1, Double_t x2, Double_t y2, //that TView3D wants to draw itself in a XOR mode, via //gVirtualX. // TODO: only here set line attributes to virtual x - if (fWinContext && (gVirtualX->GetDrawModeW(fWinContext) == TVirtualX::kInvert)) { + if (IsInvertMode()) gVirtualX->DrawBoxW(fWinContext, gPad->XtoAbsPixel(x1), gPad->YtoAbsPixel(y1), gPad->XtoAbsPixel(x2), gPad->YtoAbsPixel(y2), (TVirtualX::EBoxMode) mode); - } return; } @@ -521,9 +517,20 @@ void TGLPadPainter::DrawFillArea(Int_t n, const Float_t *x, const Float_t *y) //////////////////////////////////////////////////////////////////////////////// ///Draw poly-line in user coordinates. -void TGLPadPainter::DrawPolyLine(Int_t n, const Double_t *x, const Double_t *y) +template +void TGLPadPainter::DrawPolyLineHelper(Int_t n, const ValueType *x, const ValueType *y) { - if (fLocked) return; + if (fLocked) { + if (IsInvertMode() && (n > 1)) { + std::vector xy(n); + for (Int_t i = 0; i < n; ++i) { + xy[i].fX = (SCoord_t) gPad->XtoAbsPixel(x[i]); + xy[i].fY = (SCoord_t) gPad->YtoAbsPixel(y[i]); + } + gVirtualX->DrawPolyLineW(fWinContext, xy.size(), xy.data()); + } + return; + } const Rgl::Pad::LineAttribSet lineAttribs(kTRUE, GetAttLine().GetLineStyle(), fLimits.GetMaxLineWidth(), kTRUE, &GetAttLine()); @@ -556,25 +563,19 @@ void TGLPadPainter::DrawPolyLine(Int_t n, const Double_t *x, const Double_t *y) } //////////////////////////////////////////////////////////////////////////////// -///Never called? +/// Draw poly-line in user coordinates. -void TGLPadPainter::DrawPolyLine(Int_t n, const Float_t *x, const Float_t *y) +void TGLPadPainter::DrawPolyLine(Int_t n, const Double_t *x, const Double_t *y) { - if (fLocked) return; - - const Rgl::Pad::LineAttribSet lineAttribs(kTRUE, GetAttLine().GetLineStyle(), fLimits.GetMaxLineWidth(), kTRUE, &GetAttLine()); - - glBegin(GL_LINE_STRIP); - - for (Int_t i = 0; i < n; ++i) - glVertex2f(x[i], y[i]); + DrawPolyLineHelper(n, x, y); +} - if (fIsHollowArea) { - glVertex2f(x[0], y[0]); - fIsHollowArea = kFALSE; - } +//////////////////////////////////////////////////////////////////////////////// +/// Draw poly-line in user coordinates. - glEnd(); +void TGLPadPainter::DrawPolyLine(Int_t n, const Float_t *x, const Float_t *y) +{ + DrawPolyLineHelper(n, x, y); } //////////////////////////////////////////////////////////////////////////////// @@ -601,9 +602,9 @@ void TGLPadPainter::DrawPolyLineNDC(Int_t n, const Double_t *u, const Double_t * /// Returns true when invert mode is configured and painter in locked state /// Used when non-opaque of objects moving is involved -Bool_t TGLPadPainter::IsInteractiveMode() +Bool_t TGLPadPainter::IsInvertMode() { - return fLocked && fWinContext && (gVirtualX->GetDrawModeW(fWinContext) == TVirtualX::kInvert); + return fWinContext && (gVirtualX->GetDrawModeW(fWinContext) == TVirtualX::kInvert); } //////////////////////////////////////////////////////////////////////////////// @@ -615,13 +616,13 @@ void TGLPadPainter::DrawPolyMarkerHelper(Int_t n, const ValueType *x, const Valu std::vector poly(n); if (fLocked) { - if (!IsInteractiveMode()) - return; - for (Int_t i = 0; i < n; ++i) { - poly[i].fX = gPad->XtoAbsPixel(x[i]); - poly[i].fY = gPad->YtoAbsPixel(y[i]); + if (IsInvertMode()) { + for (Int_t i = 0; i < n; ++i) { + poly[i].fX = gPad->XtoAbsPixel(x[i]); + poly[i].fY = gPad->YtoAbsPixel(y[i]); + } + gVirtualX->DrawPolyMarkerW(fWinContext, poly.size(), poly.data()); } - gVirtualX->DrawPolyMarkerW(fWinContext, poly.size(), poly.data()); return; } From 87ccfaf235f359238f921494013ae2abe97af047 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Mon, 11 May 2026 16:19:48 +0200 Subject: [PATCH 13/26] Further simplification of TEllipse::ExecuteEvent Now all 4 markers can be drawn together. Simplify logic how borders are handled, handle swap Fix coordinates calculation --- graf2d/graf/src/TEllipse.cxx | 210 ++++++++++++++--------------------- 1 file changed, 86 insertions(+), 124 deletions(-) diff --git a/graf2d/graf/src/TEllipse.cxx b/graf2d/graf/src/TEllipse.cxx index 3e477cc722d9a..6a11bd03622e7 100644 --- a/graf2d/graf/src/TEllipse.cxx +++ b/graf2d/graf/src/TEllipse.cxx @@ -208,21 +208,42 @@ void TEllipse::ExecuteEvent(Int_t event, Int_t px, Int_t py) Int_t kMaxDiff = 10; - // Double_t angle,dphi,ct,st,fTy,fBy,fLx,fRx; - static Int_t px1,py1,r1,r2, pTy, pBy, pLx, pRx; - - const Int_t kMinSize = 25; static enum { pNone, pTop, pL, pR, pBot, pINSIDE } mode = pNone; - static Int_t pxold, pyold; - static Int_t impair = 0; - static Int_t sdx, sdy; + static Int_t sdx = 0, sdy = 0; static Double_t oldX1, oldY1, oldR1, oldR2; + static Bool_t first_move = kTRUE; - auto paint_marker = [this,&parent,pp](Int_t dx, Int_t dy) { - Double_t x = GetX1() + dx * GetR1(); - Double_t y = GetY1() + dy * GetR2(); + auto paint_hollow = [this,&parent,pp]() { + pp->SetAttLine(*this); + std::vector x, y; + FillPoints(parent, x, y, GetX1(), GetY1(), GetR1(), GetR2(), GetPhimin(), GetPhimax(), GetTheta()); + pp->DrawPolyLine(x.size(), x.data(), y.data()); pp->SetAttMarker({GetLineColor(), 25, 2}); - pp->DrawPolyMarker(1, &x, &y); + Double_t xm[4] = { GetX1(), GetX1(), GetX1() - GetR1(), GetX1() + GetR1() }; + Double_t ym[4] = { GetY1() + GetR2(), GetY1() - GetR2(), GetY1(), GetY1() }; + for (Int_t i = 0; i < 4; ++i) { + xm[i] = parent.XtoPad(xm[i]); + ym[i] = parent.YtoPad(ym[i]); + } + pp->DrawPolyMarker(4, xm, ym); + }; + + auto changeX = [this](Int_t px1, Int_t px2) { + auto x1 = GetXCoord(px1, kFALSE, kTRUE); + auto x2 = GetXCoord(px2, kFALSE, kTRUE); + SetX1((x1 + x2) * 0.5); + SetR1(TMath::Abs((x2 - x1) * 0.5)); + if (x2 < x1) + mode = (mode == pL) ? pR : pL; + }; + + auto changeY = [this](Int_t py1, Int_t py2) { + auto y1 = GetYCoord(py1, kFALSE, kTRUE); + auto y2 = GetYCoord(py2, kFALSE, kTRUE); + SetY1((y1 + y2) * 0.5); + SetR2(TMath::Abs((y1 - y2) * 0.5)); + if (y1 < y2) + mode = (mode == pTop) ? pBot : pTop; }; Bool_t opaque = parent.OpaqueMoving(); @@ -231,33 +252,23 @@ void TEllipse::ExecuteEvent(Int_t event, Int_t px, Int_t py) case kArrowKeyPress: case kButton1Down: - oldX1 = fX1; - oldY1 = fY1; - oldR1 = fR1; - oldR2 = fR2; - impair = 0; - - pxold = px1 = parent.XtoAbsPixel(parent.XtoPad(GetX1())); - pyold = py1 = parent.YtoAbsPixel(parent.YtoPad(GetY1())); - sdx = px1 - px; - sdy = py1 - py; - - paint_marker(-1, 0); - paint_marker( 1, 0); - paint_marker( 0, -1); - paint_marker( 0, 1); + oldX1 = GetX1(); + oldY1 = GetY1(); + oldR1 = GetR1(); + oldR2 = GetR2(); + + sdx = parent.XtoAbsPixel(parent.XtoPad(GetX1())) - px; + sdy = parent.YtoAbsPixel(parent.YtoPad(GetY1())) - py; // No break !!! - case kMouseMotion: - px1 = parent.XtoAbsPixel(parent.XtoPad(GetX1())); - py1 = parent.YtoAbsPixel(parent.YtoPad(GetY1())); - pTy = parent.YtoAbsPixel(parent.YtoPad(GetY1() + GetR2())); - pBy = parent.YtoAbsPixel(parent.YtoPad(GetY1() - GetR2())); - pLx = parent.XtoAbsPixel(parent.XtoPad(GetX1() - GetR1())); - pRx = parent.XtoAbsPixel(parent.XtoPad(GetX1() + GetR1())); - r1 = (pRx - pLx) / 2; - r2 = (pBy - pTy) / 2; + case kMouseMotion: { + Int_t px1 = parent.XtoAbsPixel(parent.XtoPad(GetX1())); + Int_t py1 = parent.YtoAbsPixel(parent.YtoPad(GetY1())); + Int_t pLx = parent.XtoAbsPixel(parent.XtoPad(GetX1() - GetR1())); + Int_t pRx = parent.XtoAbsPixel(parent.XtoPad(GetX1() + GetR1())); + Int_t pBy = parent.YtoAbsPixel(parent.YtoPad(GetY1() - GetR2())); + Int_t pTy = parent.YtoAbsPixel(parent.YtoPad(GetY1() + GetR2())); mode = pNone; if ((TMath::Abs(px - px1) < kMaxDiff) && (TMath::Abs(py - pTy) < kMaxDiff)) { mode = pTop; // top edge @@ -275,98 +286,50 @@ void TEllipse::ExecuteEvent(Int_t event, Int_t px, Int_t py) mode = pINSIDE; parent.SetCursor(kMove); } - pxold = px; - pyold = py; - + first_move = kTRUE; break; + } case kArrowKeyRelease: - case kButton1Motion: - if (!opaque) { - pp->SetAttLine(*this); - std::vector x, y; - FillPoints(parent, x, y, fX1, fY1, fR1, fR2, fPhimin, fPhimax, fTheta); - pp->DrawPolyLine(x.size(), x.data(), y.data()); - - paint_marker(-1, 0); - paint_marker( 1, 0); - paint_marker( 0, -1); - paint_marker( 0, 1); - } - if (mode == pTop) { - Int_t sav1 = py1; - Int_t sav2 = r2; - py1 += (py - pyold)/2; - r2 -= (py - pyold)/2; - if (TMath::Abs(pyold-py)%2==1) impair++; - Int_t sig = py - pyold > 0 ? 1 : -1; - if (impair==2) { impair = 0; py1 += sig; r2 -= sig;} - if (py1 > pBy-kMinSize) {py1 = sav1; r2 = sav2; py = pyold;} - } else if (mode == pBot) { - Int_t sav1 = py1; - Int_t sav2 = r2; - py1 += (py - pyold)/2; - r2 += (py - pyold)/2; - if (TMath::Abs(pyold-py)%2==1) impair++; - Int_t sig = py - pyold > 0 ? 1 : -1; - if (impair==2) { impair = 0; py1 += sig; r2 += sig;} - if (py1 < pTy+kMinSize) {py1 = sav1; r2 = sav2; py = pyold;} - } else if (mode == pL) { - Int_t sav1 = px1; - Int_t sav2 = r1; - px1 += (px - pxold)/2; - r1 -= (px - pxold)/2; - if (TMath::Abs(pxold-px)%2==1) impair++; - Int_t sig = px - pxold > 0 ? 1 : -1; - if (impair==2) { impair = 0; px1 += sig; r1 -= sig;} - if (px1 > pRx-kMinSize) {px1 = sav1; r1 = sav2; px = pxold;} - } else if (mode == pR) { - Int_t sav1 = px1; - Int_t sav2 = r1; - px1 += (px - pxold)/2; - r1 += (px - pxold)/2; - if (TMath::Abs(pxold-px)%2==1) impair++; - Int_t sig = px - pxold > 0 ? 1 : -1; - if (impair==2) { impair = 0; px1 += sig; r1 += sig;} - if (px1 < pLx+kMinSize) {px1 = sav1; r1 = sav2; px = pxold;} - } - if (mode == pTop || mode == pBot || mode == pL || mode == pR) { - SetX1(GetXCoord(px1, kFALSE, kTRUE)); - SetY1(GetYCoord(py1, kFALSE, kTRUE)); - SetR1(TMath::Abs(GetXCoord(px1+r1, kFALSE, kTRUE) - GetXCoord(px1-r1, kFALSE, kTRUE)) / 2); - SetR2(TMath::Abs(GetYCoord(py1-r2, kFALSE, kTRUE) - GetYCoord(py1+r2, kFALSE, kTRUE)) / 2); - - if (opaque) { - if (mode == pTop) parent.ShowGuidelines(this, event, 't', true); - if (mode == pBot) parent.ShowGuidelines(this, event, 'b', true); - if (mode == pL) parent.ShowGuidelines(this, event, 'l', true); - if (mode == pR) parent.ShowGuidelines(this, event, 'r', true); - parent.ModifiedUpdate(); - } - } else if (mode == pINSIDE) { - px1 = px + sdx; - py1 = py + sdy; - SetX1(parent.AbsPixeltoX(px1)); - SetY1(parent.AbsPixeltoY(py1)); - if (opaque){ - parent.ShowGuidelines(this, event, 'i', true); - parent.ModifiedUpdate(); - } - } - if (!opaque){ - pp->SetAttLine(*this); - std::vector x, y; - FillPoints(parent, x, y, fX1, fY1, fR1, fR2, fPhimin, fPhimax, fTheta); - pp->DrawPolyLine(x.size(), x.data(), y.data()); - - paint_marker(-1, 0); - paint_marker( 1, 0); - paint_marker( 0, -1); - paint_marker( 0, 1); + case kButton1Motion: { + if (mode == pNone) + break; + if (!opaque && !first_move) + paint_hollow(); + char guide = 'i'; + switch (mode) { + case pNone: + break; + case pL: + changeX(px, parent.XtoAbsPixel(parent.XtoPad(GetX1() + GetR1()))); + guide = 'l'; + break; + case pR: + changeX(parent.XtoAbsPixel(parent.XtoPad(GetX1() - GetR1())), px); + guide = 'r'; + break; + case pTop: + changeY(py, parent.YtoAbsPixel(parent.YtoPad(GetY1() - GetR2()))); + guide = 't'; + break; + case pBot: + changeY(parent.YtoAbsPixel(parent.YtoPad(GetY1() + GetR2())), py); + guide = 'b'; + break; + case pINSIDE: + SetX1(GetXCoord(px + sdx, kFALSE, kTRUE)); + SetY1(GetYCoord(py + sdy, kFALSE, kTRUE)); + guide = 'i'; + break; } - pxold = px; - pyold = py; + first_move = kFALSE; + if (opaque) { + parent.ShowGuidelines(this, event, guide, true); + parent.ModifiedUpdate(); + } else + paint_hollow(); break; + } case kButton1Up: if (gROOT->IsEscaped()) { @@ -454,9 +417,8 @@ Bool_t TEllipse::FillPoints(TVirtualPad &pad, std::vector &x, std::vec //set number of points approximatively proportional to the ellipse circumference Double_t circ = kPI*(r1+r2)*(phi2-phi1)/360; Int_t n = (Int_t)(np*circ/((pad.GetX2() - pad.GetX1())+(pad.GetY2()-pad.GetY1()))); - if (n < 8) n = 8; - if (n > np) n = np; Bool_t full_circle = phi2-phi1 >= 360; + n = TMath::Min(np, TMath::Max(n, (Int_t) (full_circle ? 36 : 8))); x.resize(n + (full_circle ? 1 : 3)); y.resize(n + (full_circle ? 1 : 3)); From 8736849d7edc0817ef82325844165f8afaf8d52e Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Tue, 12 May 2026 07:40:50 +0200 Subject: [PATCH 14/26] [quartz] Add marker support to xor operations Allow to draw marker also from ExecuteEvent Move line and fill attributes handling in QuartzMarker util - avoid code duplication --- graf2d/cocoa/inc/QuartzWindow.h | 3 ++ graf2d/cocoa/inc/X11Buffer.h | 26 +++++++++++++--- graf2d/cocoa/src/QuartzWindow.mm | 23 +++++++++++++- graf2d/cocoa/src/TGQuartz.mm | 52 ++++++++++--------------------- graf2d/cocoa/src/X11Buffer.mm | 31 +++++++++++++----- graf2d/quartz/inc/QuartzMarker.h | 5 ++- graf2d/quartz/src/QuartzMarker.mm | 39 ++++++++++++++++++----- 7 files changed, 121 insertions(+), 58 deletions(-) diff --git a/graf2d/cocoa/inc/QuartzWindow.h b/graf2d/cocoa/inc/QuartzWindow.h index 79c5c3c0d999e..ae21a464459dc 100644 --- a/graf2d/cocoa/inc/QuartzWindow.h +++ b/graf2d/cocoa/inc/QuartzWindow.h @@ -19,6 +19,8 @@ #include "X11Events.h" #include "GuiTypes.h" +class TAttMarker; + namespace ROOT { namespace MacOSX { namespace X11 { @@ -134,6 +136,7 @@ class Command; - (void) removeXorWindow; - (void) addXorLine : (QuartzView *) view : (Int_t) x1 : (Int_t) y1 : (Int_t) x2 : (Int_t) y2; - (void) addXorBox : (QuartzView *) view : (Int_t) x1 : (Int_t) y1 : (Int_t) x2 : (Int_t) y2; +- (void) addXorMarker : (QuartzView *) view : (Int_t) n : (TPoint *) pnts : (const TAttMarker &) att; - (void) setDrawMode : (TVirtualX::EDrawMode) newMode; - (TVirtualX::EDrawMode) getDrawMode; diff --git a/graf2d/cocoa/inc/X11Buffer.h b/graf2d/cocoa/inc/X11Buffer.h index 75c52cc3b7bde..5348680206bcf 100644 --- a/graf2d/cocoa/inc/X11Buffer.h +++ b/graf2d/cocoa/inc/X11Buffer.h @@ -19,6 +19,8 @@ #include "CocoaGuiTypes.h" #include "GuiTypes.h" +#include "TAttMarker.h" +#include "TPoint.h" ////////////////////////////////////////////////////////////////////////////////// // // @@ -231,13 +233,29 @@ class DrawLineXor : public Command { public: DrawLineXor(Window_t windowID, const Point &p1, const Point &p2); - void Execute()const; - void Execute(CGContextRef ctx)const; + void Execute() const {} + void Execute(CGContextRef ctx) const; - Point start() const {return fP1;} - Point end() const {return fP2;} + Point start() const { return fP1; } + Point end() const { return fP2; } }; +class DrawMarkerXor : public Command { +private: + std::vector fPnts; + TAttMarker fAtt; + float fScaleFactor = 1.; + +public: + DrawMarkerXor(Window_t windowID, const TAttMarker &att) : + Command(windowID, GCValues_t()), fAtt(att) {} + void setPoints(Int_t n, TPoint *xy); + + void Execute() const {} + void Execute(CGContextRef ctx) const; +}; + + class CommandBuffer { private: CommandBuffer(const CommandBuffer &rhs); diff --git a/graf2d/cocoa/src/QuartzWindow.mm b/graf2d/cocoa/src/QuartzWindow.mm index 46a260a6fde8e..a9cced1f93166 100644 --- a/graf2d/cocoa/src/QuartzWindow.mm +++ b/graf2d/cocoa/src/QuartzWindow.mm @@ -1559,7 +1559,7 @@ - (void) addXorLine : (QuartzView *) view : (Int_t) x1 : (Int_t) y1 : (Int_t) x2 auto xorWindow = [self addXorWindow]; try { - std::unique_ptr cmd(new ROOT::MacOSX::X11::DrawLineXor(-1, ROOT::MacOSX::X11::Point(x1, y1), ROOT::MacOSX::X11::Point(x2, y2))); + auto cmd = std::make_unique(-1, ROOT::MacOSX::X11::Point(x1, y1), ROOT::MacOSX::X11::Point(x2, y2)); cmd->setView(view); auto cv = (XorDrawingView *)xorWindow.contentView; @@ -1590,6 +1590,27 @@ - (void) addXorBox : (QuartzView *) view : (Int_t) x1 : (Int_t) y1 : (Int_t) x2 } } +//______________________________________________________________________________ +- (void) addXorMarker : (QuartzView *) view : (Int_t) n : (TPoint *) pnts : (const TAttMarker &) att +{ + auto xorWindow = [self addXorWindow]; + + try { + auto cmd = std::make_unique(-1, att); + cmd->setView(view); + cmd->setPoints(n, pnts); + + auto cv = (XorDrawingView *)xorWindow.contentView; + [cv addXorCommand : cmd.get()]; + cmd.release(); + [cv setNeedsDisplay : YES]; + + } catch (const std::exception &) { + throw; + } + +} + //______________________________________________________________________________ - (void) setDrawMode : (TVirtualX::EDrawMode) newMode diff --git a/graf2d/cocoa/src/TGQuartz.mm b/graf2d/cocoa/src/TGQuartz.mm index c85d39c7a9f95..34732fecf12a3 100644 --- a/graf2d/cocoa/src/TGQuartz.mm +++ b/graf2d/cocoa/src/TGQuartz.mm @@ -372,57 +372,39 @@ void ConvertPointsROOTToCocoa(Int_t nPoints, const TPoint *xy, std::vector * const) wctxt; if (!drawable0) return; - //Do some checks first. - if ([drawable0 isDirectDraw]) - return; - auto &attmark = GetAttMarker(wctxt); + if ([drawable0 isDirectDraw]) { + if (!drawable0.fIsPixmap) { + QuartzView * const view = (QuartzView *)fPimpl->GetWindow(drawable0.fID).fContentView; + if (!view) { + ::Warning("DrawPolyMarkerW", "Invalid view/window for XOR-mode"); + return; + } + + [view.fQuartzWindow addXorMarker: view : n : xy : attmark ]; + } + + return; + } auto drawable = (NSObject * const) GetPixmapDrawable(drawable0, "DrawPolyMarkerW"); if (!drawable) return; + ConvertPointsROOTToCocoa(n, xy, fConvertedPoints, drawable); + CGContextRef ctx = drawable.fContext; const Quartz::CGStateGuard ctxGuard(ctx); //AA flag is not a part of a state. const Quartz::CGAAStateGuard aaCtxGuard(ctx, fUseAA); - if (!Quartz::SetFillColor(ctx, attmark.GetMarkerColor())) { - Error("DrawPolyMarker", "Could not find TColor for index %d", attmark.GetMarkerColor()); - return; - } - - Quartz::SetLineColor(ctx, attmark.GetMarkerColor());//Can not fail (for coverity). - Quartz::SetLineStyle(ctx, 1); - Quartz::SetLineWidth(ctx, TMath::Max(1, Int_t(TAttMarker::GetMarkerLineWidth(attmark.GetMarkerStyle())))); - - ConvertPointsROOTToCocoa(n, xy, fConvertedPoints, drawable); - - if (drawable.fScaleFactor > 1.) - CGContextScaleCTM(ctx, 1. / drawable.fScaleFactor, 1. / drawable.fScaleFactor); - - Style_t markerstyle = TAttMarker::GetMarkerStyleBase(attmark.GetMarkerStyle()); - - // The fast pixel markers need to be treated separately - if (markerstyle == 1 || markerstyle == 6 || markerstyle == 7) { - CGContextSetLineJoin(ctx, kCGLineJoinMiter); - CGContextSetLineCap(ctx, kCGLineCapButt); - } else { - CGContextSetLineJoin(ctx, kCGLineJoinRound); - CGContextSetLineCap(ctx, kCGLineCapRound); - } - - Float_t MarkerSizeReduced = GetMarkerSize() - TMath::Floor(TAttMarker::GetMarkerLineWidth(attmark.GetMarkerStyle())/2.)/4.; - Quartz::DrawPolyMarker(ctx, n, &fConvertedPoints[0], MarkerSizeReduced * drawable.fScaleFactor, markerstyle); - - CGContextSetLineJoin(ctx, kCGLineJoinMiter); - CGContextSetLineCap(ctx, kCGLineCapButt); + Quartz::DrawPolyMarker(ctx, n, fConvertedPoints.data(), attmark, drawable.fScaleFactor); } //______________________________________________________________________________ diff --git a/graf2d/cocoa/src/X11Buffer.mm b/graf2d/cocoa/src/X11Buffer.mm index 76cc0ed250f1e..7a7eba2a18142 100644 --- a/graf2d/cocoa/src/X11Buffer.mm +++ b/graf2d/cocoa/src/X11Buffer.mm @@ -22,6 +22,7 @@ #include "ROOTOpenGLView.h" #include "CocoaPrivate.h" #include "QuartzWindow.h" +#include "QuartzMarker.h" #include "QuartzPixmap.h" #include "QuartzUtils.h" #include "X11Drawable.h" @@ -303,13 +304,7 @@ } //______________________________________________________________________________ -void DrawLineXor::Execute()const -{ - //Noop. -} - -//______________________________________________________________________________ -void DrawLineXor::Execute(CGContextRef ctx)const +void DrawLineXor::Execute(CGContextRef ctx) const { assert(ctx && "Execute, invalid (nullptr) parameter 'ctx'"); @@ -326,6 +321,28 @@ CGContextStrokePath(ctx); } +//______________________________________________________________________________ +void DrawMarkerXor::setPoints(Int_t n, TPoint *xy) +{ + fPnts.resize(n); + for (Int_t i = 0; i < n; ++i) { + auto point = NSPoint{CGFloat(xy[i].fX), CGFloat(xy[i].fY)}; + point = [view convertPoint : point toView : nil]; + fPnts[i].fX = (SCoord_t) point.x; + fPnts[i].fY = (SCoord_t) point.y; + } +} + +//______________________________________________________________________________ +void DrawMarkerXor::Execute(CGContextRef ctx) const +{ + assert(ctx && "Execute, invalid (nullptr) parameter 'ctx'"); + + const Quartz::CGStateGuard ctxGuard(ctx); + + Quartz::DrawPolyMarker(ctx, fPnts.size(), fPnts.data(), fAtt, fScaleFactor); +} + //______________________________________________________________________________ CommandBuffer::CommandBuffer() { diff --git a/graf2d/quartz/inc/QuartzMarker.h b/graf2d/quartz/inc/QuartzMarker.h index 9191799fcc4d6..533a85d9e1594 100644 --- a/graf2d/quartz/inc/QuartzMarker.h +++ b/graf2d/quartz/inc/QuartzMarker.h @@ -27,14 +27,13 @@ #include "Rtypes.h" #include "TPoint.h" +#include "TAttMarker.h" namespace ROOT { namespace Quartz { -void DrawPolyMarker(CGContextRef ctx, const std::vector &marker, - Size_t markerSize, Style_t markerStyle); void DrawPolyMarker(CGContextRef ctx, unsigned nPoints, const TPoint *marker, - Size_t markerSize, Style_t markerStyle); + const TAttMarker &attmark, float scaleFactor); } } diff --git a/graf2d/quartz/src/QuartzMarker.mm b/graf2d/quartz/src/QuartzMarker.mm index 87af296b0cb6a..ee2cd6771c785 100644 --- a/graf2d/quartz/src/QuartzMarker.mm +++ b/graf2d/quartz/src/QuartzMarker.mm @@ -12,6 +12,9 @@ #include "TAttMarker.h" #include "QuartzMarker.h" +#include "QuartzLine.h" +#include "QuartzFillArea.h" +#include "TMath.h" namespace ROOT { namespace Quartz { @@ -906,8 +909,32 @@ void DrawMarkerFourSquaresPlus(CGContextRef ctx, unsigned n, const TPoint *xy, //______________________________________________________________________________ void DrawPolyMarker(CGContextRef ctx, unsigned nPoints, const TPoint *xy, - Size_t markerSize, Style_t markerStyle) + const TAttMarker &attmark, float scaleFactor) { + if (!Quartz::SetFillColor(ctx, attmark.GetMarkerColor())) + return; + + Quartz::SetLineColor(ctx, attmark.GetMarkerColor());//Can not fail (for coverity). + Quartz::SetLineStyle(ctx, 1); + Quartz::SetLineWidth(ctx, TMath::Max(1, Int_t(TAttMarker::GetMarkerLineWidth(attmark.GetMarkerStyle())))); + + if (scaleFactor > 1.) + CGContextScaleCTM(ctx, 1. / scaleFactor, 1. / scaleFactor); + + Style_t markerStyle = TAttMarker::GetMarkerStyleBase(attmark.GetMarkerStyle()); + + // The fast pixel markers need to be treated separately + if (markerStyle == 1 || markerStyle == 6 || markerStyle == 7) { + CGContextSetLineJoin(ctx, kCGLineJoinMiter); + CGContextSetLineCap(ctx, kCGLineCapButt); + } else { + CGContextSetLineJoin(ctx, kCGLineJoinRound); + CGContextSetLineCap(ctx, kCGLineCapRound); + } + + Float_t markerSize = attmark.GetMarkerSize() - TMath::Floor(TAttMarker::GetMarkerLineWidth(attmark.GetMarkerStyle())/2.)/4.; + markerSize *= scaleFactor; + switch (markerStyle) { case kDot: DrawMarkerDot(ctx, nPoints, xy, markerSize); @@ -1018,15 +1045,11 @@ void DrawPolyMarker(CGContextRef ctx, unsigned nPoints, const TPoint *xy, DrawMarkerFourSquaresPlus(ctx, nPoints, xy, markerSize); break; } -} - -//______________________________________________________________________________ -void DrawPolyMarker(CGContextRef ctx, const std::vector &xy, - Size_t markerSize, Style_t markerStyle) -{ - DrawPolyMarker(ctx, xy.size(), &xy[0], markerSize, markerStyle); + CGContextSetLineJoin(ctx, kCGLineJoinMiter); + CGContextSetLineCap(ctx, kCGLineCapButt); } + }//namespace Quartz }//namespace ROOT From 25ce4e4b72516c1bd16f6faba67bd5d1612a53b4 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Tue, 12 May 2026 11:09:48 +0200 Subject: [PATCH 15/26] [quartz] support polyline in xor operations Used in many ExecuteEvent methods therefore better handle single points buffer with line attributes instead of multiple calls to gVirtualX->DrawLine --- graf2d/cocoa/inc/QuartzWindow.h | 2 ++ graf2d/cocoa/inc/X11Buffer.h | 17 +++++++++++++++++ graf2d/cocoa/src/QuartzWindow.mm | 22 ++++++++++++++++++++-- graf2d/cocoa/src/TGQuartz.mm | 17 ++++++++++++++--- graf2d/cocoa/src/X11Buffer.mm | 32 ++++++++++++++++++++++++++++++++ graf2d/quartz/inc/QuartzLine.h | 2 +- graf2d/quartz/src/QuartzLine.mm | 2 +- 7 files changed, 87 insertions(+), 7 deletions(-) diff --git a/graf2d/cocoa/inc/QuartzWindow.h b/graf2d/cocoa/inc/QuartzWindow.h index ae21a464459dc..aab99954186fd 100644 --- a/graf2d/cocoa/inc/QuartzWindow.h +++ b/graf2d/cocoa/inc/QuartzWindow.h @@ -20,6 +20,7 @@ #include "GuiTypes.h" class TAttMarker; +class TAttLine; namespace ROOT { namespace MacOSX { @@ -136,6 +137,7 @@ class Command; - (void) removeXorWindow; - (void) addXorLine : (QuartzView *) view : (Int_t) x1 : (Int_t) y1 : (Int_t) x2 : (Int_t) y2; - (void) addXorBox : (QuartzView *) view : (Int_t) x1 : (Int_t) y1 : (Int_t) x2 : (Int_t) y2; +- (void) addXorPolyLine : (QuartzView *) view : (Int_t) n : (TPoint *) pnts : (const TAttLine &) att; - (void) addXorMarker : (QuartzView *) view : (Int_t) n : (TPoint *) pnts : (const TAttMarker &) att; - (void) setDrawMode : (TVirtualX::EDrawMode) newMode; - (TVirtualX::EDrawMode) getDrawMode; diff --git a/graf2d/cocoa/inc/X11Buffer.h b/graf2d/cocoa/inc/X11Buffer.h index 5348680206bcf..555075340db60 100644 --- a/graf2d/cocoa/inc/X11Buffer.h +++ b/graf2d/cocoa/inc/X11Buffer.h @@ -20,6 +20,7 @@ #include "CocoaGuiTypes.h" #include "GuiTypes.h" #include "TAttMarker.h" +#include "TAttLine.h" #include "TPoint.h" ////////////////////////////////////////////////////////////////////////////////// @@ -240,6 +241,22 @@ class DrawLineXor : public Command { Point end() const { return fP2; } }; +class DrawPolyLineXor : public Command { +private: + std::vector fPnts; + TAttLine fAtt; + float fScaleFactor = 1.; + +public: + DrawPolyLineXor(Window_t windowID, const TAttLine &att) : + Command(windowID, GCValues_t()), fAtt(att) {} + void setPoints(Int_t n, TPoint *xy); + + void Execute() const {} + void Execute(CGContextRef ctx) const; +}; + + class DrawMarkerXor : public Command { private: std::vector fPnts; diff --git a/graf2d/cocoa/src/QuartzWindow.mm b/graf2d/cocoa/src/QuartzWindow.mm index a9cced1f93166..2d21811ce7ed9 100644 --- a/graf2d/cocoa/src/QuartzWindow.mm +++ b/graf2d/cocoa/src/QuartzWindow.mm @@ -1590,6 +1590,26 @@ - (void) addXorBox : (QuartzView *) view : (Int_t) x1 : (Int_t) y1 : (Int_t) x2 } } +//______________________________________________________________________________ +- (void) addXorPolyLine : (QuartzView *) view : (Int_t) n : (TPoint *) pnts : (const TAttLine &) att +{ + auto xorWindow = [self addXorWindow]; + + try { + auto cmd = std::make_unique(-1, att); + cmd->setView(view); + cmd->setPoints(n, pnts); + + auto cv = (XorDrawingView *)xorWindow.contentView; + [cv addXorCommand : cmd.get()]; + cmd.release(); + [cv setNeedsDisplay : YES]; + + } catch (const std::exception &) { + throw; + } +} + //______________________________________________________________________________ - (void) addXorMarker : (QuartzView *) view : (Int_t) n : (TPoint *) pnts : (const TAttMarker &) att { @@ -1608,10 +1628,8 @@ - (void) addXorMarker : (QuartzView *) view : (Int_t) n : (TPoint *) pnts : (con } catch (const std::exception &) { throw; } - } - //______________________________________________________________________________ - (void) setDrawMode : (TVirtualX::EDrawMode) newMode { diff --git a/graf2d/cocoa/src/TGQuartz.mm b/graf2d/cocoa/src/TGQuartz.mm index 34732fecf12a3..7c6f7f8b19998 100644 --- a/graf2d/cocoa/src/TGQuartz.mm +++ b/graf2d/cocoa/src/TGQuartz.mm @@ -316,11 +316,22 @@ void ConvertPointsROOTToCocoa(Int_t nPoints, const TPoint *xy, std::vectorGetWindow(drawable0.fID).fContentView; + if (!view) { + ::Warning("DrawPolyLineW", "Invalid view/window for XOR-mode"); + return; + } - auto &attline = GetAttLine(wctxt); + [view.fQuartzWindow addXorPolyLine: view : n : xy : attline ]; + } + + return; + } auto drawable = (NSObject * const) GetPixmapDrawable(drawable0, "DrawPolyLineW"); if (!drawable) diff --git a/graf2d/cocoa/src/X11Buffer.mm b/graf2d/cocoa/src/X11Buffer.mm index 7a7eba2a18142..ea3fc1738817c 100644 --- a/graf2d/cocoa/src/X11Buffer.mm +++ b/graf2d/cocoa/src/X11Buffer.mm @@ -26,6 +26,7 @@ #include "QuartzPixmap.h" #include "QuartzUtils.h" #include "X11Drawable.h" +#include "QuartzLine.h" #include "X11Buffer.h" #include "TGWindow.h" #include "TGClient.h" @@ -321,6 +322,37 @@ CGContextStrokePath(ctx); } +//______________________________________________________________________________ +void DrawPolyLineXor::setPoints(Int_t n, TPoint *xy) +{ + fPnts.resize(n); + for (Int_t i = 0; i < n; ++i) { + auto point = NSPoint{CGFloat(xy[i].fX), CGFloat(xy[i].fY)}; + point = [view convertPoint : point toView : nil]; + fPnts[i].fX = (SCoord_t) point.x; + fPnts[i].fY = (SCoord_t) point.y; + } +} + +//______________________________________________________________________________ +void DrawPolyLineXor::Execute(CGContextRef ctx) const +{ + assert(ctx && "Execute, invalid (nullptr) parameter 'ctx'"); + + const Quartz::CGStateGuard ctxGuard(ctx); + + if (!Quartz::SetLineColor(ctx, fAtt.GetLineColor())) + return; + + Quartz::SetLineStyle(ctx, fAtt.GetLineStyle()); + Quartz::SetLineWidth(ctx, fAtt.GetLineWidth()); + + if (fScaleFactor > 1.) + CGContextScaleCTM(ctx, 1. / fScaleFactor, 1. / fScaleFactor); + + Quartz::DrawPolyLine(ctx, fPnts.size(), fPnts.data()); +} + //______________________________________________________________________________ void DrawMarkerXor::setPoints(Int_t n, TPoint *xy) { diff --git a/graf2d/quartz/inc/QuartzLine.h b/graf2d/quartz/inc/QuartzLine.h index cff6c96459af6..762f82f669c1e 100644 --- a/graf2d/quartz/inc/QuartzLine.h +++ b/graf2d/quartz/inc/QuartzLine.h @@ -34,7 +34,7 @@ void SetLineStyle(CGContextRef ctx, Int_t lstyle); void SetLineWidth(CGContextRef ctx, Int_t width); void DrawLine(CGContextRef ctx, Int_t x1, Int_t y1, Int_t x2, Int_t y2); -void DrawPolyLine(CGContextRef ctx, Int_t n, TPoint * xy); +void DrawPolyLine(CGContextRef ctx, Int_t n, const TPoint *xy); } } diff --git a/graf2d/quartz/src/QuartzLine.mm b/graf2d/quartz/src/QuartzLine.mm index 36e33e7c133d7..9568073919a5d 100644 --- a/graf2d/quartz/src/QuartzLine.mm +++ b/graf2d/quartz/src/QuartzLine.mm @@ -133,7 +133,7 @@ void DrawLine(CGContextRef ctx, Int_t x1, Int_t y1, Int_t x2, Int_t y2) //______________________________________________________________________________ -void DrawPolyLine(CGContextRef ctx, Int_t n, TPoint * xy) +void DrawPolyLine(CGContextRef ctx, Int_t n, const TPoint *xy) { // Draw a line through all points. // n : number of points From 144a61e36a7d5829b80c01baefc432b14852290d Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Tue, 12 May 2026 11:23:46 +0200 Subject: [PATCH 16/26] [quartz] use override syntax, use temporary points buffer TGQuartz use internal vector for coordinates conversion Do not keep it while vector can be deleted afterwards --- graf2d/cocoa/inc/TGQuartz.h | 4 -- graf2d/cocoa/inc/X11Buffer.h | 64 ++++++++++++++++---------------- graf2d/cocoa/src/QuartzWindow.mm | 2 +- graf2d/cocoa/src/TGQuartz.mm | 20 ++++++---- graf2d/cocoa/src/X11Buffer.mm | 4 -- 5 files changed, 46 insertions(+), 48 deletions(-) diff --git a/graf2d/cocoa/inc/TGQuartz.h b/graf2d/cocoa/inc/TGQuartz.h index e64fe08adcdb3..325046ab70ec1 100644 --- a/graf2d/cocoa/inc/TGQuartz.h +++ b/graf2d/cocoa/inc/TGQuartz.h @@ -95,10 +95,6 @@ class TGQuartz : public TGCocoa { private: - //Unfortunately, I have to convert from - //top-left to bottom-left corner system. - std::vector fConvertedPoints; - //Lines with AA can be quite different //from what we always had with X11. //Now this is a switch in our configuration file (system.rootrc), diff --git a/graf2d/cocoa/inc/X11Buffer.h b/graf2d/cocoa/inc/X11Buffer.h index 555075340db60..be7792274b9e7 100644 --- a/graf2d/cocoa/inc/X11Buffer.h +++ b/graf2d/cocoa/inc/X11Buffer.h @@ -57,11 +57,11 @@ class Command { Command(Drawable_t wid, const GCValues_t &gc); virtual ~Command(); - virtual bool HasOperand(Drawable_t drawable)const; - virtual bool IsGraphicsCommand()const;//By-default - false. + virtual bool HasOperand(Drawable_t drawable) const; + virtual bool IsGraphicsCommand() const;//By-default - false. - virtual void Execute()const = 0; - virtual void Execute(CGContextRef /*ctx*/)const; + virtual void Execute() const = 0; + virtual void Execute(CGContextRef /*ctx*/) const; void setView(NSView *v) { @@ -79,8 +79,8 @@ class DrawLine : public Command { public: DrawLine(Drawable_t wid, const GCValues_t &gc, const Point &p1, const Point &p2); - void Execute()const; - bool IsGraphicsCommand()const + void Execute() const override; + bool IsGraphicsCommand() const override { return true; } @@ -92,8 +92,8 @@ class DrawSegments : public Command { public: DrawSegments(Drawable_t wid, const GCValues_t &gc, const Segment_t *segments, Int_t nSegments); - void Execute()const; - bool IsGraphicsCommand()const + void Execute() const override; + bool IsGraphicsCommand() const override { return true; } @@ -105,8 +105,8 @@ class ClearArea : public Command { public: ClearArea(Window_t wid, const Rectangle_t &area); - void Execute()const; - bool IsGraphicsCommand()const + void Execute() const override; + bool IsGraphicsCommand() const override { return true; } @@ -121,13 +121,13 @@ class CopyArea : public Command { public: CopyArea(Drawable_t src, Drawable_t dst, const GCValues_t &gc, const Rectangle_t &area, const Point &dstPoint); - bool HasOperand(Drawable_t drawable)const; - bool IsGraphicsCommand()const + bool HasOperand(Drawable_t drawable) const override; + bool IsGraphicsCommand() const override { return true; } - void Execute()const; + void Execute() const override; }; @@ -139,12 +139,12 @@ class DrawString : public Command { public: DrawString(Drawable_t wid, const GCValues_t &gc, const Point &point, const std::string &text); - bool IsGraphicsCommand()const + bool IsGraphicsCommand() const override { return true; } - void Execute()const; + void Execute() const override; }; class FillRectangle : public Command { @@ -154,12 +154,12 @@ class FillRectangle : public Command { public: FillRectangle(Drawable_t wid, const GCValues_t &gc, const Rectangle_t &rectangle); - bool IsGraphicsCommand()const + bool IsGraphicsCommand() const override { return true; } - void Execute()const; + void Execute() const override; }; class FillPolygon : public Command { @@ -169,12 +169,12 @@ class FillPolygon : public Command { public: FillPolygon(Drawable_t wid, const GCValues_t &gc, const Point_t *points, Int_t nPoints); - bool IsGraphicsCommand()const + bool IsGraphicsCommand() const override { return true; } - void Execute()const; + void Execute() const override; }; class DrawRectangle : public Command { @@ -184,12 +184,12 @@ class DrawRectangle : public Command { public: DrawRectangle(Drawable_t wid, const GCValues_t &gc, const Rectangle_t &rectangle); - bool IsGraphicsCommand()const + bool IsGraphicsCommand() const override { return true; } - void Execute()const; + void Execute() const override; }; class UpdateWindow : public Command { @@ -199,18 +199,18 @@ class UpdateWindow : public Command { public: UpdateWindow(QuartzView *view); - bool IsGraphicsCommand()const + bool IsGraphicsCommand() const override { return true; } - void Execute()const; + void Execute() const override; }; class DeletePixmap : public Command { public: DeletePixmap(Pixmap_t pixmap); - void Execute()const; + void Execute() const override; }; //Set of 'xor' operations, required by TCanvas and ExecuteEvent's machinery. @@ -222,8 +222,8 @@ class DrawBoxXor : public Command { public: DrawBoxXor(Window_t windowID, const Point &p1, const Point &p2); - void Execute()const; - void Execute(CGContextRef ctx)const; + void Execute() const override {} + void Execute(CGContextRef ctx) const override; }; class DrawLineXor : public Command { @@ -234,8 +234,8 @@ class DrawLineXor : public Command { public: DrawLineXor(Window_t windowID, const Point &p1, const Point &p2); - void Execute() const {} - void Execute(CGContextRef ctx) const; + void Execute() const override {} + void Execute(CGContextRef ctx) const override; Point start() const { return fP1; } Point end() const { return fP2; } @@ -252,8 +252,8 @@ class DrawPolyLineXor : public Command { Command(windowID, GCValues_t()), fAtt(att) {} void setPoints(Int_t n, TPoint *xy); - void Execute() const {} - void Execute(CGContextRef ctx) const; + void Execute() const override {} + void Execute(CGContextRef ctx) const override; }; @@ -268,8 +268,8 @@ class DrawMarkerXor : public Command { Command(windowID, GCValues_t()), fAtt(att) {} void setPoints(Int_t n, TPoint *xy); - void Execute() const {} - void Execute(CGContextRef ctx) const; + void Execute() const override {} + void Execute(CGContextRef ctx) const override; }; diff --git a/graf2d/cocoa/src/QuartzWindow.mm b/graf2d/cocoa/src/QuartzWindow.mm index 2d21811ce7ed9..e4d8b0a8d0d51 100644 --- a/graf2d/cocoa/src/QuartzWindow.mm +++ b/graf2d/cocoa/src/QuartzWindow.mm @@ -1578,7 +1578,7 @@ - (void) addXorBox : (QuartzView *) view : (Int_t) x1 : (Int_t) y1 : (Int_t) x2 auto xorWindow = [self addXorWindow]; try { - std::unique_ptr cmd(new ROOT::MacOSX::X11::DrawBoxXor(-1, ROOT::MacOSX::X11::Point(x1, y1), ROOT::MacOSX::X11::Point(x2, y2))); + auto cmd = std::make_unique(-1, ROOT::MacOSX::X11::Point(x1, y1), ROOT::MacOSX::X11::Point(x2, y2)); cmd->setView(view); auto cv = (XorDrawingView *)xorWindow.contentView; diff --git a/graf2d/cocoa/src/TGQuartz.mm b/graf2d/cocoa/src/TGQuartz.mm index 7c6f7f8b19998..7ee0508a2c785 100644 --- a/graf2d/cocoa/src/TGQuartz.mm +++ b/graf2d/cocoa/src/TGQuartz.mm @@ -203,8 +203,10 @@ void ConvertPointsROOTToCocoa(Int_t nPoints, const TPoint *xy, std::vector pnts; + //Convert points to bottom-left system: - ConvertPointsROOTToCocoa(n, xy, fConvertedPoints, drawable); + ConvertPointsROOTToCocoa(n, xy, pnts, drawable); const Quartz::CGStateGuard ctxGuard(ctx); //AA flag is not a part of a state. @@ -223,7 +225,7 @@ void ConvertPointsROOTToCocoa(Int_t nPoints, const TPoint *xy, std::vector(fillColor)) { Quartz::DrawPolygonWithGradientFill(ctx, gradient, CGSizeMake(drawable.fWidth, drawable.fHeight), - n, &fConvertedPoints[0], kFALSE);//kFALSE == don't draw a shadow. + pnts.size(), pnts.data(), kFALSE);//kFALSE == don't draw a shadow. } else { unsigned patternIndex = 0; if (!Quartz::SetFillAreaParameters(ctx, &patternIndex, attfill)) { @@ -233,7 +235,7 @@ void ConvertPointsROOTToCocoa(Int_t nPoints, const TPoint *xy, std::vector pnts; + //Convert to bottom-left-corner system. - ConvertPointsROOTToCocoa(n, xy, fConvertedPoints, drawable); + ConvertPointsROOTToCocoa(n, xy, pnts, drawable); if (drawable.fScaleFactor > 1.) CGContextScaleCTM(ctx, 1. / drawable.fScaleFactor, 1. / drawable.fScaleFactor); - Quartz::DrawPolyLine(ctx, n, &fConvertedPoints[0]); + Quartz::DrawPolyLine(ctx, pnts.size(), pnts.data()); // CTM (current transformation matrix) is restored by 'ctxGuard's dtor. } @@ -408,14 +412,16 @@ void ConvertPointsROOTToCocoa(Int_t nPoints, const TPoint *xy, std::vector pnts; + + ConvertPointsROOTToCocoa(n, xy, pnts, drawable); CGContextRef ctx = drawable.fContext; const Quartz::CGStateGuard ctxGuard(ctx); //AA flag is not a part of a state. const Quartz::CGAAStateGuard aaCtxGuard(ctx, fUseAA); - Quartz::DrawPolyMarker(ctx, n, fConvertedPoints.data(), attmark, drawable.fScaleFactor); + Quartz::DrawPolyMarker(ctx, pnts.size(), pnts.data(), attmark, drawable.fScaleFactor); } //______________________________________________________________________________ diff --git a/graf2d/cocoa/src/X11Buffer.mm b/graf2d/cocoa/src/X11Buffer.mm index ea3fc1738817c..77ad33109e51f 100644 --- a/graf2d/cocoa/src/X11Buffer.mm +++ b/graf2d/cocoa/src/X11Buffer.mm @@ -276,10 +276,6 @@ } //______________________________________________________________________________ -void DrawBoxXor::Execute()const -{ - //Noop. -} const auto rootToNs = [](Point rp) { return NSPoint{CGFloat(rp.fX), CGFloat(rp.fY)}; From 1b7c4aadb08a849d43a9e0f21e789cee4ccba363 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Tue, 12 May 2026 15:38:01 +0200 Subject: [PATCH 17/26] [glpainter] use GL_LINE_LOOP for box border It is more direct usage of line drawings in GL instead of using `glRectd` with disabled polygon drawing --- graf3d/gl/src/TGLPadPainter.cxx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/graf3d/gl/src/TGLPadPainter.cxx b/graf3d/gl/src/TGLPadPainter.cxx index 7d4bf07608428..58649477b6453 100644 --- a/graf3d/gl/src/TGLPadPainter.cxx +++ b/graf3d/gl/src/TGLPadPainter.cxx @@ -441,11 +441,12 @@ void TGLPadPainter::DrawBox(Double_t x1, Double_t y1, Double_t x2, Double_t y2, if (mode == kHollow) { const Rgl::Pad::LineAttribSet lineAttribs(kTRUE, 0, fLimits.GetMaxLineWidth(), kTRUE, &GetAttLine()); - // - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - glRectd(x1, y1, x2, y2); - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - glLineWidth(1.f); + glBegin(GL_LINE_LOOP); + glVertex2d(x1, y1); + glVertex2d(x2, y1); + glVertex2d(x2, y2); + glVertex2d(x1, y2); + glEnd(); } else { const Rgl::Pad::FillAttribSet fillAttribs(fSSet, kFALSE, &fGlFillAtt);//Set filling parameters. glRectd(x1, y1, x2, y2); From 04054d3f9a53b28c1535f2157d9c0e3758b0e717 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Wed, 13 May 2026 07:22:12 +0200 Subject: [PATCH 18/26] Update stressGraphics for ellipse Now TEllipse paint at least 36 points when full 2Pi angle is covered Updating SVG which include ellipses Update tlatex0 ref values while it make small dots --- test/stressGraphics.ref | 2 +- test/stressGraphics_zlibng.ref | 2 +- test/svg_ref/tlatex0.svg | 192 ++++++++++++++++++++++++--------- test/svg_ref/waves.svg | 12 +-- 4 files changed, 152 insertions(+), 56 deletions(-) diff --git a/test/stressGraphics.ref b/test/stressGraphics.ref index c94bd663a397f..fe80e763bd830 100644 --- a/test/stressGraphics.ref +++ b/test/stressGraphics.ref @@ -9,7 +9,7 @@ piechart 67345 200 77066 200 32180 15000 29168 15000 66962 200 ttext1 1025 150 12862 200 33468 9900 30846 5000 1072 200 ttext2 430 50 12729 50 9558 100 5325 700 471 50 - tlatex0 6857 50 15580 50 47722 7000 64693 12000 6890 70 + tlatex0 9558 50 19030 70 47722 7000 64693 12000 9683 70 tlatex1 5130 50 14090 50 16143 1300 12230 500 5170 70 tlatex2 5442 80 13533 50 18430 700 12398 300 5469 80 tlatex3 9253 100 14437 150 19851 2400 12199 900 9283 100 diff --git a/test/stressGraphics_zlibng.ref b/test/stressGraphics_zlibng.ref index da4ccbd7b3a72..1aa4af2beee9b 100644 --- a/test/stressGraphics_zlibng.ref +++ b/test/stressGraphics_zlibng.ref @@ -9,7 +9,7 @@ piechart 67345 200 74560 3000 32180 15000 29168 15000 66962 200 ttext1 1025 150 12866 150 32266 9900 29901 5000 1072 200 ttext2 432 50 12743 50 9517 150 5306 700 473 50 - tlatex0 6857 50 15570 100 47722 7000 64693 12000 6890 70 + tlatex0 9558 50 19030 70 47722 7000 64693 12000 9683 70 tlatex1 5140 50 14050 50 16377 1300 12462 500 5170 70 tlatex2 5488 80 13507 100 18439 700 12061 500 5502 80 tlatex3 9154 100 14323 150 20441 2400 12143 900 9283 100 diff --git a/test/svg_ref/tlatex0.svg b/test/svg_ref/tlatex0.svg index 44ca63a6a96ea..891bbb0afb702 100644 --- a/test/svg_ref/tlatex0.svg +++ b/test/svg_ref/tlatex0.svg @@ -12,72 +12,120 @@ tlatex0.svg stroke-dasharray=" 1.000, 2.0"/> Font 42 - - + + Align 11 - - + + Align 21 - - + + Align 31 - - + + Align 12 - - + + Align 22 - - + + Align 32 - - + + Align 13 - - + + Align 23 - - + + Align 33 - - + + Align 11 - - + + Align 21 - - + + Align 31 @@ -88,72 +136,120 @@ tlatex0.svg stroke-dasharray=" 1.000, 2.0"/> Font 43 - - + + Align 11 - - + + Align 21 - - + + Align 31 - - + + Align 12 - - + + Align 22 - - + + Align 32 - - + + Align 13 - - + + Align 23 - - + + Align 33 - - + + Align 11 - - + + Align 21 - - + + Align 31 diff --git a/test/svg_ref/waves.svg b/test/svg_ref/waves.svg index 5376057b833e6..47a0d1e4546d9 100644 --- a/test/svg_ref/waves.svg +++ b/test/svg_ref/waves.svg @@ -40055,12 +40055,12 @@ waves.svg 350 - - - - - - + + + + + + From edf0626deee6eaabb15ecb3061b208d2f8f9bcbb Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Tue, 12 May 2026 16:26:09 +0200 Subject: [PATCH 19/26] Adjust TPolyMarker::Paint Use std::vector for intermediate buffer --- hist/hist/src/TPolyMarker.cxx | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/hist/hist/src/TPolyMarker.cxx b/hist/hist/src/TPolyMarker.cxx index 7fcc0073f97d6..b28e63a910600 100644 --- a/hist/hist/src/TPolyMarker.cxx +++ b/hist/hist/src/TPolyMarker.cxx @@ -270,21 +270,23 @@ void TPolyMarker::Paint(Option_t *option) void TPolyMarker::PaintPolyMarker(Int_t n, Double_t *x, Double_t *y, Option_t *option) { - if (n <= 0) return; - TAttMarker::Modify(); //Change marker attributes only if necessary - Double_t *xx = x; - Double_t *yy = y; + if ((n <= 0) || !gPad) + return; + std::vector xx, yy; if (gPad->GetLogx()) { - xx = new Double_t[n]; - for (Int_t ix=0;ixXtoPad(x[ix]); + xx.resize(n); + for (Int_t ix = 0; ix < n; ix++) + xx[ix] = gPad->XtoPad(x[ix]); + x = xx.data(); } if (gPad->GetLogy()) { - yy = new Double_t[n]; - for (Int_t iy=0;iyYtoPad(y[iy]); + yy.resize(n); + for (Int_t iy = 0; iy < n; iy++) + yy[iy] = gPad->YtoPad(y[iy]); + y = yy.data(); } - gPad->PaintPolyMarker(n,xx,yy,option); - if (x != xx) delete [] xx; - if (y != yy) delete [] yy; + TAttMarker::ModifyOn(*gPad); //Change marker attributes only if necessary + gPad->PaintPolyMarker(n, x, y, option); } //////////////////////////////////////////////////////////////////////////////// From 6a6c71f8923c0b5a353f1da6fae0ec4a8e0f169f Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Tue, 12 May 2026 16:31:57 +0200 Subject: [PATCH 20/26] Implement TMarker::PaintMarkerNDC Was declared but never implemented --- graf2d/graf/src/TMarker.cxx | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/graf2d/graf/src/TMarker.cxx b/graf2d/graf/src/TMarker.cxx index c336155f6f619..5f6531d107c22 100644 --- a/graf2d/graf/src/TMarker.cxx +++ b/graf2d/graf/src/TMarker.cxx @@ -300,14 +300,12 @@ void TMarker::ls(Option_t *) const void TMarker::Paint(Option_t *) { - if (!gPad) return; - if (TestBit(kMarkerNDC)) { - Double_t u = gPad->GetX1() + fX*(gPad->GetX2()-gPad->GetX1()); - Double_t v = gPad->GetY1() + fY*(gPad->GetY2()-gPad->GetY1()); - PaintMarker(u,v); - } else { - PaintMarker(gPad->XtoPad(fX),gPad->YtoPad(fY)); - } + if (!gPad) + return; + if (TestBit(kMarkerNDC)) + PaintMarkerNDC(GetX(), GetY()); + else + PaintMarker(gPad->XtoPad(GetX()), gPad->YtoPad(GetY())); } //////////////////////////////////////////////////////////////////////////////// @@ -316,14 +314,17 @@ void TMarker::Paint(Option_t *) void TMarker::PaintMarker(Double_t x, Double_t y) { TAttMarker::Modify(); //Change line attributes only if necessary - if (gPad) gPad->PaintPolyMarker(-1,&x,&y,""); + if (gPad) gPad->PaintPolyMarker(-1, &x, &y, ""); } //////////////////////////////////////////////////////////////////////////////// /// Draw this marker with new coordinates in NDC. -void TMarker::PaintMarkerNDC(Double_t, Double_t) +void TMarker::PaintMarkerNDC(Double_t u, Double_t v) { + Double_t x = gPad->GetX1() + u * (gPad->GetX2() - gPad->GetX1()); + Double_t y = gPad->GetY1() + v * (gPad->GetY2() - gPad->GetY1()); + PaintMarker(x, y); } //////////////////////////////////////////////////////////////////////////////// From 213acddf8cda77c814b834ae2dcff02683239c34 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Wed, 13 May 2026 10:31:22 +0200 Subject: [PATCH 21/26] Adjust TCurlyLine::ExecuteEvent and bbox Use original object coordinates - pixels only when needed Reduce number of static variables Correctly use conversion to/from pixels Simplify bbox handling --- graf2d/graf/inc/TCurlyLine.h | 2 - graf2d/graf/src/TCurlyLine.cxx | 325 +++++++++++---------------------- 2 files changed, 106 insertions(+), 221 deletions(-) diff --git a/graf2d/graf/inc/TCurlyLine.h b/graf2d/graf/inc/TCurlyLine.h index e40d496766f79..9104279cfefe2 100644 --- a/graf2d/graf/inc/TCurlyLine.h +++ b/graf2d/graf/inc/TCurlyLine.h @@ -64,8 +64,6 @@ class TCurlyLine : public TPolyLine, public TAttBBox2D { static Bool_t GetDefaultIsCurly (); Rectangle_t GetBBox() override; - TPoint GetBBoxCenter() override; - void SetBBoxCenter(const TPoint &p) override; void SetBBoxCenterX(const Int_t x) override; void SetBBoxCenterY(const Int_t y) override; void SetBBoxX1(const Int_t x) override; diff --git a/graf2d/graf/src/TCurlyLine.cxx b/graf2d/graf/src/TCurlyLine.cxx index a2ee829179ffa..29c1808226932 100644 --- a/graf2d/graf/src/TCurlyLine.cxx +++ b/graf2d/graf/src/TCurlyLine.cxx @@ -27,7 +27,7 @@ End_Macro #include "TCurlyLine.h" #include "TVirtualPad.h" -#include "TVirtualX.h" +#include "TVirtualPadPainter.h" #include "TMath.h" #include "TPoint.h" @@ -79,21 +79,19 @@ void TCurlyLine::Build() Double_t wavelengthPix,amplitudePix, lengthPix, hPix; Double_t px1, py1, px2, py2; + if (gPad) { - Double_t ww = (Double_t)gPad->GetWw(); - Double_t wh = (Double_t)gPad->GetWh(); - Double_t pxrange = gPad->GetAbsWNDC()*ww; - Double_t pyrange = - gPad->GetAbsHNDC()*wh; + Double_t pxrange = gPad->GetPadWidth(); + Double_t pyrange = -1. * gPad->GetPadHeight(); Double_t xrange = gPad->GetX2() - gPad->GetX1(); Double_t yrange = gPad->GetY2() - gPad->GetY1(); pixeltoX = xrange / pxrange; - pixeltoY = yrange/pyrange; - hPix = TMath::Max(gPad->GetAbsHNDC() * gPad->GetWh(), gPad->GetAbsWNDC() * gPad->GetWw()); + pixeltoY = yrange / pyrange; + hPix = TMath::Max(pxrange, -pyrange); px1 = gPad->XtoAbsPixel(fX1); py1 = gPad->YtoAbsPixel(fY1); px2 = gPad->XtoAbsPixel(fX2); py2 = gPad->YtoAbsPixel(fY2); - lengthPix = TMath::Sqrt((px2-px1)*(px2-px1) + (py1-py2)*(py1-py2)); wavelengthPix = hPix*fWaveLength; amplitudePix = hPix*fAmplitude; @@ -182,153 +180,105 @@ Int_t TCurlyLine::DistancetoPrimitive(Int_t px, Int_t py) void TCurlyLine::ExecuteEvent(Int_t event, Int_t px, Int_t py) { - if (!gPad) return; + if (!gPad || !gPad->IsEditable()) return; + + constexpr Int_t kMaxDiff = 20; + static Int_t pxold, pyold, selectPoint; + + auto &parent = *gPad; - Int_t kMaxDiff = 20; - static Int_t d1,d2,px1,px2,py1,py2; - static Int_t pxold, pyold, px1old, py1old, px2old, py2old; - static Bool_t p1, p2, pL; - Int_t dx, dy; + Bool_t opaque = parent.OpaqueMoving(); + + auto paint = [this, &parent]() { + auto pp = parent.GetPainter(); + pp->SetAttLine(*this); + pp->DrawLine(parent.XtoPad(fX1), parent.YtoPad(fY1), parent.XtoPad(fX2), parent.YtoPad(fY2)); + }; + + auto set_coord = [this](Int_t _x1, Int_t _y1, Int_t _x2, Int_t _y2) { + if (selectPoint & 1) { + fX1 = GetXCoord(_x1, kFALSE, kTRUE); + fY1 = GetYCoord(_y1, kFALSE, kTRUE); + } + if (selectPoint & 2) { + fX2 = GetXCoord(_x2, kFALSE, kTRUE); + fY2 = GetYCoord(_y2, kFALSE, kTRUE); + } + }; - Bool_t opaque = gPad->OpaqueMoving(); + Int_t px1 = parent.XtoAbsPixel(parent.XtoPad(fX1)); + Int_t py1 = parent.YtoAbsPixel(parent.YtoPad(fY1)); + Int_t px2 = parent.XtoAbsPixel(parent.XtoPad(fX2)); + Int_t py2 = parent.YtoAbsPixel(parent.YtoPad(fY2)); switch (event) { case kArrowKeyPress: case kButton1Down: - if (!opaque) { - gVirtualX->SetLineColor(-1); - TAttLine::Modify(); //Change line attributes only if necessary - } // No break !!! case kMouseMotion: - px1 = gPad->XtoAbsPixel(fX1); - py1 = gPad->YtoAbsPixel(fY1); - px2 = gPad->XtoAbsPixel(fX2); - py2 = gPad->YtoAbsPixel(fY2); - - p1 = p2 = pL = kFALSE; - - d1 = TMath::Abs(px1 - px) + TMath::Abs(py1-py); //simply take sum of pixels differences - if (d1 < kMaxDiff) { //*-*================>OK take point number 1 - px1old = px1; py1old = py1; - p1 = kTRUE; - gPad->SetCursor(kPointer); - return; - } - d2 = TMath::Abs(px2 - px) + TMath::Abs(py2-py); //simply take sum of pixels differences - if (d2 < kMaxDiff) { //*-*================>OK take point number 2 - px2old = px2; py2old = py2; - p2 = kTRUE; - gPad->SetCursor(kPointer); - return; + if (abs(px1 - px) + abs(py1 - py) < kMaxDiff) { + selectPoint = 1; + parent.SetCursor(kPointer); + } else if (abs(px2 - px) + abs(py2 - py) < kMaxDiff) { + selectPoint = 2; + parent.SetCursor(kPointer); + } else { + selectPoint = 3; + pxold = px; + pyold = py; + parent.SetCursor(kMove); } - pL = kTRUE; - pxold = px; pyold = py; - gPad->SetCursor(kMove); - break; case kArrowKeyRelease: case kButton1Motion: - - if (p1) { - if (!opaque) { - gVirtualX->DrawLine(px1old, py1old, px2, py2); - gVirtualX->DrawLine(px, py, px2, py2); - } - else this->SetStartPoint(gPad->AbsPixeltoX(px),gPad->AbsPixeltoY(py)); - px1old = px; - py1old = py; - } - if (p2) { - if (!opaque) { - gVirtualX->DrawLine(px1, py1, px2old, py2old); - gVirtualX->DrawLine(px1, py1, px, py); - } - else this->SetEndPoint(gPad->AbsPixeltoX(px), gPad->AbsPixeltoY(py)); - px2old = px; - py2old = py; - } - if (pL) { - if (!opaque) gVirtualX->DrawLine(px1, py1, px2, py2); - dx = px-pxold; dy = py-pyold; - px1 += dx; py1 += dy; px2 += dx; py2 += dy; - if (!opaque) gVirtualX->DrawLine(px1, py1, px2, py2); + if (!opaque) + paint(); + if (selectPoint == 1) { + set_coord(px, py, 0, 0); + } else if (selectPoint == 2) { + set_coord(0, 0, px, py); + fX2 = GetXCoord(px, kFALSE, kTRUE); + fY2 = GetYCoord(py, kFALSE, kTRUE); + } else if (selectPoint == 3) { + set_coord(px1 + px - pxold, py1 + py - pyold, px2 + px - pxold, py2 + py - pyold); pxold = px; pyold = py; - if (opaque) { - this->SetStartPoint(gPad->AbsPixeltoX(px1),gPad->AbsPixeltoY(py1)); - this->SetEndPoint(gPad->AbsPixeltoX(px2), gPad->AbsPixeltoY(py2)); - } } - - if (opaque) { - if (p1) { - //check in which corner the BBox is edited - if (fX1>fX2) { - if (fY1>fY2) - gPad->ShowGuidelines(this, event, '2', true); - else - gPad->ShowGuidelines(this, event, '3', true); - } - else { - if (fY1>fY2) - gPad->ShowGuidelines(this, event, '1', true); - else - gPad->ShowGuidelines(this, event, '4', true); - } - } - if (p2) { - //check in which corner the BBox is edited - if (fX1>fX2) { - if (fY1>fY2) - gPad->ShowGuidelines(this, event, '4', true); - else - gPad->ShowGuidelines(this, event, '1', true); - } - else { - if (fY1>fY2) - gPad->ShowGuidelines(this, event, '3', true); - else - gPad->ShowGuidelines(this, event, '2', true); - } + if (!opaque) + paint(); + else { + char guide = selectPoint == 3 ? 'i' : '\0'; + if ((selectPoint == 1) || (selectPoint == 2)) { + static const char GUIDES[2][2][2] = { + { { '4', '1' }, { '3', '2' } }, + { { '2', '3' }, { '1', '4' } } + }; + int x_idx = fX1 > fX2 ? 1 : 0; + int y_idx = fY1 > fY2 ? 1 : 0; + guide = GUIDES[selectPoint-1][x_idx][y_idx]; } - if (pL) { - gPad->ShowGuidelines(this, event, 'i', true); - } - gPad->Modified(kTRUE); - gPad->Update(); + if (guide) + parent.ShowGuidelines(this, event, guide, true); + + Build(); + parent.ModifiedUpdate(); } break; case kButton1Up: - if (opaque) { - gPad->ShowGuidelines(this, event); - } else { - if (p1) { - fX1 = gPad->AbsPixeltoX(px); - fY1 = gPad->AbsPixeltoY(py); - } - if (p2) { - fX2 = gPad->AbsPixeltoX(px); - fY2 = gPad->AbsPixeltoY(py); - } - if (pL) { - fX1 = gPad->AbsPixeltoX(px1); - fY1 = gPad->AbsPixeltoY(py1); - fX2 = gPad->AbsPixeltoX(px2); - fY2 = gPad->AbsPixeltoY(py2); - } + if (opaque) + parent.ShowGuidelines(this, event); + else { + Build(); + parent.ModifiedUpdate(); } - Build(); - gPad->Modified(); - if (!opaque) gVirtualX->SetLineColor(-1); } } @@ -457,71 +407,24 @@ Bool_t TCurlyLine::GetDefaultIsCurly() Rectangle_t TCurlyLine::GetBBox() { - Rectangle_t BBox{0,0,0,0}; + Rectangle_t bbox{0,0,0,0}; if (gPad) { Int_t px1 = gPad->XtoPixel(fX1); Int_t px2 = gPad->XtoPixel(fX2); Int_t py1 = gPad->YtoPixel(fY1); Int_t py2 = gPad->YtoPixel(fY2); - if (px1 > px2) { - Int_t tmp = px1; - px1 = px2; - px2 = tmp; - } - if (py1 > py2) { - Int_t tmp = py1; - py1 = py2; - py2 = tmp; - } - - BBox.fX = px1; - BBox.fY = py1; - BBox.fWidth = px2 - px1; - BBox.fHeight = py2 - py1; - } - return BBox; -} - -//////////////////////////////////////////////////////////////////////////////// -/// Return the center of the BoundingBox as TPoint in pixels - -TPoint TCurlyLine::GetBBoxCenter() -{ - TPoint p(0,0); - if (gPad) { - p.SetX(gPad->XtoPixel(TMath::Min(fX1, fX2) + 0.5 * (TMath::Max(fX1, fX2) - TMath::Min(fX1, fX2)))); - p.SetY(gPad->YtoPixel(TMath::Min(fY1, fY2) + 0.5 * (TMath::Max(fY1, fY2) - TMath::Min(fY1, fY2)))); - } - return p; -} - -//////////////////////////////////////////////////////////////////////////////// -/// Set center of the BoundingBox + if (px1 > px2) + std::swap(px1, px2); + if (py1 > py2) + std::swap(py1, py2); -void TCurlyLine::SetBBoxCenter(const TPoint &p) -{ - if (!gPad) return; - Double_t w = TMath::Max(fX1, fX2)-TMath::Min(fX1, fX2); - Double_t h = TMath::Max(fY1, fY2)-TMath::Min(fY1, fY2); - Double_t x1, x2, y1, y2; - - if (fX2 > fX1) { - x1 = gPad->PixeltoX(p.GetX())-0.5*w; - x2 = gPad->PixeltoX(p.GetX())+0.5*w; - } else { - x2 = gPad->PixeltoX(p.GetX())-0.5*w; - x1 = gPad->PixeltoX(p.GetX())+0.5*w; + bbox.fX = px1; + bbox.fY = py1; + bbox.fWidth = px2 - px1; + bbox.fHeight = py2 - py1; } - if (fY2 > fY1) { - y1 = gPad->PixeltoY(p.GetY()-gPad->VtoPixel(0))-0.5*h; - y2 = gPad->PixeltoY(p.GetY()-gPad->VtoPixel(0))+0.5*h; - } else { - y2 = gPad->PixeltoY(p.GetY()-gPad->VtoPixel(0))-0.5*h; - y1 = gPad->PixeltoY(p.GetY()-gPad->VtoPixel(0))+0.5*h; - } - this->SetStartPoint(x1, y1); - this->SetEndPoint(x2, y2); + return bbox; } //////////////////////////////////////////////////////////////////////////////// @@ -529,16 +432,10 @@ void TCurlyLine::SetBBoxCenter(const TPoint &p) void TCurlyLine::SetBBoxCenterX(const Int_t x) { - if (!gPad) return; - Double_t w = TMath::Max(fX1, fX2)-TMath::Min(fX1, fX2); - if (fX2>fX1) { - this->SetStartPoint(gPad->PixeltoX(x)-0.5*w, fY1); - this->SetEndPoint(gPad->PixeltoX(x)+0.5*w, fY2); - } - else { - this->SetEndPoint(gPad->PixeltoX(x)-0.5*w, fY2); - this->SetStartPoint(gPad->PixeltoX(x)+0.5*w, fY1); - } + Double_t w2 = 0.5 * (fX2 - fX1); + Double_t midx = GetXCoord(x); + SetStartPoint(midx - w2, fY1); + SetEndPoint(midx + w2, fY2); } //////////////////////////////////////////////////////////////////////////////// @@ -546,16 +443,10 @@ void TCurlyLine::SetBBoxCenterX(const Int_t x) void TCurlyLine::SetBBoxCenterY(const Int_t y) { - if (!gPad) return; - Double_t h = TMath::Max(fY1, fY2)-TMath::Min(fY1, fY2); - if (fY2>fY1) { - this->SetStartPoint(fX1, gPad->PixeltoY(y-gPad->VtoPixel(0))-0.5*h); - this->SetEndPoint(fX2, gPad->PixeltoY(y-gPad->VtoPixel(0))+0.5*h); - } - else { - this->SetEndPoint(fX2, gPad->PixeltoY(y-gPad->VtoPixel(0))-0.5*h); - this->SetStartPoint(fX1, gPad->PixeltoY(y-gPad->VtoPixel(0))+0.5*h); - } + Double_t h2 = 0.5 * (fY2 - fY1); + Double_t midy = GetYCoord(y); + SetStartPoint(fX1, midy - h2); + SetEndPoint(fX2, midy + h2); } //////////////////////////////////////////////////////////////////////////////// @@ -564,11 +455,10 @@ void TCurlyLine::SetBBoxCenterY(const Int_t y) void TCurlyLine::SetBBoxX1(const Int_t x) { - if (!gPad) return; - if (fX2>fX1) - this->SetStartPoint(gPad->PixeltoX(x), fY1); + if (fX2 > fX1) + SetStartPoint(GetXCoord(x), fY1); else - this->SetEndPoint(gPad->PixeltoX(x), fY2); + SetEndPoint(GetXCoord(x), fY2); } //////////////////////////////////////////////////////////////////////////////// @@ -577,11 +467,10 @@ void TCurlyLine::SetBBoxX1(const Int_t x) void TCurlyLine::SetBBoxX2(const Int_t x) { - if (!gPad) return; if (fX2>fX1) - this->SetEndPoint(gPad->PixeltoX(x), fY2); + SetEndPoint(GetXCoord(x), fY2); else - this->SetStartPoint(gPad->PixeltoX(x), fY1); + SetStartPoint(GetXCoord(x), fY1); } //////////////////////////////////////////////////////////////////////////////// @@ -589,11 +478,10 @@ void TCurlyLine::SetBBoxX2(const Int_t x) void TCurlyLine::SetBBoxY1(const Int_t y) { - if (!gPad) return; - if (fY2>fY1) - this->SetEndPoint(fX2, gPad->PixeltoY(y - gPad->VtoPixel(0))); + if (fY2 > fY1) + SetEndPoint(fX2, GetYCoord(y)); else - this->SetStartPoint(fX1, gPad->PixeltoY(y - gPad->VtoPixel(0))); + SetStartPoint(fX1, GetYCoord(y)); } //////////////////////////////////////////////////////////////////////////////// @@ -602,9 +490,8 @@ void TCurlyLine::SetBBoxY1(const Int_t y) void TCurlyLine::SetBBoxY2(const Int_t y) { - if (!gPad) return; - if (fY2>fY1) - this->SetStartPoint(fX1, gPad->PixeltoY(y - gPad->VtoPixel(0))); + if (fY2 > fY1) + SetStartPoint(fX1, GetYCoord(y)); else - this->SetEndPoint(fX2, gPad->PixeltoY(y - gPad->VtoPixel(0))); + SetEndPoint(fX2, GetYCoord(y)); } From 752e2cd1934f77dad29b1f06099e9c96fceb0643 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Wed, 13 May 2026 10:33:00 +0200 Subject: [PATCH 22/26] Optimize TLine::ExecuteEvent and GetBBox Make ExecuteEvent similar to TCurlyLine implementation In GetBBox support NDC coordinates --- graf2d/graf/src/TLine.cxx | 167 +++++++++++++++++--------------------- 1 file changed, 76 insertions(+), 91 deletions(-) diff --git a/graf2d/graf/src/TLine.cxx b/graf2d/graf/src/TLine.cxx index ade905fd26d50..939d1eb8d4e15 100644 --- a/graf2d/graf/src/TLine.cxx +++ b/graf2d/graf/src/TLine.cxx @@ -134,55 +134,60 @@ void TLine::ExecuteEvent(Int_t event, Int_t px, Int_t py) if (!gPad || !gPad->IsEditable()) return; constexpr Int_t kMaxDiff = 20; - static Int_t px1,px2,py1,py2,pxold,pyold; static Double_t oldX1, oldY1, oldX2, oldY2; - static Int_t selectPoint; + static Int_t pxold,pyold, selectPoint; auto &parent = *gPad; Bool_t opaque = parent.OpaqueMoving(); - auto action = [this, &parent](Int_t code, Int_t _x1, Int_t _y1, Int_t _x2 = 0, Int_t _y2 = 0) { + auto paint = [this, &parent]() { + auto pp = parent.GetPainter(); + pp->SetAttLine(*this); + if (TestBit(kLineNDC)) + pp->DrawLineNDC(GetX1(), GetY1(), GetX2(), GetY2()); + else + pp->DrawLine(parent.XtoPad(GetX1()), parent.YtoPad(GetY1()), parent.XtoPad(GetX2()), parent.YtoPad(GetY2())); + }; + auto set_coord = [this](Int_t _x1, Int_t _y1, Int_t _x2, Int_t _y2) { Bool_t isndc = TestBit(kLineNDC); - - Double_t x1 = GetXCoord(_x1, isndc, kTRUE); - Double_t y1 = GetYCoord(_y1, isndc, kTRUE); - Double_t x2 = GetXCoord(_x2, isndc, kTRUE); - Double_t y2 = GetYCoord(_y2, isndc, kTRUE); - - if (code == 0) { - auto pp = parent.GetPainter(); - pp->SetAttLine(*this); - if (isndc) - pp->DrawLineNDC(x1, y1, x2, y2); + if (selectPoint & 1) { + SetX1(GetXCoord(_x1, isndc, kTRUE)); + SetY1(GetYCoord(_y1, isndc, kTRUE)); + } + if (selectPoint & 2) { + SetX2(GetXCoord(_x2, isndc, kTRUE)); + SetY2(GetYCoord(_y2, isndc, kTRUE)); + } + if (TestBit(kVertical)) { + if (selectPoint & 1) + SetX2(GetX1()); else - pp->DrawLine(parent.XtoPad(x1), parent.YtoPad(y1), parent.XtoPad(x2), parent.YtoPad(y2)); - } else { - - if (code & 1) { - SetX1(x1); - SetY1(y1); - } - if (code & 2) { - SetX2(x2); - SetY2(y2); - } - if (TestBit(kVertical)) { - if (code & 1) - SetX2(GetX1()); - else - SetX1(GetX2()); - } - if (TestBit(kHorizontal)) { - if (code & 1) - SetY2(GetY1()); - else - SetY1(GetY2()); - } + SetX1(GetX2()); + } + if (TestBit(kHorizontal)) { + if (selectPoint & 1) + SetY2(GetY1()); + else + SetY1(GetY2()); } }; + Int_t px1, py1, px2, py2; + + if (TestBit(kLineNDC)) { + px1 = parent.UtoAbsPixel(GetX1()); + py1 = parent.VtoAbsPixel(GetY1()); + px2 = parent.UtoAbsPixel(GetX2()); + py2 = parent.VtoAbsPixel(GetY2()); + } else { + px1 = parent.XtoAbsPixel(parent.XtoPad(GetX1())); + py1 = parent.YtoAbsPixel(parent.YtoPad(GetY1())); + px2 = parent.XtoAbsPixel(parent.XtoPad(GetX2())); + py2 = parent.YtoAbsPixel(parent.YtoPad(GetY2())); + } + switch (event) { case kArrowKeyPress: @@ -194,25 +199,12 @@ void TLine::ExecuteEvent(Int_t event, Int_t px, Int_t py) // No break !!! - case kMouseMotion: - - if (TestBit(kLineNDC)) { - px1 = parent.UtoAbsPixel(GetX1()); - py1 = parent.VtoAbsPixel(GetY1()); - px2 = parent.UtoAbsPixel(GetX2()); - py2 = parent.VtoAbsPixel(GetY2()); - } else { - px1 = parent.XtoAbsPixel(parent.XtoPad(GetX1())); - py1 = parent.YtoAbsPixel(parent.YtoPad(GetY1())); - px2 = parent.XtoAbsPixel(parent.XtoPad(GetX2())); - py2 = parent.YtoAbsPixel(parent.YtoPad(GetY2())); - } - + case kMouseMotion: { //simply take sum of pixels differences - if (abs(px1 - px) + abs(py1 - py) < kMaxDiff) { //*-*================>OK take point number 1 + if (abs(px1 - px) + abs(py1 - py) < kMaxDiff) { selectPoint = 1; parent.SetCursor(kPointer); - } else if (abs(px2 - px) + abs(py2 - py) < kMaxDiff) { //*-*================>OK take point number 2 + } else if (abs(px2 - px) + abs(py2 - py) < kMaxDiff) { selectPoint = 2; parent.SetCursor(kPointer); } else { @@ -223,42 +215,38 @@ void TLine::ExecuteEvent(Int_t event, Int_t px, Int_t py) } break; + } case kArrowKeyRelease: case kButton1Motion: if (!opaque) - action(0, px1, py1, px2, py2); + paint(); if (selectPoint == 1) { - px1 = px; - py1 = py; + set_coord(px, py, 0, 0); } else if (selectPoint == 2) { + set_coord(0, 0, px, py); px2 = px; py2 = py; } else if (selectPoint == 3) { - px1 += px - pxold; - py1 += py - pyold; - px2 += px - pxold; - py2 += py -pyold; + set_coord(px1 + px - pxold, py1 + py - pyold, px2 + px - pxold, py2 + py - pyold); pxold = px; pyold = py; } - action(!opaque ? 0 : selectPoint, px1, py1, px2, py2); - if (opaque) { - if (selectPoint == 1) { - //check in which corner the BBox is edited - if (GetX1() > GetX2()) - parent.ShowGuidelines(this, event, GetY1() > GetY2() ? '2' : '3', true); - else - parent.ShowGuidelines(this, event, GetY1() > GetY2() ? '1' : '4', true); - } else if (selectPoint == 2) { - //check in which corner the BBox is edited - if (GetX1() > GetX2()) - parent.ShowGuidelines(this, event, GetY1() > GetY2() ? '4' : '1', true); - else - parent.ShowGuidelines(this, event, GetY1() > GetY2() ? '3' : '2', true); - } else if (selectPoint == 3) { - parent.ShowGuidelines(this, event, 'i', true); + if (!opaque) + paint(); + else { + char guide = selectPoint == 3 ? 'i' : '\0'; + if ((selectPoint == 1) || (selectPoint == 2)) { + static const char GUIDES[2][2][2] = { + { { '4', '1' }, { '3', '2' } }, + { { '2', '3' }, { '1', '4' } } + }; + int x_idx = GetX1() > GetX2() ? 1 : 0; + int y_idx = GetY1() > GetY2() ? 1 : 0; + guide = GUIDES[selectPoint-1][x_idx][y_idx]; } + if (guide) + parent.ShowGuidelines(this, event, guide, true); parent.ModifiedUpdate(); } break; @@ -277,17 +265,14 @@ void TLine::ExecuteEvent(Int_t event, Int_t px, Int_t py) } break; } - if (opaque) { + if (opaque) parent.ShowGuidelines(this, event); - } else { - action(selectPoint, px1, py1, px2, py2); + else parent.ModifiedUpdate(); - } selectPoint = 0; break; case kButton1Locate: - // Sergey: code is never used, has to be removed in ROOT7 ExecuteEvent(kButton1Down, px, py); while (true) { @@ -503,24 +488,24 @@ void TLine::Streamer(TBuffer &R__b) Rectangle_t TLine::GetBBox() { - Rectangle_t BBox{0, 0, 0, 0}; + Rectangle_t bbox{0, 0, 0, 0}; if (gPad) { - Int_t px1 = gPad->XtoPixel(fX1); - Int_t px2 = gPad->XtoPixel(fX2); - Int_t py1 = gPad->YtoPixel(fY1); - Int_t py2 = gPad->YtoPixel(fY2); + Int_t px1 = TestBit(kLineNDC) ? gPad->UtoPixel(fX1) : gPad->XtoPixel(fX1); + Int_t px2 = TestBit(kLineNDC) ? gPad->UtoPixel(fX2) : gPad->XtoPixel(fX2); + Int_t py1 = TestBit(kLineNDC) ? gPad->VtoPixel(fY1) : gPad->YtoPixel(fY1); + Int_t py2 = TestBit(kLineNDC) ? gPad->VtoPixel(fY2) : gPad->YtoPixel(fY2); if (px1 > px2) std::swap(px1, px2); if (py1 > py2) std::swap(py1, py2); - BBox.fX = px1; - BBox.fY = py1; - BBox.fWidth = px2 - px1; - BBox.fHeight = py2 - py1; + bbox.fX = px1; + bbox.fY = py1; + bbox.fWidth = px2 - px1; + bbox.fHeight = py2 - py1; } - return BBox; + return bbox; } //////////////////////////////////////////////////////////////////////////////// From 766c6a87409fd6d33fe306701f2935fbef0b01ea Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Wed, 13 May 2026 13:17:47 +0200 Subject: [PATCH 23/26] Better TCurlyLine::ExecuteEvent --- graf2d/graf/src/TCurlyLine.cxx | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/graf2d/graf/src/TCurlyLine.cxx b/graf2d/graf/src/TCurlyLine.cxx index 29c1808226932..a66a71d8ba877 100644 --- a/graf2d/graf/src/TCurlyLine.cxx +++ b/graf2d/graf/src/TCurlyLine.cxx @@ -183,7 +183,7 @@ void TCurlyLine::ExecuteEvent(Int_t event, Int_t px, Int_t py) if (!gPad || !gPad->IsEditable()) return; constexpr Int_t kMaxDiff = 20; - static Int_t pxold, pyold, selectPoint; + static Int_t sdx = 0, sdy = 0, selectPoint; auto &parent = *gPad; @@ -228,8 +228,8 @@ void TCurlyLine::ExecuteEvent(Int_t event, Int_t px, Int_t py) parent.SetCursor(kPointer); } else { selectPoint = 3; - pxold = px; - pyold = py; + sdx = px1 - px; + sdy = py1 - py; parent.SetCursor(kMove); } @@ -243,12 +243,8 @@ void TCurlyLine::ExecuteEvent(Int_t event, Int_t px, Int_t py) set_coord(px, py, 0, 0); } else if (selectPoint == 2) { set_coord(0, 0, px, py); - fX2 = GetXCoord(px, kFALSE, kTRUE); - fY2 = GetYCoord(py, kFALSE, kTRUE); } else if (selectPoint == 3) { - set_coord(px1 + px - pxold, py1 + py - pyold, px2 + px - pxold, py2 + py - pyold); - pxold = px; - pyold = py; + set_coord(px + sdx, py + sdy, px + sdx + px2 - px1, py + sdy + py2 - py1); } if (!opaque) paint(); @@ -467,7 +463,7 @@ void TCurlyLine::SetBBoxX1(const Int_t x) void TCurlyLine::SetBBoxX2(const Int_t x) { - if (fX2>fX1) + if (fX2 > fX1) SetEndPoint(GetXCoord(x), fY2); else SetStartPoint(GetXCoord(x), fY1); From f627947c0914ecbba84dbdb16e774d0baa090065 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Wed, 13 May 2026 13:18:47 +0200 Subject: [PATCH 24/26] Optimize TCurlyArc::ExecuteEvent and BBox Use proper coordinates transformation Avoid gVirtualX Correctly calculate distance to primitive --- graf2d/graf/inc/TCurlyArc.h | 1 - graf2d/graf/src/TCurlyArc.cxx | 430 +++++++++++----------------------- 2 files changed, 139 insertions(+), 292 deletions(-) diff --git a/graf2d/graf/inc/TCurlyArc.h b/graf2d/graf/inc/TCurlyArc.h index 0bbebf394e0c0..c365b2cab2ed7 100644 --- a/graf2d/graf/inc/TCurlyArc.h +++ b/graf2d/graf/inc/TCurlyArc.h @@ -53,7 +53,6 @@ class TCurlyArc : public TCurlyLine { Rectangle_t GetBBox() override; TPoint GetBBoxCenter() override; - void SetBBoxCenter(const TPoint &p) override; void SetBBoxCenterX(const Int_t x) override; void SetBBoxCenterY(const Int_t y) override; void SetBBoxX1(const Int_t x) override; diff --git a/graf2d/graf/src/TCurlyArc.cxx b/graf2d/graf/src/TCurlyArc.cxx index 6225794716ec0..4e29fc4dc6200 100644 --- a/graf2d/graf/src/TCurlyArc.cxx +++ b/graf2d/graf/src/TCurlyArc.cxx @@ -29,7 +29,8 @@ End_Macro #include "TCurlyArc.h" #include "TROOT.h" #include "TVirtualPad.h" -#include "TVirtualX.h" +#include "TVirtualPadPainter.h" +#include "TAttMarker.h" #include "TMath.h" #include "TPoint.h" @@ -77,10 +78,8 @@ void TCurlyArc::Build() Double_t pixeltoY = 1; Double_t rPix = fR1; if (gPad) { - Double_t ww = (Double_t)gPad->GetWw(); - Double_t wh = (Double_t)gPad->GetWh(); - Double_t pxrange = gPad->GetAbsWNDC()*ww; - Double_t pyrange = - gPad->GetAbsHNDC()*wh; + Double_t pxrange = gPad->GetPadWidth(); + Double_t pyrange = -1. * gPad->GetPadHeight(); Double_t xrange = gPad->GetX2() - gPad->GetX1(); Double_t yrange = gPad->GetY2() - gPad->GetY1(); pixeltoX = xrange / pxrange; @@ -110,7 +109,8 @@ void TCurlyArc::Build() xv[i] = xx + fX1; yv[i] = yy + fY1; } - if (gPad) gPad->Modified(); + if (gPad) + gPad->Modified(); } //////////////////////////////////////////////////////////////////////////////// @@ -123,8 +123,8 @@ Int_t TCurlyArc::DistancetoPrimitive(Int_t px, Int_t py) { if (!gPad) return 9999; // Compute distance of point to center of arc - Int_t pxc = gPad->XtoAbsPixel(fX1); - Int_t pyc = gPad->YtoAbsPixel(fY1); + Int_t pxc = gPad->XtoAbsPixel(gPad->XtoPad(fX1)); + Int_t pyc = gPad->YtoAbsPixel(gPad->YtoPad(fY1)); Double_t dist = TMath::Sqrt(Long64_t(pxc-px)*(pxc-px)+Long64_t(pyc-py)*(pyc-py)); Double_t cosa = (px - pxc)/dist; Double_t sina = (pyc - py)/dist; @@ -136,9 +136,10 @@ Int_t TCurlyArc::DistancetoPrimitive(Int_t px, Int_t py) } else { if (phi > fPhimin && phi < fPhimax) return 9999; } - Int_t pxr = gPad->XtoPixel(fR1)- gPad->XtoPixel(0); - Double_t distr = TMath::Abs(dist-pxr); - return Int_t(distr); + Int_t pxa = gPad->XtoAbsPixel(gPad->XtoPad(fX1 + cosa*fR1)); + Int_t pya = gPad->YtoAbsPixel(gPad->YtoPad(fY1 + sina*fR1)); + Double_t dista = TMath::Sqrt(Long64_t(pxc-pxa)*(pxc-pxa)+Long64_t(pyc-pya)*(pyc-pya)); + return (Int_t) TMath::Abs(dist - dista); } //////////////////////////////////////////////////////////////////////////////// @@ -154,255 +155,140 @@ Int_t TCurlyArc::DistancetoPrimitive(Int_t px, Int_t py) void TCurlyArc::ExecuteEvent(Int_t event, Int_t px, Int_t py) { - if (!gPad) return; - - Int_t kMaxDiff = 10; - const Int_t np = 10; - const Double_t pi = TMath::Pi(); - static Int_t x[np+3], y[np+3]; - static Int_t px1,py1,npe,r1; - static Int_t pxold, pyold; - Int_t i, dpx, dpy; - Double_t angle,dx,dy,dphi,rLx,rRx; - Double_t phi0; - static Bool_t pTop, pL, pR, pBot, pINSIDE; - static Int_t pTx,pTy,pLx,pLy,pRx,pRy,pBx,pBy; - - Bool_t opaque = gPad->OpaqueMoving(); + if (!gPad || !gPad->IsEditable()) return; + + auto &parent = *gPad; + + constexpr Int_t kMaxDiff = 10; + static enum { pNone, pTop, pL, pR, pBot, pINSIDE } mode = pNone; + static Int_t sdx = 0, sdy = 0; + static Bool_t first_move = kTRUE; + + auto paint_hollow = [this, &parent]() { + int np = 10; + std::vector x(np+3), y(np+3); + + Double_t dphi = (fPhimax - fPhimin) * TMath::Pi() / 180; + if (dphi < 0) + dphi += 2 * TMath::Pi(); + Double_t phi0 = fPhimin * TMath::Pi() / 180; + for (int i = 0; i <= np; i++) { + Double_t angle = phi0 + i*dphi/np; + x[i] = parent.XtoPad(fX1 + fR1*TMath::Cos(angle)); + y[i] = parent.YtoPad(fY1 + fR1*TMath::Sin(angle)); + } + if (fPhimax - fPhimin < 360) { + ++np; + x[np] = parent.XtoPad(fX1); + y[np] = parent.YtoPad(fY1); + } + ++np; + x[np] = x[0]; + y[np] = y[0]; + + auto pp = parent.GetPainter(); + pp->SetAttLine(*this); + pp->DrawPolyLine(np + 1, x.data(), y.data()); + + Double_t xm[4] = { fX1, fX1, fX1 - fR1, fX1 + fR1 }; + Double_t ym[4] = { fY1 + fR1, fY1 - fR1, fY1, fY1 }; + for (Int_t i = 0; i < 4; ++i) { + xm[i] = parent.XtoPad(xm[i]); + ym[i] = parent.YtoPad(ym[i]); + } + pp->SetAttMarker({GetLineColor(), 25, 2}); + pp->DrawPolyMarker(4, xm, ym); + }; + + Bool_t opaque = parent.OpaqueMoving(); switch (event) { case kArrowKeyPress: case kButton1Down: - if (!opaque) { - gVirtualX->SetLineColor(-1); - TAttLine::Modify(); - dphi = (fPhimax-fPhimin) * pi / 180; - if (dphi<0) dphi += 2 * pi; - dphi /= np; - phi0 = fPhimin * pi / 180; - for (i=0;i<=np;i++) { - angle = Double_t(i)*dphi + phi0; - dx = fR1*TMath::Cos(angle); - dy = fR1*TMath::Sin(angle); - Int_t rpixY = gPad->XtoAbsPixel(dy) - gPad->XtoAbsPixel(0); - x[i] = gPad->XtoAbsPixel(fX1 + dx); - y[i] = gPad->YtoAbsPixel(fY1) + rpixY; - } - if (fPhimax-fPhimin >= 360 ) { - x[np+1] = x[0]; - y[np+1] = y[0]; - npe = np; - } else { - x[np+1] = gPad->XtoAbsPixel(fX1); - y[np+1] = gPad->YtoAbsPixel(fY1); - x[np+2] = x[0]; - y[np+2] = y[0]; - npe = np + 2; - } - } - px1 = gPad->XtoAbsPixel(fX1); - py1 = gPad->YtoAbsPixel(fY1); - pTx = pBx = px1; - pLy = pRy = py1; - pLx = gPad->XtoAbsPixel(-fR1+fX1); - pRx = gPad->XtoAbsPixel( fR1+fX1); - r1 = TMath::Abs(pLx-pRx)/2; - // a circle in pixels, radius measured along X - pTy = gPad->YtoAbsPixel(fY1) + r1; - pBy = gPad->YtoAbsPixel(fY1) - r1; - - if (!opaque) { - gVirtualX->DrawLine(pRx+4, py1+4, pRx-4, py1+4); - gVirtualX->DrawLine(pRx-4, py1+4, pRx-4, py1-4); - gVirtualX->DrawLine(pRx-4, py1-4, pRx+4, py1-4); - gVirtualX->DrawLine(pRx+4, py1-4, pRx+4, py1+4); - gVirtualX->DrawLine(pLx+4, py1+4, pLx-4, py1+4); - gVirtualX->DrawLine(pLx-4, py1+4, pLx-4, py1-4); - gVirtualX->DrawLine(pLx-4, py1-4, pLx+4, py1-4); - gVirtualX->DrawLine(pLx+4, py1-4, pLx+4, py1+4); - gVirtualX->DrawLine(px1+4, pBy+4, px1-4, pBy+4); - gVirtualX->DrawLine(px1-4, pBy+4, px1-4, pBy-4); - gVirtualX->DrawLine(px1-4, pBy-4, px1+4, pBy-4); - gVirtualX->DrawLine(px1+4, pBy-4, px1+4, pBy+4); - gVirtualX->DrawLine(px1+4, pTy+4, px1-4, pTy+4); - gVirtualX->DrawLine(px1-4, pTy+4, px1-4, pTy-4); - gVirtualX->DrawLine(px1-4, pTy-4, px1+4, pTy-4); - gVirtualX->DrawLine(px1+4, pTy-4, px1+4, pTy+4); - } // No break !!! - - case kMouseMotion: - px1 = gPad->XtoAbsPixel(fX1); - py1 = gPad->YtoAbsPixel(fY1); - pTx = pBx = px1; - pLy = pRy = py1; - pLx = gPad->XtoAbsPixel(-fR1+fX1); - pRx = gPad->XtoAbsPixel( fR1+fX1); - - pTy = gPad->YtoAbsPixel(fY1) + TMath::Abs(pLx-pRx)/2; - pBy = gPad->YtoAbsPixel(fY1) - TMath::Abs(pLx-pRx)/2; - - pTop = pL = pR = pBot = pINSIDE = kFALSE; - if ((TMath::Abs(px - pTx) < kMaxDiff) && - (TMath::Abs(py - pTy) < kMaxDiff)) { // top edge - pTop = kTRUE; - gPad->SetCursor(kTopSide); - } - else - if ((TMath::Abs(px - pBx) < kMaxDiff) && - (TMath::Abs(py - pBy) < kMaxDiff)) { // bottom edge - pBot = kTRUE; - gPad->SetCursor(kBottomSide); - } - else - if ((TMath::Abs(py - pLy) < kMaxDiff) && - (TMath::Abs(px - pLx) < kMaxDiff)) { // left edge - pL = kTRUE; - gPad->SetCursor(kLeftSide); - } - else - if ((TMath::Abs(py - pRy) < kMaxDiff) && - (TMath::Abs(px - pRx) < kMaxDiff)) { // right edge - pR = kTRUE; - gPad->SetCursor(kRightSide); + case kMouseMotion: { + Int_t px1 = parent.XtoAbsPixel(parent.XtoPad(fX1)); + Int_t py1 = parent.YtoAbsPixel(parent.YtoPad(fY1)); + Int_t pLx = parent.XtoAbsPixel(parent.XtoPad(fX1 - fR1)); + Int_t pRx = parent.XtoAbsPixel(parent.XtoPad(fX1 + fR1)); + Int_t pTy = parent.YtoAbsPixel(parent.YtoPad(fY1 + fR1)); + Int_t pBy = parent.YtoAbsPixel(parent.YtoPad(fY1 - fR1)); + + if ((abs(px - px1) < kMaxDiff) && (abs(py - pTy) < kMaxDiff)) { + mode = pTop; + parent.SetCursor(kTopSide); + } else if ((abs(px - px1) < kMaxDiff) && (abs(py - pBy) < kMaxDiff)) { + mode = pBot; + parent.SetCursor(kBottomSide); + } else if ((abs(py - py1) < kMaxDiff) && (abs(px - pLx) < kMaxDiff)) { + mode = pL; + parent.SetCursor(kLeftSide); + } else if ((abs(py - py1) < kMaxDiff) && (abs(px - pRx) < kMaxDiff)) { + mode = pR; + parent.SetCursor(kRightSide); + } else { + mode = pINSIDE; + sdx = px1 - px; + sdy = py1 - py; + parent.SetCursor(kMove); } - else {pINSIDE= kTRUE; gPad->SetCursor(kMove); } - pxold = px; pyold = py; + first_move = kTRUE; break; + } case kArrowKeyRelease: - case kButton1Motion: - if (!opaque) { - gVirtualX->DrawLine(pRx+4, py1+4, pRx-4, py1+4); - gVirtualX->DrawLine(pRx-4, py1+4, pRx-4, py1-4); - gVirtualX->DrawLine(pRx-4, py1-4, pRx+4, py1-4); - gVirtualX->DrawLine(pRx+4, py1-4, pRx+4, py1+4); - gVirtualX->DrawLine(pLx+4, py1+4, pLx-4, py1+4); - gVirtualX->DrawLine(pLx-4, py1+4, pLx-4, py1-4); - gVirtualX->DrawLine(pLx-4, py1-4, pLx+4, py1-4); - gVirtualX->DrawLine(pLx+4, py1-4, pLx+4, py1+4); - gVirtualX->DrawLine(px1+4, pBy+4, px1-4, pBy+4); - gVirtualX->DrawLine(px1-4, pBy+4, px1-4, pBy-4); - gVirtualX->DrawLine(px1-4, pBy-4, px1+4, pBy-4); - gVirtualX->DrawLine(px1+4, pBy-4, px1+4, pBy+4); - gVirtualX->DrawLine(px1+4, pTy+4, px1-4, pTy+4); - gVirtualX->DrawLine(px1-4, pTy+4, px1-4, pTy-4); - gVirtualX->DrawLine(px1-4, pTy-4, px1+4, pTy-4); - gVirtualX->DrawLine(px1+4, pTy-4, px1+4, pTy+4); - for (i=0;iDrawLine(x[i], y[i], x[i+1], y[i+1]); - } - if (pTop) { - r1 += (py - pyold); - } - if (pBot) { - r1 -= (py - pyold); - } - if (pL) { - r1 -= (px - pxold); - } - if (pR) { - r1 += (px - pxold); - } - if (pTop || pBot || pL || pR) { - if (!opaque) { - gVirtualX->SetLineColor(-1); - TAttLine::Modify(); - dphi = (fPhimax-fPhimin) * pi / 180; - if (dphi<0) dphi += 2 * pi; - dphi /= np; - phi0 = fPhimin * pi / 180; - Double_t ur1 = r1; - Int_t pX1 = gPad->XtoAbsPixel(fX1); - Int_t pY1 = gPad->YtoAbsPixel(fY1); - for (i=0;i<=np;i++) { - angle = Double_t(i)*dphi + phi0; - dx = ur1 * TMath::Cos(angle); - dy = ur1 * TMath::Sin(angle); - x[i] = pX1 + (Int_t)dx; - y[i] = pY1 + (Int_t)dy; - } - if (fPhimax-fPhimin >= 360 ) { - x[np+1] = x[0]; - y[np+1] = y[0]; - npe = np; - } else { - x[np+1] = pX1; - y[np+1] = pY1; - x[np+2] = x[0]; - y[np+2] = y[0]; - npe = np + 2; - } - for (i=0;iDrawLine(x[i], y[i], x[i+1], y[i+1]); - } - } - else { - this->SetStartPoint(gPad->AbsPixeltoX(px1), gPad->AbsPixeltoY(py1)); - this->SetRadius(TMath::Abs(gPad->AbsPixeltoX(px1-r1)-gPad->AbsPixeltoX(px1+r1))/2); - if (pTop) gPad->ShowGuidelines(this, event, 't', true); - if (pBot) gPad->ShowGuidelines(this, event, 'b', true); - if (pL) gPad->ShowGuidelines(this, event, 'l', true); - if (pR) gPad->ShowGuidelines(this, event, 'r', true); - gPad->Modified(kTRUE); - gPad->Update(); - } + case kButton1Motion: { + if (!opaque && !first_move) + paint_hollow(); + char guide = 0; + switch (mode) { + case pNone: + break; + case pTop: + fR1 = GetYCoord(py, kFALSE, kTRUE) - fY1; + guide = 't'; + break; + case pBot: + fR1 = fY1 - GetYCoord(py, kFALSE, kTRUE); + guide = 'b'; + break; + case pL: + fR1 = fX1 - GetXCoord(px, kFALSE, kTRUE); + guide = 'l'; + break; + case pR: + fR1 = GetXCoord(px, kFALSE, kTRUE) - fX1; + guide = 'r'; + break; + case pINSIDE: + fX1 = GetXCoord(px + sdx, kFALSE, kTRUE); + fY1 = GetYCoord(py + sdy, kFALSE, kTRUE); + guide = 'i'; + break; } - if (pINSIDE) { - dpx = px-pxold; dpy = py-pyold; - px1 += dpx; py1 += dpy; - if (!opaque) { - for (i=0;i<=npe;i++) { x[i] += dpx; y[i] += dpy;} - for (i=0;iDrawLine(x[i], y[i], x[i+1], y[i+1]); - } else { - this->SetStartPoint(gPad->AbsPixeltoX(px1), gPad->AbsPixeltoY(py1)); - gPad->ShowGuidelines(this, event, 'i', true); - gPad->Modified(kTRUE); - gPad->Update(); - } - } - pTx = pBx = px1; - pRx = px1+r1; - pLx = px1-r1; - pRy = pLy = py1; - pTy = py1-r1; - pBy = py1+r1; - if (!opaque) { - gVirtualX->DrawLine(pRx+4, py1+4, pRx-4, py1+4); - gVirtualX->DrawLine(pRx-4, py1+4, pRx-4, py1-4); - gVirtualX->DrawLine(pRx-4, py1-4, pRx+4, py1-4); - gVirtualX->DrawLine(pRx+4, py1-4, pRx+4, py1+4); - gVirtualX->DrawLine(pLx+4, py1+4, pLx-4, py1+4); - gVirtualX->DrawLine(pLx-4, py1+4, pLx-4, py1-4); - gVirtualX->DrawLine(pLx-4, py1-4, pLx+4, py1-4); - gVirtualX->DrawLine(pLx+4, py1-4, pLx+4, py1+4); - gVirtualX->DrawLine(px1+4, pBy+4, px1-4, pBy+4); - gVirtualX->DrawLine(px1-4, pBy+4, px1-4, pBy-4); - gVirtualX->DrawLine(px1-4, pBy-4, px1+4, pBy-4); - gVirtualX->DrawLine(px1+4, pBy-4, px1+4, pBy+4); - gVirtualX->DrawLine(px1+4, pTy+4, px1-4, pTy+4); - gVirtualX->DrawLine(px1-4, pTy+4, px1-4, pTy-4); - gVirtualX->DrawLine(px1-4, pTy-4, px1+4, pTy-4); - gVirtualX->DrawLine(px1+4, pTy-4, px1+4, pTy+4); + + first_move = kFALSE; + + if (!opaque) + paint_hollow(); + else { + if (guide) + parent.ShowGuidelines(this, event, guide, true); + Build(); + parent.ModifiedUpdate(); } - pxold = px; - pyold = py; break; + } case kButton1Up: if (opaque) { - gPad->ShowGuidelines(this, event); + parent.ShowGuidelines(this, event); } else { - fX1 = gPad->AbsPixeltoX(px1); - fY1 = gPad->AbsPixeltoY(py1); - rLx = gPad->AbsPixeltoX(px1+r1); - rRx = gPad->AbsPixeltoX(px1-r1); - fR1 = TMath::Abs(rRx-rLx)/2; + Build(); + parent.Modified(kTRUE); } - Build(); - gPad->Modified(kTRUE); - if (!opaque) gVirtualX->SetLineColor(-1); } } @@ -512,15 +398,15 @@ Bool_t TCurlyArc::GetDefaultIsCurly() Rectangle_t TCurlyArc::GetBBox() { - Rectangle_t BBox{0, 0, 0, 0}; + Rectangle_t bbox{0, 0, 0, 0}; if (gPad) { Double_t R2 = fR1 * TMath::Abs(gPad->GetY2() - gPad->GetY1()) / TMath::Abs(gPad->GetX2() - gPad->GetX1()); - BBox.fX = gPad->XtoPixel(fX1 - fR1); - BBox.fY = gPad->YtoPixel(fY1 + R2); - BBox.fWidth = gPad->XtoPixel(fX1 + fR1) - gPad->XtoPixel(fX1 - fR1); - BBox.fHeight = gPad->YtoPixel(fY1 - R2) - gPad->YtoPixel(fY1 + R2); + bbox.fX = gPad->XtoPixel(fX1 - fR1); + bbox.fY = gPad->YtoPixel(fY1 + R2); + bbox.fWidth = gPad->XtoPixel(fX1 + fR1) - bbox.fX; + bbox.fHeight = gPad->YtoPixel(fY1 - R2) - bbox.fY; } - return BBox; + return bbox; } //////////////////////////////////////////////////////////////////////////////// @@ -536,24 +422,12 @@ TPoint TCurlyArc::GetBBoxCenter() return p; } -//////////////////////////////////////////////////////////////////////////////// -/// Set center of the BoundingBox - -void TCurlyArc::SetBBoxCenter(const TPoint &p) -{ - if (!gPad) return; - fX1 = gPad->PixeltoX(p.GetX()); - fY1 = gPad->PixeltoY(p.GetY()-gPad->VtoPixel(0)); - Build(); -} - //////////////////////////////////////////////////////////////////////////////// /// Set X coordinate of the center of the BoundingBox void TCurlyArc::SetBBoxCenterX(const Int_t x) { - if (!gPad) return; - fX1 = gPad->PixeltoX(x); + fX1 = GetXCoord(x); Build(); } @@ -562,8 +436,7 @@ void TCurlyArc::SetBBoxCenterX(const Int_t x) void TCurlyArc::SetBBoxCenterY(const Int_t y) { - if (!gPad) return; - fY1 = gPad->PixeltoY(y-gPad->VtoPixel(0)); + fY1 = GetYCoord(y); Build(); } @@ -573,12 +446,7 @@ void TCurlyArc::SetBBoxCenterY(const Int_t y) void TCurlyArc::SetBBoxX1(const Int_t x) { - if (!gPad) return; - Double_t x1 = gPad->PixeltoX(x); - if (x1>fX1+fR1) return; - - fR1 = (fX1+fR1-x1)*0.5; - fX1 = x1 + fR1; + fR1 = fX1 - GetXCoord(x); } //////////////////////////////////////////////////////////////////////////////// @@ -587,12 +455,7 @@ void TCurlyArc::SetBBoxX1(const Int_t x) void TCurlyArc::SetBBoxX2(const Int_t x) { - if (!gPad) return; - Double_t x2 = gPad->PixeltoX(x); - if (x2GetY2()-gPad->GetY1())/TMath::Abs(gPad->GetX2()-gPad->GetX1()); - - Double_t y1 = gPad->PixeltoY(y-gPad->VtoPixel(0)); - if (y1GetY2()-gPad->GetY1())/TMath::Abs(gPad->GetX2()-gPad->GetX1())); - fY1 = y1-R2; + fR1 = GetYCoord(y) - fY1; } //////////////////////////////////////////////////////////////////////////////// @@ -616,13 +472,5 @@ void TCurlyArc::SetBBoxY1(const Int_t y) void TCurlyArc::SetBBoxY2(const Int_t y) { - if (!gPad) return; - Double_t R2 = fR1 * TMath::Abs(gPad->GetY2()-gPad->GetY1())/TMath::Abs(gPad->GetX2()-gPad->GetX1()); - - Double_t y2 = gPad->PixeltoY(y-gPad->VtoPixel(0)); - - if (y2>fY1+R2) return; - - fR1 = (fY1+R2-y2)*0.5 / (TMath::Abs(gPad->GetY2()-gPad->GetY1())/TMath::Abs(gPad->GetX2()-gPad->GetX1())); - fY1 = y2+R2; + fR1 = fY1 - GetYCoord(y); } From b10682ce987c37c16884fa746c88c4cf987ccc60 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Wed, 13 May 2026 13:26:26 +0200 Subject: [PATCH 25/26] Optimize TEllipse::ExecuteEvent Reuse same coordinates calculations --- graf2d/graf/src/TEllipse.cxx | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/graf2d/graf/src/TEllipse.cxx b/graf2d/graf/src/TEllipse.cxx index 6a11bd03622e7..d56251034a593 100644 --- a/graf2d/graf/src/TEllipse.cxx +++ b/graf2d/graf/src/TEllipse.cxx @@ -204,16 +204,15 @@ void TEllipse::ExecuteEvent(Int_t event, Int_t px, Int_t py) auto &parent = *gPad; - auto pp = parent.GetPainter(); - - Int_t kMaxDiff = 10; + constexpr Int_t kMaxDiff = 10; static enum { pNone, pTop, pL, pR, pBot, pINSIDE } mode = pNone; static Int_t sdx = 0, sdy = 0; static Double_t oldX1, oldY1, oldR1, oldR2; static Bool_t first_move = kTRUE; - auto paint_hollow = [this,&parent,pp]() { + auto paint_hollow = [this,&parent]() { + auto pp = parent.GetPainter(); pp->SetAttLine(*this); std::vector x, y; FillPoints(parent, x, y, GetX1(), GetY1(), GetR1(), GetR2(), GetPhimin(), GetPhimax(), GetTheta()); @@ -247,6 +246,12 @@ void TEllipse::ExecuteEvent(Int_t event, Int_t px, Int_t py) }; Bool_t opaque = parent.OpaqueMoving(); + Int_t px1 = parent.XtoAbsPixel(parent.XtoPad(GetX1())); + Int_t py1 = parent.YtoAbsPixel(parent.YtoPad(GetY1())); + Int_t pLx = parent.XtoAbsPixel(parent.XtoPad(GetX1() - GetR1())); + Int_t pRx = parent.XtoAbsPixel(parent.XtoPad(GetX1() + GetR1())); + Int_t pBy = parent.YtoAbsPixel(parent.YtoPad(GetY1() - GetR2())); + Int_t pTy = parent.YtoAbsPixel(parent.YtoPad(GetY1() + GetR2())); switch (event) { @@ -257,18 +262,12 @@ void TEllipse::ExecuteEvent(Int_t event, Int_t px, Int_t py) oldR1 = GetR1(); oldR2 = GetR2(); - sdx = parent.XtoAbsPixel(parent.XtoPad(GetX1())) - px; - sdy = parent.YtoAbsPixel(parent.YtoPad(GetY1())) - py; + sdx = px1 - px; + sdy = py1 - py; // No break !!! case kMouseMotion: { - Int_t px1 = parent.XtoAbsPixel(parent.XtoPad(GetX1())); - Int_t py1 = parent.YtoAbsPixel(parent.YtoPad(GetY1())); - Int_t pLx = parent.XtoAbsPixel(parent.XtoPad(GetX1() - GetR1())); - Int_t pRx = parent.XtoAbsPixel(parent.XtoPad(GetX1() + GetR1())); - Int_t pBy = parent.YtoAbsPixel(parent.YtoPad(GetY1() - GetR2())); - Int_t pTy = parent.YtoAbsPixel(parent.YtoPad(GetY1() + GetR2())); mode = pNone; if ((TMath::Abs(px - px1) < kMaxDiff) && (TMath::Abs(py - pTy) < kMaxDiff)) { mode = pTop; // top edge @@ -301,19 +300,19 @@ void TEllipse::ExecuteEvent(Int_t event, Int_t px, Int_t py) case pNone: break; case pL: - changeX(px, parent.XtoAbsPixel(parent.XtoPad(GetX1() + GetR1()))); + changeX(px, pRx); guide = 'l'; break; case pR: - changeX(parent.XtoAbsPixel(parent.XtoPad(GetX1() - GetR1())), px); + changeX(pLx, px); guide = 'r'; break; case pTop: - changeY(py, parent.YtoAbsPixel(parent.YtoPad(GetY1() - GetR2()))); + changeY(py, pBy); guide = 't'; break; case pBot: - changeY(parent.YtoAbsPixel(parent.YtoPad(GetY1() + GetR2())), py); + changeY(pTy, py); guide = 'b'; break; case pINSIDE: From a5a11dc57b0b42ee7a66989f637a3d6fef4d3824 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Wed, 13 May 2026 16:01:28 +0200 Subject: [PATCH 26/26] Modernize TPolyLine::ExecuteEvent Fix bug when last point was moved but not updating first point - opening closed figure Support both normal coordinates and NDC coordinates Change directly values of poly line Remove most of static variables Fully remove gVirtualX usage --- graf2d/graf/src/TPolyLine.cxx | 349 +++++++++++++--------------------- 1 file changed, 132 insertions(+), 217 deletions(-) diff --git a/graf2d/graf/src/TPolyLine.cxx b/graf2d/graf/src/TPolyLine.cxx index a9fe3b7e4c407..c50fe518030ae 100644 --- a/graf2d/graf/src/TPolyLine.cxx +++ b/graf2d/graf/src/TPolyLine.cxx @@ -15,7 +15,8 @@ #include "TBuffer.h" #include "TMath.h" #include "TVirtualPad.h" -#include "TVirtualX.h" +#include "TVirtualPadPainter.h" +#include "TAttMarker.h" #include "TPolyLine.h" @@ -243,222 +244,138 @@ TPolyLine *TPolyLine::DrawPolyLine(Int_t n, Double_t *x, Double_t *y, Option_t * void TPolyLine::ExecuteEvent(Int_t event, Int_t px, Int_t py) { - if (!gPad) return; + if (!gPad || !gPad->IsEditable()) return; - Int_t i, d; - Double_t xmin, xmax, ymin, ymax, dx, dy, dxr, dyr; - const Int_t kMaxDiff = 10; - static Bool_t middle; - static Int_t ipoint, pxp, pyp; - static Int_t px1,px2,py1,py2; - static Int_t pxold, pyold, px1old, py1old, px2old, py2old; - static Int_t dpx, dpy; - static std::vector x, y; - Bool_t opaque = gPad->OpaqueMoving(); + auto &parent = *gPad; - if (!gPad->IsEditable()) return; + constexpr Int_t kMaxDiff = 10; + Bool_t opaque = parent.OpaqueMoving(); + static Int_t sdx, sdy, ipoint; + static Bool_t first_move; Int_t np = Size(); + Bool_t is_last_same = (np > 1) && (fX[0] == fX[np-1]) && (fY[0] == fY[np-1]); + if (is_last_same) + np--; + + auto paint_hollow = [this,&parent,is_last_same] () { + auto pp = parent.GetPainter(); + pp->SetAttLine({1,1,GetLineWidth()}); + Double_t *x = fX, *y = fY; + if (TestBit(kPolyLineNDC)) { + pp->DrawPolyLineNDC(Size(), x, y); + } else { + std::vector xx, yy; + if (parent.GetLogx()) { + xx.resize(Size()); + for (Int_t ix = 0; ix < Size(); ix++) + xx[ix] = parent.XtoPad(x[ix]); + x = xx.data(); + } + if (parent.GetLogy()) { + yy.resize(Size()); + for (Int_t iy = 0; iy < Size(); iy++) + yy[iy] = parent.YtoPad(y[iy]); + y = yy.data(); + } + pp->DrawPolyLine(Size(), x, y); - switch (event) { - - case kButton1Down: - gVirtualX->SetLineColor(-1); - TAttLine::Modify(); //Change line attributes only if necessary - px1 = gPad->XtoAbsPixel(gPad->GetX1()); - py1 = gPad->YtoAbsPixel(gPad->GetY1()); - px2 = gPad->XtoAbsPixel(gPad->GetX2()); - py2 = gPad->YtoAbsPixel(gPad->GetY2()); - ipoint = -1; - + pp->SetAttMarker({1,25,1}); + pp->DrawPolyMarker(is_last_same ? Size()-1 : Size(), x, y); + } + }; - if (!x.empty() || !y.empty()) break; - x.resize(np+1, 0); - y.resize(np+1, 0); - for (i=0;iXtoAbsPixel(gPad->XtoPad(fX[i])); - pyp = gPad->YtoAbsPixel(gPad->YtoPad(fY[i])); - if (!opaque) { - gVirtualX->DrawLine(pxp-4, pyp-4, pxp+4, pyp-4); - gVirtualX->DrawLine(pxp+4, pyp-4, pxp+4, pyp+4); - gVirtualX->DrawLine(pxp+4, pyp+4, pxp-4, pyp+4); - gVirtualX->DrawLine(pxp-4, pyp+4, pxp-4, pyp-4); - } - x[i] = pxp; - y[i] = pyp; - d = TMath::Abs(pxp-px) + TMath::Abs(pyp-py); - if (d < kMaxDiff) ipoint =i; + auto get_point = [this, &parent](Int_t i, Int_t &pntx, Int_t &pnty) { + if (TestBit(kPolyLineNDC)) { + pntx = parent.UtoAbsPixel(fX[i]); + pnty = parent.VtoAbsPixel(fY[i]); + } else { + pntx = parent.XtoAbsPixel(parent.XtoPad(fX[i])); + pnty = parent.YtoAbsPixel(parent.YtoPad(fY[i])); } - dpx = 0; - dpy = 0; - pxold = px; - pyold = py; - if (ipoint < 0) return; - if (ipoint == 0) { - px1old = 0; - py1old = 0; - px2old = gPad->XtoAbsPixel(fX[1]); - py2old = gPad->YtoAbsPixel(fY[1]); - } else if (ipoint == fN-1) { - px1old = gPad->XtoAbsPixel(gPad->XtoPad(fX[fN-2])); - py1old = gPad->YtoAbsPixel(gPad->YtoPad(fY[fN-2])); - px2old = 0; - py2old = 0; + }; + + auto set_point = [this, &parent](Int_t i, Int_t pntx, Int_t pnty) { + if (TestBit(kPolyLineNDC)) { + Double_t ww = parent.GetWw(); + Double_t wndc = parent.GetAbsWNDC(); + Double_t wh = parent.GetWh(); + Double_t hndc = parent.GetAbsHNDC(); + fX[i] = ww > 0 && wndc > 0 ? (pntx / ww - parent.GetAbsXlowNDC()) / wndc : 0.; + fY[i] = wh > 0 && hndc > 0 ? ((1. - pnty / wh) - parent.GetAbsYlowNDC()) / hndc : 0.; } else { - px1old = gPad->XtoAbsPixel(gPad->XtoPad(fX[ipoint-1])); - py1old = gPad->YtoAbsPixel(gPad->YtoPad(fY[ipoint-1])); - px2old = gPad->XtoAbsPixel(gPad->XtoPad(fX[ipoint+1])); - py2old = gPad->YtoAbsPixel(gPad->YtoPad(fY[ipoint+1])); + fX[i] = parent.PadtoX(parent.AbsPixeltoX(pntx)); + fY[i] = parent.PadtoY(parent.AbsPixeltoY(pnty)); } - pxold = gPad->XtoAbsPixel(gPad->XtoPad(fX[ipoint])); - pyold = gPad->YtoAbsPixel(gPad->YtoPad(fY[ipoint])); - - break; + }; + switch (event) { - case kMouseMotion: + case kArrowKeyPress: + case kButton1Down: + // No break !!! + case kMouseMotion: { - middle = kTRUE; - for (i=0;iXtoAbsPixel(gPad->XtoPad(fX[i])); - pyp = gPad->YtoAbsPixel(gPad->YtoPad(fY[i])); - d = TMath::Abs(pxp-px) + TMath::Abs(pyp-py); - if (d < kMaxDiff) middle = kFALSE; + Int_t minDiff = kMaxDiff; + ipoint = -1; + for (Int_t i = 0; i < np; i++) { + Int_t pxp, pyp; + get_point(i, pxp, pyp); + if (i == 0) { + sdx = pxp - px; + sdy = pyp - py; + } + Int_t d = TMath::Abs(pxp - px) + TMath::Abs(pyp - py); + if (d < minDiff) { + ipoint = i; + minDiff = d; + sdx = pxp - px; + sdy = pyp - py; + } } - - - // check if point is close to an axis - if (middle) gPad->SetCursor(kMove); - else gPad->SetCursor(kHand); + first_move = kTRUE; + if (ipoint < 0) + parent.SetCursor(kMove); + else + parent.SetCursor(kHand); break; + } case kButton1Motion: - if (!opaque) { - if (middle) { - for(i=0;iDrawLine(x[i]+dpx, y[i]+dpy, x[i+1]+dpx, y[i+1]+dpy); - pxp = x[i]+dpx; - pyp = y[i]+dpy; - gVirtualX->DrawLine(pxp-4, pyp-4, pxp+4, pyp-4); - gVirtualX->DrawLine(pxp+4, pyp-4, pxp+4, pyp+4); - gVirtualX->DrawLine(pxp+4, pyp+4, pxp-4, pyp+4); - gVirtualX->DrawLine(pxp-4, pyp+4, pxp-4, pyp-4); - } - pxp = x[np-1]+dpx; - pyp = y[np-1]+dpy; - gVirtualX->DrawLine(pxp-4, pyp-4, pxp+4, pyp-4); - gVirtualX->DrawLine(pxp+4, pyp-4, pxp+4, pyp+4); - gVirtualX->DrawLine(pxp+4, pyp+4, pxp-4, pyp+4); - gVirtualX->DrawLine(pxp-4, pyp+4, pxp-4, pyp-4); - dpx += px - pxold; - dpy += py - pyold; - pxold = px; - pyold = py; - for(i=0;iDrawLine(x[i]+dpx, y[i]+dpy, x[i+1]+dpx, y[i+1]+dpy); - pxp = x[i]+dpx; - pyp = y[i]+dpy; - gVirtualX->DrawLine(pxp-4, pyp-4, pxp+4, pyp-4); - gVirtualX->DrawLine(pxp+4, pyp-4, pxp+4, pyp+4); - gVirtualX->DrawLine(pxp+4, pyp+4, pxp-4, pyp+4); - gVirtualX->DrawLine(pxp-4, pyp+4, pxp-4, pyp-4); + if (!opaque && !first_move) + paint_hollow(); + + if (ipoint < 0) { + Int_t pxp0, pyp0, pxp, pyp; + // move all points + for (Int_t i = 0; i < np; i++) { + get_point(i, pxp, pyp); + if (i == 0) { + pxp0 = pxp; + pyp0 = pyp; } - pxp = x[np-1]+dpx; - pyp = y[np-1]+dpy; - gVirtualX->DrawLine(pxp-4, pyp-4, pxp+4, pyp-4); - gVirtualX->DrawLine(pxp+4, pyp-4, pxp+4, pyp+4); - gVirtualX->DrawLine(pxp+4, pyp+4, pxp-4, pyp+4); - gVirtualX->DrawLine(pxp-4, pyp+4, pxp-4, pyp-4); - } else { - if (px1old) gVirtualX->DrawLine(px1old, py1old, pxold, pyold); - if (px2old) gVirtualX->DrawLine(pxold, pyold, px2old, py2old); - gVirtualX->DrawLine(pxold-4, pyold-4, pxold+4, pyold-4); - gVirtualX->DrawLine(pxold+4, pyold-4, pxold+4, pyold+4); - gVirtualX->DrawLine(pxold+4, pyold+4, pxold-4, pyold+4); - gVirtualX->DrawLine(pxold-4, pyold+4, pxold-4, pyold-4); - pxold = px; - pxold = TMath::Max(pxold, px1); - pxold = TMath::Min(pxold, px2); - pyold = py; - pyold = TMath::Max(pyold, py2); - pyold = TMath::Min(pyold, py1); - if (px1old) gVirtualX->DrawLine(px1old, py1old, pxold, pyold); - if (px2old) gVirtualX->DrawLine(pxold, pyold, px2old, py2old); - gVirtualX->DrawLine(pxold-4, pyold-4, pxold+4, pyold-4); - gVirtualX->DrawLine(pxold+4, pyold-4, pxold+4, pyold+4); - gVirtualX->DrawLine(pxold+4, pyold+4, pxold-4, pyold+4); - gVirtualX->DrawLine(pxold-4, pyold+4, pxold-4, pyold-4); + set_point(i, px + sdx + pxp - pxp0, py + sdy + pyp - pyp0); } } else { - if (middle) { - for(i=0;iPadtoX(gPad->AbsPixeltoX(x[i]+dpx)); - fY[i] = gPad->PadtoY(gPad->AbsPixeltoY(y[i]+dpy)); - } - } else { - fX[ipoint] = gPad->PadtoX(gPad->AbsPixeltoX(pxold)); - fY[ipoint] = gPad->PadtoY(gPad->AbsPixeltoY(pyold)); - } - } - gPad->Modified(kTRUE); + // move only selected point + set_point(ipoint, px + sdx, py + sdy); + } + if (is_last_same) { + fX[np] = fX[0]; + fY[np] = fY[0]; } + + first_move = kFALSE; + if (!opaque) + paint_hollow(); + else + parent.ModifiedUpdate(); break; case kButton1Up: - - // Compute x,y range - xmin = gPad->GetUxmin(); - xmax = gPad->GetUxmax(); - ymin = gPad->GetUymin(); - ymax = gPad->GetUymax(); - dx = xmax-xmin; - dy = ymax-ymin; - dxr = dx/(1 - gPad->GetLeftMargin() - gPad->GetRightMargin()); - dyr = dy/(1 - gPad->GetBottomMargin() - gPad->GetTopMargin()); - - // Range() could change the size of the pad pixmap and therefore should - // be called before the other paint routines - gPad->Range(xmin - dxr*gPad->GetLeftMargin(), - ymin - dyr*gPad->GetBottomMargin(), - xmax + dxr*gPad->GetRightMargin(), - ymax + dyr*gPad->GetTopMargin()); - gPad->RangeAxis(xmin, ymin, xmax, ymax); - - if (!x.empty() && !y.empty()) { - if (middle) { - for(i=0;iPadtoX(gPad->AbsPixeltoX(x[i]+dpx)); - fY[i] = gPad->PadtoY(gPad->AbsPixeltoY(y[i]+dpy)); - } - } else { - fX[ipoint] = gPad->PadtoX(gPad->AbsPixeltoX(pxold)); - fY[ipoint] = gPad->PadtoY(gPad->AbsPixeltoY(pyold)); - } - x.clear(); - y.clear(); - } - gPad->Modified(kTRUE); - gVirtualX->SetLineColor(-1); + if (!opaque) + parent.ModifiedUpdate(); + break; } } @@ -513,13 +430,10 @@ Int_t TPolyLine::Merge(TCollection *li) void TPolyLine::Paint(Option_t *option) { - if (TestBit(kPolyLineNDC)) { - if (option && strlen(option)) PaintPolyLineNDC(fLastPoint+1, fX, fY, option); - else PaintPolyLineNDC(fLastPoint+1, fX, fY, fOption.Data()); - } else { - if (option && strlen(option)) PaintPolyLine(fLastPoint+1, fX, fY, option); - else PaintPolyLine(fLastPoint+1, fX, fY, fOption.Data()); - } + if (TestBit(kPolyLineNDC)) + PaintPolyLineNDC(fLastPoint+1, fX, fY, option && *option ? option : fOption.Data()); + else + PaintPolyLine(fLastPoint+1, fX, fY, option && *option ? option : fOption.Data()); } //////////////////////////////////////////////////////////////////////////////// @@ -533,24 +447,23 @@ void TPolyLine::PaintPolyLine(Int_t n, Double_t *x, Double_t *y, Option_t *optio if (!gPad || n <= 0) return; TAttLine::Modify(); //Change line attributes only if necessary TAttFill::Modify(); //Change fill area attributes only if necessary - Double_t *xx = x; - Double_t *yy = y; + std::vector xx, yy; if (gPad->GetLogx()) { - xx = new Double_t[n]; - for (Int_t ix=0;ixXtoPad(x[ix]); + xx.resize(n); + for (Int_t ix = 0; ix < n; ix++) + xx[ix] = gPad->XtoPad(x[ix]); + x = xx.data(); } if (gPad->GetLogy()) { - yy = new Double_t[n]; - for (Int_t iy=0;iyYtoPad(y[iy]); + yy.resize(n); + for (Int_t iy = 0; iy < n; iy++) + yy[iy] = gPad->YtoPad(y[iy]); + y = yy.data(); } if (option && (*option == 'f' || *option == 'F')) - gPad->PaintFillArea(n, xx, yy, option); + gPad->PaintFillArea(n, x, y, option); else - gPad->PaintPolyLine(n, xx, yy, option); - if (x != xx) - delete[] xx; - if (y != yy) - delete[] yy; + gPad->PaintPolyLine(n, x, y, option); } //////////////////////////////////////////////////////////////////////////////// @@ -558,10 +471,12 @@ void TPolyLine::PaintPolyLine(Int_t n, Double_t *x, Double_t *y, Option_t *optio void TPolyLine::PaintPolyLineNDC(Int_t n, Double_t *x, Double_t *y, Option_t *option) { - TAttLine::Modify(); //Change line attributes only if necessary - TAttFill::Modify(); //Change fill area attributes only if necessary - if (*option == 'f' || *option == 'F') gPad->PaintFillAreaNDC(n,x,y,option); - else gPad->PaintPolyLineNDC(n,x,y,option); + TAttLine::Modify(); // Change line attributes only if necessary + TAttFill::Modify(); // Change fill area attributes only if necessary + if (option && (*option == 'f' || *option == 'F')) + gPad->PaintFillAreaNDC(n, x, y, option); + else + gPad->PaintPolyLineNDC(n, x, y, option); } ////////////////////////////////////////////////////////////////////////////////