From f688133bfb990628a95f53ed32a76b5322478d60 Mon Sep 17 00:00:00 2001 From: adam-urbanczyk <13981538+adam-urbanczyk@users.noreply.github.com> Date: Thu, 16 Jul 2026 21:07:51 +0200 Subject: [PATCH 1/6] Better imprint for assy --- cadquery/occ_impl/assembly.py | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/cadquery/occ_impl/assembly.py b/cadquery/occ_impl/assembly.py index d2485e612..8fabd162b 100644 --- a/cadquery/occ_impl/assembly.py +++ b/cadquery/occ_impl/assembly.py @@ -37,7 +37,7 @@ ) from OCP.BRepAlgoAPI import BRepAlgoAPI_Fuse from OCP.TopTools import TopTools_ListOfShape -from OCP.BOPAlgo import BOPAlgo_GlueEnum, BOPAlgo_MakeConnected +from OCP.BOPAlgo import BOPAlgo_GlueEnum, BOPAlgo_Builder from OCP.TopoDS import TopoDS_Shape from OCP.gp import gp_EulerSequence @@ -49,7 +49,7 @@ ) from .geom import Location -from .shapes import Shape, Solid, Compound +from .shapes import Shape, Solid, Compound, GlueLiteral, _set_glue, _set_builder_options from .exporters.vtk import toString, extractEdgesFaces from ..cq import Workplane from ..utils import BiDict @@ -855,7 +855,9 @@ def toFusedCAF( return top_level_lbl, doc -def imprint(assy: AssemblyProtocol) -> Tuple[Shape, Dict[Shape, Tuple[str, ...]]]: +def imprint( + assy: AssemblyProtocol, tol: float = 0.0, glue: GlueLiteral = "full", +) -> Tuple[Shape, Dict[Shape, Tuple[str, ...]]]: """ Imprint all the solids and construct a dictionary mapping imprinted solids to names from the input assy. """ @@ -868,22 +870,28 @@ def imprint(assy: AssemblyProtocol) -> Tuple[Shape, Dict[Shape, Tuple[str, ...]] id_map[s] = name # connect topologically - bldr = BOPAlgo_MakeConnected() - bldr.SetRunParallel(True) - bldr.SetUseOBB(True) + builder = BOPAlgo_Builder() + + _set_glue(builder, glue) + _set_builder_options(builder, tol) for obj in id_map: - bldr.AddArgument(obj.wrapped) + builder.AddArgument(obj.wrapped) - bldr.Perform() - res = Shape(bldr.Shape()) + builder.Perform() + res = Shape(builder.Shape()) # make the connected solid -> id map + ocp_origins = builder.Origins() origins: Dict[Shape, Tuple[str, ...]] = {} for s in res.Solids(): - ids = tuple(id_map[Solid(el)] for el in bldr.GetOrigins(s.wrapped)) - # if GetOrigins yields nothing, solid was not modified + if ocp_origins.IsBound(s.wrapped): + # if GetOrigins yields nothing, solid was not modified + ids = tuple(id_map[Solid(el)] for el in ocp_origins.Find(s.wrapped)) + else: + ids = () + origins[s] = ids if ids else (id_map[s],) return res, origins From a4acc30c190525614c1ecfcd6c6d5944c1c18532 Mon Sep 17 00:00:00 2001 From: adam-urbanczyk <13981538+adam-urbanczyk@users.noreply.github.com> Date: Fri, 17 Jul 2026 11:26:38 +0200 Subject: [PATCH 2/6] Typing fix --- cadquery/occ_impl/nurbs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cadquery/occ_impl/nurbs.py b/cadquery/occ_impl/nurbs.py index 4a958d35f..9f77b1949 100644 --- a/cadquery/occ_impl/nurbs.py +++ b/cadquery/occ_impl/nurbs.py @@ -357,7 +357,7 @@ def isoline(self, param: float, dir: Literal["u", "v"] = "u") -> Curve: @njiti def _preprocess( u: Array, order: int, knots: Array, periodic: bool -) -> Tuple[Array, Array, Optional[int], Optional[int], int]: +) -> Tuple[Array, Array, int, int, int]: """ Helper for handling periodicity. This function extends the knot vector, wraps the parameters and calculates the delta span. From bd53fc8d6bb971f40ac0ebf9699f514f15d0ec44 Mon Sep 17 00:00:00 2001 From: adam-urbanczyk <13981538+adam-urbanczyk@users.noreply.github.com> Date: Fri, 17 Jul 2026 15:17:19 +0200 Subject: [PATCH 3/6] Add a note about glue and threads. --- cadquery/occ_impl/assembly.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cadquery/occ_impl/assembly.py b/cadquery/occ_impl/assembly.py index 8fabd162b..eb22cc811 100644 --- a/cadquery/occ_impl/assembly.py +++ b/cadquery/occ_impl/assembly.py @@ -860,6 +860,9 @@ def imprint( ) -> Tuple[Shape, Dict[Shape, Tuple[str, ...]]]: """ Imprint all the solids and construct a dictionary mapping imprinted solids to names from the input assy. + + Depending on the use case, it might be required do use different `glue` option. Moreover, for large models + it might be needed to limit the number of threads using :meth:`cadquery.func.setThreads` """ # make the id map From f66dbfb442fd6acd4202b5f418b757f976056c7b Mon Sep 17 00:00:00 2001 From: adam-urbanczyk <13981538+adam-urbanczyk@users.noreply.github.com> Date: Fri, 24 Jul 2026 13:52:17 +0200 Subject: [PATCH 4/6] Tweak comments and docstrings --- cadquery/occ_impl/assembly.py | 2 +- cadquery/occ_impl/shapes.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/cadquery/occ_impl/assembly.py b/cadquery/occ_impl/assembly.py index eb22cc811..ad991d6d4 100644 --- a/cadquery/occ_impl/assembly.py +++ b/cadquery/occ_impl/assembly.py @@ -890,7 +890,7 @@ def imprint( for s in res.Solids(): if ocp_origins.IsBound(s.wrapped): - # if GetOrigins yields nothing, solid was not modified + # if ocp_origins does not contain the solid, it was not modified ids = tuple(id_map[Solid(el)] for el in ocp_origins.Find(s.wrapped)) else: ids = () diff --git a/cadquery/occ_impl/shapes.py b/cadquery/occ_impl/shapes.py index 7d3c665a9..d20c17c8b 100644 --- a/cadquery/occ_impl/shapes.py +++ b/cadquery/occ_impl/shapes.py @@ -6962,6 +6962,9 @@ def imprint( ) -> Shape: """ Imprint arbitrary number of shapes. + + Depending on the use case, it might be required do use different `glue` option. Moreover, for large models + it might be needed to limit the number of threads using :meth:`cadquery.func.setThreads` """ builder = BOPAlgo_Builder() From 4d80f4c5ed509faed0b3099684cd43742e1368db Mon Sep 17 00:00:00 2001 From: adam-urbanczyk <13981538+adam-urbanczyk@users.noreply.github.com> Date: Fri, 24 Jul 2026 14:08:29 +0200 Subject: [PATCH 5/6] Add a test for subassy imprinting --- tests/test_assembly.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/test_assembly.py b/tests/test_assembly.py index 08d4a5f21..35da309da 100644 --- a/tests/test_assembly.py +++ b/tests/test_assembly.py @@ -10,6 +10,8 @@ from pytest import approx import cadquery as cq + +from cadquery import Location from cadquery.occ_impl.exporters.assembly import ( exportAssembly, exportStepMeta, @@ -2324,6 +2326,16 @@ def touching_assy(): return cq.Assembly().add(b1).add(b2) +@pytest.fixture +def complex_assy(touching_assy): + + return ( + cq.Assembly() + .add(touching_assy, name="sub1") + .add(touching_assy, loc=Location((5, 0, 0)), name="sub2") + ) + + @pytest.fixture def disjoint_assy(): @@ -2354,6 +2366,26 @@ def test_imprinting(touching_assy, disjoint_assy): assert s in o +def test_subassy_imprinting(complex_assy): + + # imprint subassys separately + res1, origins1 = cq.occ_impl.assembly.imprint(complex_assy.sub1) + res2, origins2 = cq.occ_impl.assembly.imprint(complex_assy.sub2) + + # reference one step imprint + ref, _ = cq.occ_impl.assembly.imprint(complex_assy) + + # combine the results + res = res1 | res2 + origins = origins1 | origins2 + + assert len(res.Solids()) == len(ref.Solids()) + assert len(res.Faces()) == len(ref.Faces()) + + for s in res.Solids(): + assert s in origins + + def test_order_of_transform(): part = cq.Workplane().box(1, 1, 1).faces(">Z").vertices(" Date: Fri, 24 Jul 2026 14:20:54 +0200 Subject: [PATCH 6/6] Changed the default option to partial --- cadquery/occ_impl/assembly.py | 2 +- cadquery/occ_impl/shapes.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cadquery/occ_impl/assembly.py b/cadquery/occ_impl/assembly.py index ad991d6d4..b00083d40 100644 --- a/cadquery/occ_impl/assembly.py +++ b/cadquery/occ_impl/assembly.py @@ -856,7 +856,7 @@ def toFusedCAF( def imprint( - assy: AssemblyProtocol, tol: float = 0.0, glue: GlueLiteral = "full", + assy: AssemblyProtocol, tol: float = 0.0, glue: GlueLiteral = "partial", ) -> Tuple[Shape, Dict[Shape, Tuple[str, ...]]]: """ Imprint all the solids and construct a dictionary mapping imprinted solids to names from the input assy. diff --git a/cadquery/occ_impl/shapes.py b/cadquery/occ_impl/shapes.py index d20c17c8b..c2f2420ce 100644 --- a/cadquery/occ_impl/shapes.py +++ b/cadquery/occ_impl/shapes.py @@ -6956,7 +6956,7 @@ def enclose( def imprint( *shapes: Shape, tol: float = 0.0, - glue: GlueLiteral = "full", + glue: GlueLiteral = "partial", history: History | None = None, name: str | None = None, ) -> Shape: