Skip to content

Commit 9a67dea

Browse files
jacobrkerstetterpre-commit-ci[bot]pyansys-ci-bot
authored
feat: export glb (#1741)
Co-authored-by: jkerstet <jacob.kerstetter@ansys.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: pyansys-ci-bot <92810346+pyansys-ci-bot@users.noreply.github.com>
1 parent be66ad2 commit 9a67dea

File tree

4 files changed

+126
-1
lines changed

4 files changed

+126
-1
lines changed

doc/changelog.d/1741.added.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export glb

pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +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",
3839
"pyvista>=0.37.0,<1",
3940
"requests>=2,<3",
4041
"scipy>=1.7.3,<2",
@@ -61,6 +62,7 @@ tests = [
6162
"numpy==2.2.3",
6263
"Pint==0.24.4",
6364
"protobuf==5.29.3",
65+
"pygltflib==1.16.3",
6466
"pytest==8.3.4",
6567
"pytest-cov==6.0.0",
6668
"pytest-pyvista==0.1.9",

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

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,11 @@
2222
"""Provides plotting for various PyAnsys Geometry objects."""
2323

2424
from itertools import cycle
25+
from pathlib import Path
2526
from typing import Any
2627

2728
import numpy as np
29+
from pygltflib.utils import gltf2glb
2830
import pyvista as pv
2931
from pyvista.plotting.tools import create_axes_marker
3032

@@ -236,8 +238,19 @@ def add_body(self, body: Body, merge: bool = False, **plotting_options: dict | N
236238
Keyword arguments. For allowable keyword arguments,
237239
see the :meth:`Plotter.add_mesh <pyvista.Plotter.add_mesh>` method.
238240
"""
241+
if body.is_suppressed:
242+
return
243+
239244
if self.use_service_colors:
240-
plotting_options["color"] = body.color
245+
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
241254
# WORKAROUND: multi_colors is not properly supported in PyVista PolyData
242255
# so if multi_colors is True and merge is True (returns PolyData) then
243256
# we need to set the color manually
@@ -466,3 +479,29 @@ def show(
466479
else: # Either a PyAnsys Geometry object or a PyVista object
467480
lib_objects.append(element)
468481
return lib_objects
482+
483+
def export_glb(
484+
self, plotting_object: Any = None, screenshot: str | None = None, **plotting_options
485+
) -> None:
486+
"""Export the design to a glb file. Does not support picked objects.
487+
488+
Parameters
489+
----------
490+
plotting_object : Any, default: None
491+
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.
494+
**plotting_options : dict, default: None
495+
Keyword arguments for the plotter. Arguments depend of the backend implementation
496+
you are using.
497+
"""
498+
if plotting_object is not None:
499+
self.plot(plotting_object, **plotting_options)
500+
501+
gltf_filepath = str(screenshot) + ".gltf"
502+
503+
self.backend._pl._scene.hide_axes()
504+
self.backend._pl._scene.export_gltf(gltf_filepath)
505+
506+
gltf2glb(gltf_filepath)
507+
Path.unlink(gltf_filepath)

tests/integration/test_plotter.py

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -926,3 +926,86 @@ def test_plot_design_face_colors(modeler: Modeler, verify_image_cache):
926926
merge=False,
927927
multi_colors=True,
928928
)
929+
930+
931+
@skip_no_xserver
932+
def test_export_glb(modeler: Modeler, verify_image_cache):
933+
"""Test exporting a box to glb."""
934+
# Create a Sketch
935+
sketch = Sketch()
936+
sketch.box(Point2D([10, 10], UNITS.mm), Quantity(10, UNITS.mm), Quantity(10, UNITS.mm))
937+
938+
# Create your design on the server side
939+
design = modeler.create_design("BoxExtrusions")
940+
941+
# Extrude the sketch to create a body
942+
box_body = design.extrude_sketch("JustABox", sketch, Quantity(10, UNITS.mm))
943+
944+
pl = GeometryPlotter()
945+
pl.plot(box_body)
946+
947+
output_glb_path = Path(IMAGE_RESULTS_DIR, "plot_box_glb")
948+
pl.export_glb(screenshot=output_glb_path)
949+
950+
951+
@skip_no_xserver
952+
def test_export_glb_with_color(modeler: Modeler, verify_image_cache):
953+
"""Test exporting a box to glb."""
954+
# Create a Sketch
955+
sketch = Sketch()
956+
sketch.box(Point2D([10, 10], UNITS.mm), Quantity(10, UNITS.mm), Quantity(10, UNITS.mm))
957+
958+
# Create your design on the server side
959+
design = modeler.create_design("BoxExtrusions")
960+
961+
# Extrude the sketch to create a body
962+
box_body = design.extrude_sketch("JustABox", sketch, Quantity(10, UNITS.mm))
963+
box_body.set_color((255, 0, 0))
964+
965+
pl = GeometryPlotter(use_service_colors=True)
966+
pl.plot(box_body)
967+
968+
output_glb_path = Path(IMAGE_RESULTS_DIR, "plot_box_glb_colored")
969+
pl.export_glb(screenshot=output_glb_path)
970+
971+
972+
@skip_no_xserver
973+
def test_export_glb_with_face_color(modeler: Modeler, verify_image_cache):
974+
"""Test exporting a box to glb."""
975+
# Create a Sketch
976+
sketch = Sketch()
977+
sketch.box(Point2D([10, 10], UNITS.m), Quantity(10, UNITS.m), Quantity(10, UNITS.m))
978+
979+
# Create your design on the server side
980+
design = modeler.create_design("BoxExtrusions")
981+
982+
# Extrude the sketch to create a body
983+
box_body = design.extrude_sketch("JustABox", sketch, Quantity(10, UNITS.m))
984+
box_body.set_color((255, 0, 0))
985+
box_body.faces[0].set_color((0, 0, 255))
986+
box_body.faces[1].set_color((0, 255, 0))
987+
988+
pl = GeometryPlotter(use_service_colors=True)
989+
990+
output_glb_path = Path(IMAGE_RESULTS_DIR, "plot_box_glb_face_colored")
991+
pl.export_glb(box_body, screenshot=output_glb_path)
992+
993+
994+
@skip_no_xserver
995+
def test_export_glb_cylinder_with_face_color(modeler: Modeler, verify_image_cache):
996+
"""Test exporting a cylinder to glb."""
997+
# Create your design on the server side
998+
design = modeler.create_design("BoxExtrusions")
999+
1000+
# Create a sketch of a circle (overlapping the box slightly)
1001+
sketch_circle = Sketch().circle(Point2D([20, 0], unit=UNITS.m), radius=3 * UNITS.m)
1002+
cyl = design.extrude_sketch("Cylinder", sketch_circle, 50 * UNITS.m)
1003+
1004+
cyl.set_color((255, 0, 0))
1005+
cyl.faces[0].set_color((0, 0, 255))
1006+
cyl.faces[1].set_color((0, 255, 0))
1007+
1008+
pl = GeometryPlotter(use_service_colors=True)
1009+
1010+
output_glb_path = Path(IMAGE_RESULTS_DIR, "plot_cylinder_glb_face_colored")
1011+
pl.export_glb(cyl, screenshot=output_glb_path)

0 commit comments

Comments
 (0)