1+ # Copyright (C) 2024 - 2025 ANSYS, Inc. and/or its affiliates.
2+ # SPDX-License-Identifier: MIT
3+ #
4+ #
5+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6+ # of this software and associated documentation files (the "Software"), to deal
7+ # in the Software without restriction, including without limitation the rights
8+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+ # copies of the Software, and to permit persons to whom the Software is
10+ # furnished to do so, subject to the following conditions:
11+ #
12+ # The above copyright notice and this permission notice shall be included in all
13+ # copies or substantial portions of the Software.
14+ #
15+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+ # SOFTWARE.
22+
23+ """
24+ .. _ref_picker:
25+
26+ ===================
27+ Activate the picker
28+ ===================
29+
30+ This example shows how to create a custom picker. In this case we will show how the default
31+ picker is implemented through the ``AbstractPicker`` class.
32+ """
33+
34+ #####################################
35+ # Import the ``AbstractPicker`` class
36+ # ===================================
37+
38+ # Import the abstract picker class
39+ from ansys .tools .visualization_interface .backends .pyvista .picker import AbstractPicker
40+
41+ # Import custom object meshes
42+ from ansys .tools .visualization_interface .types .mesh_object_plot import MeshObjectPlot
43+
44+ # Import plotter and color enum
45+ from ansys .tools .visualization_interface import Plotter
46+ from ansys .tools .visualization_interface .utils .color import Color
47+
48+
49+ #####################################
50+ # Create a custom picker class
51+ # =================================
52+
53+ class CustomPicker (AbstractPicker ):
54+ def __init__ (self , plotter_backend : "Plotter" , plot_picked_names : bool = True ) -> None :
55+ """Initialize the ``Picker`` class."""
56+ # Picking variables
57+ self ._plotter_backend = plotter_backend
58+ self ._plot_picked_names = plot_picked_names
59+
60+ # Map that relates PyVista actors with the added actors by the picker
61+ self ._picker_added_actors_map = {}
62+
63+ # Dictionary of picked objects in MeshObject format.
64+ self ._picked_dict = {}
65+
66+ # Map that saves original colors of the plotted objects.
67+ self ._origin_colors = {}
68+
69+ # Hovering variables
70+ self ._added_hover_labels = []
71+
72+ def pick_select_object (self , custom_object : MeshObjectPlot , pt : "np.ndarray" ) -> None :
73+ """Add actor to picked list and add label if required.
74+
75+ Parameters
76+ ----------
77+ custom_object : MeshObjectPlot
78+ The object to be selected.
79+ pt : np.ndarray
80+ The point where the object was picked.
81+ """
82+ added_actors = []
83+
84+ # Pick only custom objects
85+ if isinstance (custom_object , MeshObjectPlot ):
86+ self ._origin_colors [custom_object ] = custom_object .actor .prop .color
87+ custom_object .actor .prop .color = Color .PICKED .value
88+
89+ # Get the name for the text label
90+ text = custom_object .name
91+
92+ # If picking names is enabled, add a label to the picked object
93+ if self ._plot_picked_names :
94+ label_actor = self ._plotter_backend .pv_interface .scene .add_point_labels (
95+ [pt ],
96+ [text ],
97+ always_visible = True ,
98+ point_size = 0 ,
99+ render_points_as_spheres = False ,
100+ show_points = False ,
101+ )
102+ # Add the label actor to the list of added actors
103+ added_actors .append (label_actor )
104+
105+ # Add the picked object to the picked dictionary if not already present, to keep track of it
106+ if custom_object .name not in self ._picked_dict :
107+ self ._picked_dict [custom_object .name ] = custom_object
108+ # Add the picked object to the picked dictionary if not already present, to keep track of it
109+ self ._picker_added_actors_map [custom_object .actor .name ] = added_actors
110+
111+ def pick_unselect_object (self , custom_object : MeshObjectPlot ) -> None :
112+ """Remove actor from picked list and remove label if required.
113+
114+ Parameters
115+ ----------
116+ custom_object : MeshObjectPlot
117+ The object to be unselected.
118+ """
119+ # remove actor from picked list and from scene
120+ if custom_object .name in self ._picked_dict :
121+ self ._picked_dict .pop (custom_object .name )
122+
123+ # Restore original color if it was changed
124+ if isinstance (custom_object , MeshObjectPlot ) and custom_object in self ._origin_colors :
125+ custom_object .actor .prop .color = self ._origin_colors [custom_object ]
126+
127+ # Remove any added actors (like labels) associated with this picked object
128+ if custom_object .actor .name in self ._picker_added_actors_map :
129+ self ._plotter_backend ._pl .scene .remove_actor (self ._picker_added_actors_map [custom_object .actor .name ])
130+ self ._picker_added_actors_map .pop (custom_object .actor .name )
131+
132+ def hover_select_object (self , custom_object : MeshObjectPlot , actor : "Actor" ) -> None :
133+ """Add label to hovered object if required.
134+
135+ Parameters
136+ ----------
137+ custom_object : MeshObjectPlot
138+ The object to be hovered over.
139+ actor : vtkActor
140+ The actor corresponding to the hovered object.
141+ """
142+ for label in self ._added_hover_labels :
143+ self ._plotter_backend ._pl .scene .remove_actor (label )
144+ label_actor = self ._plotter_backend ._pl .scene .add_point_labels (
145+ [actor .GetCenter ()],
146+ [custom_object .name ],
147+ always_visible = True ,
148+ point_size = 0 ,
149+ render_points_as_spheres = False ,
150+ show_points = False ,
151+ )
152+ self ._added_hover_labels .append (label_actor )
153+
154+ def hover_unselect_object (self ):
155+ """Remove all hover labels from the scene."""
156+ for label in self ._added_hover_labels :
157+ self ._plotter_backend ._pl .scene .remove_actor (label )
158+
159+ @property
160+ def picked_dict (self ) -> dict :
161+ """Return the dictionary of picked objects.
162+
163+ Returns
164+ -------
165+ dict
166+ Dictionary of picked objects.
167+ """
168+ return self ._picked_dict
169+
170+ #######################################################
171+ # Initialize the plotter backend with the custom picker
172+ # =====================================================
173+
174+ from ansys .tools .visualization_interface .backends .pyvista import PyVistaBackend
175+ pl_backend = PyVistaBackend (allow_picking = True , custom_picker = CustomPicker )
176+
177+ # Create the plotter with the custom backend
178+ pl = Plotter (backend = pl_backend )
179+
180+ #################################################
181+ # Create a custom object with a name to be picked
182+ # ===============================================
183+
184+ import pyvista as pv
185+
186+ class CustomObject :
187+ def __init__ (self ):
188+ self .name = "CustomObject"
189+ self .mesh = pv .Cube (center = (1 , 1 , 0 ))
190+
191+ def get_mesh (self ):
192+ return self .mesh
193+
194+ def name (self ):
195+ return self .name
196+
197+ # Create a custom object
198+ custom_cube = CustomObject ()
199+ custom_cube .name = "CustomCube"
200+
201+ #########################################
202+ # Create a ``MeshObjectPlot`` instance
203+ # =======================================
204+
205+ from ansys .tools .visualization_interface import MeshObjectPlot
206+ # Create an instance
207+ mesh_object_cube = MeshObjectPlot (custom_cube , custom_cube .get_mesh ())
208+
209+ ##############################
210+ # Plot the custom object
211+ # ============================
212+
213+ pl .plot (mesh_object_cube )
214+ pl .show ()
0 commit comments