Skip to content

Commit f182592

Browse files
fix: Add support for MultiBlock
1 parent 5b1fa06 commit f182592

File tree

5 files changed

+117
-22
lines changed

5 files changed

+117
-22
lines changed

examples/01-basic-plotly-examples/plain-usage.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,15 @@
4646
# Plot the mesh
4747
pl.plot(mesh)
4848

49+
50+
# Create a PyVista MultiBlock
51+
multi_block = pv.MultiBlock()
52+
multi_block.append(pv.Sphere(center=(-1, -1, 0)))
53+
multi_block.append(pv.Cube(center=(-1, 1, 0)))
54+
55+
# Plot the MultiBlock
56+
pl.plot(multi_block)
57+
4958
#####################
5059
# Display the plotter
5160
#
@@ -66,7 +75,6 @@ def name(self):
6675
return self.name
6776

6877

69-
7078
# Create a custom object
7179
custom_cube = CustomObject()
7280
custom_cube.name = "CustomCube"
@@ -111,6 +119,8 @@ def name(self):
111119
)
112120
pl.plot(scatter)
113121

122+
123+
114124
###########################
115125
# Display the plotter again
116126
# =========================

src/ansys/tools/visualization_interface/backends/plotly/plotly_interface.py

Lines changed: 64 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
from typing import Any, Iterable, Union
2525

2626
import plotly.graph_objects as go
27+
import pyvista as pv
2728
from pyvista import PolyData
2829

2930
from ansys.tools.visualization_interface.backends._base import BaseBackend
@@ -37,8 +38,46 @@ def __init__(self) -> None:
3738
"""Initialize the Plotly backend."""
3839
self._fig = go.Figure()
3940

40-
def _pv_to_mesh3d(self, pv_mesh: PolyData) -> go.Mesh3d:
41-
"""Convert a PyVista PolyData mesh to Plotly Mesh3d format.
41+
def _pv_to_mesh3d(self, pv_mesh: Union[PolyData, pv.MultiBlock]) -> Union[go.Mesh3d, list]:
42+
"""Convert a PyVista PolyData or MultiBlock mesh to Plotly Mesh3d format.
43+
44+
Parameters
45+
----------
46+
pv_mesh : Union[PolyData, pv.MultiBlock]
47+
The PyVista PolyData or MultiBlock mesh to convert.
48+
49+
Returns
50+
-------
51+
Union[go.Mesh3d, list]
52+
The converted Plotly Mesh3d object(s). Returns a single Mesh3d for PolyData,
53+
or a list of Mesh3d objects for MultiBlock.
54+
"""
55+
if isinstance(pv_mesh, pv.MultiBlock):
56+
# Handle MultiBlock by converting each block and returning a list
57+
mesh_list = []
58+
for i, block in enumerate(pv_mesh):
59+
if block is not None:
60+
# Convert each block to PolyData if needed
61+
if hasattr(block, 'extract_surface'):
62+
# For volume meshes, extract the surface
63+
block = block.extract_surface()
64+
elif not isinstance(block, PolyData):
65+
# Try to convert to PolyData
66+
try:
67+
block = block.cast_to_polydata()
68+
except AttributeError:
69+
continue # Skip blocks that can't be converted
70+
71+
# Now convert the PolyData block
72+
mesh_3d = self._convert_polydata_to_mesh3d(block)
73+
mesh_list.append(mesh_3d)
74+
return mesh_list
75+
else:
76+
# Handle single PolyData
77+
return self._convert_polydata_to_mesh3d(pv_mesh)
78+
79+
def _convert_polydata_to_mesh3d(self, pv_mesh: PolyData) -> go.Mesh3d:
80+
"""Convert a single PolyData mesh to Plotly Mesh3d format.
4281
4382
Parameters
4483
----------
@@ -96,23 +135,36 @@ def plot_iter(self, plotting_list: Iterable[Any]) -> None:
96135
self.plot(item)
97136

98137

99-
def plot(self, plottable_object: Union[PolyData, MeshObjectPlot, go.Mesh3d], **plotting_options) -> None:
138+
def plot(
139+
self,
140+
plottable_object: Union[PolyData, pv.MultiBlock, MeshObjectPlot, go.Mesh3d],
141+
**plotting_options
142+
) -> None:
100143
"""Plot a single object using Plotly.
101144
102145
Parameters
103146
----------
104-
plottable_object : Union[PolyData, MeshObjectPlot, go.Mesh3d]
105-
The object to plot. Can be a PyVista PolyData, a MeshObjectPlot, or a Plotly Mesh3d.
147+
plottable_object : Union[PolyData, pv.MultiBlock, MeshObjectPlot, go.Mesh3d]
148+
The object to plot. Can be a PyVista PolyData, MultiBlock, a MeshObjectPlot, or a Plotly Mesh3d.
106149
plotting_options : dict
107150
Additional plotting options.
108151
"""
109-
if isinstance(plottable_object, PolyData):
110-
mesh = self._pv_to_mesh3d(plottable_object)
111-
self._fig.add_trace(mesh)
112-
elif isinstance(plottable_object, MeshObjectPlot):
113-
pv_mesh = plottable_object.mesh
114-
mesh = self._pv_to_mesh3d(pv_mesh)
115-
self._fig.add_trace(mesh)
152+
if isinstance(plottable_object, MeshObjectPlot):
153+
mesh = plottable_object.mesh
154+
else:
155+
mesh = plottable_object
156+
157+
if isinstance(mesh, (PolyData, pv.MultiBlock)):
158+
mesh_result = self._pv_to_mesh3d(mesh)
159+
160+
# Handle both single mesh and list of meshes
161+
if isinstance(mesh_result, list):
162+
# MultiBlock case - add all meshes
163+
for mesh_3d in mesh_result:
164+
self._fig.add_trace(mesh_3d)
165+
else:
166+
# Single PolyData case
167+
self._fig.add_trace(mesh_result)
116168
elif isinstance(plottable_object, go.Mesh3d):
117169
self._fig.add_trace(plottable_object)
118170
else:

src/ansys/tools/visualization_interface/types/edge_plot.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@
2222
"""Provides the edge type for plotting."""
2323

2424

25-
from typing import TYPE_CHECKING, Any
25+
from typing import TYPE_CHECKING, Any, Union
2626

27+
from plotly.graph_objects import Mesh3d
2728
import pyvista as pv
2829

2930
if TYPE_CHECKING:
@@ -34,7 +35,7 @@ class EdgePlot:
3435
3536
Parameters
3637
----------
37-
actor : ~pyvista.Actor
38+
actor : Union[~pyvista.Actor, Mesh3d]
3839
PyVista actor that represents the edge.
3940
edge_object : Edge
4041
PyAnsys object edge that is represented by the PyVista actor.
@@ -43,7 +44,7 @@ class EdgePlot:
4344
4445
"""
4546

46-
def __init__(self, actor: pv.Actor, edge_object: Any, parent: Any = None) -> None:
47+
def __init__(self, actor: Union[pv.Actor, Mesh3d], edge_object: Any, parent: Any = None) -> None:
4748
"""Initialize ``EdgePlot`` variables."""
4849
self._actor = actor
4950
self._object = edge_object

src/ansys/tools/visualization_interface/types/mesh_object_plot.py

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@
2222
"""Provides the ``MeshObjectPlot`` class."""
2323

2424

25-
from typing import Any, List, Union
25+
from typing import Any, List, Type, Union
2626

27+
from plotly.graph_objects import Mesh3d
2728
import pyvista as pv
2829

2930
from ansys.tools.visualization_interface.types.edge_plot import EdgePlot
@@ -35,7 +36,7 @@ class MeshObjectPlot:
3536
def __init__(
3637
self,
3738
custom_object: Any,
38-
mesh: Union[pv.PolyData, pv.MultiBlock],
39+
mesh: Union[pv.PolyData, pv.MultiBlock, Mesh3d],
3940
actor: pv.Actor = None,
4041
edges: List[EdgePlot] = None,
4142
) -> None:
@@ -49,7 +50,7 @@ def __init__(
4950
----------
5051
custom_object : Any
5152
Any object that the consumer library wants to relate with a mesh.
52-
mesh : Union[pv.PolyData, pv.MultiBlock]
53+
mesh : Union[pv.PolyData, pv.MultiBlock, Mesh3d]
5354
PyVista mesh that represents the custom object.
5455
actor : pv.Actor, default: None
5556
Actor of the mesh in the plotter.
@@ -63,7 +64,7 @@ def __init__(
6364
self._edges = edges
6465

6566
@property
66-
def mesh(self) -> Union[pv.PolyData, pv.MultiBlock]:
67+
def mesh(self) -> Union[pv.PolyData, pv.MultiBlock, Mesh3d]:
6768
"""Mesh of the object in PyVista format.
6869
6970
Returns
@@ -75,12 +76,12 @@ def mesh(self) -> Union[pv.PolyData, pv.MultiBlock]:
7576
return self._mesh
7677

7778
@mesh.setter
78-
def mesh(self, mesh: Union[pv.PolyData, pv.MultiBlock]):
79+
def mesh(self, mesh: Union[pv.PolyData, pv.MultiBlock, Mesh3d]):
7980
"""Set the mesh of the object in PyVista format.
8081
8182
Parameters
8283
----------
83-
mesh : Union[pv.PolyData, pv.MultiBlock]
84+
mesh : Union[pv.PolyData, pv.MultiBlock, Mesh3d]
8485
Mesh of the object.
8586
8687
"""
@@ -174,3 +175,15 @@ def name(self) -> str:
174175
return self._custom_object.id
175176
else:
176177
return "Unknown"
178+
179+
@property
180+
def mesh_type(self) -> Type:
181+
"""Type of the mesh.
182+
183+
Returns
184+
-------
185+
type
186+
Type of the mesh.
187+
188+
"""
189+
return type(self._mesh)

tests/test_plotly_backend.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,25 @@ def test_plot_pyvista_mesh(tmp_path, image_compare):
7171
assert image_compare(file)
7272

7373

74+
def test_plot_pyvista_multiblock(tmp_path, image_compare):
75+
"""Test plotting a PyVista MultiBlock mesh."""
76+
# Create a plotter with the Plotly backend
77+
pl = Plotter(backend=PlotlyBackend())
78+
79+
# Create a PyVista MultiBlock
80+
multi_block = pv.MultiBlock()
81+
multi_block.append(pv.Sphere(center=(-1, -1, 0)))
82+
multi_block.append(pv.Cube(center=(-1, 1, 0)))
83+
84+
# Plot the MultiBlock
85+
pl.plot(multi_block)
86+
87+
# Show the plot (this will open a browser window)
88+
file = tmp_path / "test_plot_pyvista_multiblock.png"
89+
pl.show(screenshot=file)
90+
assert image_compare(file)
91+
92+
7493
def test_plot_mesh_object_plot(tmp_path, image_compare):
7594
"""Test plotting a MeshObjectPlot."""
7695
# Create a plotter with the Plotly backend

0 commit comments

Comments
 (0)