Skip to content

Commit 82a043a

Browse files
Assembly hierarchy - 2nd take (#545)
* Reworked nested assy querying * Support subclassing * Added docstring
1 parent a576f1d commit 82a043a

File tree

2 files changed

+45
-10
lines changed

2 files changed

+45
-10
lines changed

cadquery/assembly.py

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,26 @@
2121
ConstraintKinds = Literal["Plane", "Point", "Axis"]
2222
ExportLiterals = Literal["STEP", "XML"]
2323

24+
PATH_DELIM = "/"
25+
2426
# enitity selector grammar definiiton
2527
def _define_grammar():
2628

27-
from pyparsing import Literal as Literal, Word, Optional, alphas, alphanums
29+
from pyparsing import (
30+
Literal as Literal,
31+
Word,
32+
Optional,
33+
alphas,
34+
alphanums,
35+
delimitedList,
36+
)
2837

2938
Separator = Literal("@").suppress()
3039
TagSeparator = Literal("?").suppress()
3140

32-
Name = Word(alphas, alphanums + "_").setResultsName("name")
41+
Name = delimitedList(
42+
Word(alphas, alphanums + "_"), PATH_DELIM, combine=True
43+
).setResultsName("name")
3344
Tag = Word(alphas, alphanums + "_").setResultsName("tag")
3445
Selector = _selector_grammar.setResultsName("selector")
3546

@@ -234,6 +245,11 @@ def add(self, arg, **kwargs):
234245

235246
if isinstance(arg, Assembly):
236247

248+
# enforce unique names
249+
name = kwargs["name"] if kwargs.get("name") else arg.name
250+
if name in self.objects:
251+
raise ValueError("Unique name is required")
252+
237253
subassy = arg._copy()
238254

239255
subassy.loc = kwargs["loc"] if kwargs.get("loc") else arg.loc
@@ -242,11 +258,10 @@ def add(self, arg, **kwargs):
242258
subassy.parent = self
243259

244260
self.children.append(subassy)
245-
self.objects[subassy.name] = subassy
246-
self.objects.update(subassy.objects)
261+
self.objects.update(subassy._flatten())
247262

248263
else:
249-
assy = Assembly(arg, **kwargs)
264+
assy = self.__class__(arg, **kwargs)
250265
assy.parent = self
251266

252267
self.add(assy)
@@ -454,3 +469,17 @@ def traverse(self) -> Iterator[Tuple[str, "Assembly"]]:
454469
yield el
455470

456471
yield (self.name, self)
472+
473+
def _flatten(self, parents=[]):
474+
"""
475+
Generate a dict with all ancestors with keys indicating parent-child relations.
476+
"""
477+
478+
rv = {}
479+
480+
for ch in self.children:
481+
rv.update(ch._flatten(parents=parents + [self.name]))
482+
483+
rv[PATH_DELIM.join(parents + [self.name])] = self
484+
485+
return rv

tests/test_assembly.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,8 @@ def test_constrain(simple_assy, nested_assy):
153153

154154
assert len(simple_assy.constraints) == 3
155155

156-
nested_assy.constrain("TOP@faces@>Z", "BOTTOM@faces@<Z", "Plane")
157-
nested_assy.constrain("TOP@faces@>X", "BOTTOM@faces@<X", "Axis")
156+
nested_assy.constrain("TOP@faces@>Z", "SECOND/BOTTOM@faces@<Z", "Plane")
157+
nested_assy.constrain("TOP@faces@>X", "SECOND/BOTTOM@faces@<X", "Axis")
158158

159159
assert len(nested_assy.constraints) == 2
160160

@@ -168,7 +168,7 @@ def test_constrain(simple_assy, nested_assy):
168168
.IsEqual(gp_XYZ(), 1e-9)
169169
)
170170
assert constraint.sublocs[1].wrapped.IsEqual(
171-
nested_assy.objects["BOTTOM"].loc.wrapped
171+
nested_assy.objects["SECOND/BOTTOM"].loc.wrapped
172172
)
173173

174174
simple_assy.solve()
@@ -199,10 +199,16 @@ def test_constrain(simple_assy, nested_assy):
199199
def test_constrain_with_tags(nested_assy):
200200

201201
nested_assy.add(None, name="dummy")
202-
nested_assy.constrain("TOP?top_face", "BOTTOM", "Plane")
202+
nested_assy.constrain("TOP?top_face", "SECOND/BOTTOM", "Plane")
203203

204204
assert len(nested_assy.constraints) == 1
205205

206206
# test selection of a non-shape object
207207
with pytest.raises(ValueError):
208-
nested_assy.constrain("BOTTOM ? pts", "dummy", "Plane")
208+
nested_assy.constrain("SECOND/BOTTOM ? pts", "dummy", "Plane")
209+
210+
211+
def test_duplicate_name(nested_assy):
212+
213+
with pytest.raises(ValueError):
214+
nested_assy.add(None, name="SECOND")

0 commit comments

Comments
 (0)