@@ -1796,7 +1796,8 @@ def __init__(self, ax, onselect, useblit=False, button=None,
1796
1796
self .connect_default_events ()
1797
1797
1798
1798
self ._state_modifier_keys = dict (move = ' ' , clear = 'escape' ,
1799
- square = 'shift' , center = 'control' )
1799
+ square = 'shift' , center = 'control' ,
1800
+ data_coordinates = 'd' )
1800
1801
self ._state_modifier_keys .update (state_modifier_keys or {})
1801
1802
1802
1803
self .background = None
@@ -1984,6 +1985,12 @@ def on_key_press(self, event):
1984
1985
artist .set_visible (False )
1985
1986
self .update ()
1986
1987
return
1988
+ if key == 'd' and key in self ._state_modifier_keys .values ():
1989
+ modifier = 'data_coordinates'
1990
+ if modifier in self ._default_state :
1991
+ self ._default_state .remove (modifier )
1992
+ else :
1993
+ self .add_default_state (modifier )
1987
1994
for (state , modifier ) in self ._state_modifier_keys .items ():
1988
1995
if modifier in key :
1989
1996
self ._state .add (state )
@@ -2185,7 +2192,8 @@ def __init__(self, ax, onselect, direction, minspan=0, useblit=False,
2185
2192
if state_modifier_keys is None :
2186
2193
state_modifier_keys = dict (clear = 'escape' ,
2187
2194
square = 'not-applicable' ,
2188
- center = 'not-applicable' )
2195
+ center = 'not-applicable' ,
2196
+ data_coordinates = 'not-applicable' )
2189
2197
super ().__init__ (ax , onselect , useblit = useblit , button = button ,
2190
2198
state_modifier_keys = state_modifier_keys )
2191
2199
@@ -2754,8 +2762,12 @@ def onselect(eclick: MouseEvent, erelease: MouseEvent)
2754
2762
- "clear": Clear the current shape, default: "escape".
2755
2763
- "square": Makes the shape square, default: "shift".
2756
2764
- "center": change the shape around its center, default: "ctrl".
2765
+ - "data_coordinates": define if data or figure coordinates should be
2766
+ used to define the square shape, default: "d"
2757
2767
2758
- "square" and "center" can be combined.
2768
+ "square" and "center" can be combined. The square shape can be defined
2769
+ in data or figure coordinates as determined by the ``data_coordinates``
2770
+ modifier, which can be enable and disable by pressing the 'd' key.
2759
2771
2760
2772
drag_from_anywhere : bool, default: False
2761
2773
If `True`, the widget can be moved by clicking anywhere within
@@ -2990,61 +3002,75 @@ def _onmove(self, event):
2990
3002
"""Motion notify event handler."""
2991
3003
2992
3004
state = self ._state | self ._default_state
3005
+
3006
+ dx = event .xdata - self ._eventpress .xdata
3007
+ dy = event .ydata - self ._eventpress .ydata
3008
+ refmax = None
3009
+ if 'data_coordinates' in state :
3010
+ aspect_ratio = 1
3011
+ refx , refy = dx , dy
3012
+ else :
3013
+ figure_size = self .ax .get_figure ().get_size_inches ()
3014
+ ll , ur = self .ax .get_position () * figure_size
3015
+ width , height = ur - ll
3016
+ aspect_ratio = height / width * self .ax .get_data_ratio ()
3017
+ refx = event .xdata / (self ._eventpress .xdata + 1e-6 )
3018
+ refy = event .ydata / (self ._eventpress .ydata + 1e-6 )
3019
+
2993
3020
# resize an existing shape
2994
3021
if self ._active_handle and self ._active_handle != 'C' :
2995
3022
x0 , x1 , y0 , y1 = self ._extents_on_press
2996
3023
sizepress = [x1 - x0 , y1 - y0 ]
2997
3024
center = [x0 + sizepress [0 ] / 2 , y0 + sizepress [1 ] / 2 ]
2998
- dx = event .xdata - self ._eventpress .xdata
2999
- dy = event .ydata - self ._eventpress .ydata
3000
-
3001
- # change sign of relative changes to simplify calculation
3002
- # Switch variables so that only x1 and/or y1 are updated on move
3003
- x_factor = y_factor = 1
3004
- if 'W' in self ._active_handle :
3005
- x_factor *= - 1
3006
- dx *= x_factor
3007
- x0 = x1
3008
- if 'S' in self ._active_handle :
3009
- y_factor *= - 1
3010
- dy *= y_factor
3011
- y0 = y1
3012
3025
3013
3026
# from center
3014
3027
if 'center' in state :
3015
3028
if 'square' in state :
3016
- if self . _active_handle in [ 'E' , 'W' ]:
3017
- # using E, W handle we need to update dy accordingly
3018
- dy = dx
3019
- elif self ._active_handle in ['S ' , 'N' ] :
3020
- # using S, N handle, we need to update dx accordingly
3021
- dx = dy
3029
+ # when using a corner, find which reference to use
3030
+ if self . _active_handle in self . _corner_order :
3031
+ refmax = max ( refx , refy , key = abs )
3032
+ if self ._active_handle in ['E ' , 'W' ] or refmax == refx :
3033
+ dw = event . xdata - center [ 0 ]
3034
+ dh = dw / aspect_ratio
3022
3035
else :
3023
- dx = dy = max (dx , dy , key = abs )
3024
-
3025
- dw = sizepress [0 ] / 2 + dx
3026
- dh = sizepress [1 ] / 2 + dy
3027
-
3028
- if 'square' not in state :
3036
+ dh = event .ydata - center [1 ]
3037
+ dw = dh * aspect_ratio
3038
+ else :
3039
+ dw = sizepress [0 ] / 2
3040
+ dh = sizepress [1 ] / 2
3029
3041
# cancel changes in perpendicular direction
3030
- if self ._active_handle in ['E' , 'W' ]:
3031
- dh = sizepress [ 1 ] / 2
3032
- if self ._active_handle in ['N' , 'S' ]:
3033
- dw = sizepress [ 0 ] / 2
3042
+ if self ._active_handle in ['E' , 'W' ] + self . _corner_order :
3043
+ dw = abs ( event . xdata - center [ 0 ])
3044
+ if self ._active_handle in ['N' , 'S' ] + self . _corner_order :
3045
+ dh = abs ( event . ydata - center [ 1 ])
3034
3046
3035
3047
x0 , x1 , y0 , y1 = (center [0 ] - dw , center [0 ] + dw ,
3036
3048
center [1 ] - dh , center [1 ] + dh )
3037
3049
3038
3050
else :
3051
+ # change sign of relative changes to simplify calculation
3052
+ # Switch variables so that x1 and/or y1 are updated on move
3053
+ x_factor = y_factor = 1
3054
+ if 'W' in self ._active_handle :
3055
+ x0 = x1
3056
+ x_factor *= - 1
3057
+ if 'S' in self ._active_handle :
3058
+ y0 = y1
3059
+ y_factor *= - 1
3060
+ if self ._active_handle in ['E' , 'W' ] + self ._corner_order :
3061
+ x1 = event .xdata
3062
+ if self ._active_handle in ['N' , 'S' ] + self ._corner_order :
3063
+ y1 = event .ydata
3039
3064
if 'square' in state :
3040
- dx = dy = max (dx , dy , key = abs )
3041
- x1 = x0 + x_factor * (dx + sizepress [0 ])
3042
- y1 = y0 + y_factor * (dy + sizepress [1 ])
3043
- else :
3044
- if self ._active_handle in ['E' , 'W' ] + self ._corner_order :
3045
- x1 = event .xdata
3046
- if self ._active_handle in ['N' , 'S' ] + self ._corner_order :
3047
- y1 = event .ydata
3065
+ # when using a corner, find which reference to use
3066
+ if self ._active_handle in self ._corner_order :
3067
+ refmax = max (refx , refy , key = abs )
3068
+ if self ._active_handle in ['E' , 'W' ] or refmax == refx :
3069
+ sign = np .sign (event .ydata - y0 )
3070
+ y1 = y0 + sign * abs (x1 - x0 ) / aspect_ratio
3071
+ else :
3072
+ sign = np .sign (event .xdata - x0 )
3073
+ x1 = x0 + sign * abs (y1 - y0 ) * aspect_ratio
3048
3074
3049
3075
# move existing shape
3050
3076
elif self ._active_handle == 'C' :
@@ -3063,21 +3089,16 @@ def _onmove(self, event):
3063
3089
if self .ignore_event_outside and self ._selection_completed :
3064
3090
return
3065
3091
center = [self ._eventpress .xdata , self ._eventpress .ydata ]
3066
- center_pix = [self ._eventpress .x , self ._eventpress .y ]
3067
3092
dx = (event .xdata - center [0 ]) / 2.
3068
3093
dy = (event .ydata - center [1 ]) / 2.
3069
3094
3070
3095
# square shape
3071
3096
if 'square' in state :
3072
- dx_pix = abs (event .x - center_pix [0 ])
3073
- dy_pix = abs (event .y - center_pix [1 ])
3074
- if not dx_pix :
3075
- return
3076
- maxd = max (abs (dx_pix ), abs (dy_pix ))
3077
- if abs (dx_pix ) < maxd :
3078
- dx *= maxd / (abs (dx_pix ) + 1e-6 )
3079
- if abs (dy_pix ) < maxd :
3080
- dy *= maxd / (abs (dy_pix ) + 1e-6 )
3097
+ refmax = max (refx , refy , key = abs )
3098
+ if refmax == refx :
3099
+ dy = dx / aspect_ratio
3100
+ else :
3101
+ dx = dy * aspect_ratio
3081
3102
3082
3103
# from center
3083
3104
if 'center' in state :
@@ -3435,7 +3456,8 @@ def __init__(self, ax, onselect, useblit=False,
3435
3456
state_modifier_keys = dict (clear = 'escape' , move_vertex = 'control' ,
3436
3457
move_all = 'shift' , move = 'not-applicable' ,
3437
3458
square = 'not-applicable' ,
3438
- center = 'not-applicable' )
3459
+ center = 'not-applicable' ,
3460
+ data_coordinates = 'not-applicable' )
3439
3461
super ().__init__ (ax , onselect , useblit = useblit ,
3440
3462
state_modifier_keys = state_modifier_keys )
3441
3463
0 commit comments