Skip to content

Commit

Permalink
Ignore the window when picking points (#1908)
Browse files Browse the repository at this point in the history
  • Loading branch information
Paul Smith committed Dec 23, 2021
1 parent a331ab3 commit dc60a8d
Show file tree
Hide file tree
Showing 5 changed files with 202 additions and 2 deletions.
54 changes: 54 additions & 0 deletions examples/02-plot/point-picking.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
"""
.. _point_picking_example:
Picking points on a mesh
~~~~~~~~~~~~~~~~~~~~~~~~
This example demonstrates how to pick points on meshes using
:func:`enable_point_picking() <pyvista.Plotter.enable_point_picking>`.
"""

# sphinx_gallery_thumbnail_number = 2
import pyvista as pv

###############################################################################
# Pick points on a sphere
# +++++++++++++++++++++++
#
sphere = pv.Sphere()

p = pv.Plotter()
p.add_mesh(sphere, pickable=True)
p.enable_point_picking()
p.show()

###############################################################################
# Ignore the 3D window
# ++++++++++++++++++++
#
# In the above example, both points on the mesh and points in the 3d window can be
# selected. It is possible instead pick only points on the mesh.
sphere = pv.Sphere()

p = pv.Plotter()
p.add_mesh(sphere, pickable=True)
p.enable_point_picking(pickable_window=False) # Make the 3D window unpickable
p.show()

###############################################################################
# Modify which actors are pickable
# ++++++++++++++++++++++++++++++++
#
# After enabling point picking, we can modify which actors are pickable.
sphere = pv.Sphere()
cube = pv.Cube()
cube.translate([10, 10, 0])

p = pv.Plotter()
sphere_actor = p.add_mesh(sphere, pickable=True) # initially pickable
cube_actor = p.add_mesh(cube, pickable=False) # initially unpickable
p.enable_point_picking(pickable_window=False)

p.pickable_actors = [sphere_actor, cube_actor] # now both are pickable
p.view_xy()
p.show()
24 changes: 22 additions & 2 deletions pyvista/plotting/picking.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ def visible_pick_call_back(picker, event_id):
def enable_point_picking(self, callback=None, show_message=True,
font_size=18, color='pink', point_size=10,
use_mesh=False, show_point=True, tolerance=0.025,
**kwargs):
pickable_window=False, **kwargs):
"""Enable picking at points.
Enable picking a point at the mouse location in the render
Expand Down Expand Up @@ -276,16 +276,36 @@ def enable_point_picking(self, callback=None, show_message=True,
is specified as fraction of rendering window
size. Rendering window size is measured across diagonal.
pickable_window : bool, optional
When True, points in the 3D window are pickable. Default to ``True``.
**kwargs : dict, optional
All remaining keyword arguments are used to control how
the picked point is interactively displayed.
Examples
--------
Enable point picking with a custom message.
>>> import pyvista as pv
>>> pl = pv.Plotter()
>>> _ = pl.add_mesh(pv.Sphere())
>>> _ = pl.add_mesh(pv.Cube(), pickable=False)
>>> pl.enable_point_picking(show_message="Press P to pick")
See :ref:`point_picking_example` for a full example using this method.
"""

def _end_pick_event(picker, event):

picked_point_id = picker.GetPointId()
if (not pickable_window) and (picked_point_id < 0):
return None

self.picked_point = np.array(picker.GetPickPosition())
self.picked_mesh = picker.GetDataSet()
self.picked_point_id = picker.GetPointId()
self.picked_point_id = picked_point_id
if show_point:
self.add_mesh(self.picked_point, color=color,
point_size=point_size, name='_picked_point',
Expand Down
61 changes: 61 additions & 0 deletions pyvista/plotting/plotting.py
Original file line number Diff line number Diff line change
Expand Up @@ -1277,6 +1277,67 @@ def untrack_click_position(self):
"""Stop tracking the click position."""
self.iren.untrack_click_position()

@property
def pickable_actors(self):
"""Return or set the pickable actors.
When setting, this will be the list of actors to make
pickable. All actors not in the list will be made unpickable.
If ``actors`` is ``None``, all actors will be made unpickable.
Returns
-------
list of vtk.vtkActors
Examples
--------
Add two actors to a :class:`pyvista.Plotter`, make one
pickable, and then list the pickable actors.
>>> import pyvista as pv
>>> pl = pv.Plotter()
>>> sphere_actor = pl.add_mesh(pv.Sphere())
>>> cube_actor = pl.add_mesh(pv.Cube(), pickable=False, style='wireframe')
>>> len(pl.pickable_actors)
1
Set the pickable actors to both actors.
>>> pl.pickable_actors = [sphere_actor, cube_actor]
>>> len(pl.pickable_actors)
2
Set the pickable actors to ``None``.
>>> pl.pickable_actors = None
>>> len(pl.pickable_actors)
0
"""
pickable = []
for renderer in self.renderers:
for actor in renderer.actors.values():
if actor.GetPickable():
pickable.append(actor)
return pickable

@pickable_actors.setter
def pickable_actors(self, actors=None):
"""Set the pickable actors."""
actors = [] if actors is None else actors
if isinstance(actors, _vtk.vtkActor):
actors = [actors]

if not all([isinstance(actor, _vtk.vtkActor) for actor in actors]):
raise TypeError(
f'Expected a vtkActor instance or a list of vtkActors, got '
f'{[type(actor) for actor in actors]} instead.'
)

for renderer in self.renderers:
for actor in renderer.actors.values():
actor.SetPickable(actor in actors)

def _prep_for_close(self):
"""Make sure a screenshot is acquired before closing.
Expand Down
36 changes: 36 additions & 0 deletions tests/test_picking.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,42 @@ def test_point_picking():
picker.Pick(50, 50, 0, renderer)
plotter.close()

@skip_no_vtk9
@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_point_picking_window_not_pickable():

plotter = pyvista.Plotter(
window_size=(100, 100),
)

# bottom left corner, pickable
sphere = pyvista.Sphere()
sphere.translate([-100, -100, 0])
plotter.add_mesh(sphere, pickable=True)

# top right corner, not pickable
unpickable_sphere = pyvista.Sphere()
unpickable_sphere.translate([100, 100, 0])
plotter.add_mesh(unpickable_sphere, pickable=False)

plotter.view_xy()
plotter.enable_point_picking(
pickable_window=False,
tolerance=0.2,
)

# simulate the pick
renderer = plotter.renderer
picker = plotter.iren.get_picker()

successful_pick = picker.Pick(0, 0, 0, renderer)
assert successful_pick

successful_pick = picker.Pick(100, 100, 0, renderer)
assert not successful_pick

plotter.close()


@skip_no_vtk9
@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
Expand Down
29 changes: 29 additions & 0 deletions tests/test_plotter.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,35 @@ def test_disable_hidden_line_removal():
assert not plotter.renderers[1].GetUseHiddenLineRemoval()


def test_pickable_actors():

plotter = pyvista.Plotter()
sphere = plotter.add_mesh(pyvista.Sphere(), pickable=True)
cube = plotter.add_mesh(pyvista.Cube(), pickable=False)

pickable = plotter.pickable_actors
assert sphere in pickable
assert cube not in pickable

plotter.pickable_actors = cube
pickable = plotter.pickable_actors
assert sphere not in pickable
assert cube in pickable

plotter.pickable_actors = [sphere, cube]
pickable = plotter.pickable_actors
assert sphere in pickable
assert cube in pickable

plotter.pickable_actors = None
pickable = plotter.pickable_actors
assert sphere not in pickable
assert cube not in pickable

with pytest.raises(TypeError, match="Expected a vtkActor instance or "):
plotter.pickable_actors = [0, 10]


def test_prepare_smooth_shading_texture(globe):
"""Test edge cases for smooth shading"""
mesh, scalars = _plotting.prepare_smooth_shading(
Expand Down

0 comments on commit dc60a8d

Please sign in to comment.