Skip to content

Commit b70e431

Browse files
feat: performance enhancements to plotter (#1496)
Co-authored-by: pyansys-ci-bot <[email protected]>
1 parent 39d412a commit b70e431

File tree

12 files changed

+132
-147
lines changed

12 files changed

+132
-147
lines changed

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ exclude: "tests/integration/files"
77
repos:
88

99
- repo: https://github.com/astral-sh/ruff-pre-commit
10-
rev: v0.6.9
10+
rev: v0.7.0
1111
hooks:
1212
- id: ruff
1313
- id: ruff-format

doc/changelog.d/1496.added.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
performance enhancements to plotter
-59.3 KB
Binary file not shown.

doc/source/examples/01_getting_started/05_plotter_picker.mystnb

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ the ``plot()`` method.
176176

177177
In the following cell we will create a new design and plot a prism and a cylinder in different colors.
178178

179-
```python
179+
```{code-cell} ipython
180180
design = modeler.create_design("MultiColors")
181181

182182
# Create a sketch of a box
@@ -193,8 +193,6 @@ design.extrude_sketch("Cylinder", sketch_circle, 50 * UNITS.m)
193193
design.plot(multi_colors=True)
194194
```
195195

196-
![](../../_static/assets/multicolors.png)
197-
198196
## Clip objects
199197

200198
You can clip any object represented in the plotter by defining a ``Plane`` object that

doc/source/examples/03_modeling/tessellation_usage.mystnb

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -76,26 +76,18 @@ combines all the faces of each individual body into a single dataset without
7676
separating faces.
7777

7878
```{code-cell} ipython3
79-
dataset = comp.tessellate(merge_bodies=True)
79+
dataset = comp.tessellate()
8080
dataset
8181
```
82-
If you want to tessellate the body and return the geometry as triangles, single body tessellation
83-
is possible. If you want to merge the individual faces of the tessellation, enable the
84-
``merge`` option so that the body is rendered into a single mesh. This preserves the number of
85-
triangles and only merges the topology.
8682

87-
**Code without merging the body**
83+
Single body tessellation is possible. In that case, users can request the body-level tessellation
84+
method to tessellate the body and merge all the faces into a single dataset.
8885

8986
```{code-cell} ipython3
90-
dataset = body.tessellate()
87+
dataset = comp.bodies[0].tessellate()
9188
dataset
9289
```
93-
**Code with merging the body**
9490

95-
```{code-cell} ipython3
96-
mesh = body.tessellate(merge=True)
97-
mesh
98-
```
9991
## Plot design
10092

10193
Plot the design.

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ all = [
4949
tests = [
5050
"ansys-platform-instancemanagement==1.1.2",
5151
"ansys-tools-path==0.6.0",
52-
"ansys-tools-visualization-interface==0.4.5",
52+
"ansys-tools-visualization-interface==0.4.6",
5353
"beartype==0.19.0",
5454
"docker==7.1.0",
5555
"grpcio==1.67.0",
@@ -77,7 +77,7 @@ tests-minimal = [
7777
doc = [
7878
"ansys-sphinx-theme[autoapi]==1.1.6",
7979
"ansys-tools-path==0.6.0",
80-
"ansys-tools-visualization-interface==0.4.5",
80+
"ansys-tools-visualization-interface==0.4.6",
8181
"beartype==0.19.0",
8282
"docker==7.1.0",
8383
"grpcio==1.67.0",

src/ansys/geometry/core/connection/conversions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,7 @@ def tess_to_pd(tess: Tessellation) -> "PolyData":
382382
import numpy as np
383383
import pyvista as pv
384384

385-
return pv.PolyData(np.array(tess.vertices).reshape(-1, 3), tess.faces)
385+
return pv.PolyData(var_inp=np.array(tess.vertices).reshape(-1, 3), faces=tess.faces)
386386

387387

388388
def grpc_matrix_to_matrix(m: GRPCMatrix) -> Matrix44:

src/ansys/geometry/core/designer/body.py

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -544,7 +544,7 @@ def tessellate(self, merge: bool = False) -> Union["PolyData", "MultiBlock"]:
544544
@abstractmethod
545545
def plot(
546546
self,
547-
merge: bool = False,
547+
merge: bool = True,
548548
screenshot: str | None = None,
549549
use_trame: bool | None = None,
550550
use_service_colors: bool | None = None,
@@ -554,10 +554,11 @@ def plot(
554554
555555
Parameters
556556
----------
557-
merge : bool, default: False
558-
Whether to merge the body into a single mesh. When ``False`` (default),
559-
the number of triangles are preserved and only the topology is merged.
560-
When ``True``, the individual faces of the tessellation are merged.
557+
merge : bool, default: True
558+
Whether to merge the body into a single mesh. Performance improved when ``True``.
559+
When ``True`` (default), the individual faces of the tessellation are merged.
560+
When ``False``, the number of triangles are preserved and only the topology
561+
is merged.
561562
screenshot : str, default: None
562563
Path for saving a screenshot of the image that is being represented.
563564
use_trame : bool, default: None
@@ -1118,14 +1119,16 @@ def tessellate( # noqa: D102
11181119

11191120
pdata = [tess_to_pd(tess).transform(transform) for tess in self._tessellation]
11201121
comp = pv.MultiBlock(pdata)
1122+
11211123
if merge:
11221124
ugrid = comp.combine()
1123-
return pv.PolyData(ugrid.points, ugrid.cells, n_faces=ugrid.n_cells)
1124-
return comp
1125+
return pv.PolyData(var_inp=ugrid.points, faces=ugrid.cells)
1126+
else:
1127+
return comp
11251128

11261129
def plot( # noqa: D102
11271130
self,
1128-
merge: bool = False,
1131+
merge: bool = True,
11291132
screenshot: str | None = None,
11301133
use_trame: bool | None = None,
11311134
use_service_colors: bool | None = None,
@@ -1510,7 +1513,7 @@ def tessellate( # noqa: D102
15101513

15111514
def plot( # noqa: D102
15121515
self,
1513-
merge: bool = False,
1516+
merge: bool = True,
15141517
screenshot: str | None = None,
15151518
use_trame: bool | None = None,
15161519
use_service_colors: bool | None = None,
@@ -1529,6 +1532,9 @@ def plot( # noqa: D102
15291532
else pyansys_geometry.USE_SERVICE_COLORS
15301533
)
15311534

1535+
# Add to plotting options as well... to be used by the plotter if necessary
1536+
plotting_options["merge_bodies"] = merge
1537+
15321538
mesh_object = (
15331539
self if use_service_colors else MeshObjectPlot(self, self.tessellate(merge=merge))
15341540
)

src/ansys/geometry/core/designer/component.py

Lines changed: 41 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1350,85 +1350,47 @@ def _kill_component_on_client(self) -> None:
13501350
# Kill itself
13511351
self._is_alive = False
13521352

1353-
def tessellate(
1354-
self, merge_component: bool = False, merge_bodies: bool = False
1355-
) -> Union["PolyData", "MultiBlock"]:
1353+
def tessellate(self, _recursive_call: bool = False) -> Union["PolyData", list["MultiBlock"]]:
13561354
"""Tessellate the component.
13571355
13581356
Parameters
13591357
----------
1360-
merge_component : bool, default: False
1361-
Whether to merge this component into a single dataset. When ``True``,
1362-
all the individual bodies are effectively combined into a single
1363-
dataset without any hierarchy.
1364-
merge_bodies : bool, default: False
1365-
Whether to merge each body into a single dataset. When ``True``,
1366-
all the faces of each individual body are effectively
1367-
merged into a single dataset without separating faces.
1358+
_recursive_call: bool, default: False
1359+
Internal flag to indicate if this method is being called recursively.
1360+
Not to be used by the user.
13681361
13691362
Returns
13701363
-------
1371-
~pyvista.PolyData, ~pyvista.MultiBlock
1372-
Merged :class:`pyvista.PolyData` if ``merge_component=True`` or a
1373-
composite dataset.
1364+
~pyvista.PolyData, list[~pyvista.MultiBlock]
1365+
Tessellated component as a single PolyData object.
1366+
If the method is called recursively, a list of MultiBlock objects is returned.
13741367
1375-
Examples
1376-
--------
1377-
Create two stacked bodies and return the tessellation as two merged bodies:
1378-
1379-
>>> from ansys.geometry.core.sketch import Sketch
1380-
>>> from ansys.geometry.core import Modeler
1381-
>>> from ansys.geometry.core.math import Point2D, Point3D, Plane
1382-
>>> from ansys.geometry.core.misc import UNITS
1383-
>>> modeler = Modeler("10.54.0.72", "50051")
1384-
>>> sketch_1 = Sketch()
1385-
>>> box = sketch_1.box(
1386-
>>> Point2D([10, 10], UNITS.m), Quantity(10, UNITS.m), Quantity(5, UNITS.m))
1387-
>>> sketch_1.circle(Point2D([0, 0], UNITS.m), Quantity(25, UNITS.m))
1388-
>>> design = modeler.create_design("MyDesign")
1389-
>>> comp = design.add_component("MyComponent")
1390-
>>> distance = Quantity(10, UNITS.m)
1391-
>>> body = comp.extrude_sketch("Body", sketch=sketch_1, distance=distance)
1392-
>>> sketch_2 = Sketch(Plane([0, 0, 10]))
1393-
>>> box = sketch_2.box(
1394-
>>> Point2D([10, 10], UNITS.m), Quantity(10, UNITS.m), Quantity(5, UNITS.m))
1395-
>>> circle = sketch_2.circle(Point2D([0, 0], UNITS.m), Quantity(25, UNITS.m))
1396-
>>> body = comp.extrude_sketch("Body", sketch=sketch_2, distance=distance)
1397-
>>> dataset = comp.tessellate(merge_bodies=True)
1398-
>>> dataset
1399-
MultiBlock (0x7ff6bcb511e0)
1400-
N Blocks: 2
1401-
X Bounds: -25.000, 25.000
1402-
Y Bounds: -24.991, 24.991
1403-
Z Bounds: 0.000, 20.000
14041368
"""
14051369
import pyvista as pv
14061370

14071371
# Tessellate the bodies in this component
1408-
datasets = [body.tessellate(merge_bodies) for body in self.bodies]
1409-
1410-
blocks_list = [pv.MultiBlock(datasets)]
1372+
datasets: list["MultiBlock"] = [body.tessellate(merge=False) for body in self.bodies]
14111373

14121374
# Now, go recursively inside its subcomponents (with no arguments) and
14131375
# merge the PolyData obtained into our blocks
14141376
for comp in self._components:
14151377
if not comp.is_alive:
14161378
continue
1417-
blocks_list.append(comp.tessellate(merge_bodies=merge_bodies))
1418-
1419-
# Transform the list of MultiBlock objects into a single MultiBlock
1420-
blocks = pv.MultiBlock(blocks_list)
1379+
datasets.extend(comp.tessellate(_recursive_call=True))
14211380

1422-
if merge_component:
1423-
ugrid = blocks.combine()
1424-
# Convert to polydata as it's slightly faster than extract surface
1425-
return pv.PolyData(ugrid.points, ugrid.cells, n_faces=ugrid.n_cells)
1426-
return blocks
1381+
# Convert to polydata as it's slightly faster than extract surface
1382+
# plus this method is only for visualizing the component as a whole (no
1383+
# need to keep the hierarchy)
1384+
if _recursive_call:
1385+
return datasets
1386+
else:
1387+
ugrid = pv.MultiBlock(datasets).combine()
1388+
return pv.PolyData(var_inp=ugrid.points, faces=ugrid.cells)
14271389

14281390
def plot(
14291391
self,
1430-
merge_component: bool = False,
1431-
merge_bodies: bool = False,
1392+
merge_component: bool = True,
1393+
merge_bodies: bool = True,
14321394
screenshot: str | None = None,
14331395
use_trame: bool | None = None,
14341396
use_service_colors: bool | None = None,
@@ -1438,14 +1400,15 @@ def plot(
14381400
14391401
Parameters
14401402
----------
1441-
merge_component : bool, default: False
1442-
Whether to merge the component into a single dataset. When ``True``,
1443-
all the individual bodies are effectively merged into a single
1444-
dataset without any hierarchy.
1445-
merge_bodies : bool, default: False
1446-
Whether to merge each body into a single dataset. When ``True``,
1447-
all the faces of each individual body are effectively merged
1448-
into a single dataset without separating faces.
1403+
merge_component : bool, default: True
1404+
Whether to merge the component into a single dataset. By default, ``True``.
1405+
Performance improved. When ``True``, all the faces of the component are effectively
1406+
merged into a single dataset. If ``False``, the individual bodies are kept separate.
1407+
merge_bodies : bool, default: True
1408+
Whether to merge each body into a single dataset. By default, ``True``.
1409+
Performance improved. When ``True``, all the faces of each individual body are
1410+
effectively merged into a single dataset. If ``False``, the individual faces are kept
1411+
separate.
14491412
screenshot : str, default: None
14501413
Path for saving a screenshot of the image being represented.
14511414
use_trame : bool, default: None
@@ -1496,24 +1459,28 @@ def plot(
14961459
"""
14971460
import ansys.geometry.core as pyansys_geometry
14981461
from ansys.geometry.core.plotting import GeometryPlotter
1499-
from ansys.tools.visualization_interface.types.mesh_object_plot import MeshObjectPlot
15001462

15011463
use_service_colors = (
15021464
use_service_colors
15031465
if use_service_colors is not None
15041466
else pyansys_geometry.USE_SERVICE_COLORS
15051467
)
15061468

1507-
mesh_object = (
1508-
self
1509-
if use_service_colors
1510-
else MeshObjectPlot(
1511-
custom_object=self,
1512-
mesh=self.tessellate(merge_component=merge_component, merge_bodies=merge_bodies),
1469+
# Add merge_component and merge_bodies to the plotting options
1470+
plotting_options["merge_component"] = merge_component
1471+
plotting_options["merge_bodies"] = merge_bodies
1472+
1473+
# At component level, if ``multi_colors`` or ``use_service_colors`` are defined
1474+
# we should not merge the component.
1475+
if plotting_options.get("multi_colors", False) or use_service_colors:
1476+
plotting_options["merge_component"] = False
1477+
self._grpc_client.log.info(
1478+
"Ignoring 'merge_component=True' (default behavior) as "
1479+
"'multi_colors' or 'use_service_colors' are defined."
15131480
)
1514-
)
1481+
15151482
pl = GeometryPlotter(use_trame=use_trame, use_service_colors=use_service_colors)
1516-
pl.plot(mesh_object, **plotting_options)
1483+
pl.plot(self, **plotting_options)
15171484
pl.show(screenshot=screenshot, **plotting_options)
15181485

15191486
def __repr__(self) -> str:

0 commit comments

Comments
 (0)