[varLib.interpolatable] Support discrete axes in .designspace
Fixes https://github.com/fonttools/fonttools/issues/3597
This commit is contained in:
parent
16dbe3f5e2
commit
ead2a18d4b
@ -135,6 +135,7 @@ def test_gen(
|
||||
kinkiness=DEFAULT_KINKINESS,
|
||||
upem=DEFAULT_UPEM,
|
||||
show_all=False,
|
||||
discrete_axes=[],
|
||||
):
|
||||
if tolerance >= 10:
|
||||
tolerance *= 0.01
|
||||
@ -150,7 +151,9 @@ def test_gen(
|
||||
# ... risks the sparse master being the first one, and only processing a subset of the glyphs
|
||||
glyphs = {g for glyphset in glyphsets for g in glyphset.keys()}
|
||||
|
||||
parents, order = find_parents_and_order(glyphsets, locations)
|
||||
parents, order = find_parents_and_order(
|
||||
glyphsets, locations, discrete_axes=discrete_axes
|
||||
)
|
||||
|
||||
def grand_parent(i, glyphname):
|
||||
if i is None:
|
||||
@ -701,6 +704,7 @@ def main(args=None):
|
||||
fonts = []
|
||||
names = []
|
||||
locations = []
|
||||
discrete_axes = set()
|
||||
upem = DEFAULT_UPEM
|
||||
|
||||
original_args_inputs = tuple(args.inputs)
|
||||
@ -713,8 +717,13 @@ def main(args=None):
|
||||
designspace = DesignSpaceDocument.fromfile(args.inputs[0])
|
||||
args.inputs = [master.path for master in designspace.sources]
|
||||
locations = [master.location for master in designspace.sources]
|
||||
discrete_axes = {
|
||||
a.name for a in designspace.axes if not hasattr(a, "minimum")
|
||||
}
|
||||
axis_triples = {
|
||||
a.name: (a.minimum, a.default, a.maximum) for a in designspace.axes
|
||||
a.name: (a.minimum, a.default, a.maximum)
|
||||
for a in designspace.axes
|
||||
if a.name not in discrete_axes
|
||||
}
|
||||
axis_mappings = {a.name: a.map for a in designspace.axes}
|
||||
axis_triples = {
|
||||
@ -879,7 +888,13 @@ def main(args=None):
|
||||
glyphset[gn] = None
|
||||
|
||||
# Normalize locations
|
||||
locations = [normalizeLocation(loc, axis_triples) for loc in locations]
|
||||
locations = [
|
||||
{
|
||||
**normalizeLocation(loc, axis_triples),
|
||||
**{k: v for k, v in loc.items() if k in discrete_axes},
|
||||
}
|
||||
for loc in locations
|
||||
]
|
||||
tolerance = args.tolerance or DEFAULT_TOLERANCE
|
||||
kinkiness = args.kinkiness if args.kinkiness is not None else DEFAULT_KINKINESS
|
||||
|
||||
@ -896,6 +911,7 @@ def main(args=None):
|
||||
tolerance=tolerance,
|
||||
kinkiness=kinkiness,
|
||||
show_all=args.show_all,
|
||||
discrete_axes=discrete_axes,
|
||||
)
|
||||
problems = defaultdict(list)
|
||||
|
||||
|
@ -293,17 +293,19 @@ def add_isomorphisms(points, isomorphisms, reverse):
|
||||
)
|
||||
|
||||
|
||||
def find_parents_and_order(glyphsets, locations):
|
||||
def find_parents_and_order(glyphsets, locations, *, discrete_axes=set()):
|
||||
parents = [None] + list(range(len(glyphsets) - 1))
|
||||
order = list(range(len(glyphsets)))
|
||||
if locations:
|
||||
# Order base master first
|
||||
bases = (i for i, l in enumerate(locations) if all(v == 0 for v in l.values()))
|
||||
bases = [
|
||||
i
|
||||
for i, l in enumerate(locations)
|
||||
if all(v == 0 for k, v in l.items() if k not in discrete_axes)
|
||||
]
|
||||
if bases:
|
||||
base = next(bases)
|
||||
logging.info("Base master index %s, location %s", base, locations[base])
|
||||
logging.info("Found %s base masters: %s", len(bases), bases)
|
||||
else:
|
||||
base = 0
|
||||
logging.warning("No base master location found")
|
||||
|
||||
# Form a minimum spanning tree of the locations
|
||||
@ -317,9 +319,17 @@ def find_parents_and_order(glyphsets, locations):
|
||||
axes = sorted(axes)
|
||||
vectors = [tuple(l.get(k, 0) for k in axes) for l in locations]
|
||||
for i, j in itertools.combinations(range(len(locations)), 2):
|
||||
i_discrete_location = {
|
||||
k: v for k, v in zip(axes, vectors[i]) if k in discrete_axes
|
||||
}
|
||||
j_discrete_location = {
|
||||
k: v for k, v in zip(axes, vectors[j]) if k in discrete_axes
|
||||
}
|
||||
if i_discrete_location != j_discrete_location:
|
||||
continue
|
||||
graph[i][j] = vdiff_hypot2(vectors[i], vectors[j])
|
||||
|
||||
tree = minimum_spanning_tree(graph)
|
||||
tree = minimum_spanning_tree(graph, overwrite=True)
|
||||
rows, cols = tree.nonzero()
|
||||
graph = defaultdict(set)
|
||||
for row, col in zip(rows, cols):
|
||||
@ -330,7 +340,7 @@ def find_parents_and_order(glyphsets, locations):
|
||||
parents = [None] * len(locations)
|
||||
order = []
|
||||
visited = set()
|
||||
queue = deque([base])
|
||||
queue = deque(bases)
|
||||
while queue:
|
||||
i = queue.popleft()
|
||||
visited.add(i)
|
||||
@ -339,6 +349,9 @@ def find_parents_and_order(glyphsets, locations):
|
||||
if j not in visited:
|
||||
parents[j] = i
|
||||
queue.append(j)
|
||||
assert len(order) == len(
|
||||
parents
|
||||
), "Not all masters are reachable; report an issue"
|
||||
|
||||
except ImportError:
|
||||
pass
|
||||
|
Loading…
x
Reference in New Issue
Block a user