diff --git a/cadquery/assembly.py b/cadquery/assembly.py index ccdd6bbe..494dc53d 100644 --- a/cadquery/assembly.py +++ b/cadquery/assembly.py @@ -12,7 +12,13 @@ from .occ_impl.solver import ( ConstraintMarker, Constraint as ConstraintPOD, ) -from .occ_impl.exporters.assembly import exportAssembly, exportCAF +from .occ_impl.exporters.assembly import ( + exportAssembly, + exportCAF, + exportVTKJS, + exportVRML, + exportGLTF, +) from .selectors import _expression_grammar as _selector_grammar from OCP.BRepTools import BRepTools @@ -22,7 +28,7 @@ from OCP.Precision import Precision # type definitions AssemblyObjects = Union[Shape, Workplane, None] ConstraintKinds = Literal["Plane", "Point", "Axis", "PointInPlane"] -ExportLiterals = Literal["STEP", "XML"] +ExportLiterals = Literal["STEP", "XML", "GLTF", "VTKJS", "VRML"] PATH_DELIM = "/" @@ -462,18 +468,24 @@ class Assembly(object): return self def save( - self, path: str, exportType: Optional[ExportLiterals] = None + self, + path: str, + exportType: Optional[ExportLiterals] = None, + tolerance: float = 0.1, + angularTolerance: float = 0.1, ) -> "Assembly": """ save as STEP or OCCT native XML file :param path: filepath :param exportType: export format (default: None, results in format being inferred form the path) + :param tolerance: the deflection tolerance, in model units. Only used for GLTF. Default 0.1. + :param angularTolerance: the angular tolerance, in radians. Only used for GLTF. Default 0.1. """ if exportType is None: t = path.split(".")[-1].upper() - if t in ("STEP", "XML"): + if t in ("STEP", "XML", "VRML", "VTKJS", "GLTF"): exportType = cast(ExportLiterals, t) else: raise ValueError("Unknown extension, specify export type explicitly") @@ -482,6 +494,12 @@ class Assembly(object): exportAssembly(self, path) elif exportType == "XML": exportCAF(self, path) + elif exportType == "VRML": + exportVRML(self, path) + elif exportType == "GLTF": + exportGLTF(self, path, True, tolerance, angularTolerance) + elif exportType == "VTKJS": + exportVTKJS(self, path) else: raise ValueError(f"Unknown format: {exportType}") diff --git a/cadquery/occ_impl/exporters/assembly.py b/cadquery/occ_impl/exporters/assembly.py index 15402e91..00f0c388 100644 --- a/cadquery/occ_impl/exporters/assembly.py +++ b/cadquery/occ_impl/exporters/assembly.py @@ -2,6 +2,7 @@ import os.path from tempfile import TemporaryDirectory from shutil import make_archive +from itertools import chain from vtkmodules.vtkIOExport import vtkJSONSceneExporter, vtkVRMLExporter from vtkmodules.vtkRenderingCore import vtkRenderer, vtkRenderWindow @@ -17,6 +18,9 @@ from OCP.XmlDrivers import ( ) from OCP.TCollection import TCollection_ExtendedString, TCollection_AsciiString from OCP.PCDM import PCDM_StoreStatus +from OCP.RWGltf import RWGltf_CafWriter +from OCP.TColStd import TColStd_IndexedDataMapOfStringString +from OCP.Message import Message_ProgressRange from ..assembly import AssemblyProtocol, toCAF, toVTK @@ -116,3 +120,27 @@ def exportVRML(assy: AssemblyProtocol, path: str): exporter.SetFileName(path) exporter.SetRenderWindow(_vtkRenderWindow(assy)) exporter.Write() + + +def exportGLTF( + assy: AssemblyProtocol, + path: str, + binary: bool = True, + tolerance: float = 0.1, + angularTolerance: float = 0.1, +): + """ + Export an assembly to a gltf file. + """ + + # mesh all the shapes + for _, el in assy.traverse(): + for s in el.shapes: + s.mesh(tolerance, angularTolerance) + + _, doc = toCAF(assy, True) + + writer = RWGltf_CafWriter(TCollection_AsciiString(path), binary) + return writer.Perform( + doc, TColStd_IndexedDataMapOfStringString(), Message_ProgressRange() + ) diff --git a/tests/test_assembly.py b/tests/test_assembly.py index 7bdaf25f..ca44db04 100644 --- a/tests/test_assembly.py +++ b/tests/test_assembly.py @@ -50,6 +50,22 @@ def nested_assy(): return assy +@pytest.fixture +def nested_assy_sphere(): + + b1 = cq.Workplane().box(1, 1, 1).faces(" 50 * 1024 - simple_assy.save("simple.caf", "XML") - assert os.path.exists("simple.caf") + +def test_save_vtkjs(nested_assy): + + nested_assy.save("nested", "VTKJS") + assert os.path.exists("nested.zip") + + +def test_save_raises(nested_assy): with pytest.raises(ValueError): - simple_assy.save("simple.dxf") + nested_assy.save("nested.dxf") with pytest.raises(ValueError): - simple_assy.save("simple.step", "DXF") + nested_assy.save("nested.step", "DXF") def test_constrain(simple_assy, nested_assy):