Skip to content

Commit c04ef26

Browse files
chore: enhancements to GLB export and object plot() methods (#1750)
Co-authored-by: pyansys-ci-bot <92810346+pyansys-ci-bot@users.noreply.github.com>
1 parent 9a67dea commit c04ef26

File tree

6 files changed

+71
-18
lines changed

6 files changed

+71
-18
lines changed

doc/changelog.d/1750.maintenance.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
enhancements to GLB export and object ``plot()`` methods

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ dependencies = [
3535
"numpy>=1.20.3,<3",
3636
"Pint>=0.18,<1",
3737
"protobuf>=3.20.2,<6",
38-
"pygltflib>=1.16.3",
38+
"pygltflib>=1.16,<2",
3939
"pyvista>=0.37.0,<1",
4040
"requests>=2,<3",
4141
"scipy>=1.7.3,<2",

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1745,6 +1745,14 @@ def plot( # noqa: D102
17451745
# Add to plotting options as well... to be used by the plotter if necessary
17461746
plotting_options["merge_bodies"] = merge
17471747

1748+
# In case that service colors are requested, merge will be ignored.
1749+
# An info message will be issued to the user.
1750+
if use_service_colors and merge:
1751+
self._template._grpc_client.log.info(
1752+
"Ignoring 'merge' option since 'use_service_colors' is set to True."
1753+
)
1754+
plotting_options["merge_bodies"] = False
1755+
17481756
mesh_object = (
17491757
self if use_service_colors else MeshObjectPlot(self, self.tessellate(merge=merge))
17501758
)

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1588,13 +1588,19 @@ def plot(
15881588
plotting_options["merge_bodies"] = merge_bodies
15891589

15901590
# At component level, if ``multi_colors`` or ``use_service_colors`` are defined
1591-
# we should not merge the component.
1591+
# we should not merge the component or the bodies (only if ``use_service_colors`` is True).
15921592
if plotting_options.get("multi_colors", False) or use_service_colors:
15931593
plotting_options["merge_component"] = False
15941594
self._grpc_client.log.info(
15951595
"Ignoring 'merge_component=True' (default behavior) as "
15961596
"'multi_colors' or 'use_service_colors' are defined."
15971597
)
1598+
if use_service_colors:
1599+
plotting_options["merge_bodies"] = False
1600+
self._grpc_client.log.info(
1601+
"Ignoring 'merge_bodies=True' (default behavior) as "
1602+
"'use_service_colors' is defined."
1603+
)
15981604

15991605
pl = GeometryPlotter(
16001606
use_trame=use_trame,

src/ansys/geometry/core/plotting/plotter.py

Lines changed: 50 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -243,14 +243,24 @@ def add_body(self, body: Body, merge: bool = False, **plotting_options: dict | N
243243

244244
if self.use_service_colors:
245245
faces = body.faces
246-
dataset = body.tessellate()
247-
for i, block in enumerate(dataset):
248-
if faces[i].color != Color.DEFAULT.value:
249-
plotting_options["color"] = faces[i].color
250-
else:
251-
plotting_options["color"] = body.color
252-
self._backend.pv_interface.plot(block, **plotting_options)
253-
return
246+
dataset = body.tessellate(merge=merge)
247+
body_color = body.color
248+
if not merge:
249+
# ASSUMPTION: the faces returned by the service are in the same order
250+
# as the tessellation information returned by the service...
251+
elems = [elem for elem in dataset]
252+
for face, block in zip(faces, elems):
253+
face_color = face.color
254+
if face_color != Color.DEFAULT.value:
255+
plotting_options["color"] = face_color
256+
else:
257+
plotting_options["color"] = body_color
258+
self._backend.pv_interface.plot(block, **plotting_options)
259+
return
260+
else:
261+
plotting_options["color"] = body_color
262+
self._backend.pv_interface.plot(dataset, **plotting_options)
263+
return
254264
# WORKAROUND: multi_colors is not properly supported in PyVista PolyData
255265
# so if multi_colors is True and merge is True (returns PolyData) then
256266
# we need to set the color manually
@@ -481,27 +491,55 @@ def show(
481491
return lib_objects
482492

483493
def export_glb(
484-
self, plotting_object: Any = None, screenshot: str | None = None, **plotting_options
494+
self, plotting_object: Any = None, filename: str | Path | None = None, **plotting_options
485495
) -> None:
486496
"""Export the design to a glb file. Does not support picked objects.
487497
488498
Parameters
489499
----------
490500
plotting_object : Any, default: None
491501
Object you can add to the plotter.
492-
screenshot : str, default: None
493-
Path to save a screenshot of the plotter. Do not include file extension.
502+
filename : str | ~pathlib.Path, default: None
503+
Path to save a GLB file of the plotter. If None, the file will be saved as
504+
temp_glb.glb.
494505
**plotting_options : dict, default: None
495506
Keyword arguments for the plotter. Arguments depend of the backend implementation
496507
you are using.
508+
509+
Returns
510+
-------
511+
~pathlib.Path
512+
Path to the exported glb file.
497513
"""
498514
if plotting_object is not None:
499515
self.plot(plotting_object, **plotting_options)
500516

501-
gltf_filepath = str(screenshot) + ".gltf"
517+
# Depending on whether a name is provided, the file will be saved with the name
518+
# provided or with a default name (temp_glb). If a name is provided, the file will
519+
# be saved in the current working directory. If a path is provided, the file will be
520+
# saved in the provided path. We will also make sure that the file has the .glb extension,
521+
# and if not, we will add it.
522+
if filename is None:
523+
glb_filepath = Path("temp_glb.glb")
524+
else:
525+
glb_filepath = filename if isinstance(filename, Path) else Path(filename)
526+
if glb_filepath.suffix != ".glb":
527+
glb_filepath = glb_filepath.with_suffix(".glb")
502528

529+
# Temporary gltf file to export the scene
530+
gltf_filepath = glb_filepath.with_suffix(".gltf")
531+
532+
# Hide the axes before exporting the gltf and then export it
503533
self.backend._pl._scene.hide_axes()
504534
self.backend._pl._scene.export_gltf(gltf_filepath)
505535

536+
# Convert the gltf to glb and remove the gltf file
506537
gltf2glb(gltf_filepath)
507538
Path.unlink(gltf_filepath)
539+
540+
# Check if the file was exported correctly
541+
if not glb_filepath.exists(): # pragma: no cover
542+
raise FileNotFoundError(f"GLB file not found at {glb_filepath}. Export failed.")
543+
544+
# Finally, return the path to the exported glb file
545+
return glb_filepath

tests/integration/test_plotter.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -945,7 +945,7 @@ def test_export_glb(modeler: Modeler, verify_image_cache):
945945
pl.plot(box_body)
946946

947947
output_glb_path = Path(IMAGE_RESULTS_DIR, "plot_box_glb")
948-
pl.export_glb(screenshot=output_glb_path)
948+
pl.export_glb(filename=output_glb_path)
949949

950950

951951
@skip_no_xserver
@@ -966,7 +966,7 @@ def test_export_glb_with_color(modeler: Modeler, verify_image_cache):
966966
pl.plot(box_body)
967967

968968
output_glb_path = Path(IMAGE_RESULTS_DIR, "plot_box_glb_colored")
969-
pl.export_glb(screenshot=output_glb_path)
969+
pl.export_glb(filename=output_glb_path)
970970

971971

972972
@skip_no_xserver
@@ -988,7 +988,7 @@ def test_export_glb_with_face_color(modeler: Modeler, verify_image_cache):
988988
pl = GeometryPlotter(use_service_colors=True)
989989

990990
output_glb_path = Path(IMAGE_RESULTS_DIR, "plot_box_glb_face_colored")
991-
pl.export_glb(box_body, screenshot=output_glb_path)
991+
pl.export_glb(box_body, filename=output_glb_path)
992992

993993

994994
@skip_no_xserver
@@ -1008,4 +1008,4 @@ def test_export_glb_cylinder_with_face_color(modeler: Modeler, verify_image_cach
10081008
pl = GeometryPlotter(use_service_colors=True)
10091009

10101010
output_glb_path = Path(IMAGE_RESULTS_DIR, "plot_cylinder_glb_face_colored")
1011-
pl.export_glb(cyl, screenshot=output_glb_path)
1011+
pl.export_glb(cyl, filename=output_glb_path)

0 commit comments

Comments
 (0)