Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
3702b86
Added unit handling to STEP import and export
jmwright Mar 30, 2026
a93c70d
Update docs for units changes
jmwright Mar 30, 2026
ca8fd22
Added notes about a scaling gotcha
jmwright Mar 30, 2026
cd88632
Added outputUnit parameter for export and changed unit to be the inte…
jmwright Mar 31, 2026
8f6d395
Modified one test to prove that the outputUnit setting has the intend…
jmwright Mar 31, 2026
a9e4ab5
Updated the docs for the new outputUnit parameter
jmwright Mar 31, 2026
f80d65d
Accept mm form as well as MM
jmwright Apr 5, 2026
d4c1aaf
Support mm form as well as MM
jmwright Apr 5, 2026
6eb0745
Update doc/importexport.rst
jmwright Apr 5, 2026
cf7062a
Update cadquery/occ_impl/shapes.py
jmwright Apr 6, 2026
1b804c1
Finish changes to make sure that 'mm' and 'MM' are treated the same
jmwright Apr 9, 2026
94acf6f
Changes based on suggestions by Adam
jmwright Apr 10, 2026
1518b86
Update cadquery/occ_impl/types.py
jmwright Apr 17, 2026
76b7723
Updated all references to STEPUnitLiterals to UnitLiterals
jmwright Apr 17, 2026
d01db3b
Merge branch 'master' of github.com:CadQuery/cadquery into unit-impor…
jmwright May 7, 2026
d7bc061
Seeing if pinning viskores fixes AppVeyor CI
jmwright May 7, 2026
101a616
Removing viskores pin since it did not help
jmwright May 7, 2026
4ddc02c
Try again with viskores 1.1.0
adam-urbanczyk May 8, 2026
eb9392e
Remove temp pin
adam-urbanczyk May 8, 2026
27e6438
Merge branch 'master' of github.com:CadQuery/cadquery into unit-impor…
jmwright May 8, 2026
992d247
Merge branch 'unit-import-export' of github.com:CadQuery/cadquery int…
jmwright May 8, 2026
f08a746
Add unit to xbf and xml import
adam-urbanczyk May 10, 2026
e7d6ef0
Apply suggestions from code review
adam-urbanczyk May 10, 2026
9162352
Add unit to exportCAF
adam-urbanczyk May 10, 2026
ab54440
Apply suggestion
adam-urbanczyk May 10, 2026
9854d9e
Apply suggestion
adam-urbanczyk May 10, 2026
840195c
Black fix
adam-urbanczyk May 11, 2026
060f649
Rework units handling
adam-urbanczyk May 12, 2026
06f6c58
Add progression test
adam-urbanczyk May 12, 2026
6a7fcf5
Apply suggestions from code review
adam-urbanczyk May 13, 2026
7b8540d
Fix deletion
adam-urbanczyk May 13, 2026
107f3e2
Reorganize types
adam-urbanczyk May 13, 2026
7fb0e1a
Fix tests
adam-urbanczyk May 13, 2026
c844cf9
Adjust the docs
adam-urbanczyk May 13, 2026
250032c
Doc tweaks
adam-urbanczyk May 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
build/
__pycache__
*.pyc
doc/_build/*
dist/*
Expand Down
3 changes: 2 additions & 1 deletion cadquery/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
from .sketch import Sketch
from .cq import CQ, Workplane
from .assembly import Assembly, Color, Constraint, Material
from .types import UnitLiterals
from . import selectors
from . import plugins

Expand Down Expand Up @@ -75,6 +76,6 @@
"DirectionMinMaxSelector",
"StringSyntaxSelector",
"Selector",
"plugins",
"Sketch",
"UnitLiterals",
]
34 changes: 25 additions & 9 deletions cadquery/assembly.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
)
from .occ_impl.importers.assembly import importStep as _importStep, importXbf, importXml

from .types import UnitLiterals

from .selectors import _expression_grammar as _selector_grammar
from .utils import deprecate, BiDict, instance_of

Expand Down Expand Up @@ -566,6 +568,8 @@ def export(
mode: STEPExportModeLiterals = "default",
tolerance: float = 0.1,
angularTolerance: float = 0.1,
unit: UnitLiterals = "MM",
outputUnit: Optional[UnitLiterals] = None,
**kwargs,
) -> Self:
"""
Expand All @@ -576,6 +580,12 @@ def export(
:param mode: STEP only - See :meth:`~cadquery.occ_impl.exporters.assembly.exportAssembly`.
:param tolerance: the deflection tolerance, in model units. Only used for glTF, VRML. Default 0.1.
:param angularTolerance: the angular tolerance, in radians. Only used for glTF, VRML. Default 0.1.
:param unit: The internal unit of the model's geometry values. Only used for STEP. Default "MM".
:type unit: UnitLiterals
:param outputUnit: The unit to use in the STEP file header. If None, defaults to the value of ``unit``.
Use this when you want the output file to declare a different unit than the model's internal unit,
for example to export a MM model as a STEP file declaring meters.
:type outputUnit: UnitLiterals or None
:param \\**kwargs: Additional keyword arguments. Only used for STEP, glTF and STL.
See :meth:`~cadquery.occ_impl.exporters.assembly.exportAssembly`.
:param ascii: STL only - Sets whether or not STL export should be text or binary
Expand All @@ -594,11 +604,11 @@ def export(
raise ValueError("Unknown extension, specify export type explicitly")

if exportType == "STEP":
exportAssembly(self, path, mode, **kwargs)
exportAssembly(self, path, mode, unit, outputUnit, **kwargs)
elif exportType == "XML":
exportCAF(self, path)
exportCAF(self, path, False)
elif exportType == "XBF":
exportCAF(self, path, binary=True)
exportCAF(self, path, True)
elif exportType == "VRML":
exportVRML(self, path, tolerance, angularTolerance)
elif exportType == "GLTF" or exportType == "GLB":
Expand All @@ -618,33 +628,39 @@ def export(
return self

@classmethod
def importStep(cls, path: str) -> Self:
def importStep(cls, path: str, unit: UnitLiterals = "MM") -> Self:
"""
Reads an assembly from a STEP file.

:param path: Path and filename for reading.
:param unit: The unit of measurement for the STEP file. Default "MM".
:return: An Assembly object.
"""

return cls.load(path, importType="STEP")
return cls.load(path, importType="STEP", unit=unit)

@classmethod
def load(cls, path: str, importType: Optional[ImportLiterals] = None,) -> Self:
def load(
cls,
path: str,
importType: Optional[ImportLiterals] = None,
unit: UnitLiterals = "MM",
) -> Self:
"""
Load step, xbf or xml.
Load step, xbf or xml. Only STEP supports unit conversion on loading.
"""

if importType is None:
t = path.split(".")[-1].upper()
if t in ("STEP", "XML", "XBF"):
importType = cast(ImportLiterals, t)
else:
raise ValueError("Unknown extension, specify export type explicitly")
raise ValueError("Unknown extension, specify import type explicitly")

assy = cls()

if importType == "STEP":
_importStep(assy, path)
_importStep(assy, path, unit)
elif importType == "XML":
importXml(assy, path)
elif importType == "XBF":
Expand Down
17 changes: 16 additions & 1 deletion cadquery/cq.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
from .occ_impl.exporters import export

from .utils import deprecate, deprecate_kwarg_name, get_arity
from .types import UnitLiterals

from .selectors import (
Selector,
Expand Down Expand Up @@ -4527,6 +4528,8 @@ def export(
fname: str,
tolerance: float = 0.1,
angularTolerance: float = 0.1,
unit: UnitLiterals = "MM",
outputUnit: Optional[UnitLiterals] = None,
opt: Optional[Dict[str, Any]] = None,
) -> T:
"""
Expand All @@ -4535,12 +4538,24 @@ def export(
:param path: Filename.
:param tolerance: the deflection tolerance, in model units. Default 0.1.
:param angularTolerance: the angular tolerance, in radians. Default 0.1.
:param unit: The internal unit of the model's geometry values. Only used for STEP. Default "MM".
:type unit: UnitLiterals
:param outputUnit: The unit to use in the STEP file header. If None, defaults to the value of ``unit``.
Use this when you want the output file to declare a different unit than the model's internal unit,
for example to export a MM model as a STEP file declaring meters.
:type outputUnit: UnitLiterals or None
:param opt: additional options passed to the specific exporter. Default None.
:return: Self.
"""

export(
self, fname, tolerance=tolerance, angularTolerance=angularTolerance, opt=opt
self,
fname,
tolerance=tolerance,
angularTolerance=angularTolerance,
unit=unit,
outputUnit=outputUnit,
opt=opt,
)

return self
Expand Down
11 changes: 10 additions & 1 deletion cadquery/occ_impl/exporters/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

from ...utils import deprecate
from ..shapes import Shape, compound
from ...types import UnitLiterals

from .svg import getSVG
from .json import JsonMesh
Expand Down Expand Up @@ -43,6 +44,8 @@ def export(
exportType: Optional[ExportLiterals] = None,
tolerance: float = 0.1,
angularTolerance: float = 0.1,
unit: UnitLiterals = "MM",
outputUnit: Optional[UnitLiterals] = None,
opt: Optional[Dict[str, Any]] = None,
):

Expand All @@ -54,6 +57,12 @@ def export(
:param exportType: the exportFormat to use. If None will be inferred from the extension. Default: None.
:param tolerance: the deflection tolerance, in model units. Default 0.1.
:param angularTolerance: the angular tolerance, in radians. Default 0.1.
:param unit: The internal unit of the model's geometry values. Only used for STEP. Default "MM".
:type unit: UnitLiterals
:param outputUnit: The unit to use in the STEP file header. If None, defaults to the value of ``unit``.
Use this when you want the output file to declare a different unit than the model's internal unit,
for example to export a MM model as a STEP file declaring meters.
:type outputUnit: UnitLiterals or None
:param opt: additional options passed to the specific exporter. Default None.
"""

Expand Down Expand Up @@ -109,7 +118,7 @@ def export(
exportDXF(w, fname, **opt)

elif exportType == ExportTypes.STEP:
shape.exportStep(fname, **opt)
shape.exportStep(fname, unit=unit, outputUnit=outputUnit, **opt)

elif exportType == ExportTypes.STL:
if opt:
Expand Down
29 changes: 27 additions & 2 deletions cadquery/occ_impl/exporters/assembly.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
from ..assembly import AssemblyProtocol, toCAF, toVTK, toFusedCAF
from ..geom import Location
from ..shapes import Shape, Compound
from ...types import UnitLiterals


class ExportModes:
Expand All @@ -62,6 +63,8 @@ def exportAssembly(
assy: AssemblyProtocol,
path: str,
mode: STEPExportModeLiterals = "default",
unit: UnitLiterals = "MM",
outputUnit: Optional[UnitLiterals] = None,
**kwargs,
) -> bool:
"""
Expand All @@ -73,6 +76,12 @@ def exportAssembly(
:param path: Path and filename for writing
:param mode: STEP export mode. The options are "default", and "fused" (a single fused compound).
It is possible that fused mode may exhibit low performance.
:param unit: The internal unit of the model's geometry values. Default "MM".
:type unit: UnitLiterals
:param outputUnit: The unit to use in the STEP file header. If None, defaults to the value of ``unit``.
Use this when you want the output file to declare a different unit than the model's internal unit,
for example to export a MM model as a STEP file declaring meters.
:type outputUnit: UnitLiterals or None
:param fuzzy_tol: OCCT fuse operation tolerance setting used only for fused assembly export.
:type fuzzy_tol: float
:param glue: Enable gluing mode for improved performance during fused assembly export.
Expand Down Expand Up @@ -103,16 +112,21 @@ def exportAssembly(
if mode == "fused":
_, doc = toFusedCAF(assy, glue, fuzzy_tol)
else: # Includes "default"
_, doc = toCAF(assy, True)
_, doc = toCAF(assy, True,)

session = XSControl_WorkSession()
writer = STEPCAFControl_Writer(session, False)
writer.SetColorMode(True)
writer.SetLayerMode(True)
writer.SetNameMode(True)

Interface_Static.SetIVal_s("write.surfacecurve.mode", pcurves)
Interface_Static.SetIVal_s("write.precision.mode", precision_mode)
Interface_Static.SetIVal_s("write.stepcaf.subshapes.name", 1)
Interface_Static.SetCVal_s("xstep.cascade.unit", unit.upper())
Interface_Static.SetCVal_s(
"write.step.unit", outputUnit if outputUnit is not None else unit.upper()
)
writer.Transfer(doc, STEPControl_StepModelType.STEPControl_AsIs)

if name_geometries:
Expand Down Expand Up @@ -157,6 +171,8 @@ def exportStepMeta(
path: str,
write_pcurves: bool = True,
precision_mode: int = 0,
unit: UnitLiterals = "MM",
outputUnit: Optional[UnitLiterals] = None,
) -> bool:
"""
Export an assembly to a STEP file with faces tagged with names and colors. This is done as a
Expand All @@ -172,6 +188,12 @@ def exportStepMeta(
If False, writes STEP file without pcurves. This decreases the size of the resulting STEP file.
:param precision_mode: Controls the uncertainty value for STEP entities. Specify -1, 0, or 1. Default 0.
See OCCT documentation.
:param unit: The internal unit of the model's geometry values. Default "MM".
:type unit: UnitLiterals
:param outputUnit: The unit to use in the STEP file header. If None, defaults to the value of ``unit``.
Use this when you want the output file to declare a different unit than the model's internal unit,
for example to export a MM model as a STEP file declaring meters.
:type outputUnit: UnitLiterals or None
"""

pcurves = 1
Expand Down Expand Up @@ -311,6 +333,10 @@ def _process_assembly(
writer.SetNameMode(True)
Interface_Static.SetIVal_s("write.surfacecurve.mode", pcurves)
Interface_Static.SetIVal_s("write.precision.mode", precision_mode)
Interface_Static.SetCVal_s("xstep.cascade.unit", unit.upper())
Interface_Static.SetCVal_s(
"write.step.unit", outputUnit if outputUnit is not None else unit.upper()
)
writer.Transfer(doc, STEPControl_StepModelType.STEPControl_AsIs)

status = writer.Write(path)
Expand All @@ -335,7 +361,6 @@ def exportCAF(assy: AssemblyProtocol, path: str, binary: bool = False) -> bool:

# XBF
if binary:
ret = XmlXCAFDrivers_DocumentRetrievalDriver()
format_name = TCollection_AsciiString("BinXCAF")
format_desc = TCollection_AsciiString("Binary XCAF Document")
store = BinXCAFDrivers_DocumentStorageDriver()
Expand Down
20 changes: 17 additions & 3 deletions cadquery/occ_impl/importers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@

import OCP.IFSelect
from OCP.STEPControl import STEPControl_Reader
from OCP.Interface import Interface_Static

from ... import cq
from ..shapes import Shape
from .dxf import _importDXF
from ...types import UnitLiterals

RAD2DEG = 360.0 / (2 * pi)

Expand All @@ -24,18 +26,24 @@ class UNITS:


def importShape(
importType: Literal["STEP", "DXF", "BREP", "BIN"], fileName: str, *args, **kwargs
importType: Literal["STEP", "DXF", "BREP", "BIN"],
fileName: str,
unit: UnitLiterals = "MM",
*args,
**kwargs,
) -> "cq.Workplane":
"""
Imports a file based on the type (STEP, STL, etc)

:param importType: The type of file that we're importing
:param fileName: The name of the file that we're importing
:param unit: The unit of measurement for the STEP file. Default "MM".
:type unit: UnitLiterals
"""

# Check to see what type of file we're working with
if importType == ImportTypes.STEP:
return importStep(fileName)
return importStep(fileName, unit)
elif importType == ImportTypes.DXF:
return importDXF(fileName, *args, **kwargs)
elif importType == ImportTypes.BREP:
Expand Down Expand Up @@ -76,13 +84,19 @@ def importBin(fileName: str) -> "cq.Workplane":


# Loads a STEP file into a CQ.Workplane object
def importStep(fileName: str) -> "cq.Workplane":
def importStep(fileName: str, unit: UnitLiterals = "MM") -> "cq.Workplane":
"""
Accepts a file name and loads the STEP file into a cadquery Workplane

:param fileName: The path and name of the STEP file to be imported
:param unit: Sets the target OpenCASCADE unit - OCCT scales from the file's
declared unit to this unit. Default "MM".
:type unit: UnitLiterals
"""

# Set the target cascade unit - OCCT scales from the file's declared unit to this unit
Interface_Static.SetCVal_s("xstep.cascade.unit", unit.upper())

# Now read and return the shape
reader = STEPControl_Reader()
readStatus = reader.ReadFile(fileName)
Expand Down
Loading