[Library] fix dedup()
- use consistent deduplicated target name - remove shape indices per dedup
This commit is contained in:
parent
2b29e46b93
commit
462a05a665
2 changed files with 57 additions and 3 deletions
|
|
@ -1037,6 +1037,18 @@ class ILibrary(ILibraryView, MutableMapping[str, 'Pattern'], metaclass=ABCMeta):
|
||||||
def label2name(label: tuple) -> str: # noqa: ARG001
|
def label2name(label: tuple) -> str: # noqa: ARG001
|
||||||
return self.get_name(SINGLE_USE_PREFIX + 'shape')
|
return self.get_name(SINGLE_USE_PREFIX + 'shape')
|
||||||
|
|
||||||
|
used_names = set(self.keys())
|
||||||
|
|
||||||
|
def reserve_target_name(label: tuple) -> str:
|
||||||
|
base_name = label2name(label)
|
||||||
|
name = base_name
|
||||||
|
ii = sum(1 for nn in used_names if nn.startswith(base_name)) if base_name in used_names else 0
|
||||||
|
while name in used_names or name == '':
|
||||||
|
name = base_name + b64suffix(ii)
|
||||||
|
ii += 1
|
||||||
|
used_names.add(name)
|
||||||
|
return name
|
||||||
|
|
||||||
shape_counts: MutableMapping[tuple, int] = defaultdict(int)
|
shape_counts: MutableMapping[tuple, int] = defaultdict(int)
|
||||||
shape_funcs = {}
|
shape_funcs = {}
|
||||||
|
|
||||||
|
|
@ -1053,6 +1065,7 @@ class ILibrary(ILibraryView, MutableMapping[str, 'Pattern'], metaclass=ABCMeta):
|
||||||
shape_counts[label] += 1
|
shape_counts[label] += 1
|
||||||
|
|
||||||
shape_pats = {}
|
shape_pats = {}
|
||||||
|
target_names = {}
|
||||||
for label, count in shape_counts.items():
|
for label, count in shape_counts.items():
|
||||||
if count < threshold:
|
if count < threshold:
|
||||||
continue
|
continue
|
||||||
|
|
@ -1061,6 +1074,7 @@ class ILibrary(ILibraryView, MutableMapping[str, 'Pattern'], metaclass=ABCMeta):
|
||||||
shape_pat = Pattern()
|
shape_pat = Pattern()
|
||||||
shape_pat.shapes[label[-1]] += [shape_func()]
|
shape_pat.shapes[label[-1]] += [shape_func()]
|
||||||
shape_pats[label] = shape_pat
|
shape_pats[label] = shape_pat
|
||||||
|
target_names[label] = reserve_target_name(label)
|
||||||
|
|
||||||
# ## Second pass ##
|
# ## Second pass ##
|
||||||
for pat in tuple(self.values()):
|
for pat in tuple(self.values()):
|
||||||
|
|
@ -1085,10 +1099,10 @@ class ILibrary(ILibraryView, MutableMapping[str, 'Pattern'], metaclass=ABCMeta):
|
||||||
# For repeated shapes, create a `Pattern` holding a normalized shape object,
|
# For repeated shapes, create a `Pattern` holding a normalized shape object,
|
||||||
# and add `pat.refs` entries for each occurrence in pat. Also, note down that
|
# and add `pat.refs` entries for each occurrence in pat. Also, note down that
|
||||||
# we should delete the `pat.shapes` entries for which we made `Ref`s.
|
# we should delete the `pat.shapes` entries for which we made `Ref`s.
|
||||||
shapes_to_remove = []
|
|
||||||
for label, shape_entries in shape_table.items():
|
for label, shape_entries in shape_table.items():
|
||||||
layer = label[-1]
|
layer = label[-1]
|
||||||
target = label2name(label)
|
target = target_names[label]
|
||||||
|
shapes_to_remove = []
|
||||||
for ii, values in shape_entries:
|
for ii, values in shape_entries:
|
||||||
offset, scale, rotation, mirror_x = values
|
offset, scale, rotation, mirror_x = values
|
||||||
pat.ref(target=target, offset=offset, scale=scale,
|
pat.ref(target=target, offset=offset, scale=scale,
|
||||||
|
|
@ -1100,7 +1114,7 @@ class ILibrary(ILibraryView, MutableMapping[str, 'Pattern'], metaclass=ABCMeta):
|
||||||
del pat.shapes[layer][ii]
|
del pat.shapes[layer][ii]
|
||||||
|
|
||||||
for ll, pp in shape_pats.items():
|
for ll, pp in shape_pats.items():
|
||||||
self[label2name(ll)] = pp
|
self[target_names[ll]] = pp
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -259,3 +259,43 @@ def test_library_dedup_shapes_does_not_merge_custom_capped_paths() -> None:
|
||||||
|
|
||||||
assert not lib["top"].refs
|
assert not lib["top"].refs
|
||||||
assert len(lib["top"].shapes[(1, 0)]) == 2
|
assert len(lib["top"].shapes[(1, 0)]) == 2
|
||||||
|
|
||||||
|
|
||||||
|
def test_library_dedup_handles_multiple_duplicate_groups() -> None:
|
||||||
|
from ..shapes import Circle
|
||||||
|
|
||||||
|
lib = Library()
|
||||||
|
pat = Pattern()
|
||||||
|
pat.shapes[(1, 0)] += [Circle(radius=1, offset=(0, 0)), Circle(radius=1, offset=(10, 0))]
|
||||||
|
pat.shapes[(2, 0)] += [Path(vertices=[[0, 0], [5, 0]], width=2), Path(vertices=[[10, 0], [15, 0]], width=2)]
|
||||||
|
lib["top"] = pat
|
||||||
|
|
||||||
|
lib.dedup(exclude_types=(), norm_value=1, threshold=2)
|
||||||
|
|
||||||
|
assert len(lib["top"].refs) == 2
|
||||||
|
assert all(len(refs) == 2 for refs in lib["top"].refs.values())
|
||||||
|
assert len(lib["top"].shapes[(1, 0)]) == 0
|
||||||
|
assert len(lib["top"].shapes[(2, 0)]) == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_library_dedup_uses_stable_target_names_per_label() -> None:
|
||||||
|
from ..shapes import Circle
|
||||||
|
|
||||||
|
lib = Library()
|
||||||
|
|
||||||
|
p1 = Pattern()
|
||||||
|
p1.shapes[(1, 0)] += [Circle(radius=1, offset=(0, 0)), Circle(radius=1, offset=(10, 0))]
|
||||||
|
lib["p1"] = p1
|
||||||
|
|
||||||
|
p2 = Pattern()
|
||||||
|
p2.shapes[(2, 0)] += [Path(vertices=[[0, 0], [5, 0]], width=2), Path(vertices=[[10, 0], [15, 0]], width=2)]
|
||||||
|
lib["p2"] = p2
|
||||||
|
|
||||||
|
lib.dedup(exclude_types=(), norm_value=1, threshold=2)
|
||||||
|
|
||||||
|
circle_target = next(iter(lib["p1"].refs))
|
||||||
|
path_target = next(iter(lib["p2"].refs))
|
||||||
|
|
||||||
|
assert circle_target != path_target
|
||||||
|
assert all(isinstance(shape, Circle) for shapes in lib[circle_target].shapes.values() for shape in shapes)
|
||||||
|
assert all(isinstance(shape, Path) for shapes in lib[path_target].shapes.values() for shape in shapes)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue