Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
TBoxInteractive.cxx
Go to the documentation of this file.
1// @(#)root/graf:$Id$
2// Author: Sergey Linev 18/09/2026
3
4/*************************************************************************
5 * Copyright (C) 1995-2026, Rene Brun and Fons Rademakers. *
6 * All rights reserved. *
7 * *
8 * For the licensing terms see $ROOTSYS/LICENSE. *
9 * For the list of contributors see $ROOTSYS/README/CREDITS. *
10 *************************************************************************/
11
12#include "TBoxInteractive.h"
13
14#include "TMath.h"
15
16/** \class TBoxInterctive
17 *
18 * Internal interactive class to implement moving of box-like objects
19 * in the ExecuteEvent
20 */
21
22///////////////////////////////////////////////////////////////////////////////
23/// Constructor.
24
33
34///////////////////////////////////////////////////////////////////////////////
35/// Calculate pixel coordinates
36
38{
39 px1 = parent.XtoAbsPixel(isBox ? parent.XtoPad(x1) : x1);
40 py1 = parent.YtoAbsPixel(isBox ? parent.YtoPad(y1) : y1);
41 px2 = parent.XtoAbsPixel(isBox ? parent.XtoPad(x2) : x2);
42 py2 = parent.YtoAbsPixel(isBox ? parent.YtoPad(y2) : y2);
43 if (px1 > px2)
44 std::swap(px1, px2);
45 if (py1 < py2)
46 std::swap(py1, py2);
47}
48
49///////////////////////////////////////////////////////////////////////////////
50/// Select corner or side for the inetarctive action
51
53{
54 constexpr Int_t kMaxDiff = 7;
55
56 if (TMath::Abs(px - px1) <= kMaxDiff && TMath::Abs(py - py2) <= kMaxDiff) {
57 fMode = canX && canY ? pA : (canX ? pL : pTop);
58 } else if (TMath::Abs(px - px2) <= kMaxDiff && TMath::Abs(py - py2) <= kMaxDiff) {
59 fMode = canX && canY ? pB : (canX ? pR : pTop);
60 } else if (TMath::Abs(px - px2) <= kMaxDiff && TMath::Abs(py - py1) <= kMaxDiff) {
61 fMode = canX && canY ? pC : (canX ? pR : pBot);
62 } else if (TMath::Abs(px - px1) <= kMaxDiff && TMath::Abs(py - py1) <= kMaxDiff) {
63 fMode = canX && canY ? pD : (canX ? pL : pBot);
64 } else if ((px > px1 + kMaxDiff && px < px2 - kMaxDiff) && TMath::Abs(py - py2) < kMaxDiff) {
65 fMode = canY ? pTop : pNone;
66 } else if ((px > px1 + kMaxDiff && px < px2 - kMaxDiff) && TMath::Abs(py - py1) < kMaxDiff) {
67 fMode = canY ? pBot : pNone;
68 } else if ((py > py2 + kMaxDiff && py < py1 - kMaxDiff) && TMath::Abs(px - px1) < kMaxDiff) {
69 fMode = canX ? pL : pNone;
70 } else if ((py > py2 + kMaxDiff && py < py1 - kMaxDiff) && TMath::Abs(px - px2) < kMaxDiff) {
71 fMode = canX ? pR : pNone;
72 } else if ((px > px1 + kMaxDiff && px < px2 - kMaxDiff) && (py > py2 + kMaxDiff && py < py1 - kMaxDiff)) {
73 dpx1 = px - px1; // cursor position relative to top-left corner
74 dpy2 = py - py2;
75 fMode = pINSIDE;
76 } else {
77 fMode = pNone;
78 }
79 return fMode != pNone;
80}
81
82///////////////////////////////////////////////////////////////////////////////
83/// Select diamond corner - or inside area
84/// Returns true if any mode was selected
85/// Can be reused for diamond and ellipse
86
88{
89 constexpr Int_t kMaxDiff = 7;
90
91 Double_t xd[4] = {(px1 + px2) / 2., 1. * px1, (px1 + px2) / 2., 1. * px2};
92 Double_t yd[4] = {1. * py2, (py1 + py2) / 2., 1. * py1, (py1 + py2) / 2.};
93 EMode modes[4] = {pTop, pL, pBot, pR};
94
95 fMode = pNone;
96
97 for (Int_t i = 0; i < 4; ++i)
98 if ((TMath::Abs(px - xd[i]) < kMaxDiff) && (TMath::Abs(py - yd[i]) < kMaxDiff)) {
99 fMode = modes[i];
100 break;
101 }
102
103 // use Double_t signature of IsInside because of rounding problems with Int_t
104 if ((fMode == pNone) && (!testInside || TMath::IsInside((Double_t)px, (Double_t)py, 4, xd, yd))) {
105 fMode = pINSIDE;
106 dpx1 = px - px1; // cursor position relative to top-left corner
107 dpy2 = py - py2;
108 }
109
110 return fMode != pNone;
111}
112
113///////////////////////////////////////////////////////////////////////////////
114/// Return true if doing resize
115
117{
118 return fMode != pNone && fMode != pINSIDE;
119}
120
121///////////////////////////////////////////////////////////////////////////////
122/// Return selected opaque mode
123
125{
126 return IsResizing() ? parent.OpaqueResizing() : parent.OpaqueMoving();
127}
128
129///////////////////////////////////////////////////////////////////////////////
130/// Process change of moue position during dragging
131
133{
134 constexpr Int_t kMinSize = 20;
135
136 Int_t px1p = parent.XtoAbsPixel(parent.GetX1()) + parent.GetBorderSize();
137 Int_t py1p = parent.YtoAbsPixel(parent.GetY1()) - parent.GetBorderSize();
138 Int_t px2p = parent.XtoAbsPixel(parent.GetX2()) - parent.GetBorderSize();
139 Int_t py2p = parent.YtoAbsPixel(parent.GetY2()) + parent.GetBorderSize();
140 if (px1p > px2p)
141 std::swap(px1p, px2p);
142 if (py1p < py2p)
143 std::swap(py1p, py2p);
144
145 enum { noAdjust = 1111, adjWidth = 11, adjBot = -1, adjHeight = 0, adjTop = 1 } choise = noAdjust;
147 prevpy1 = py1, prevpy2 = py2;
148
149 switch (fMode) {
150 case pNone: return kFALSE;
151 case pA:
152 choise = adjBot;
155 break;
156 case pB:
157 choise = adjBot;
160 break;
161 case pC:
162 choise = adjTop;
165 break;
166 case pD:
167 choise = adjTop;
170 break;
171 case pTop:
174 break;
175 case pBot:
178 break;
179 case pL:
182 break;
183 case pR:
186 break;
187 case pINSIDE:
188 if (canX) {
189 px2 += px - dpx1 - px1;
190 px1 = px - dpx1;
191 if (px1 < px1p) {
192 px2 += px1p - px1;
193 px1 = px1p;
194 }
195 if (px2 > px2p) {
196 px1 -= px2 - px2p;
197 px2 = px2p;
198 }
199 }
200 if (canY) {
201 py1 += py - dpy2 - py2;
202 py2 = py - dpy2;
203 if (py1 > py1p) {
204 py2 -= py1 - py1p;
205 py1 = py1p;
206 }
207 if (py2 < py2p) {
208 py1 += py2p - py2;
209 py2 = py2p;
210 }
211 }
212 break;
213 }
214
215 if (!aspectRatio || (choise == noAdjust))
216 return kTRUE;
217
218 if (choise == adjWidth) {
219 Int_t dx = parent.UtoPixel(aspectRatio * (py1 - py2) / parent.VtoPixel(0));
220 Int_t npx1 = (px1 + px2) / 2 - dx / 2, npx2 = npx1 + dx;
221 if ((npx1 >= px1p) && (npx2 <= px2p)) {
222 px1 = npx1;
223 px2 = npx2;
224 return kTRUE;
225 }
226 } else {
227 Int_t dy = parent.VtoPixel(1. - (0. + px2 - px1) / parent.UtoPixel(1.) / aspectRatio);
228 Int_t npy1 = py1, npy2 = py2;
229 if (choise == adjBot)
230 npy2 = py1 - dy;
231 else if (choise == adjTop)
232 npy1 = py2 + dy;
233 else if (choise == adjHeight) {
234 npy2 = (py1 + py2) / 2 - dy / 2;
235 npy1 = npy2 + dy;
236 }
237 if ((npy1 <= py1p) && (npy2 >= py2p)) {
238 py1 = npy1;
239 py2 = npy2;
240 return kTRUE;
241 }
242 }
243 px1 = prevpx1;
244 px2 = prevpx2;
245 py1 = prevpy1;
246 py2 = prevpy2;
247
248 return kFALSE;
249}
250
251///////////////////////////////////////////////////////////////////////////////
252/// Paint object outline.
253
255{
256 // "i" is interactive painting, "l" is line, "box" is id
257 parent.PaintBox(newX1, newY1, newX2, newY2, "ilbox");
258}
259
260///////////////////////////////////////////////////////////////////////////////
261/// Apply pixel coordinates changes back to box coordinates
262
264{
265 newX1 = parent.AbsPixeltoX(px1);
266 newY1 = parent.AbsPixeltoY(py1);
267 newX2 = parent.AbsPixeltoX(px2);
268 newY2 = parent.AbsPixeltoY(py2);
269
270 if (!IsOpaque(parent)) {
271 TAttLine(kBlack, 1, 1).ModifyOn(parent);
272 PaintOutline(parent);
273 parent.UpdateAsync();
274 }
275
276 if (isBox) {
277 newX1 = parent.PadtoX(newX1);
278 newX2 = parent.PadtoX(newX2);
279 newY1 = parent.PadtoY(newY1);
280 newY2 = parent.PadtoY(newY2);
281 }
282};
283
284///////////////////////////////////////////////////////////////////////////////
285/// Set cursor for current mode
286
288{
289 switch (fMode) {
290 case pNone: parent.SetCursor(kCross); break;
291 case pA: parent.SetCursor(kTopLeft); break;
292 case pB: parent.SetCursor(kTopRight); break;
293 case pC: parent.SetCursor(kBottomRight); break;
294 case pD: parent.SetCursor(kBottomLeft); break;
295 case pTop: parent.SetCursor(kTopSide); break;
296 case pL: parent.SetCursor(kLeftSide); break;
297 case pR: parent.SetCursor(kRightSide); break;
298 case pBot: parent.SetCursor(kBottomSide); break;
299 case pINSIDE: parent.SetCursor(is_down ? kMove : kCross); break;
300 }
301}
302
303///////////////////////////////////////////////////////////////////////////////
304/// Return character for selecting guide action
305
307{
308 switch (fMode) {
309 case pINSIDE: return 'i';
310 case pTop: return 't';
311 case pBot: return 'b';
312 case pL: return 'l';
313 case pR: return 'r';
314 case pA: return '1';
315 case pB: return '2';
316 case pC: return '3';
317 case pD: return '4';
318 default: return 0; // not involved
319 }
320}
@ kRightSide
Definition GuiTypes.h:374
@ kBottomSide
Definition GuiTypes.h:374
@ kTopLeft
Definition GuiTypes.h:373
@ kBottomRight
Definition GuiTypes.h:373
@ kTopSide
Definition GuiTypes.h:374
@ kLeftSide
Definition GuiTypes.h:374
@ kMove
Definition GuiTypes.h:375
@ kTopRight
Definition GuiTypes.h:373
@ kBottomLeft
Definition GuiTypes.h:373
@ kCross
Definition GuiTypes.h:375
constexpr Bool_t kFALSE
Definition RtypesCore.h:109
constexpr Bool_t kTRUE
Definition RtypesCore.h:108
@ kBlack
Definition Rtypes.h:65
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
Option_t Option_t TPoint TPoint const char x2
Option_t Option_t TPoint TPoint const char x1
Option_t Option_t TPoint TPoint const char y2
Option_t Option_t TPoint TPoint const char y1
Line Attributes class.
Definition TAttLine.h:21
virtual void ModifyOn(TVirtualPad &pad)
Change current line attributes on specified pad.
Definition TAttLine.cxx:255
char GetGuideChar() const
Return character for selecting guide action.
Bool_t IsOpaque(const TVirtualPad &parent) const
Return selected opaque mode.
void ApplyChanges(TVirtualPad &parent)
Apply pixel coordinates changes back to box coordinates.
Bool_t ProcessMouseMove(const TVirtualPad &parent, Int_t px, Int_t py, Bool_t canX=kTRUE, Bool_t canY=kTRUE, Double_t aspectRatio=0.)
Process change of moue position during dragging.
Bool_t IsResizing() const
Return true if doing resize.
enum TBoxInteractive::EMode fMode
void CalcPixelCoord(const TVirtualPad &parent, Double_t x1, Double_t y1, Double_t x2, Double_t y2)
Calculate pixel coordinates.
Bool_t SelectCorner(Int_t px, Int_t py, Bool_t canX=kTRUE, Bool_t canY=kTRUE)
Select corner or side for the inetarctive action.
TBoxInteractive(Bool_t bx=kTRUE, Double_t x1=0., Double_t y1=0., Double_t x2=0., Double_t y2=0.)
Constructor.
void SetCursor(TVirtualPad &parent, Bool_t is_down) const
Set cursor for current mode.
Bool_t SelectDiamondCorner(Int_t px, Int_t py, Bool_t testInside=kTRUE)
Select diamond corner - or inside area Returns true if any mode was selected Can be reused for diamon...
virtual void PaintOutline(TVirtualPad &parent)
Paint object outline.
TVirtualPad is an abstract base class for the Pad and Canvas classes.
Definition TVirtualPad.h:51
virtual Double_t PadtoX(Double_t x) const =0
virtual Int_t YtoAbsPixel(Double_t y) const =0
virtual Double_t GetX2() const =0
virtual void UpdateAsync()=0
virtual Double_t AbsPixeltoX(Double_t px)=0
virtual Int_t XtoAbsPixel(Double_t x) const =0
virtual Double_t GetY1() const =0
virtual void PaintBox(Double_t x1, Double_t y1, Double_t x2, Double_t y2, Option_t *option="")=0
virtual Double_t PadtoY(Double_t y) const =0
virtual Double_t YtoPad(Double_t y) const =0
virtual Int_t VtoPixel(Double_t v) const =0
virtual Double_t XtoPad(Double_t x) const =0
virtual Double_t GetY2() const =0
virtual Int_t UtoPixel(Double_t u) const =0
virtual Bool_t OpaqueResizing() const =0
virtual Double_t AbsPixeltoY(Double_t py)=0
virtual void SetCursor(ECursor cursor)=0
virtual Short_t GetBorderSize() const =0
virtual Double_t GetX1() const =0
virtual Bool_t OpaqueMoving() const =0
Short_t Max(Short_t a, Short_t b)
Returns the largest of a and b.
Definition TMathBase.h:249
Bool_t IsInside(T xp, T yp, Int_t np, T *x, T *y)
Function which returns kTRUE if point xp,yp lies inside the polygon defined by the np points in array...
Definition TMath.h:1326
Short_t Min(Short_t a, Short_t b)
Returns the smallest of a and b.
Definition TMathBase.h:197
Short_t Abs(Short_t d)
Returns the absolute value of parameter Short_t d.
Definition TMathBase.h:122