Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c95ed34

Browse files
authored
Refactor selector drag behavior (#800)
* Refactor selector drag behavior * black * Use | instead of Union
1 parent 5e910cc commit c95ed34

File tree

9 files changed

+129
-103
lines changed

9 files changed

+129
-103
lines changed

examples/selection_tools/linear_region_line_collection.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,11 @@ def update_zoomed_subplots(ev):
5959

6060
for i in range(len(zoomed_data)):
6161
# interpolate y-vals
62-
data = interpolate(zoomed_data[i], axis=1)
63-
figure[i + 1, 0]["zoomed"].data[:, 1] = data
62+
if zoomed_data[i].size == 0:
63+
figure[i + 1, 0]["zoomed"].data[:, 1] = 0
64+
else:
65+
data = interpolate(zoomed_data[i], axis=1)
66+
figure[i + 1, 0]["zoomed"].data[:, 1] = data
6467
figure[i + 1, 0].auto_scale()
6568

6669

examples/selection_tools/linear_region_selector.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,9 @@ def set_zoom_x(ev):
7979
if selected_data.size == 0:
8080
# no data selected
8181
zoomed_x.data[:, 1] = 0
82-
83-
# interpolate the y-values since y = f(x)
84-
zoomed_x.data[:, 1] = interpolate(selected_data, axis=1)
82+
else:
83+
# interpolate the y-values since y = f(x)
84+
zoomed_x.data[:, 1] = interpolate(selected_data, axis=1)
8585
figure[1, 0].auto_scale()
8686

8787

@@ -92,9 +92,9 @@ def set_zoom_y(ev):
9292
if selected_data.size == 0:
9393
# no data selected
9494
zoomed_y.data[:, 1] = 0
95-
96-
# interpolate the x values since this x = f(y)
97-
zoomed_y.data[:, 1] = -interpolate(selected_data, axis=0)
95+
else:
96+
# interpolate the x values since this x = f(y)
97+
zoomed_y.data[:, 1] = -interpolate(selected_data, axis=0)
9898
figure[1, 1].auto_scale()
9999

100100

examples/selection_tools/linear_region_selectors_match_offsets.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,9 @@ def set_zoom_x(ev):
7474
if selected_data.size == 0:
7575
# no data selected
7676
zoomed_x.data[:, 1] = 0
77-
78-
# interpolate the y-values since y = f(x)
79-
zoomed_x.data[:, 1] = interpolate(selected_data, axis=1)
77+
else:
78+
# interpolate the y-values since y = f(x)
79+
zoomed_x.data[:, 1] = interpolate(selected_data, axis=1)
8080
figure[1, 0].auto_scale()
8181

8282

@@ -87,9 +87,9 @@ def set_zoom_y(ev):
8787
if selected_data.size == 0:
8888
# no data selected
8989
zoomed_y.data[:, 1] = 0
90-
91-
# interpolate the x values since this x = f(y)
92-
zoomed_y.data[:, 1] = -interpolate(selected_data, axis=0)
90+
else:
91+
# interpolate the x values since this x = f(y)
92+
zoomed_y.data[:, 1] = -interpolate(selected_data, axis=0)
9393
figure[1, 1].auto_scale()
9494

9595

fastplotlib/graphics/selectors/_base_selector.py

Lines changed: 45 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,17 @@ class MoveInfo:
1616
stores move info for a WorldObject
1717
"""
1818

19-
# last position for an edge, fill, or vertex in world coordinates
20-
# can be None, such as key events
21-
last_position: Union[np.ndarray, None]
19+
# The initial selection. Differs per type of selector
20+
start_selection: Any
21+
22+
# The initial world position of the cursor
23+
start_position: np.ndarray | None
24+
25+
# Delta position in world coordinates
26+
delta: np.ndarray
2227

2328
# WorldObject or "key" event
24-
source: Union[WorldObject, str]
29+
source: WorldObject | str
2530

2631

2732
# key bindings used to move the selector
@@ -148,9 +153,6 @@ def __init__(
148153

149154
self._axis = axis
150155

151-
# current delta in world coordinates
152-
self.delta: np.ndarray = None
153-
154156
self.arrow_keys_modifier = arrow_keys_modifier
155157
# if not False, moves the slider on every render cycle
156158
self._key_move_value = False
@@ -278,9 +280,14 @@ def _move_start(self, event_source: WorldObject, ev):
278280
pygfx ``Event``
279281
280282
"""
281-
last_position = self._plot_area.map_screen_to_world(ev)
283+
position = self._plot_area.map_screen_to_world(ev)
282284

283-
self._move_info = MoveInfo(last_position=last_position, source=event_source)
285+
self._move_info = MoveInfo(
286+
start_selection=None,
287+
start_position=position,
288+
delta=np.zeros_like(position),
289+
source=event_source,
290+
)
284291
self._moving = True
285292

286293
self._initial_controller_state = self._plot_area.controller.enabled
@@ -303,21 +310,14 @@ def _move(self, ev):
303310
# disable controller during moves
304311
self._plot_area.controller.enabled = False
305312

306-
# get pointer current world position
307-
world_pos = self._plot_area.map_screen_to_world(ev)
308-
309-
# outside this viewport
310-
if world_pos is None:
311-
return
313+
# get pointer current world position, in 'mouse capute mode'
314+
world_pos = self._plot_area.map_screen_to_world(ev, allow_outside=True)
312315

313-
# compute the delta
314-
self.delta = world_pos - self._move_info.last_position
316+
# update the delta
317+
self._move_info.delta = world_pos - self._move_info.start_position
315318
self._pygfx_event = ev
316319

317-
self._move_graphic(self.delta)
318-
319-
# update last position
320-
self._move_info.last_position = world_pos
320+
self._move_graphic(self._move_info)
321321

322322
# restore the initial controller state
323323
# if it was disabled, keep it disabled
@@ -370,22 +370,26 @@ def _move_to_pointer(self, ev):
370370
if world_pos is None:
371371
return
372372

373-
self.delta = world_pos - current_pos_world
373+
delta = world_pos - current_pos_world
374374
self._pygfx_event = ev
375375

376376
# use fill by default as the source, such as in region selectors
377377
if len(self._fill) > 0:
378-
self._move_info = MoveInfo(
379-
last_position=current_pos_world, source=self._fill[0]
378+
move_info = MoveInfo(
379+
start_selection=None,
380+
start_position=None,
381+
delta=delta,
382+
source=self._fill[0],
380383
)
381384
# else use an edge, such as for linear selector
382385
else:
383-
self._move_info = MoveInfo(
384-
last_position=current_pos_world, source=self._edges[0]
386+
move_info = MoveInfo(
387+
start_position=current_pos_world,
388+
last_position=current_pos_world,
389+
source=self._edges[0],
385390
)
386391

387-
self._move_graphic(self.delta)
388-
self._move_info = None
392+
self._move_graphic(move_info)
389393

390394
def _pointer_enter(self, ev):
391395

@@ -428,15 +432,23 @@ def _key_hold(self):
428432
# set event source
429433
# use fill by default as the source
430434
if len(self._fill) > 0:
431-
self._move_info = MoveInfo(last_position=None, source=self._fill[0])
435+
move_info = MoveInfo(
436+
start_selection=None,
437+
start_position=None,
438+
delta=delta,
439+
source=self._fill[0],
440+
)
432441
# else use an edge
433442
else:
434-
self._move_info = MoveInfo(last_position=None, source=self._edges[0])
443+
move_info = MoveInfo(
444+
start_selection=None,
445+
start_position=None,
446+
delta=delta,
447+
source=self._edges[0],
448+
)
435449

436450
# move the graphic
437-
self._move_graphic(delta=delta)
438-
439-
self._move_info = None
451+
self._move_graphic(move_info)
440452

441453
def _key_down(self, ev):
442454
# key bind modifier must be set and must be used for the event
@@ -458,8 +470,6 @@ def _key_up(self, ev):
458470
if ev.key in key_bind_direction.keys():
459471
self._key_move_value = False
460472

461-
self._move_info = None
462-
463473
def _fpl_prepare_del(self):
464474
if hasattr(self, "_pfunc_fill"):
465475
self._plot_area.renderer.remove_event_handler(

fastplotlib/graphics/selectors/_linear.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from .._base import Graphic
99
from .._collection_base import GraphicCollection
1010
from ..features._selection_features import LinearSelectionFeature
11-
from ._base_selector import BaseSelector
11+
from ._base_selector import BaseSelector, MoveInfo
1212

1313

1414
class LinearSelector(BaseSelector):
@@ -177,8 +177,6 @@ def __init__(
177177
world_object.add(self.line_outer)
178178
world_object.add(line_inner)
179179

180-
self._move_info: dict = None
181-
182180
if axis == "x":
183181
offset = (parent.offset[0], center + parent.offset[1], 0)
184182
elif axis == "y":
@@ -276,7 +274,7 @@ def _get_selected_index(self, graphic):
276274

277275
return min(round(index), upper_bound)
278276

279-
def _move_graphic(self, delta: np.ndarray):
277+
def _move_graphic(self, move_info: MoveInfo):
280278
"""
281279
Moves the graphic
282280
@@ -287,7 +285,9 @@ def _move_graphic(self, delta: np.ndarray):
287285
288286
"""
289287

290-
if self.axis == "x":
291-
self.selection = self.selection + delta[0]
292-
else:
293-
self.selection = self.selection + delta[1]
288+
# If this the first move in this drag, store initial selection
289+
if move_info.start_selection is None:
290+
move_info.start_selection = self.selection
291+
292+
delta = move_info.delta[0] if self.axis == "x" else move_info.delta[1]
293+
self.selection = move_info.start_selection + delta

fastplotlib/graphics/selectors/_linear_region.py

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from .._base import Graphic
88
from .._collection_base import GraphicCollection
99
from ..features._selection_features import LinearRegionSelectionFeature
10-
from ._base_selector import BaseSelector
10+
from ._base_selector import BaseSelector, MoveInfo
1111

1212

1313
class LinearRegionSelector(BaseSelector):
@@ -288,7 +288,7 @@ def get_selected_data(
288288
# slices n_datapoints dim
289289
data_selections.append(g.data[s])
290290

291-
return source.data[s]
291+
return data_selections
292292
else:
293293
if ixs.size == 0:
294294
# empty selection
@@ -368,40 +368,40 @@ def get_selected_indices(
368368
# indices map directly to grid geometry for image data buffer
369369
return np.arange(*bounds, dtype=int)
370370

371-
def _move_graphic(self, delta: np.ndarray):
371+
def _move_graphic(self, move_info: MoveInfo):
372+
373+
# If this the first move in this drag, store initial selection
374+
if move_info.start_selection is None:
375+
move_info.start_selection = self.selection
376+
372377
# add delta to current min, max to get new positions
373-
if self.axis == "x":
374-
# add x value
375-
new_min, new_max = self.selection + delta[0]
378+
delta = move_info.delta[0] if self.axis == "x" else move_info.delta[1]
376379

377-
elif self.axis == "y":
378-
# add y value
379-
new_min, new_max = self.selection + delta[1]
380+
# Get original selection
381+
cur_min, cur_max = move_info.start_selection
380382

381383
# move entire selector if event source was fill
382384
if self._move_info.source == self.fill:
383-
# prevent weird shrinkage of selector if one edge is already at the limit
384-
if self.selection[0] == self.limits[0] and new_min < self.limits[0]:
385-
# self._move_end(None) # TODO: cancel further movement to prevent weird asynchronization with pointer
386-
return
387-
if self.selection[1] == self.limits[1] and new_max > self.limits[1]:
388-
# self._move_end(None)
389-
return
390-
391-
# move entire selector
392-
self._selection.set_value(self, (new_min, new_max))
385+
# Limit the delta to avoid weird resizine behavior
386+
min_delta = self.limits[0] - cur_min
387+
max_delta = self.limits[1] - cur_max
388+
delta = np.clip(delta, min_delta, max_delta)
389+
# Update both bounds with equal amount
390+
self._selection.set_value(self, (cur_min + delta, cur_max + delta))
393391
return
394392

395-
# if selector is not resizable return
393+
# if selector not resizable return
396394
if not self._resizable:
397395
return
398396

399397
# if event source was an edge and selector is resizable,
400398
# move the edge that caused the event
401399
if self._move_info.source == self.edges[0]:
402400
# change only left or bottom bound
403-
self._selection.set_value(self, (new_min, self._selection.value[1]))
401+
new_min = min(cur_min + delta, cur_max)
402+
self._selection.set_value(self, (new_min, cur_max))
404403

405404
elif self._move_info.source == self.edges[1]:
406405
# change only right or top bound
407-
self._selection.set_value(self, (self.selection[0], new_max))
406+
new_max = max(cur_max + delta, cur_min)
407+
self._selection.set_value(self, (cur_min, new_max))

fastplotlib/graphics/selectors/_polygon.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,16 @@ def _add_segment(self, ev):
6262
"""After click event, adds a new line segment"""
6363
self._current_mode = "add"
6464

65-
last_position = self._plot_area.map_screen_to_world(ev)
66-
self._move_info = MoveInfo(last_position=last_position, source=None)
65+
position = self._plot_area.map_screen_to_world(ev)
66+
self._move_info = MoveInfo(
67+
start_selection=None,
68+
start_position=position,
69+
delta=np.zeros_like(position),
70+
source=None,
71+
)
6772

6873
# line with same position for start and end until mouse moves
69-
data = np.array([last_position, last_position])
74+
data = np.array([position, position])
7075

7176
new_line = pygfx.Line(
7277
geometry=pygfx.Geometry(positions=data.astype(np.float32)),

0 commit comments

Comments
 (0)