@@ -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
@@ -1985,6 +1986,12 @@ def on_key_press(self, event):
1985
1986
artist .set_visible (False )
1986
1987
self .update ()
1987
1988
return
1989
+ if key == 'd' and key in self ._state_modifier_keys .values ():
1990
+ modifier = 'data_coordinates'
1991
+ if modifier in self ._default_state :
1992
+ self ._default_state .remove (modifier )
1993
+ else :
1994
+ self .add_default_state (modifier )
1988
1995
for (state , modifier ) in self ._state_modifier_keys .items ():
1989
1996
if modifier in key :
1990
1997
self ._state .add (state )
@@ -2152,7 +2159,8 @@ def __init__(self, ax, onselect, direction, minspan=0, useblit=False,
2152
2159
if state_modifier_keys is None :
2153
2160
state_modifier_keys = dict (clear = 'escape' ,
2154
2161
square = 'not-applicable' ,
2155
- center = 'not-applicable' )
2162
+ center = 'not-applicable' ,
2163
+ data_coordinates = 'not-applicable' )
2156
2164
super ().__init__ (ax , onselect , useblit = useblit , button = button ,
2157
2165
state_modifier_keys = state_modifier_keys )
2158
2166
@@ -2713,8 +2721,12 @@ def onselect(eclick: MouseEvent, erelease: MouseEvent)
2713
2721
- "clear": Clear the current shape, default: "escape".
2714
2722
- "square": Makes the shape square, default: "shift".
2715
2723
- "center": change the shape around its center, default: "ctrl".
2724
+ - "data_coordinates": define if data or figure coordinates should be
2725
+ used to define the square shape, default: "d"
2716
2726
2717
- "square" and "center" can be combined.
2727
+ "square" and "center" can be combined. The square shape can be defined
2728
+ in data or figure coordinates as determined by the ``data_coordinates``
2729
+ modifier, which can be enable and disable by pressing the 'd' key.
2718
2730
2719
2731
drag_from_anywhere : bool, default: False
2720
2732
If `True`, the widget can be moved by clicking anywhere within
@@ -2947,61 +2959,75 @@ def _onmove(self, event):
2947
2959
"""Motion notify event handler."""
2948
2960
2949
2961
state = self ._state | self ._default_state
2962
+
2963
+ dx = event .xdata - self ._eventpress .xdata
2964
+ dy = event .ydata - self ._eventpress .ydata
2965
+ refmax = None
2966
+ if 'data_coordinates' in state :
2967
+ aspect_ratio = 1
2968
+ refx , refy = dx , dy
2969
+ else :
2970
+ figure_size = self .ax .get_figure ().get_size_inches ()
2971
+ ll , ur = self .ax .get_position () * figure_size
2972
+ width , height = ur - ll
2973
+ aspect_ratio = height / width * self .ax .get_data_ratio ()
2974
+ refx = event .xdata / (self ._eventpress .xdata + 1e-6 )
2975
+ refy = event .ydata / (self ._eventpress .ydata + 1e-6 )
2976
+
2950
2977
# resize an existing shape
2951
2978
if self ._active_handle and self ._active_handle != 'C' :
2952
2979
x0 , x1 , y0 , y1 = self ._extents_on_press
2953
2980
sizepress = [x1 - x0 , y1 - y0 ]
2954
2981
center = [x0 + sizepress [0 ] / 2 , y0 + sizepress [1 ] / 2 ]
2955
- dx = event .xdata - self ._eventpress .xdata
2956
- dy = event .ydata - self ._eventpress .ydata
2957
-
2958
- # change sign of relative changes to simplify calculation
2959
- # Switch variables so that only x1 and/or y1 are updated on move
2960
- x_factor = y_factor = 1
2961
- if 'W' in self ._active_handle :
2962
- x_factor *= - 1
2963
- dx *= x_factor
2964
- x0 = x1
2965
- if 'S' in self ._active_handle :
2966
- y_factor *= - 1
2967
- dy *= y_factor
2968
- y0 = y1
2969
2982
2970
2983
# from center
2971
2984
if 'center' in state :
2972
2985
if 'square' in state :
2973
- if self . _active_handle in [ 'E' , 'W' ]:
2974
- # using E, W handle we need to update dy accordingly
2975
- dy = dx
2976
- elif self ._active_handle in ['S ' , 'N' ] :
2977
- # using S, N handle, we need to update dx accordingly
2978
- dx = dy
2986
+ # when using a corner, find which reference to use
2987
+ if self . _active_handle in self . _corner_order :
2988
+ refmax = max ( refx , refy , key = abs )
2989
+ if self ._active_handle in ['E ' , 'W' ] or refmax == refx :
2990
+ dw = event . xdata - center [ 0 ]
2991
+ dh = dw / aspect_ratio
2979
2992
else :
2980
- dx = dy = max (dx , dy , key = abs )
2981
-
2982
- dw = sizepress [0 ] / 2 + dx
2983
- dh = sizepress [1 ] / 2 + dy
2984
-
2985
- if 'square' not in state :
2993
+ dh = event .ydata - center [1 ]
2994
+ dw = dh * aspect_ratio
2995
+ else :
2996
+ dw = sizepress [0 ] / 2
2997
+ dh = sizepress [1 ] / 2
2986
2998
# cancel changes in perpendicular direction
2987
- if self ._active_handle in ['E' , 'W' ]:
2988
- dh = sizepress [ 1 ] / 2
2989
- if self ._active_handle in ['N' , 'S' ]:
2990
- dw = sizepress [ 0 ] / 2
2999
+ if self ._active_handle in ['E' , 'W' ] + self . _corner_order :
3000
+ dw = abs ( event . xdata - center [ 0 ])
3001
+ if self ._active_handle in ['N' , 'S' ] + self . _corner_order :
3002
+ dh = abs ( event . ydata - center [ 1 ])
2991
3003
2992
3004
x0 , x1 , y0 , y1 = (center [0 ] - dw , center [0 ] + dw ,
2993
3005
center [1 ] - dh , center [1 ] + dh )
2994
3006
2995
3007
else :
3008
+ # change sign of relative changes to simplify calculation
3009
+ # Switch variables so that x1 and/or y1 are updated on move
3010
+ x_factor = y_factor = 1
3011
+ if 'W' in self ._active_handle :
3012
+ x0 = x1
3013
+ x_factor *= - 1
3014
+ if 'S' in self ._active_handle :
3015
+ y0 = y1
3016
+ y_factor *= - 1
3017
+ if self ._active_handle in ['E' , 'W' ] + self ._corner_order :
3018
+ x1 = event .xdata
3019
+ if self ._active_handle in ['N' , 'S' ] + self ._corner_order :
3020
+ y1 = event .ydata
2996
3021
if 'square' in state :
2997
- dx = dy = max (dx , dy , key = abs )
2998
- x1 = x0 + x_factor * (dx + sizepress [0 ])
2999
- y1 = y0 + y_factor * (dy + sizepress [1 ])
3000
- else :
3001
- if self ._active_handle in ['E' , 'W' ] + self ._corner_order :
3002
- x1 = event .xdata
3003
- if self ._active_handle in ['N' , 'S' ] + self ._corner_order :
3004
- y1 = event .ydata
3022
+ # when using a corner, find which reference to use
3023
+ if self ._active_handle in self ._corner_order :
3024
+ refmax = max (refx , refy , key = abs )
3025
+ if self ._active_handle in ['E' , 'W' ] or refmax == refx :
3026
+ sign = np .sign (event .ydata - y0 )
3027
+ y1 = y0 + sign * abs (x1 - x0 ) / aspect_ratio
3028
+ else :
3029
+ sign = np .sign (event .xdata - x0 )
3030
+ x1 = x0 + sign * abs (y1 - y0 ) * aspect_ratio
3005
3031
3006
3032
# move existing shape
3007
3033
elif self ._active_handle == 'C' :
@@ -3020,21 +3046,16 @@ def _onmove(self, event):
3020
3046
if self .ignore_event_outside and self ._selection_completed :
3021
3047
return
3022
3048
center = [self ._eventpress .xdata , self ._eventpress .ydata ]
3023
- center_pix = [self ._eventpress .x , self ._eventpress .y ]
3024
3049
dx = (event .xdata - center [0 ]) / 2.
3025
3050
dy = (event .ydata - center [1 ]) / 2.
3026
3051
3027
3052
# square shape
3028
3053
if 'square' in state :
3029
- dx_pix = abs (event .x - center_pix [0 ])
3030
- dy_pix = abs (event .y - center_pix [1 ])
3031
- if not dx_pix :
3032
- return
3033
- maxd = max (abs (dx_pix ), abs (dy_pix ))
3034
- if abs (dx_pix ) < maxd :
3035
- dx *= maxd / (abs (dx_pix ) + 1e-6 )
3036
- if abs (dy_pix ) < maxd :
3037
- dy *= maxd / (abs (dy_pix ) + 1e-6 )
3054
+ refmax = max (refx , refy , key = abs )
3055
+ if refmax == refx :
3056
+ dy = dx / aspect_ratio
3057
+ else :
3058
+ dx = dy * aspect_ratio
3038
3059
3039
3060
# from center
3040
3061
if 'center' in state :
@@ -3390,7 +3411,8 @@ def __init__(self, ax, onselect, useblit=False,
3390
3411
state_modifier_keys = dict (clear = 'escape' , move_vertex = 'control' ,
3391
3412
move_all = 'shift' , move = 'not-applicable' ,
3392
3413
square = 'not-applicable' ,
3393
- center = 'not-applicable' )
3414
+ center = 'not-applicable' ,
3415
+ data_coordinates = 'not-applicable' )
3394
3416
super ().__init__ (ax , onselect , useblit = useblit ,
3395
3417
state_modifier_keys = state_modifier_keys )
3396
3418
0 commit comments