Skip to content

Commit 3032e0f

Browse files
authored
Merge pull request #1083 from CadQuery/step
Added quality controls to STEP export for shape and assembly
2 parents 8d10631 + bda8995 commit 3032e0f

File tree

7 files changed

+98
-13
lines changed

7 files changed

+98
-13
lines changed

cadquery/assembly.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -440,14 +440,17 @@ def save(
440440
exportType: Optional[ExportLiterals] = None,
441441
tolerance: float = 0.1,
442442
angularTolerance: float = 0.1,
443+
**kwargs,
443444
) -> "Assembly":
444445
"""
445-
save as STEP or OCCT native XML file
446+
Save assembly to a file.
446447
447-
:param path: filepath
448+
:param path: Path and filename for writing.
448449
:param exportType: export format (default: None, results in format being inferred form the path)
449450
:param tolerance: the deflection tolerance, in model units. Only used for GLTF, VRML. Default 0.1.
450451
:param angularTolerance: the angular tolerance, in radians. Only used for GLTF, VRML. Default 0.1.
452+
:param **kwargs: Additional keyword arguments. Only used for STEP.
453+
See :meth:`~cadquery.occ_impl.exporters.assembly.exportAssembly`.
451454
"""
452455

453456
if exportType is None:
@@ -458,7 +461,7 @@ def save(
458461
raise ValueError("Unknown extension, specify export type explicitly")
459462

460463
if exportType == "STEP":
461-
exportAssembly(self, path)
464+
exportAssembly(self, path, **kwargs)
462465
elif exportType == "XML":
463466
exportCAF(self, path)
464467
elif exportType == "VRML":

cadquery/occ_impl/exporters/__init__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,10 @@ def export(
100100
raise ValueError("Only Workplanes can be exported as DXF")
101101

102102
elif exportType == ExportTypes.STEP:
103-
shape.exportStep(fname)
103+
if opt:
104+
shape.exportStep(fname, **opt)
105+
else:
106+
shape.exportStep(fname)
104107

105108
elif exportType == ExportTypes.STL:
106109
shape.exportStl(fname, tolerance, angularTolerance)

cadquery/occ_impl/exporters/assembly.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,22 +21,43 @@
2121
from OCP.RWGltf import RWGltf_CafWriter
2222
from OCP.TColStd import TColStd_IndexedDataMapOfStringString
2323
from OCP.Message import Message_ProgressRange
24+
from OCP.Interface import Interface_Static
2425

2526
from ..assembly import AssemblyProtocol, toCAF, toVTK
2627

2728

28-
def exportAssembly(assy: AssemblyProtocol, path: str) -> bool:
29+
def exportAssembly(assy: AssemblyProtocol, path: str, **kwargs) -> bool:
2930
"""
30-
Export an assembly to a step a file.
31+
Export an assembly to a STEP file.
32+
33+
kwargs is used to provide optional keyword arguments to configure the exporter.
34+
35+
:param assy: assembly
36+
:param path: Path and filename for writing
37+
:param write_pcurves: Enable or disable writing parametric curves to the STEP file. Default True.
38+
39+
If False, writes STEP file without pcurves. This decreases the size of the resulting STEP file.
40+
:type write_pcurves: boolean
41+
:param precision_mode: Controls the uncertainty value for STEP entities. Specify -1, 0, or 1. Default 0.
42+
See OCCT documentation.
43+
:type precision_mode: int
3144
"""
3245

46+
# Handle the extra settings for the STEP export
47+
pcurves = 1
48+
if "write_pcurves" in kwargs and not kwargs["write_pcurves"]:
49+
pcurves = 0
50+
precision_mode = kwargs["precision_mode"] if "precision_mode" in kwargs else 0
51+
3352
_, doc = toCAF(assy, True)
3453

3554
session = XSControl_WorkSession()
3655
writer = STEPCAFControl_Writer(session, False)
3756
writer.SetColorMode(True)
3857
writer.SetLayerMode(True)
3958
writer.SetNameMode(True)
59+
Interface_Static.SetIVal_s("write.surfacecurve.mode", pcurves)
60+
Interface_Static.SetIVal_s("write.precision.mode", precision_mode)
4061
writer.Transfer(doc, STEPControl_StepModelType.STEPControl_AsIs)
4162

4263
status = writer.Write(path)

cadquery/occ_impl/shapes.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,8 @@
230230
# for catching exceptions
231231
from OCP.Standard import Standard_NoSuchObject, Standard_Failure
232232

233+
from OCP.Interface import Interface_Static
234+
233235
from math import pi, sqrt
234236
import warnings
235237

@@ -449,12 +451,31 @@ def exportStl(
449451

450452
return writer.Write(self.wrapped, fileName)
451453

452-
def exportStep(self, fileName: str) -> IFSelect_ReturnStatus:
454+
def exportStep(self, fileName: str, **kwargs) -> IFSelect_ReturnStatus:
453455
"""
454-
Export this shape to a STEP file
456+
Export this shape to a STEP file.
457+
458+
kwargs is used to provide optional keyword arguments to configure the exporter.
459+
460+
:param fileName: Path and filename for writing.
461+
:param write_pcurves: Enable or disable writing parametric curves to the STEP file. Default True.
462+
463+
If False, writes STEP file without pcurves. This decreases the size of the resulting STEP file.
464+
:type write_pcurves: boolean
465+
:param precision_mode: Controls the uncertainty value for STEP entities. Specify -1, 0, or 1. Default 0.
466+
See OCCT documentation.
467+
:type precision_mode: int
455468
"""
456469

470+
# Handle the extra settings for the STEP export
471+
pcurves = 1
472+
if "write_pcurves" in kwargs and not kwargs["write_pcurves"]:
473+
pcurves = 0
474+
precision_mode = kwargs["precision_mode"] if "precision_mode" in kwargs else 0
475+
457476
writer = STEPControl_Writer()
477+
Interface_Static.SetIVal_s("write.surfacecurve.mode", pcurves)
478+
Interface_Static.SetIVal_s("write.precision.mode", precision_mode)
458479
writer.Transfer(self.wrapped, STEPControl_AsIs)
459480

460481
return writer.Write(fileName)

doc/classreference.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,3 +95,7 @@ Class Details
9595
.. automodule:: cadquery.selectors
9696
:show-inheritance:
9797
:members:
98+
99+
.. automodule:: cadquery.occ_impl.exporters.assembly
100+
:show-inheritance:
101+
:members:

tests/test_assembly.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -179,17 +179,29 @@ def test_assembly(simple_assy, nested_assy):
179179
assert kvs[-1][0] == "TOP"
180180

181181

182-
def test_step_export(nested_assy):
183-
184-
exportAssembly(nested_assy, "nested.step")
182+
def test_step_export(nested_assy, tmp_path_factory):
183+
# Use a temporary directory
184+
tmpdir = tmp_path_factory.mktemp("out")
185+
nested_path = os.path.join(tmpdir, "nested.step")
186+
nested_options_path = os.path.join(tmpdir, "nested_options.step")
187+
188+
exportAssembly(nested_assy, nested_path)
189+
exportAssembly(
190+
nested_assy, nested_options_path, write_pcurves=False, precision_mode=0
191+
)
185192

186-
w = cq.importers.importStep("nested.step")
193+
w = cq.importers.importStep(nested_path)
194+
o = cq.importers.importStep(nested_options_path)
187195
assert w.solids().size() == 4
196+
assert o.solids().size() == 4
188197

189198
# check that locations were applied correctly
190199
c = cq.Compound.makeCompound(w.solids().vals()).Center()
191200
c.toTuple()
192201
assert pytest.approx(c.toTuple()) == (0, 4, 0)
202+
c2 = cq.Compound.makeCompound(o.solids().vals()).Center()
203+
c2.toTuple()
204+
assert pytest.approx(c2.toTuple()) == (0, 4, 0)
193205

194206

195207
def test_native_export(simple_assy):

tests/test_exporters.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Tests basic workplane functionality
33
"""
44
# core modules
5-
import sys
5+
import os
66
import io
77
from pathlib import Path
88
import re
@@ -15,6 +15,27 @@
1515
from OCP.BRepBuilderAPI import BRepBuilderAPI_MakeEdge
1616

1717

18+
def test_step_options(tmp_path_factory):
19+
"""
20+
Exports a box using the options to decrease STEP file size and
21+
then imports that STEP to validate it.
22+
"""
23+
# Use a temporary directory
24+
tmpdir = tmp_path_factory.mktemp("out")
25+
box_path = os.path.join(tmpdir, "out.step")
26+
27+
# Simple object to export
28+
box = Workplane().box(1, 1, 1)
29+
30+
# Export the STEP with the size-saving options and then import it back in
31+
box.val().exportStep(box_path, write_pcurves=False, precision_mode=0)
32+
w = importers.importStep(box_path)
33+
34+
# Make sure there was a valid box in the exported file
35+
assert w.solids().size() == 1
36+
assert w.faces().size() == 6
37+
38+
1839
class TestExporters(BaseTest):
1940
def _exportBox(self, eType, stringsToFind, tolerance=0.1, angularTolerance=0.1):
2041
"""

0 commit comments

Comments
 (0)