From d8cb214c8f49af06f1eee9944cb7f71bed627b34 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Thu, 7 May 2026 09:44:16 +0200 Subject: [PATCH 01/18] 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 0878467cca64a6a7799f87cd4322b133b5990ffd Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Thu, 7 May 2026 10:15:17 +0200 Subject: [PATCH 02/18] 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 d2dca987e3f55fb4dcaef0f72b6a84cd23c3d7d1 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Thu, 7 May 2026 10:38:39 +0200 Subject: [PATCH 03/18] 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 e4e19d187e351271ac540d000e69e63aa9fa233c Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Thu, 7 May 2026 11:20:24 +0200 Subject: [PATCH 04/18] 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 d1a56ee5e9014a9b25f78dce7caa5e3f2e770d14 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Thu, 7 May 2026 11:20:54 +0200 Subject: [PATCH 05/18] 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 af7dd83ff1bac6f5f60516d07cf3f78e11afb423 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Thu, 7 May 2026 12:20:15 +0200 Subject: [PATCH 06/18] 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 c2103f46f300f5b72bac7781a836b5d898c3bb94 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Thu, 7 May 2026 13:05:36 +0200 Subject: [PATCH 07/18] 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 86db13176bc1bcb618df3bc0a50eb0b9c89fabf1 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Thu, 7 May 2026 13:35:50 +0200 Subject: [PATCH 08/18] 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 21e05b7dc2b41b1c4e97297995465b65a12f05dc Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Thu, 7 May 2026 15:27:19 +0200 Subject: [PATCH 09/18] 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 436526b7e59ed20314a09a7b830376c9df577cc9 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Mon, 11 May 2026 11:19:31 +0200 Subject: [PATCH 10/18] [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 25b63bd079c1744c6d6942f6f90ee7e404c17c7a Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Mon, 11 May 2026 11:30:02 +0200 Subject: [PATCH 11/18] [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 47edb3f0dc93a7d0a89877bfe05eb66a7f5dbdea Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Mon, 11 May 2026 12:43:31 +0200 Subject: [PATCH 12/18] [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 d35337e922121e0c0c1f3d8e19d658d3aa377c63 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Mon, 11 May 2026 16:19:48 +0200 Subject: [PATCH 13/18] 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 c192a5063d9f44c0da7d3a70c46e855c0ab81773 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Tue, 12 May 2026 07:40:50 +0200 Subject: [PATCH 14/18] [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 eeb442065ad2c7be81a2e9b81c169da92c071579 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Tue, 12 May 2026 11:09:48 +0200 Subject: [PATCH 15/18] [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 2f13c0c3a5aa6ba7bb475772fbb6b928051ed3ea Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Tue, 12 May 2026 11:23:46 +0200 Subject: [PATCH 16/18] [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 31a01a07b8e350dfd9c93c6dfb60cbc4dde9a67e Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Tue, 12 May 2026 15:38:01 +0200 Subject: [PATCH 17/18] [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 536e23015e90824328d817a9b2bdd72cba369bd8 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Wed, 13 May 2026 07:22:12 +0200 Subject: [PATCH 18/18] 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 5b3d019887e4b..e1eff55bae501 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 5bd9ccf88228b..8fbf893487bec 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 - - - - - - + + + + + +