[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
|
||||
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_funcs = {}
|
||||
|
||||
|
|
@ -1053,6 +1065,7 @@ class ILibrary(ILibraryView, MutableMapping[str, 'Pattern'], metaclass=ABCMeta):
|
|||
shape_counts[label] += 1
|
||||
|
||||
shape_pats = {}
|
||||
target_names = {}
|
||||
for label, count in shape_counts.items():
|
||||
if count < threshold:
|
||||
continue
|
||||
|
|
@ -1061,6 +1074,7 @@ class ILibrary(ILibraryView, MutableMapping[str, 'Pattern'], metaclass=ABCMeta):
|
|||
shape_pat = Pattern()
|
||||
shape_pat.shapes[label[-1]] += [shape_func()]
|
||||
shape_pats[label] = shape_pat
|
||||
target_names[label] = reserve_target_name(label)
|
||||
|
||||
# ## Second pass ##
|
||||
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,
|
||||
# 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.
|
||||
shapes_to_remove = []
|
||||
for label, shape_entries in shape_table.items():
|
||||
layer = label[-1]
|
||||
target = label2name(label)
|
||||
target = target_names[label]
|
||||
shapes_to_remove = []
|
||||
for ii, values in shape_entries:
|
||||
offset, scale, rotation, mirror_x = values
|
||||
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]
|
||||
|
||||
for ll, pp in shape_pats.items():
|
||||
self[label2name(ll)] = pp
|
||||
self[target_names[ll]] = pp
|
||||
|
||||
return self
|
||||
|
||||
|
|
|
|||
|
|
@ -259,3 +259,43 @@ def test_library_dedup_shapes_does_not_merge_custom_capped_paths() -> None:
|
|||
|
||||
assert not lib["top"].refs
|
||||
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