from collections.abc import Sequence
import numpy as np
from fury.decorators import warn_on_args_to_kwargs
from fury.lib import (
    CellPicker,
    DataObject,
    HardwareSelector,
    PointPicker,
    PropPicker,
    WorldPointPicker,
    numpy_support,
)
[docs]
class PickingManager:
    """Picking Manager helps with picking 3D objects."""
    @warn_on_args_to_kwargs()
    def __init__(
        self,
        *,
        vertices=True,
        faces=True,
        actors=True,
        world_coords=True,
    ):
        """Initialize Picking Manager.
        Parameters
        ----------
        vertices : bool
            If True allows to pick vertex indices.
        faces : bool
            If True allows to pick face indices.
        actors : bool
            If True allows to pick actor indices.
        world_coords : bool
            If True allows to pick xyz position in world coordinates.
        """
        self.pickers = {}
        if vertices:
            self.pickers["vertices"] = PointPicker()
        if faces:
            self.pickers["faces"] = CellPicker()
        if actors:
            self.pickers["actors"] = PropPicker()
        if world_coords:
            self.pickers["world_coords"] = WorldPointPicker()
[docs]
    def pick(self, disp_xy, sc):
        """Pick on display coordinates.
        Parameters
        ----------
        disp_xy : tuple
            Display coordinates x, y.
        sc : Scene
        """
        x, y = disp_xy
        z = 0
        info = {"vertex": None, "face": None, "actor": None, "xyz": None}
        keys = self.pickers.keys()
        if "vertices" in keys:
            self.pickers["vertices"].Pick(x, y, z, sc)
            info["vertex"] = self.pickers["vertices"].GetPointId()
        if "faces" in keys:
            self.pickers["faces"].Pick(x, y, z, sc)
            info["vertex"] = self.pickers["faces"].GetPointId()
            info["face"] = self.pickers["faces"].GetCellId()
        if "actors" in keys:
            self.pickers["actors"].Pick(x, y, z, sc)
            info["actor"] = self.pickers["actors"].GetViewProp()
        if "world_coords" in keys:
            self.pickers["world_coords"].Pick(x, y, z, sc)
            info["xyz"] = self.pickers["world_coords"].GetPickPosition()
        return info 
[docs]
    def event_position(self, iren):
        """Return event display position from interactor.
        Parameters
        ----------
        iren : interactor
            The interactor object can be retrieved for example
            using providing ShowManager's iren attribute.
        """
        return iren.GetEventPosition() 
[docs]
    def pickable_on(self, actors):
        """Choose which actors can be picked.
        Parameters
        ----------
        actors : actor or sequence of actors
        """
        if isinstance(actors, Sequence):
            for a in actors:
                a.PickableOn()
        else:
            actors.PickableOn() 
[docs]
    def pickable_off(self, actors):
        """Choose which actors cannot be picked.
        Parameters
        ----------
        actors : actor or sequence of actors
        """
        if isinstance(actors, Sequence):
            for a in actors:
                a.PickableOff()
        else:
            actors.PickableOff() 
 
[docs]
class SelectionManager:
    """Selection Manager helps with picking many objects simultaneously."""
    @warn_on_args_to_kwargs()
    def __init__(self, *, select="faces"):
        """Initialize Selection Manager.
        Parameters
        ----------
        select : 'faces'
            Options are 'faces', 'vertices' or 'actors'.
            Default 'faces'.
        Methods
        -------
        select()
        pick()
        """
        self.hsel = HardwareSelector()
        self.update_selection_type(select)
[docs]
    def update_selection_type(self, select):
        """Update selection type.
        Parameters
        ----------
        select : 'faces'
            Options are 'faces', 'vertices' or 'actors'.
            Default 'faces'.
        """
        self.selected_type = select.lower()
        if select == "faces" or select == "edges":
            self.hsel.SetFieldAssociation(DataObject.FIELD_ASSOCIATION_CELLS)
        elif select == "points" or select == "vertices":
            self.hsel.SetFieldAssociation(DataObject.FIELD_ASSOCIATION_POINTS)
        elif select == "actors":
            self.hsel.SetActorPassOnly(True)
        else:
            raise ValueError("Unknown parameter select") 
[docs]
    def pick(self, disp_xy, sc):
        """Pick on display coordinates returns a single object.
        Parameters
        ----------
        disp_xy : tuple
            Display coordinates x, y.
        sc : Scene
        """
        return self.select(disp_xy, sc, area=0)[0] 
[docs]
    @warn_on_args_to_kwargs()
    def select(self, disp_xy, sc, *, area=0):
        """Select multiple objects using display coordinates.
        Parameters
        ----------
        disp_xy : tuple
            Display coordinates x, y.
        sc : Scene
        area : int or 2d tuple of ints
            Selection area around x, y coords.
        """
        info_plus = {}
        self.hsel.SetRenderer(sc)
        if isinstance(area, int):
            picking_area = area, area
        else:
            picking_area = area
        try:
            self.hsel.SetArea(
                disp_xy[0] - picking_area[0],
                disp_xy[1] - picking_area[1],
                disp_xy[0] + picking_area[0],
                disp_xy[1] + picking_area[1],
            )
            res = self.hsel.Select()
        except OverflowError:
            return {
                0: {
                    "node": None,
                    "vertex": None,
                    "face": None,
                    "actor": None,
                }
            }
        num_nodes = res.GetNumberOfNodes()
        if num_nodes < 1:
            sel_node = None
            return {
                0: {
                    "node": None,
                    "vertex": None,
                    "face": None,
                    "actor": None,
                }
            }
        else:
            for i in range(num_nodes):
                sel_node = res.GetNode(i)
                info = {
                    "node": None,
                    "vertex": None,
                    "face": None,
                    "actor": None,
                }
                if sel_node is not None:
                    selected_nodes = set(
                        np.floor(
                            numpy_support.vtk_to_numpy(
                                sel_node.GetSelectionList(),
                            )
                        ).astype(int)
                    )
                    info["node"] = sel_node
                    info["actor"] = sel_node.GetProperties().Get(
                        sel_node.PROP(),
                    )
                    if self.selected_type == "faces":
                        info["face"] = list(selected_nodes)
                    if self.selected_type == "vertex":
                        info["vertex"] = list(selected_nodes)
                info_plus[i] = info
        return info_plus 
[docs]
    def event_position(self, iren):
        """Return event display position from interactor.
        Parameters
        ----------
        iren : interactor
            The interactor object can be retrieved for example
            using ShowManager's iren attribute.
        """
        return iren.GetEventPosition() 
[docs]
    def selectable_on(self, actors):
        """Choose which actors can be selected.
        Parameters
        ----------
        actors : actor or sequence of actors
        """
        if isinstance(actors, Sequence):
            for a in actors:
                a.PickableOn()
        else:
            actors.PickableOn() 
[docs]
    def selectable_off(self, actors):
        """Choose which actors cannot be selected.
        Parameters
        ----------
        actors : actor or sequence of actors
        """
        if isinstance(actors, Sequence):
            for a in actors:
                a.PickableOff()
        else:
            actors.PickableOff()