Skip to content
Merged
Changes from all commits
Commits
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
65 changes: 31 additions & 34 deletions fastplotlib/graphics/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ class Graphic:
_fpl_support_tooltip: bool = True

def __init_subclass__(cls, **kwargs):

# set of all features
cls._features = {
**cls._features,
Expand Down Expand Up @@ -326,9 +325,7 @@ def _add_group_graphic_map(self, wo: pygfx.Group):
# used by images since they create new WorldObject ImageTiles when a different buffer size is required
# also used by GraphicCollections inititally, but not used for reseting like images
for child in wo.children:
if isinstance(
child, (pygfx.Image, pygfx.Volume, pygfx.Points, pygfx.Line)
):
if isinstance(child, (pygfx.Image, pygfx.Volume, pygfx.Points, pygfx.Line)):
# unique 32 bit integer id for each world object
global_id = child.id
WORLD_OBJECT_TO_GRAPHIC[global_id] = self
Expand All @@ -338,9 +335,7 @@ def _add_group_graphic_map(self, wo: pygfx.Group):
def _remove_group_graphic_map(self, wo: pygfx.Group):
# remove the children of the group to the WorldObject -> Graphic map
for child in wo.children:
if isinstance(
child, (pygfx.Image, pygfx.Volume, pygfx.Points, pygfx.Line)
):
if isinstance(child, (pygfx.Image, pygfx.Volume, pygfx.Points, pygfx.Line)):
# unique 32 bit integer id for each world object
global_id = child.id
WORLD_OBJECT_TO_GRAPHIC.pop(global_id)
Expand Down Expand Up @@ -528,6 +523,23 @@ def my_handler(event):
feature = getattr(self, f"_{t}")
feature.remove_event_handler(wrapper)

def _parse_positions(self, position: tuple | np.ndarray) -> np.ndarray:
"""
Converts position data (in the form of tuple or np.ndarray) into a (num_points, 3)-shaped np.ndarray for processing
"""
position = np.asarray(position)

if position.ndim not in (1,2):
raise ValueError(f"position must be of shape (num_points, 3) or (3,)")

if position.ndim == 1:
position = position[None, :]

if position.shape[-1] != 3:
raise ValueError(f"position must be of shape (num_points, 3) or (3,)")

return position

def map_model_to_world(
self, position: tuple[float, float, float] | tuple[float, float] | np.ndarray
) -> np.ndarray:
Expand All @@ -536,27 +548,18 @@ def map_model_to_world(

Parameters
----------
position: (float, float, float) or (float, float)
(x, y, z) or (x, y) position. If z is not provided then the graphic's offset z is used.
position: tuple of (x, y, z) or np.ndarray of shape (num_points, 3)
The xyz positions we wish to map to world space

Returns
-------
np.ndarray
(x, y, z) position in world space

either shape (3,) or (num_points, 3), specifying position in world space
"""

if len(position) == 2:
# use z of the graphic
position = [*position, self.offset[-1]]

if len(position) != 3:
raise ValueError(
f"position must be tuple or array indicating (x, y, z) position in *model space*"
)
position = self._parse_positions(position)

# apply world transform to project from model space to world space
return la.vec_transform(position, self.world_object.world.matrix)
return la.vec_transform(position, self.world_object.world.matrix).squeeze()

def map_world_to_model(
self, position: tuple[float, float, float] | tuple[float, float] | np.ndarray
Expand All @@ -566,26 +569,20 @@ def map_world_to_model(

Parameters
----------
position: (float, float, float) or (float, float)
(x, y, z) or (x, y) position. If z is not provided then 0 is used.
position: tuple of (x, y, z) or np.ndarray of shape (num_points, 3)
The xyz positions we wish to map to model space

Returns
-------
np.ndarray
(x, y, z) position in world space
either shape (3,) or (num_points, 3), specifying position in model space

"""
position = self._parse_positions(position)

if len(position) == 2:
# use z of the graphic
position = [*position, self.offset[-1]]

if len(position) != 3:
raise ValueError(
f"position must be tuple or array indicating (x, y, z) position in *model space*"
)

return la.vec_transform(position, self.world_object.world.inverse_matrix)
return la.vec_transform(
position, self.world_object.world.inverse_matrix
).squeeze()

def format_pick_info(self, ev: pygfx.PointerEvent) -> str:
"""
Expand Down