Skip to content

add rectangular region selector #576

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 24 commits into from
Aug 24, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion fastplotlib/graphics/_features/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
TextOutlineThickness,
)

from ._selection_features import LinearSelectionFeature, LinearRegionSelectionFeature
from ._selection_features import LinearSelectionFeature, LinearRegionSelectionFeature, RectangleSelectionFeature
from ._common import Name, Offset, Rotation, Visible, Deleted


Expand All @@ -56,6 +56,7 @@
"TextOutlineThickness",
"LinearSelectionFeature",
"LinearRegionSelectionFeature",
"RectangleRegionSelectionFeature"
"Name",
"Offset",
"Rotation",
Expand Down
137 changes: 136 additions & 1 deletion fastplotlib/graphics/_features/_selection_features.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Sequence
from typing import Sequence, Tuple

import numpy as np

Expand Down Expand Up @@ -190,3 +190,138 @@ def set_value(self, selector, value: Sequence[float]):
# TODO: user's selector event handlers can call event.graphic.get_selected_indices() to get the data index,
# and event.graphic.get_selected_data() to get the data under the selection
# this is probably a good idea so that the data isn't sliced until it's actually necessary


class RectangleSelectionFeature(GraphicFeature):
"""
**additional event attributes:**

+----------------------+----------+------------------------------------+
| attribute | type | description |
+======================+==========+====================================+
| get_selected_indices | callable | returns indices under the selector |
+----------------------+----------+------------------------------------+
| get_selected_data | callable | returns data under the selector |
+----------------------+----------+------------------------------------+

**info dict:**

+----------+------------+-------------------------------------------+
| dict key | value type | value description |
+==========+============+===========================================+
| value | np.ndarray | new [xmin, xmax, ymin, ymax] of selection |
+----------+------------+-------------------------------------------+

"""
def __init__(
self,
value: tuple[float, float, float, float],
axis: str | None,
limits: tuple[float, float, float, float]
):
super().__init__()

self._axis = axis
self._limits = limits
self._value = tuple(int(v) for v in value)

@property
def value(self) -> np.ndarray[float]:
"""
(xmin, xmax, ymin, ymax) of the selection, in data space
"""
return self._value

@property
def axis(self) -> str:
"""one of "x" | "y" """
return self._axis

def set_value(self, selector, value: Sequence[float]):
"""
Set the selection of the rectangle selector.

Parameters
----------
selector: RectangleSelector

value: (float, float, float, float)
new values (xmin, xmax, ymin, ymax) of the selection
"""
if not len(value) == 4:
raise TypeError(
"Selection must be an array, tuple, list, or sequence in the form of `(xmin, xmax, ymin, ymax)`, "
"where `xmin`, `xmax`, `ymin`, `ymax` are numeric values."
)

# convert to array, clip values if they are beyond the limits
# clip x
value = np.asarray(value, dtype=np.float32)
value[:2] = value[:2].clip(self._limits[0], self._limits[1])
# clip y
value[2:] = value[2:].clip(self._limits[2], self._limits[3])

xmin, xmax, ymin, ymax = value

# make sure `selector width >= 2` and selector height >=2 , left edge must not move past right edge!
# or bottom edge must not move past top edge!
if not (xmax - xmin) >= 0 or not (ymax - ymin) >= 0:
return

# change fill mesh
# change left x position of the fill mesh
selector.fill.geometry.positions.data[mesh_masks.x_left] = xmin

# change right x position of the fill mesh
selector.fill.geometry.positions.data[mesh_masks.x_right] = xmax

# change bottom y position of the fill mesh
selector.fill.geometry.positions.data[mesh_masks.y_bottom] = ymin

# change top position of the fill mesh
selector.fill.geometry.positions.data[mesh_masks.y_top] = ymax

# change the edge lines

# each edge line is defined by two end points which are stored in the
# geometry.positions
# [x0, y0, z0]
# [x1, y1, z0]

# left line
z = selector.edges[0].geometry.positions.data[:, -1][0]
selector.edges[0].geometry.positions.data[:] = np.array(
[[xmin, ymin, z], [xmin, ymax, z]]
)

# right line
selector.edges[1].geometry.positions.data[:] = np.array(
[[xmax, ymin, z], [xmax, ymax, z]]
)

# bottom line
selector.edges[2].geometry.positions.data[:] = np.array(
[[xmin, ymin, z], [xmax, ymin, z]]
)

# top line
selector.edges[3].geometry.positions.data[:] = np.array(
[[xmin, ymax, z], [xmax, ymax, z]]
)
#
self._value = value
#
# send changes to GPU
selector.fill.geometry.positions.update_range()
#
for edge in selector.edges:
edge.geometry.positions.update_range()

# send event
if len(self._event_handlers) < 1:
return

event = FeatureEvent("selection", {"value": self.value})

# calls any events
self._call_event_handlers(event)
3 changes: 2 additions & 1 deletion fastplotlib/graphics/selectors/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from ._linear import LinearSelector
from ._linear_region import LinearRegionSelector
from ._polygon import PolygonSelector
from ._rectangle_region import RectangleSelector


__all__ = ["LinearSelector", "LinearRegionSelector"]
__all__ = ["LinearSelector", "LinearRegionSelector", "RectangleSelector"]
3 changes: 3 additions & 0 deletions fastplotlib/graphics/selectors/_base_selector.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ def __init__(
self._edges + self._fill + self._vertices
)

for wo in self._world_objects:
wo.material.pick_write = True

self._hover_responsive: Tuple[WorldObject, ...] = hover_responsive

if hover_responsive is not None:
Expand Down
Loading
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy