Source code for fury.actor.utils

"""Utility functions for manipulating actors in FURY."""

import numpy as np

from fury.lib import (
    AffineTransform,
    Buffer,
    GfxGroup,
    RecursiveTransform,
    WorldObject,
    gfx_wgpu,
)
from fury.material import validate_opacity


[docs] def set_group_visibility(group, visibility): """ Set the visibility of a group of actors. Parameters ---------- group : Group The group of actors to set visibility for. visibility : tuple or list of bool If a single boolean value is provided, it sets the visibility for all actors in the group. If a tuple or list is provided, it sets the visibility for each actor in the group individually. """ if not isinstance(group, GfxGroup): raise TypeError("group must be an instance of Group.") if not isinstance(visibility, (tuple, list)): group.visible = visibility return if len(visibility) != len(group.children): raise ValueError( "Length of visibility must match the number of actors in the group." ) for idx, actor in enumerate(group.children): actor.visible = visibility[idx]
[docs] def set_group_opacity(group, opacity): """ Set the opacity of the group of actors. Parameters ---------- group : Group The group of actors to set opacity for. opacity : float The opacity value to set for the group of actors, ranging from 0 (fully transparent) to 1 (opaque). """ if not isinstance(group, GfxGroup): raise TypeError("group must be an instance of Group.") opacity = validate_opacity(opacity) for child in group.children: child.material.opacity = opacity
[docs] def set_opacity(actor, opacity): """ Set the opacity of an actor. Parameters ---------- actor : WorldObject The actor to set opacity for. opacity : float The opacity value to set for the actor, ranging from 0 (fully transparent) to 1 (opaque). """ if not isinstance(actor, WorldObject): raise TypeError("actor must be an instance of WorldObject.") if isinstance(actor, GfxGroup): set_group_opacity(actor, opacity) return opacity = validate_opacity(opacity) actor.material.opacity = opacity
def validate_slices_group(group): """ Validate the slices in a group. Parameters ---------- group : Group The group of actors to validate. Raises ------ TypeError If the group is not an instance of Group. ValueError If the group does not contain exactly 3 children. AttributeError If the children do not have the required material plane attribute. """ if not isinstance(group, GfxGroup): raise TypeError("group must be an instance of Group.") if group.name == "VectorFieldSlicer": if not hasattr(group, "children"): raise AttributeError("Group must have a children attribute.") elif not isinstance(group.children, (list, tuple)): raise TypeError("Group must have a children attribute that is a list.") elif not len(group.children) >= 1: raise ValueError("Group must contain at least one child.") elif not hasattr(group.children[0], "cross_section"): raise AttributeError( "Children do not have the required cross_section attribute." ) elif group.name == "Slicer": if len(group.children) != 3: raise ValueError( f"Group must contain exactly 3 children. {len(group.children)}" ) if not ( hasattr(group.children[0].material, "plane") or hasattr(group.children[0], "plane") ): raise AttributeError( "Children do not have the required material plane attribute for slices." )
[docs] def get_slices(group): """ Get the current positions of the slices. Parameters ---------- group : Group The group of actors to get the slices from. Returns ------- ndarray An array containing the current positions of the slices. """ validate_slices_group(group) if group.name == "Slicer": return np.asarray([child.material.plane[-1] for child in group.children]) elif group.name == "VectorFieldSlicer": return np.asarray(group.children[0].cross_section) else: raise ValueError( f"Group {group.name} is not a valid slices group. " "Must be either 'Slicer' or 'VectorFieldSlicer'." )
[docs] def show_slices(group, position): """ Show the slices at the specified position. Added with a small offset to avoid boundary issues. Parameters ---------- group : Group The group of actors to get the slices from. position : tuple or list or ndarray A tuple containing the positions of the slices in the 3D space. """ validate_slices_group(group) if group.name == "Slicer": for i, child in enumerate(group.children): if hasattr(child, "plane"): a, b, c, _ = child.plane child.plane = (a, b, c, position[i] + 1e-3) else: a, b, c, _ = child.material.plane child.material.plane = (a, b, c, position[i] + 1e-3) elif group.name == "VectorFieldSlicer": for child in group.children: child.cross_section = position
[docs] def apply_affine_to_group(group, affine): """ Apply a transformation to all actors in a group. Parameters ---------- group : Group The group of actors to apply the transformation to. affine : ndarray, shape (4, 4) The transformation to apply to the actors in the group. """ if not isinstance(group, GfxGroup): raise TypeError("group must be an instance of Group.") if not isinstance(affine, np.ndarray) or affine.shape != (4, 4): raise ValueError("affine must be a 4x4 numpy array.") for child in group.children: apply_affine_to_actor(child, affine)
[docs] def apply_affine_to_actor(actor, affine): """ Apply a transformation to an actor. Parameters ---------- actor : WorldObject The actor to apply the transformation to. affine : ndarray, shape (4, 4) The transformation to apply to the actor. """ if not isinstance(actor, WorldObject): raise TypeError("actor must be an instance of WorldObject.") if not isinstance(affine, np.ndarray) or affine.shape != (4, 4): raise ValueError("affine must be a 4x4 numpy array.") affine_transform = AffineTransform( state_basis="matrix", matrix=affine, is_camera_space=True ) recursive_transform = RecursiveTransform(affine_transform) actor.local = affine_transform actor.world = recursive_transform
[docs] def read_buffer(buffer, *, sync_cpu=True): """ Read the contents of a wgpu buffer into a NumPy array. Parameters ---------- buffer : wgpu.Buffer The buffer to read from. sync_cpu : bool, optional Whether to synchronize the CPU data with the GPU data. If True and the buffer has a CPU-side data array, it will be updated with the contents of the GPU buffer. Returns ------- np.ndarray The contents of the buffer as a NumPy array. """ if not isinstance(buffer, Buffer): raise ValueError("Expected a wgpu.Buffer instance.") wgpu_device = gfx_wgpu.get_shared().device raw = wgpu_device.queue.read_buffer(buffer) cpu_shape = buffer.data.shape if buffer.data is not None else None gpu_buffer = ( np.frombuffer(raw, dtype=np.float32).reshape(cpu_shape).copy() if cpu_shape is not None else np.frombuffer(raw, dtype=np.float32) ) if sync_cpu and buffer.data is not None: np.asarray(buffer.data)[...] = gpu_buffer return gpu_buffer