From 6e4cfc8bca59e3db3e324cdf0430f879cec6bd41 Mon Sep 17 00:00:00 2001 From: adam-urbanczyk <13981538+adam-urbanczyk@users.noreply.github.com> Date: Fri, 9 Jan 2026 16:33:23 +0100 Subject: [PATCH 1/6] Initial implementation of step geom naming --- cadquery/occ_impl/exporters/assembly.py | 42 ++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/cadquery/occ_impl/exporters/assembly.py b/cadquery/occ_impl/exporters/assembly.py index 4d34342cf..09d486fd0 100644 --- a/cadquery/occ_impl/exporters/assembly.py +++ b/cadquery/occ_impl/exporters/assembly.py @@ -12,6 +12,13 @@ from OCP.XSControl import XSControl_WorkSession from OCP.STEPCAFControl import STEPCAFControl_Writer from OCP.STEPControl import STEPControl_StepModelType +from OCP.STEPConstruct import STEPConstruct +from OCP.StepShape import ( + StepShape_EdgeCurve, + StepShape_AdvancedFace, + StepShape_VertexPoint, +) +from OCP.StepGeom import StepGeom_SurfaceCurve from OCP.IFSelect import IFSelect_ReturnStatus from OCP.TDF import TDF_Label from OCP.TDataStd import TDataStd_Name @@ -28,7 +35,11 @@ ) -from OCP.TCollection import TCollection_ExtendedString, TCollection_AsciiString +from OCP.TCollection import ( + TCollection_ExtendedString, + TCollection_AsciiString, + TCollection_HAsciiString, +) from OCP.PCDM import PCDM_StoreStatus from OCP.RWGltf import RWGltf_CafWriter from OCP.TColStd import TColStd_IndexedDataMapOfStringString @@ -76,6 +87,8 @@ def exportAssembly( :param precision_mode: Controls the uncertainty value for STEP entities. Specify -1, 0, or 1. Default 0. See OCCT documentation. :type precision_mode: int + :param name_geometries: Propagate subshape names to geometric STEP entities. + :type name_geometries: bool """ # Handle the extra settings for the STEP export @@ -85,6 +98,7 @@ def exportAssembly( precision_mode = kwargs["precision_mode"] if "precision_mode" in kwargs else 0 fuzzy_tol = kwargs["fuzzy_tol"] if "fuzzy_tol" in kwargs else None glue = kwargs["glue"] if "glue" in kwargs else False + name_geometries = kwargs.get("name_geometries", False) # Handle the doc differently based on which mode we are using if mode == "fused": @@ -102,6 +116,32 @@ def exportAssembly( Interface_Static.SetIVal_s("write.stepcaf.subshapes.name", 1) writer.Transfer(doc, STEPControl_StepModelType.STEPControl_AsIs) + if name_geometries: + finder = session.TransferWriter().FinderProcess() + + # iterate over all named subshapes + for child in assy.objects.values(): + for subshape, name in child._subshape_names.items(): + # find the topological STEP entity + entity = STEPConstruct.FindEntity_s(finder, subshape.wrapped) + + occ_name = TCollection_HAsciiString(name) + + # rename the underlying geometric entities + if isinstance(entity, StepShape_AdvancedFace): + entity.FaceGeometry().SetName(occ_name) + + elif isinstance(entity, StepShape_EdgeCurve): + geom = entity.EdgeGeometry() + geom.SetName(occ_name) + + # in some cases additianl levels of geometries are present + if isinstance(geom, StepGeom_SurfaceCurve): + geom.Curve3d().SetName(occ_name) + + elif isinstance(entity, StepShape_VertexPoint): + entity.VertexGeometry().SetName(occ_name) + status = writer.Write(path) return status == IFSelect_ReturnStatus.IFSelect_RetDone From f6159ba335e7f7da5808c2aa33da2a3e56ad27a0 Mon Sep 17 00:00:00 2001 From: AU Date: Thu, 15 Jan 2026 08:47:13 +0100 Subject: [PATCH 2/6] Preliminary StepShape_GeometricCurveSet case --- cadquery/occ_impl/exporters/assembly.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cadquery/occ_impl/exporters/assembly.py b/cadquery/occ_impl/exporters/assembly.py index 09d486fd0..94b867473 100644 --- a/cadquery/occ_impl/exporters/assembly.py +++ b/cadquery/occ_impl/exporters/assembly.py @@ -17,6 +17,7 @@ StepShape_EdgeCurve, StepShape_AdvancedFace, StepShape_VertexPoint, + StepShape_GeometricCurveSet ) from OCP.StepGeom import StepGeom_SurfaceCurve from OCP.IFSelect import IFSelect_ReturnStatus @@ -142,6 +143,11 @@ def exportAssembly( elif isinstance(entity, StepShape_VertexPoint): entity.VertexGeometry().SetName(occ_name) + elif isinstance(entity, StepShape_GeometricCurveSet): + crv = entity.ElementsValue(1).Curve() + crv.SetName(occ_name) + crv.BasisCurve().SetName(occ_name) + status = writer.Write(path) return status == IFSelect_ReturnStatus.IFSelect_RetDone From 655d8ca4556b5ca8dce6159ed500e92bf4900eb9 Mon Sep 17 00:00:00 2001 From: adam-urbanczyk <13981538+adam-urbanczyk@users.noreply.github.com> Date: Fri, 16 Jan 2026 16:58:03 +0100 Subject: [PATCH 3/6] Black fix --- cadquery/occ_impl/exporters/assembly.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/cadquery/occ_impl/exporters/assembly.py b/cadquery/occ_impl/exporters/assembly.py index 94b867473..ad25aedd5 100644 --- a/cadquery/occ_impl/exporters/assembly.py +++ b/cadquery/occ_impl/exporters/assembly.py @@ -17,7 +17,7 @@ StepShape_EdgeCurve, StepShape_AdvancedFace, StepShape_VertexPoint, - StepShape_GeometricCurveSet + StepShape_GeometricCurveSet, ) from OCP.StepGeom import StepGeom_SurfaceCurve from OCP.IFSelect import IFSelect_ReturnStatus @@ -144,9 +144,20 @@ def exportAssembly( entity.VertexGeometry().SetName(occ_name) elif isinstance(entity, StepShape_GeometricCurveSet): - crv = entity.ElementsValue(1).Curve() - crv.SetName(occ_name) - crv.BasisCurve().SetName(occ_name) + for i in range(entity.NbElements()): + el = entity.ElementsValue(i + 1) + case = el.CaseNum() + + if case == 1: + pt = el.Point() + pt.SetName(occ_name) + elif case == 2: + crv = el.Curve() + crv.SetName(occ_name) + crv.BasisCurve().SetName(occ_name) + elif case == 3: + srf = el.Surface() + srf.SetName(occ_name) status = writer.Write(path) From e573582129e91c17e8d0360edb575e5d8e47f356 Mon Sep 17 00:00:00 2001 From: adam-urbanczyk <13981538+adam-urbanczyk@users.noreply.github.com> Date: Fri, 16 Jan 2026 18:09:49 +0100 Subject: [PATCH 4/6] Add a test --- cadquery/occ_impl/exporters/assembly.py | 2 +- tests/test_assembly.py | 27 ++++++++++++++++++++++++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/cadquery/occ_impl/exporters/assembly.py b/cadquery/occ_impl/exporters/assembly.py index ad25aedd5..8be5e47bb 100644 --- a/cadquery/occ_impl/exporters/assembly.py +++ b/cadquery/occ_impl/exporters/assembly.py @@ -146,7 +146,7 @@ def exportAssembly( elif isinstance(entity, StepShape_GeometricCurveSet): for i in range(entity.NbElements()): el = entity.ElementsValue(i + 1) - case = el.CaseNum() + case = el.CaseNumber() if case == 1: pt = el.Point() diff --git a/tests/test_assembly.py b/tests/test_assembly.py index 71531cb6c..48db25def 100644 --- a/tests/test_assembly.py +++ b/tests/test_assembly.py @@ -17,7 +17,7 @@ exportVRML, ) from cadquery.occ_impl.assembly import toJSON, toCAF, toFusedCAF -from cadquery.occ_impl.shapes import Face, box, cone, plane, Compound +from cadquery.occ_impl.shapes import Face, box, cone, plane, Compound, segment, vertex from OCP.gp import gp_XYZ from OCP.TDocStd import TDocStd_Document @@ -2440,3 +2440,28 @@ def test_shallow_assy(): with pytest.raises(ValueError): toCAF(cq.Assembly()) + + +def test_name_geometries(tmpdir): + """ + Test name propagation to geometric entities + """ + + assy = cq.Assembly() + + assy.add(box(1,1,1), name="box") + assy.box.addSubshape(assy.box.obj.faces('>Z'), "top_face") + + assy.add(plane(1,1), name="face").face.addSubshape(assy.face.obj, name="plane_") + + assy.add(segment((0,0), (0,1)), name="edge").edge.addSubshape(assy.edge.obj, name="seg_") + + with tmpdir: + assy.save('out.step', name_geometries=True) + + lines = Path('out.step').lines() + + assert len([l for l in lines if "top_face" in l]) == 2 + assert len([l for l in lines if "plane_" in l]) == 2 + assert len([l for l in lines if "seg_" in l]) == 3 + From 1ee2bfb3b74b28ede451ebccfe1d1653fd5db19d Mon Sep 17 00:00:00 2001 From: adam-urbanczyk <13981538+adam-urbanczyk@users.noreply.github.com> Date: Fri, 16 Jan 2026 18:21:29 +0100 Subject: [PATCH 5/6] Black fix of tests --- tests/test_assembly.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/tests/test_assembly.py b/tests/test_assembly.py index 48db25def..7b8c94dff 100644 --- a/tests/test_assembly.py +++ b/tests/test_assembly.py @@ -2449,19 +2449,20 @@ def test_name_geometries(tmpdir): assy = cq.Assembly() - assy.add(box(1,1,1), name="box") - assy.box.addSubshape(assy.box.obj.faces('>Z'), "top_face") + assy.add(box(1, 1, 1), name="box") + assy.box.addSubshape(assy.box.obj.faces(">Z"), "top_face") - assy.add(plane(1,1), name="face").face.addSubshape(assy.face.obj, name="plane_") + assy.add(plane(1, 1), name="face").face.addSubshape(assy.face.obj, name="plane_") - assy.add(segment((0,0), (0,1)), name="edge").edge.addSubshape(assy.edge.obj, name="seg_") + assy.add(segment((0, 0), (0, 1)), name="edge").edge.addSubshape( + assy.edge.obj, name="seg_" + ) with tmpdir: - assy.save('out.step', name_geometries=True) + assy.save("out.step", name_geometries=True) - lines = Path('out.step').lines() + lines = Path("out.step").lines() assert len([l for l in lines if "top_face" in l]) == 2 assert len([l for l in lines if "plane_" in l]) == 2 assert len([l for l in lines if "seg_" in l]) == 3 - From e71953f7c86cf1940d36b19c08228c07ba6892a2 Mon Sep 17 00:00:00 2001 From: adam-urbanczyk <13981538+adam-urbanczyk@users.noreply.github.com> Date: Sat, 17 Jan 2026 13:06:59 +0100 Subject: [PATCH 6/6] Handle trimmed curves properly --- cadquery/occ_impl/exporters/assembly.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/cadquery/occ_impl/exporters/assembly.py b/cadquery/occ_impl/exporters/assembly.py index 8be5e47bb..127f39785 100644 --- a/cadquery/occ_impl/exporters/assembly.py +++ b/cadquery/occ_impl/exporters/assembly.py @@ -19,7 +19,7 @@ StepShape_VertexPoint, StepShape_GeometricCurveSet, ) -from OCP.StepGeom import StepGeom_SurfaceCurve +from OCP.StepGeom import StepGeom_SurfaceCurve, StepGeom_TrimmedCurve from OCP.IFSelect import IFSelect_ReturnStatus from OCP.TDF import TDF_Label from OCP.TDataStd import TDataStd_Name @@ -154,7 +154,11 @@ def exportAssembly( elif case == 2: crv = el.Curve() crv.SetName(occ_name) - crv.BasisCurve().SetName(occ_name) + + # trimmed curves also have a basis curve + if isinstance(crv, StepGeom_TrimmedCurve): + crv.BasisCurve().SetName(occ_name) + elif case == 3: srf = el.Surface() srf.SetName(occ_name)