Merge pull request #3377 from fonttools/interpolatable-refactor3
Interpolatable refactor3
This commit is contained in:
commit
39f6142f2c
@ -186,7 +186,11 @@ def test_gen(
|
||||
if not ignore_missing:
|
||||
yield (
|
||||
glyph_name,
|
||||
{"type": "missing", "master": name, "master_idx": master_idx},
|
||||
{
|
||||
"type": InterpolatableProblem.MISSING,
|
||||
"master": name,
|
||||
"master_idx": master_idx,
|
||||
},
|
||||
)
|
||||
continue
|
||||
|
||||
@ -198,10 +202,10 @@ def test_gen(
|
||||
yield (
|
||||
glyph_name,
|
||||
{
|
||||
"type": InterpolatableProblem.OPEN_PATH,
|
||||
"master": name,
|
||||
"master_idx": master_idx,
|
||||
"contour": ix,
|
||||
"type": "open_path",
|
||||
},
|
||||
)
|
||||
if has_open:
|
||||
@ -230,7 +234,7 @@ def test_gen(
|
||||
yield (
|
||||
glyph_name,
|
||||
{
|
||||
"type": "path_count",
|
||||
"type": InterpolatableProblem.PATH_COUNT,
|
||||
"master_1": names[m0idx],
|
||||
"master_2": names[m1idx],
|
||||
"master_1_idx": m0idx,
|
||||
@ -249,7 +253,7 @@ def test_gen(
|
||||
yield (
|
||||
glyph_name,
|
||||
{
|
||||
"type": "node_count",
|
||||
"type": InterpolatableProblem.NODE_COUNT,
|
||||
"path": pathIx,
|
||||
"master_1": names[m0idx],
|
||||
"master_2": names[m1idx],
|
||||
@ -265,7 +269,7 @@ def test_gen(
|
||||
yield (
|
||||
glyph_name,
|
||||
{
|
||||
"type": "node_incompatibility",
|
||||
"type": InterpolatableProblem.NODE_INCOMPATIBILITY,
|
||||
"path": pathIx,
|
||||
"node": nodeIx,
|
||||
"master_1": names[m0idx],
|
||||
@ -279,7 +283,7 @@ def test_gen(
|
||||
continue
|
||||
|
||||
#
|
||||
# "contour_order" check
|
||||
# InterpolatableProblem.CONTOUR_ORDER check
|
||||
#
|
||||
|
||||
this_tolerance, matching = test_contour_order(glyph0, glyph1)
|
||||
@ -287,7 +291,7 @@ def test_gen(
|
||||
yield (
|
||||
glyph_name,
|
||||
{
|
||||
"type": "contour_order",
|
||||
"type": InterpolatableProblem.CONTOUR_ORDER,
|
||||
"master_1": names[m0idx],
|
||||
"master_2": names[m1idx],
|
||||
"master_1_idx": m0idx,
|
||||
@ -300,7 +304,7 @@ def test_gen(
|
||||
matchings[m1idx] = matching
|
||||
|
||||
#
|
||||
# "wrong_start_point" / weight check
|
||||
# wrong-start-point / weight check
|
||||
#
|
||||
|
||||
m0Isomorphisms = glyph0.isomorphisms
|
||||
@ -354,7 +358,7 @@ def test_gen(
|
||||
yield (
|
||||
glyph_name,
|
||||
{
|
||||
"type": "wrong_start_point",
|
||||
"type": InterpolatableProblem.WRONG_START_POINT,
|
||||
"contour": ix,
|
||||
"master_1": names[m0idx],
|
||||
"master_2": names[m1idx],
|
||||
@ -400,7 +404,10 @@ def test_gen(
|
||||
t = tolerance**power
|
||||
|
||||
for overweight, problem_type in enumerate(
|
||||
("underweight", "overweight")
|
||||
(
|
||||
InterpolatableProblem.UNDERWEIGHT,
|
||||
InterpolatableProblem.OVERWEIGHT,
|
||||
)
|
||||
):
|
||||
if overweight:
|
||||
expectedSize = sqrt(size0 * size1)
|
||||
@ -579,7 +586,7 @@ def test_gen(
|
||||
yield (
|
||||
glyph_name,
|
||||
{
|
||||
"type": "kink",
|
||||
"type": InterpolatableProblem.KINK,
|
||||
"contour": ix,
|
||||
"master_1": names[m0idx],
|
||||
"master_2": names[m1idx],
|
||||
@ -598,7 +605,7 @@ def test_gen(
|
||||
yield (
|
||||
glyph_name,
|
||||
{
|
||||
"type": "nothing",
|
||||
"type": InterpolatableProblem.NOTHING,
|
||||
"master_1": names[m0idx],
|
||||
"master_2": names[m1idx],
|
||||
"master_1_idx": m0idx,
|
||||
@ -947,16 +954,16 @@ def main(args=None):
|
||||
print(f" Masters: %s:" % ", ".join(master_names), file=f)
|
||||
last_master_idxs = master_idxs
|
||||
|
||||
if p["type"] == "missing":
|
||||
if p["type"] == InterpolatableProblem.MISSING:
|
||||
print(
|
||||
" Glyph was missing in master %s" % p["master"], file=f
|
||||
)
|
||||
elif p["type"] == "open_path":
|
||||
elif p["type"] == InterpolatableProblem.OPEN_PATH:
|
||||
print(
|
||||
" Glyph has an open path in master %s" % p["master"],
|
||||
file=f,
|
||||
)
|
||||
elif p["type"] == "path_count":
|
||||
elif p["type"] == InterpolatableProblem.PATH_COUNT:
|
||||
print(
|
||||
" Path count differs: %i in %s, %i in %s"
|
||||
% (
|
||||
@ -967,7 +974,7 @@ def main(args=None):
|
||||
),
|
||||
file=f,
|
||||
)
|
||||
elif p["type"] == "node_count":
|
||||
elif p["type"] == InterpolatableProblem.NODE_COUNT:
|
||||
print(
|
||||
" Node count differs in path %i: %i in %s, %i in %s"
|
||||
% (
|
||||
@ -979,7 +986,7 @@ def main(args=None):
|
||||
),
|
||||
file=f,
|
||||
)
|
||||
elif p["type"] == "node_incompatibility":
|
||||
elif p["type"] == InterpolatableProblem.NODE_INCOMPATIBILITY:
|
||||
print(
|
||||
" Node %o incompatible in path %i: %s in %s, %s in %s"
|
||||
% (
|
||||
@ -992,7 +999,7 @@ def main(args=None):
|
||||
),
|
||||
file=f,
|
||||
)
|
||||
elif p["type"] == "contour_order":
|
||||
elif p["type"] == InterpolatableProblem.CONTOUR_ORDER:
|
||||
print(
|
||||
" Contour order differs: %s in %s, %s in %s"
|
||||
% (
|
||||
@ -1003,7 +1010,7 @@ def main(args=None):
|
||||
),
|
||||
file=f,
|
||||
)
|
||||
elif p["type"] == "wrong_start_point":
|
||||
elif p["type"] == InterpolatableProblem.WRONG_START_POINT:
|
||||
print(
|
||||
" Contour %d start point differs: %s in %s, %s in %s; reversed: %s"
|
||||
% (
|
||||
@ -1016,7 +1023,7 @@ def main(args=None):
|
||||
),
|
||||
file=f,
|
||||
)
|
||||
elif p["type"] == "underweight":
|
||||
elif p["type"] == InterpolatableProblem.UNDERWEIGHT:
|
||||
print(
|
||||
" Contour %d interpolation is underweight: %s, %s"
|
||||
% (
|
||||
@ -1026,7 +1033,7 @@ def main(args=None):
|
||||
),
|
||||
file=f,
|
||||
)
|
||||
elif p["type"] == "overweight":
|
||||
elif p["type"] == InterpolatableProblem.OVERWEIGHT:
|
||||
print(
|
||||
" Contour %d interpolation is overweight: %s, %s"
|
||||
% (
|
||||
@ -1036,7 +1043,7 @@ def main(args=None):
|
||||
),
|
||||
file=f,
|
||||
)
|
||||
elif p["type"] == "kink":
|
||||
elif p["type"] == InterpolatableProblem.KINK:
|
||||
print(
|
||||
" Contour %d has a kink at %s: %s, %s"
|
||||
% (
|
||||
@ -1047,7 +1054,7 @@ def main(args=None):
|
||||
),
|
||||
file=f,
|
||||
)
|
||||
elif p["type"] == "nothing":
|
||||
elif p["type"] == InterpolatableProblem.NOTHING:
|
||||
print(
|
||||
" Showing %s and %s"
|
||||
% (
|
||||
@ -1060,6 +1067,8 @@ def main(args=None):
|
||||
for glyphname, problem in problems_gen:
|
||||
problems[glyphname].append(problem)
|
||||
|
||||
problems = sort_problems(problems)
|
||||
|
||||
if args.pdf:
|
||||
log.info("Writing PDF to %s", args.pdf)
|
||||
from .interpolatablePlot import InterpolatablePDF
|
||||
|
@ -4,6 +4,7 @@ from fontTools.pens.recordingPen import RecordingPen, DecomposingRecordingPen
|
||||
from fontTools.misc.transform import Transform
|
||||
from collections import defaultdict, deque
|
||||
from math import sqrt, copysign, atan2, pi
|
||||
from enum import Enum
|
||||
import itertools
|
||||
|
||||
import logging
|
||||
@ -11,6 +12,53 @@ import logging
|
||||
log = logging.getLogger("fontTools.varLib.interpolatable")
|
||||
|
||||
|
||||
class InterpolatableProblem:
|
||||
NOTHING = "nothing"
|
||||
MISSING = "missing"
|
||||
OPEN_PATH = "open_path"
|
||||
PATH_COUNT = "path_count"
|
||||
NODE_COUNT = "node_count"
|
||||
NODE_INCOMPATIBILITY = "node_incompatibility"
|
||||
CONTOUR_ORDER = "contour_order"
|
||||
WRONG_START_POINT = "wrong_start_point"
|
||||
KINK = "kink"
|
||||
UNDERWEIGHT = "underweight"
|
||||
OVERWEIGHT = "overweight"
|
||||
|
||||
severity = {
|
||||
MISSING: 1,
|
||||
OPEN_PATH: 2,
|
||||
PATH_COUNT: 3,
|
||||
NODE_COUNT: 4,
|
||||
NODE_INCOMPATIBILITY: 5,
|
||||
CONTOUR_ORDER: 6,
|
||||
WRONG_START_POINT: 7,
|
||||
KINK: 8,
|
||||
UNDERWEIGHT: 9,
|
||||
OVERWEIGHT: 10,
|
||||
NOTHING: 11,
|
||||
}
|
||||
|
||||
|
||||
def sort_problems(problems):
|
||||
"""Sort problems by severity, then by glyph name, then by problem message."""
|
||||
return dict(
|
||||
sorted(
|
||||
problems.items(),
|
||||
key=lambda _: -min(
|
||||
(
|
||||
InterpolatableProblem.severity[p["type"]]
|
||||
for p in _[1]
|
||||
if InterpolatableProblem.severity[p["type"]]
|
||||
!= InterpolatableProblem.severity[InterpolatableProblem.NOTHING]
|
||||
),
|
||||
default=InterpolatableProblem.severity[InterpolatableProblem.NOTHING],
|
||||
),
|
||||
reverse=True,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def rot_list(l, k):
|
||||
"""Rotate list by k items forward. Ie. item at position 0 will be
|
||||
at position k in returned list. Negative k is allowed."""
|
||||
|
@ -1,3 +1,4 @@
|
||||
from .interpolatableHelpers import *
|
||||
from fontTools.ttLib import TTFont
|
||||
from fontTools.pens.recordingPen import (
|
||||
RecordingPen,
|
||||
@ -397,7 +398,7 @@ class InterpolatablePlot:
|
||||
)
|
||||
master_indices = [problems[0][k] for k in master_keys]
|
||||
|
||||
if problem_type == "missing":
|
||||
if problem_type == InterpolatableProblem.MISSING:
|
||||
sample_glyph = next(
|
||||
i for i, m in enumerate(self.glyphsets) if m[glyphname] is not None
|
||||
)
|
||||
@ -457,12 +458,12 @@ class InterpolatablePlot:
|
||||
if any(
|
||||
pt
|
||||
in (
|
||||
"nothing",
|
||||
"wrong_start_point",
|
||||
"contour_order",
|
||||
"kink",
|
||||
"underweight",
|
||||
"overweight",
|
||||
InterpolatableProblem.NOTHING,
|
||||
InterpolatableProblem.WRONG_START_POINT,
|
||||
InterpolatableProblem.CONTOUR_ORDER,
|
||||
InterpolatableProblem.KINK,
|
||||
InterpolatableProblem.UNDERWEIGHT,
|
||||
InterpolatableProblem.OVERWEIGHT,
|
||||
)
|
||||
for pt in problem_types
|
||||
):
|
||||
@ -489,7 +490,12 @@ class InterpolatablePlot:
|
||||
+ [
|
||||
p
|
||||
for p in problems
|
||||
if p["type"] in ("kink", "underweight", "overweight")
|
||||
if p["type"]
|
||||
in (
|
||||
InterpolatableProblem.KINK,
|
||||
InterpolatableProblem.UNDERWEIGHT,
|
||||
InterpolatableProblem.OVERWEIGHT,
|
||||
)
|
||||
],
|
||||
None,
|
||||
x=x,
|
||||
@ -502,9 +508,9 @@ class InterpolatablePlot:
|
||||
if any(
|
||||
pt
|
||||
in (
|
||||
"wrong_start_point",
|
||||
"contour_order",
|
||||
"kink",
|
||||
InterpolatableProblem.WRONG_START_POINT,
|
||||
InterpolatableProblem.CONTOUR_ORDER,
|
||||
InterpolatableProblem.KINK,
|
||||
)
|
||||
for pt in problem_types
|
||||
):
|
||||
@ -525,14 +531,14 @@ class InterpolatablePlot:
|
||||
glyphset2[glyphname].draw(perContourPen2)
|
||||
|
||||
for problem in problems:
|
||||
if problem["type"] == "contour_order":
|
||||
if problem["type"] == InterpolatableProblem.CONTOUR_ORDER:
|
||||
fixed_contours = [
|
||||
perContourPen2.value[i] for i in problems[0]["value_2"]
|
||||
]
|
||||
perContourPen2.value = fixed_contours
|
||||
|
||||
for problem in problems:
|
||||
if problem["type"] == "wrong_start_point":
|
||||
if problem["type"] == InterpolatableProblem.WRONG_START_POINT:
|
||||
# Save the wrong contours
|
||||
wrongContour1 = perContourPen1.value[problem["contour"]]
|
||||
wrongContour2 = perContourPen2.value[problem["contour"]]
|
||||
@ -578,7 +584,7 @@ class InterpolatablePlot:
|
||||
|
||||
for problem in problems:
|
||||
# If we have a kink, try to fix it.
|
||||
if problem["type"] == "kink":
|
||||
if problem["type"] == InterpolatableProblem.KINK:
|
||||
# Save the wrong contours
|
||||
wrongContour1 = perContourPen1.value[problem["contour"]]
|
||||
wrongContour2 = perContourPen2.value[problem["contour"]]
|
||||
@ -673,11 +679,11 @@ class InterpolatablePlot:
|
||||
|
||||
else:
|
||||
emoticon = self.shrug
|
||||
if "underweight" in problem_types:
|
||||
if InterpolatableProblem.UNDERWEIGHT in problem_types:
|
||||
emoticon = self.underweight
|
||||
elif "overweight" in problem_types:
|
||||
elif InterpolatableProblem.OVERWEIGHT in problem_types:
|
||||
emoticon = self.overweight
|
||||
elif "nothing" in problem_types:
|
||||
elif InterpolatableProblem.NOTHING in problem_types:
|
||||
emoticon = self.yay
|
||||
self.draw_emoticon(emoticon, x=x, y=y)
|
||||
|
||||
@ -793,7 +799,7 @@ class InterpolatablePlot:
|
||||
pen = CairoPen(glyphset, cr)
|
||||
decomposedRecording.replay(pen)
|
||||
|
||||
if self.fill_color and problem_type != "open_path":
|
||||
if self.fill_color and problem_type != InterpolatableProblem.OPEN_PATH:
|
||||
cr.set_source_rgb(*self.fill_color)
|
||||
cr.fill_preserve()
|
||||
|
||||
@ -804,11 +810,17 @@ class InterpolatablePlot:
|
||||
|
||||
cr.new_path()
|
||||
|
||||
if "underweight" in problem_types or "overweight" in problem_types:
|
||||
if (
|
||||
InterpolatableProblem.UNDERWEIGHT in problem_types
|
||||
or InterpolatableProblem.OVERWEIGHT in problem_types
|
||||
):
|
||||
perContourPen = PerContourOrComponentPen(RecordingPen, glyphset=glyphset)
|
||||
recording.replay(perContourPen)
|
||||
for problem in problems:
|
||||
if problem["type"] in ("underweight", "overweight"):
|
||||
if problem["type"] in (
|
||||
InterpolatableProblem.UNDERWEIGHT,
|
||||
InterpolatableProblem.OVERWEIGHT,
|
||||
):
|
||||
contour = perContourPen.value[problem["contour"]]
|
||||
contour.replay(CairoPen(glyphset, cr))
|
||||
cr.set_source_rgba(*self.weight_issue_contour_color)
|
||||
@ -817,9 +829,9 @@ class InterpolatablePlot:
|
||||
if any(
|
||||
t in problem_types
|
||||
for t in {
|
||||
"nothing",
|
||||
"node_count",
|
||||
"node_incompatibility",
|
||||
InterpolatableProblem.NOTHING,
|
||||
InterpolatableProblem.NODE_COUNT,
|
||||
InterpolatableProblem.NODE_INCOMPATIBILITY,
|
||||
}
|
||||
):
|
||||
cr.set_line_cap(cairo.LINE_CAP_ROUND)
|
||||
@ -873,7 +885,7 @@ class InterpolatablePlot:
|
||||
|
||||
matching = None
|
||||
for problem in problems:
|
||||
if problem["type"] == "contour_order":
|
||||
if problem["type"] == InterpolatableProblem.CONTOUR_ORDER:
|
||||
matching = problem["value_2"]
|
||||
colors = cycle(self.contour_colors)
|
||||
perContourPen = PerContourOrComponentPen(
|
||||
@ -889,7 +901,10 @@ class InterpolatablePlot:
|
||||
cr.fill()
|
||||
|
||||
for problem in problems:
|
||||
if problem["type"] in ("nothing", "wrong_start_point"):
|
||||
if problem["type"] in (
|
||||
InterpolatableProblem.NOTHING,
|
||||
InterpolatableProblem.WRONG_START_POINT,
|
||||
):
|
||||
idx = problem.get("contour")
|
||||
|
||||
# Draw suggested point
|
||||
@ -967,7 +982,7 @@ class InterpolatablePlot:
|
||||
|
||||
cr.restore()
|
||||
|
||||
if problem["type"] == "kink":
|
||||
if problem["type"] == InterpolatableProblem.KINK:
|
||||
idx = problem.get("contour")
|
||||
perContourPen = PerContourOrComponentPen(
|
||||
RecordingPen, glyphset=glyphset
|
||||
|
@ -9,16 +9,11 @@ def test_starting_point(glyph0, glyph1, ix, tolerance, matching):
|
||||
m0Vectors = glyph0.greenVectors
|
||||
m1Vectors = [glyph1.greenVectors[i] for i in matching]
|
||||
|
||||
proposed_point = 0
|
||||
reverse = False
|
||||
min_cost = first_cost = 1
|
||||
|
||||
c0 = contour0[0]
|
||||
# Next few lines duplicated below.
|
||||
costs = [vdiff_hypot2_complex(c0[0], c1[0]) for c1 in contour1]
|
||||
min_cost_idx, min_cost = min(enumerate(costs), key=lambda x: x[1])
|
||||
first_cost = costs[0]
|
||||
|
||||
proposed_point = contour1[min_cost_idx][1]
|
||||
reverse = contour1[min_cost_idx][2]
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user