Merge pull request #3366 from fonttools/interpolatable-refactor2

Interpolatable refactor2
This commit is contained in:
Behdad Esfahbod 2023-12-04 12:58:39 -05:00 committed by GitHub
commit 0641526a26
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 112 additions and 115 deletions

View File

@ -282,14 +282,8 @@ def test_gen(
# "contour_order" check # "contour_order" check
# #
matching, matching_cost, identity_cost = test_contour_order(glyph0, glyph1) this_tolerance, matching = test_contour_order(glyph0, glyph1)
if matching_cost < identity_cost * tolerance: if this_tolerance < tolerance:
log.debug(
"matching_ratio %g",
matching_cost / identity_cost,
)
this_tolerance = matching_cost / identity_cost
log.debug("tolerance: %g", this_tolerance)
yield ( yield (
glyph_name, glyph_name,
{ {
@ -352,119 +346,109 @@ def test_gen(
# after reordering above. # after reordering above.
continue continue
proposed_point, reverse, min_cost, first_cost = test_starting_point( this_tolerance, proposed_point, reverse = test_starting_point(
glyph0, glyph1, ix, tolerance, matching glyph0, glyph1, ix, tolerance, matching
) )
if proposed_point or reverse: if this_tolerance < tolerance:
this_tolerance = min_cost / first_cost yield (
log.debug("tolerance: %g", this_tolerance) glyph_name,
if min_cost < first_cost * tolerance: {
yield ( "type": "wrong_start_point",
glyph_name, "contour": ix,
{ "master_1": names[m0idx],
"type": "wrong_start_point", "master_2": names[m1idx],
"contour": ix, "master_1_idx": m0idx,
"master_1": names[m0idx], "master_2_idx": m1idx,
"master_2": names[m1idx], "value_1": 0,
"master_1_idx": m0idx, "value_2": proposed_point,
"master_2_idx": m1idx, "reversed": reverse,
"value_1": 0, "tolerance": this_tolerance,
"value_2": proposed_point, },
"reversed": reverse, )
"tolerance": this_tolerance,
},
)
else:
# Weight check.
#
# If contour could be mid-interpolated, and the two
# contours have the same area sign, proceeed.
#
# The sign difference can happen if it's a werido
# self-intersecting contour; ignore it.
contour = midRecording[ix]
normalized = False # Weight check.
if contour and (m0Vectors[ix][0] < 0) == (m1Vectors[ix][0] < 0): #
if normalized: # If contour could be mid-interpolated, and the two
midStats = StatisticsPen(glyphset=None) # contours have the same area sign, proceeed.
tpen = TransformPen( #
midStats, transform_from_stats(midStats, inverse=True) # The sign difference can happen if it's a weirdo
) # self-intersecting contour; ignore it.
contour.replay(tpen) contour = midRecording[ix]
normalized = False
if contour and (m0Vectors[ix][0] < 0) == (m1Vectors[ix][0] < 0):
if normalized:
midStats = StatisticsPen(glyphset=None)
tpen = TransformPen(
midStats, transform_from_stats(midStats, inverse=True)
)
contour.replay(tpen)
else:
midStats = StatisticsPen(glyphset=None)
contour.replay(midStats)
midVector = contour_vector_from_stats(midStats)
m0Vec = m0Vectors[ix] if not normalized else m0VectorsNormalized[ix]
m1Vec = m1Vectors[ix] if not normalized else m1VectorsNormalized[ix]
size0 = m0Vec[0] * m0Vec[0]
size1 = m1Vec[0] * m1Vec[0]
midSize = midVector[0] * midVector[0]
power = 1
t = tolerance**power
for overweight, problem_type in enumerate(
("underweight", "overweight")
):
if overweight:
expectedSize = sqrt(size0 * size1)
expectedSize = (size0 + size1) - expectedSize
expectedSize = size1 + (midSize - size1)
continue
else: else:
midStats = StatisticsPen(glyphset=None) expectedSize = sqrt(size0 * size1)
contour.replay(midStats)
midVector = contour_vector_from_stats(midStats) log.debug(
"%s: actual size %g; threshold size %g, master sizes: %g, %g",
m0Vec = ( problem_type,
m0Vectors[ix] if not normalized else m0VectorsNormalized[ix] midSize,
expectedSize,
size0,
size1,
) )
m1Vec = (
m1Vectors[ix] if not normalized else m1VectorsNormalized[ix]
)
size0 = m0Vec[0] * m0Vec[0]
size1 = m1Vec[0] * m1Vec[0]
midSize = midVector[0] * midVector[0]
power = 1 size0, size1 = sorted((size0, size1))
t = tolerance**power
for overweight, problem_type in enumerate( if (
("underweight", "overweight") not overweight and expectedSize * tolerance > midSize + 1e-5
): ) or (overweight and 1e-5 + expectedSize / tolerance < midSize):
if overweight: try:
expectedSize = sqrt(size0 * size1) if overweight:
expectedSize = (size0 + size1) - expectedSize this_tolerance = (expectedSize / midSize) ** (
expectedSize = size1 + (midSize - size1) 1 / power
continue )
else: else:
expectedSize = sqrt(size0 * size1) this_tolerance = (midSize / expectedSize) ** (
1 / power
log.debug( )
"%s: actual size %g; threshold size %g, master sizes: %g, %g", except ZeroDivisionError:
problem_type, this_tolerance = 0
midSize, log.debug("tolerance %g", this_tolerance)
expectedSize, yield (
size0, glyph_name,
size1, {
"type": problem_type,
"contour": ix,
"master_1": names[m0idx],
"master_2": names[m1idx],
"master_1_idx": m0idx,
"master_2_idx": m1idx,
"tolerance": this_tolerance,
},
) )
size0, size1 = sorted((size0, size1))
if (
not overweight
and expectedSize * tolerance > midSize + 1e-5
) or (
overweight and 1e-5 + expectedSize / tolerance < midSize
):
try:
if overweight:
this_tolerance = (expectedSize / midSize) ** (
1 / power
)
else:
this_tolerance = (midSize / expectedSize) ** (
1 / power
)
except ZeroDivisionError:
this_tolerance = 0
log.debug("tolerance %g", this_tolerance)
yield (
glyph_name,
{
"type": problem_type,
"contour": ix,
"master_1": names[m0idx],
"master_2": names[m1idx],
"master_1_idx": m0idx,
"master_2_idx": m1idx,
"tolerance": this_tolerance,
},
)
# #
# "kink" detector # "kink" detector
# #

View File

@ -1,4 +1,7 @@
from .interpolatableHelpers import * from .interpolatableHelpers import *
import logging
log = logging.getLogger("fontTools.varLib.interpolatable")
def test_contour_order(glyph0, glyph1): def test_contour_order(glyph0, glyph1):
@ -71,4 +74,9 @@ def test_contour_order(glyph0, glyph1):
matching_cost = matching_cost_green matching_cost = matching_cost_green
identity_cost = identity_cost_green identity_cost = identity_cost_green
return matching, matching_cost, identity_cost this_tolerance = matching_cost / identity_cost if identity_cost else 1
log.debug(
"test-contour-order: tolerance %g",
this_tolerance,
)
return this_tolerance, matching

View File

@ -19,8 +19,10 @@ def test_starting_point(glyph0, glyph1, ix, tolerance, matching):
min_cost_idx, min_cost = min(enumerate(costs), key=lambda x: x[1]) min_cost_idx, min_cost = min(enumerate(costs), key=lambda x: x[1])
first_cost = costs[0] first_cost = costs[0]
proposed_point = contour1[min_cost_idx][1]
reverse = contour1[min_cost_idx][2]
if min_cost < first_cost * tolerance: if min_cost < first_cost * tolerance:
this_tolerance = min_cost / first_cost
# c0 is the first isomorphism of the m0 master # c0 is the first isomorphism of the m0 master
# contour1 is list of all isomorphisms of the m1 master # contour1 is list of all isomorphisms of the m1 master
# #
@ -37,8 +39,6 @@ def test_starting_point(glyph0, glyph1, ix, tolerance, matching):
# closest point again. If it matches this time, let it # closest point again. If it matches this time, let it
# pass. # pass.
proposed_point = contour1[min_cost_idx][1]
reverse = contour1[min_cost_idx][2]
num_points = len(glyph1.points[ix]) num_points = len(glyph1.points[ix])
leeway = 3 leeway = 3
if not reverse and ( if not reverse and (
@ -102,4 +102,9 @@ def test_starting_point(glyph0, glyph1, ix, tolerance, matching):
# proposed_point = 0 # new_contour1[min_cost_idx][1] # proposed_point = 0 # new_contour1[min_cost_idx][1]
pass pass
return proposed_point, reverse, min_cost, first_cost this_tolerance = min_cost / first_cost if first_cost else 1
log.debug(
"test-starting-point: tolerance %g",
this_tolerance,
)
return this_tolerance, proposed_point, reverse