1
1
"""Provides for plotting various PyAnsys Geometry objects."""
2
- from typing import Any
3
-
4
- from beartype .typing import Dict , List , Optional
2
+ from beartype .typing import Any , Dict , List , Optional , Union
5
3
import numpy as np
6
4
import pyvista as pv
7
5
from pyvista .plotting .tools import create_axes_marker
8
6
9
7
from ansys .geometry .core .designer import Body , Component , Design , MasterBody
10
8
from ansys .geometry .core .logger import LOG as logger
11
9
from ansys .geometry .core .math import Frame , Plane
10
+ from ansys .geometry .core .plotting .plotting_types import EdgePlot , GeomObjectPlot
12
11
from ansys .geometry .core .plotting .trame_gui import _HAS_TRAME , TrameVisualizer
13
12
from ansys .geometry .core .plotting .widgets import (
14
13
CameraPanDirection ,
26
25
PICKED_COLOR = "#BB6EEE"
27
26
"""Color to use for the actors that are currently picked."""
28
27
28
+ EDGE_COLOR = "#000000"
29
+ """Default color to use for the edges."""
30
+
31
+ PICKED_EDGE_COLOR = "#9C9C9C"
32
+ """Color to use for the edges that are currently picked."""
33
+
29
34
30
35
class Plotter :
31
36
"""
@@ -71,6 +76,9 @@ def __init__(
71
76
# Save the desired number of points
72
77
self ._num_points = num_points
73
78
79
+ # geometry objects to actors mapping
80
+ self ._geom_object_actors_map = {}
81
+
74
82
# Create Plotter widgets
75
83
if enable_widgets :
76
84
self ._widgets : List [PlotterWidget ] = []
@@ -215,9 +223,35 @@ def plot_sketch(
215
223
216
224
self .add_sketch_polydata (sketch .sketch_polydata (), ** plotting_options )
217
225
226
+ def add_body_edges (self , body_plot : GeomObjectPlot , ** plotting_options : Optional [dict ]) -> None :
227
+ """
228
+ Add the outer edges of a body to the plot.
229
+
230
+ This method has the side effect of adding the edges to the GeomObject that
231
+ you pass through the parameters.
232
+
233
+ Parameters
234
+ ----------
235
+ body : GeomObjectPlot
236
+ Body of which to add the edges.
237
+ **plotting_options : dict, default: None
238
+ Keyword arguments. For allowable keyword arguments, see the
239
+ :func:`pyvista.Plotter.add_mesh` method.
240
+ """
241
+ edge_plot_list = []
242
+ for edge in body_plot .object .edges :
243
+ line = pv .Line (edge .start_point , edge .end_point )
244
+ edge_actor = self .scene .add_mesh (
245
+ line , line_width = 10 , color = EDGE_COLOR , ** plotting_options
246
+ )
247
+ edge_actor .SetVisibility (False )
248
+ edge_plot = EdgePlot (edge_actor , edge , body_plot )
249
+ edge_plot_list .append (edge_plot )
250
+ body_plot .edges = edge_plot_list
251
+
218
252
def add_body (
219
253
self , body : Body , merge : Optional [bool ] = False , ** plotting_options : Optional [Dict ]
220
- ) -> str :
254
+ ) -> None :
221
255
"""
222
256
Add a body to the scene.
223
257
@@ -232,11 +266,6 @@ def add_body(
232
266
**plotting_options : dict, default: None
233
267
Keyword arguments. For allowable keyword arguments,
234
268
see the :func:`pyvista.Plotter.add_mesh` method.
235
-
236
- Returns
237
- -------
238
- str
239
- Name of the added PyVista actor.
240
269
"""
241
270
# Use the default PyAnsys Geometry add_mesh arguments
242
271
self .__set_add_mesh_defaults (plotting_options )
@@ -246,7 +275,9 @@ def add_body(
246
275
else :
247
276
actor = self .scene .add_mesh (dataset , ** plotting_options )
248
277
249
- return actor .name
278
+ body_plot = GeomObjectPlot (actor = actor , object = body , add_body_edges = True )
279
+ self .add_body_edges (body_plot )
280
+ self ._geom_object_actors_map [actor ] = body_plot
250
281
251
282
def add_component (
252
283
self ,
@@ -303,15 +334,15 @@ def add_sketch_polydata(self, polydata_entries: List[pv.PolyData], **plotting_op
303
334
"""
304
335
# Use the default PyAnsys Geometry add_mesh arguments
305
336
for polydata in polydata_entries :
306
- self .scene .add_mesh (polydata , ** plotting_options )
337
+ self .scene .add_mesh (polydata , color = EDGE_COLOR , ** plotting_options )
307
338
308
339
def add (
309
340
self ,
310
341
object : Any ,
311
342
merge_bodies : bool = False ,
312
343
merge_components : bool = False ,
313
344
** plotting_options ,
314
- ) -> Dict [str , str ]:
345
+ ) -> Dict [pv . Actor , GeomObjectPlot ]:
315
346
"""
316
347
Add any type of object to the scene.
317
348
@@ -336,11 +367,11 @@ def add(
336
367
337
368
Returns
338
369
-------
339
- Mapping[str, str ]
370
+ Dict[pv.Actor, GeomObjectPlot ]
340
371
Mapping between the pv.Actor and the PyAnsys Geometry object.
341
372
"""
342
373
logger .debug (f"Adding object type { type (object )} to the PyVista plotter" )
343
- actor_name = None
374
+
344
375
if isinstance (object , List ) and isinstance (object [0 ], pv .PolyData ):
345
376
self .add_sketch_polydata (object , ** plotting_options )
346
377
elif isinstance (object , pv .PolyData ):
@@ -350,22 +381,20 @@ def add(
350
381
elif isinstance (object , Sketch ):
351
382
self .plot_sketch (object , ** plotting_options )
352
383
elif isinstance (object , Body ) or isinstance (object , MasterBody ):
353
- actor_name = self .add_body (object , merge_bodies , ** plotting_options )
384
+ self .add_body (object , merge_bodies , ** plotting_options )
354
385
elif isinstance (object , Design ) or isinstance (object , Component ):
355
- actor_name = self .add_component (
356
- object , merge_components , merge_bodies , ** plotting_options
357
- )
386
+ self .add_component (object , merge_components , merge_bodies , ** plotting_options )
358
387
else :
359
388
logger .warning (f"Object type { type (object )} can not be plotted." )
360
- return { actor_name : object . name } if actor_name else {}
389
+ return self . _geom_object_actors_map
361
390
362
391
def add_list (
363
392
self ,
364
393
plotting_list : List [Any ],
365
394
merge_bodies : bool = False ,
366
395
merge_components : bool = False ,
367
396
** plotting_options ,
368
- ) -> Dict [str , str ]:
397
+ ) -> Dict [pv . Actor , GeomObjectPlot ]:
369
398
"""
370
399
Add a list of any type of object to the scene.
371
400
@@ -390,17 +419,12 @@ def add_list(
390
419
391
420
Returns
392
421
-------
393
- Mapping[str, str ]
394
- Dictionary with the mapping between pv.Actor and PyAnsys Geometry objects.
422
+ Dict[pv.Actor, GeomObjectPlot ]
423
+ Mapping between the pv.Actor and the PyAnsys Geometry objects.
395
424
"""
396
- actors_objects_mapping = {}
397
425
for object in plotting_list :
398
- actor_object_mapping = self .add (
399
- object , merge_bodies , merge_components , ** plotting_options
400
- )
401
- if actor_object_mapping :
402
- actors_objects_mapping .update (actor_object_mapping )
403
- return actors_objects_mapping
426
+ _ = self .add (object , merge_bodies , merge_components , ** plotting_options )
427
+ return self ._geom_object_actors_map
404
428
405
429
def show (
406
430
self ,
@@ -490,10 +514,11 @@ def __init__(
490
514
self ._use_trame = use_trame
491
515
self ._allow_picking = allow_picking
492
516
self ._pv_off_screen_original = bool (pv .OFF_SCREEN )
493
- self ._actor_object_mapping = {}
517
+ self ._geom_object_actors_map = {}
494
518
self ._pl = None
495
519
self ._picked_list = set ()
496
520
self ._picker_added_actors_map = {}
521
+ self ._edge_actors_map = {}
497
522
498
523
if self ._use_trame and _HAS_TRAME :
499
524
# avoids GUI window popping up
@@ -511,10 +536,10 @@ def __init__(
511
536
512
537
if self ._allow_picking :
513
538
self ._pl .scene .enable_mesh_picking (
514
- callback = self .picker_callback , use_actor = True , show = False
539
+ callback = self .picker_callback , use_actor = True , show = False , show_message = False
515
540
)
516
541
517
- def select_object (self , actor : pv . Actor , body_name : str , pt : "np.Array" ) -> None :
542
+ def select_object (self , geom_object : Union [ GeomObjectPlot , EdgePlot ] , pt : "np.Array" ) -> None :
518
543
"""
519
544
Select an object in the plotter.
520
545
@@ -523,17 +548,25 @@ def select_object(self, actor: pv.Actor, body_name: str, pt: "np.Array") -> None
523
548
524
549
Parameters
525
550
----------
526
- actor : pv.Actor
527
- Actor on which to perform the operations.
528
- body_name : str
529
- Name of the Body to highlight.
551
+ geom_object : Union[GeomObjectPlot, EdgePlot]
552
+ Geometry object to select.
530
553
pt : np.Array
531
554
Set of points to determine the label position.
532
555
"""
533
556
added_actors = []
534
- actor .prop .show_edges = True
535
- actor .prop .color = PICKED_COLOR
536
- text = body_name
557
+
558
+ # Add edges if selecting an object
559
+ if isinstance (geom_object , GeomObjectPlot ):
560
+ geom_object .actor .prop .color = PICKED_COLOR
561
+ children_list = geom_object .edges
562
+ for edge in children_list :
563
+ edge .actor .SetVisibility (True )
564
+ edge .actor .prop .color = EDGE_COLOR
565
+ elif isinstance (geom_object , EdgePlot ):
566
+ geom_object .actor .prop .color = PICKED_EDGE_COLOR
567
+
568
+ text = geom_object .name
569
+
537
570
label_actor = self ._pl .scene .add_point_labels (
538
571
[pt ],
539
572
[text ],
@@ -542,13 +575,12 @@ def select_object(self, actor: pv.Actor, body_name: str, pt: "np.Array") -> None
542
575
render_points_as_spheres = False ,
543
576
show_points = False ,
544
577
)
545
- if body_name not in self ._picked_list :
546
- self ._picked_list .add (body_name )
578
+ if geom_object . name not in self ._picked_list :
579
+ self ._picked_list .add (geom_object . name )
547
580
added_actors .append (label_actor )
581
+ self ._picker_added_actors_map [geom_object .actor .name ] = added_actors
548
582
549
- self ._picker_added_actors_map [actor .name ] = added_actors
550
-
551
- def unselect_object (self , actor : pv .Actor , body_name : str ) -> None :
583
+ def unselect_object (self , geom_object : Union [GeomObjectPlot , EdgePlot ]) -> None :
552
584
"""
553
585
Unselect an object in the plotter.
554
586
@@ -557,17 +589,30 @@ def unselect_object(self, actor: pv.Actor, body_name: str) -> None:
557
589
558
590
Parameters
559
591
----------
560
- actor : pv.Actor
561
- Actor that is currently highlighted
562
- body_name : str
563
- Body name to remove
592
+ geom_object : Union[GeomObjectPlot, EdgePlot]
593
+ Object to unselect.
564
594
"""
565
- actor .prop .show_edges = False
566
- actor .prop .color = DEFAULT_COLOR
567
- self ._picked_list .remove (body_name )
568
- if actor .name in self ._picker_added_actors_map :
569
- self ._pl .scene .remove_actor (self ._picker_added_actors_map [actor .name ])
570
- self ._picker_added_actors_map .pop (actor .name )
595
+ # remove actor from picked list and from scene
596
+ object_name = geom_object .name
597
+ if object_name in self ._picked_list :
598
+ self ._picked_list .remove (object_name )
599
+
600
+ if isinstance (geom_object , GeomObjectPlot ):
601
+ geom_object .actor .prop .color = DEFAULT_COLOR
602
+ elif isinstance (geom_object , EdgePlot ):
603
+ geom_object .actor .prop .color = EDGE_COLOR
604
+
605
+ if geom_object .actor .name in self ._picker_added_actors_map :
606
+ self ._pl .scene .remove_actor (self ._picker_added_actors_map [geom_object .actor .name ])
607
+
608
+ # remove actor and its children(edges) from the scene
609
+ if isinstance (geom_object , GeomObjectPlot ):
610
+ for edge in geom_object .edges :
611
+ # hide edges in the scene
612
+ edge .actor .SetVisibility (False )
613
+ # recursion
614
+ self .unselect_object (edge )
615
+ self ._picker_added_actors_map .pop (geom_object .actor .name )
571
616
572
617
def picker_callback (self , actor : "pv.Actor" ) -> None :
573
618
"""
@@ -579,13 +624,36 @@ def picker_callback(self, actor: "pv.Actor") -> None:
579
624
Actor that we are picking.
580
625
"""
581
626
pt = self ._pl .scene .picked_point
582
- self ._actor_object_mapping .keys
583
- if actor .name in self ._actor_object_mapping :
584
- body_name = self ._actor_object_mapping [actor .name ]
585
- if body_name not in self ._picked_list :
586
- self .select_object (actor , body_name , pt )
627
+
628
+ # if object is a body/component
629
+ if actor in self ._geom_object_actors_map :
630
+ body_plot = self ._geom_object_actors_map [actor ]
631
+ if body_plot .object .name not in self ._picked_list :
632
+ self .select_object (body_plot , pt )
633
+ else :
634
+ self .unselect_object (body_plot )
635
+
636
+ # if object is an edge
637
+ elif actor in self ._edge_actors_map and actor .GetVisibility ():
638
+ edge = self ._edge_actors_map [actor ]
639
+ if edge .name not in self ._picked_list :
640
+ self .select_object (edge , pt )
587
641
else :
588
- self .unselect_object (actor , body_name )
642
+ self .unselect_object (edge )
643
+ actor .prop .color = EDGE_COLOR
644
+
645
+ def compute_edge_object_map (self ) -> Dict [pv .Actor , EdgePlot ]:
646
+ """
647
+ Compute the mapping between plotter actors and EdgePlot objects.
648
+
649
+ Returns
650
+ -------
651
+ Dict[pv.Actor, EdgePlot]
652
+ Mapping between plotter actors and EdgePlot objects.
653
+ """
654
+ for object in self ._geom_object_actors_map .values ():
655
+ for edge in object .edges :
656
+ self ._edge_actors_map [edge .actor ] = edge
589
657
590
658
def plot (
591
659
self ,
@@ -629,14 +697,17 @@ def plot(
629
697
"""
630
698
if isinstance (object , List ) and not isinstance (object [0 ], pv .PolyData ):
631
699
logger .debug ("Plotting objects in list..." )
632
- self ._actor_object_mapping = self ._pl .add_list (
700
+ self ._geom_object_actors_map = self ._pl .add_list (
633
701
object , merge_bodies , merge_component , ** plotting_options
634
702
)
635
703
else :
636
- self ._actor_object_mapping = self ._pl .add (
704
+ self ._geom_object_actors_map = self ._pl .add (
637
705
object , merge_bodies , merge_component , ** plotting_options
638
706
)
639
707
708
+ self .compute_edge_object_map ()
709
+ # Compute mapping between the objects and its edges.
710
+
640
711
if view_2d is not None :
641
712
self ._pl .scene .view_vector (
642
713
vector = view_2d ["vector" ],
0 commit comments