Assembly hierarchy - 2nd take (#545)
* Reworked nested assy querying * Support subclassing * Added docstring
This commit is contained in:
@ -21,15 +21,26 @@ AssemblyObjects = Union[Shape, Workplane, None]
|
|||||||
ConstraintKinds = Literal["Plane", "Point", "Axis"]
|
ConstraintKinds = Literal["Plane", "Point", "Axis"]
|
||||||
ExportLiterals = Literal["STEP", "XML"]
|
ExportLiterals = Literal["STEP", "XML"]
|
||||||
|
|
||||||
|
PATH_DELIM = "/"
|
||||||
|
|
||||||
# enitity selector grammar definiiton
|
# enitity selector grammar definiiton
|
||||||
def _define_grammar():
|
def _define_grammar():
|
||||||
|
|
||||||
from pyparsing import Literal as Literal, Word, Optional, alphas, alphanums
|
from pyparsing import (
|
||||||
|
Literal as Literal,
|
||||||
|
Word,
|
||||||
|
Optional,
|
||||||
|
alphas,
|
||||||
|
alphanums,
|
||||||
|
delimitedList,
|
||||||
|
)
|
||||||
|
|
||||||
Separator = Literal("@").suppress()
|
Separator = Literal("@").suppress()
|
||||||
TagSeparator = Literal("?").suppress()
|
TagSeparator = Literal("?").suppress()
|
||||||
|
|
||||||
Name = Word(alphas, alphanums + "_").setResultsName("name")
|
Name = delimitedList(
|
||||||
|
Word(alphas, alphanums + "_"), PATH_DELIM, combine=True
|
||||||
|
).setResultsName("name")
|
||||||
Tag = Word(alphas, alphanums + "_").setResultsName("tag")
|
Tag = Word(alphas, alphanums + "_").setResultsName("tag")
|
||||||
Selector = _selector_grammar.setResultsName("selector")
|
Selector = _selector_grammar.setResultsName("selector")
|
||||||
|
|
||||||
@ -234,6 +245,11 @@ class Assembly(object):
|
|||||||
|
|
||||||
if isinstance(arg, Assembly):
|
if isinstance(arg, Assembly):
|
||||||
|
|
||||||
|
# enforce unique names
|
||||||
|
name = kwargs["name"] if kwargs.get("name") else arg.name
|
||||||
|
if name in self.objects:
|
||||||
|
raise ValueError("Unique name is required")
|
||||||
|
|
||||||
subassy = arg._copy()
|
subassy = arg._copy()
|
||||||
|
|
||||||
subassy.loc = kwargs["loc"] if kwargs.get("loc") else arg.loc
|
subassy.loc = kwargs["loc"] if kwargs.get("loc") else arg.loc
|
||||||
@ -242,11 +258,10 @@ class Assembly(object):
|
|||||||
subassy.parent = self
|
subassy.parent = self
|
||||||
|
|
||||||
self.children.append(subassy)
|
self.children.append(subassy)
|
||||||
self.objects[subassy.name] = subassy
|
self.objects.update(subassy._flatten())
|
||||||
self.objects.update(subassy.objects)
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
assy = Assembly(arg, **kwargs)
|
assy = self.__class__(arg, **kwargs)
|
||||||
assy.parent = self
|
assy.parent = self
|
||||||
|
|
||||||
self.add(assy)
|
self.add(assy)
|
||||||
@ -454,3 +469,17 @@ class Assembly(object):
|
|||||||
yield el
|
yield el
|
||||||
|
|
||||||
yield (self.name, self)
|
yield (self.name, self)
|
||||||
|
|
||||||
|
def _flatten(self, parents=[]):
|
||||||
|
"""
|
||||||
|
Generate a dict with all ancestors with keys indicating parent-child relations.
|
||||||
|
"""
|
||||||
|
|
||||||
|
rv = {}
|
||||||
|
|
||||||
|
for ch in self.children:
|
||||||
|
rv.update(ch._flatten(parents=parents + [self.name]))
|
||||||
|
|
||||||
|
rv[PATH_DELIM.join(parents + [self.name])] = self
|
||||||
|
|
||||||
|
return rv
|
||||||
|
|||||||
@ -153,8 +153,8 @@ def test_constrain(simple_assy, nested_assy):
|
|||||||
|
|
||||||
assert len(simple_assy.constraints) == 3
|
assert len(simple_assy.constraints) == 3
|
||||||
|
|
||||||
nested_assy.constrain("TOP@faces@>Z", "BOTTOM@faces@<Z", "Plane")
|
nested_assy.constrain("TOP@faces@>Z", "SECOND/BOTTOM@faces@<Z", "Plane")
|
||||||
nested_assy.constrain("TOP@faces@>X", "BOTTOM@faces@<X", "Axis")
|
nested_assy.constrain("TOP@faces@>X", "SECOND/BOTTOM@faces@<X", "Axis")
|
||||||
|
|
||||||
assert len(nested_assy.constraints) == 2
|
assert len(nested_assy.constraints) == 2
|
||||||
|
|
||||||
@ -168,7 +168,7 @@ def test_constrain(simple_assy, nested_assy):
|
|||||||
.IsEqual(gp_XYZ(), 1e-9)
|
.IsEqual(gp_XYZ(), 1e-9)
|
||||||
)
|
)
|
||||||
assert constraint.sublocs[1].wrapped.IsEqual(
|
assert constraint.sublocs[1].wrapped.IsEqual(
|
||||||
nested_assy.objects["BOTTOM"].loc.wrapped
|
nested_assy.objects["SECOND/BOTTOM"].loc.wrapped
|
||||||
)
|
)
|
||||||
|
|
||||||
simple_assy.solve()
|
simple_assy.solve()
|
||||||
@ -199,10 +199,16 @@ def test_constrain(simple_assy, nested_assy):
|
|||||||
def test_constrain_with_tags(nested_assy):
|
def test_constrain_with_tags(nested_assy):
|
||||||
|
|
||||||
nested_assy.add(None, name="dummy")
|
nested_assy.add(None, name="dummy")
|
||||||
nested_assy.constrain("TOP?top_face", "BOTTOM", "Plane")
|
nested_assy.constrain("TOP?top_face", "SECOND/BOTTOM", "Plane")
|
||||||
|
|
||||||
assert len(nested_assy.constraints) == 1
|
assert len(nested_assy.constraints) == 1
|
||||||
|
|
||||||
# test selection of a non-shape object
|
# test selection of a non-shape object
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
nested_assy.constrain("BOTTOM ? pts", "dummy", "Plane")
|
nested_assy.constrain("SECOND/BOTTOM ? pts", "dummy", "Plane")
|
||||||
|
|
||||||
|
|
||||||
|
def test_duplicate_name(nested_assy):
|
||||||
|
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
nested_assy.add(None, name="SECOND")
|
||||||
|
|||||||
Reference in New Issue
Block a user