From 96c9bd236f8f663696964fcc6d1ac77103bd4145 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Sat, 2 Dec 2023 07:38:02 -0500 Subject: [PATCH 1/5] [interpolatable] Move tolerance calc into the test --- Lib/fontTools/varLib/interpolatable.py | 18 +++++------------- .../varLib/interpolatableTestContourOrder.py | 10 +++++++++- .../varLib/interpolatableTestStartingPoint.py | 7 ++++++- 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/Lib/fontTools/varLib/interpolatable.py b/Lib/fontTools/varLib/interpolatable.py index f03e94620..da5598112 100644 --- a/Lib/fontTools/varLib/interpolatable.py +++ b/Lib/fontTools/varLib/interpolatable.py @@ -282,14 +282,8 @@ def test_gen( # "contour_order" check # - matching, matching_cost, identity_cost = test_contour_order(glyph0, glyph1) - if matching_cost < identity_cost * tolerance: - log.debug( - "matching_ratio %g", - matching_cost / identity_cost, - ) - this_tolerance = matching_cost / identity_cost - log.debug("tolerance: %g", this_tolerance) + this_tolerance, matching = test_contour_order(glyph0, glyph1) + if this_tolerance < tolerance: yield ( glyph_name, { @@ -352,14 +346,12 @@ def test_gen( # after reordering above. continue - proposed_point, reverse, min_cost, first_cost = test_starting_point( + this_tolerance, proposed_point, reverse = test_starting_point( glyph0, glyph1, ix, tolerance, matching ) if proposed_point or reverse: - this_tolerance = min_cost / first_cost - log.debug("tolerance: %g", this_tolerance) - if min_cost < first_cost * tolerance: + if this_tolerance < tolerance: yield ( glyph_name, { @@ -381,7 +373,7 @@ def test_gen( # 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 + # The sign difference can happen if it's a weirdo # self-intersecting contour; ignore it. contour = midRecording[ix] diff --git a/Lib/fontTools/varLib/interpolatableTestContourOrder.py b/Lib/fontTools/varLib/interpolatableTestContourOrder.py index d089e4357..9edb1afcb 100644 --- a/Lib/fontTools/varLib/interpolatableTestContourOrder.py +++ b/Lib/fontTools/varLib/interpolatableTestContourOrder.py @@ -1,4 +1,7 @@ from .interpolatableHelpers import * +import logging + +log = logging.getLogger("fontTools.varLib.interpolatable") def test_contour_order(glyph0, glyph1): @@ -71,4 +74,9 @@ def test_contour_order(glyph0, glyph1): matching_cost = matching_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 diff --git a/Lib/fontTools/varLib/interpolatableTestStartingPoint.py b/Lib/fontTools/varLib/interpolatableTestStartingPoint.py index 9f742a14f..18a0db4f3 100644 --- a/Lib/fontTools/varLib/interpolatableTestStartingPoint.py +++ b/Lib/fontTools/varLib/interpolatableTestStartingPoint.py @@ -102,4 +102,9 @@ def test_starting_point(glyph0, glyph1, ix, tolerance, matching): # proposed_point = 0 # new_contour1[min_cost_idx][1] 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 From 2e764bf1798601fe67d08723830d1f63e0e0216c Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Sat, 2 Dec 2023 08:17:50 -0500 Subject: [PATCH 2/5] [interpolatable] Move code around --- Lib/fontTools/varLib/interpolatableTestStartingPoint.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/fontTools/varLib/interpolatableTestStartingPoint.py b/Lib/fontTools/varLib/interpolatableTestStartingPoint.py index 18a0db4f3..a84a65184 100644 --- a/Lib/fontTools/varLib/interpolatableTestStartingPoint.py +++ b/Lib/fontTools/varLib/interpolatableTestStartingPoint.py @@ -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]) first_cost = costs[0] + proposed_point = contour1[min_cost_idx][1] + reverse = contour1[min_cost_idx][2] + if min_cost < first_cost * tolerance: - this_tolerance = min_cost / first_cost # c0 is the first isomorphism of the m0 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 # pass. - proposed_point = contour1[min_cost_idx][1] - reverse = contour1[min_cost_idx][2] num_points = len(glyph1.points[ix]) leeway = 3 if not reverse and ( From ef90c377d16f5ad3b7e29c0551ed5cb316d6618b Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Sat, 2 Dec 2023 08:31:39 -0500 Subject: [PATCH 3/5] [interpolatable] Minor tweak --- Lib/fontTools/varLib/interpolatable.py | 33 +++++++++++++------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/Lib/fontTools/varLib/interpolatable.py b/Lib/fontTools/varLib/interpolatable.py index da5598112..c8b9b2f88 100644 --- a/Lib/fontTools/varLib/interpolatable.py +++ b/Lib/fontTools/varLib/interpolatable.py @@ -350,23 +350,22 @@ def test_gen( glyph0, glyph1, ix, tolerance, matching ) - if proposed_point or reverse: - if this_tolerance < tolerance: - yield ( - glyph_name, - { - "type": "wrong_start_point", - "contour": ix, - "master_1": names[m0idx], - "master_2": names[m1idx], - "master_1_idx": m0idx, - "master_2_idx": m1idx, - "value_1": 0, - "value_2": proposed_point, - "reversed": reverse, - "tolerance": this_tolerance, - }, - ) + if this_tolerance < tolerance: + yield ( + glyph_name, + { + "type": "wrong_start_point", + "contour": ix, + "master_1": names[m0idx], + "master_2": names[m1idx], + "master_1_idx": m0idx, + "master_2_idx": m1idx, + "value_1": 0, + "value_2": proposed_point, + "reversed": reverse, + "tolerance": this_tolerance, + }, + ) else: # Weight check. # From 44300a8c3f0618d5d1f4621da1674d6885aa8da9 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Sun, 3 Dec 2023 16:51:00 -0500 Subject: [PATCH 4/5] [interpolatable] Always do weight check even if start points differ Makes the output busier but I think it's good. --- Lib/fontTools/varLib/interpolatable.py | 162 ++++++++++++------------- 1 file changed, 81 insertions(+), 81 deletions(-) diff --git a/Lib/fontTools/varLib/interpolatable.py b/Lib/fontTools/varLib/interpolatable.py index c8b9b2f88..0c66532d7 100644 --- a/Lib/fontTools/varLib/interpolatable.py +++ b/Lib/fontTools/varLib/interpolatable.py @@ -366,96 +366,96 @@ def test_gen( "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 weirdo - # self-intersecting contour; ignore it. - 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) + # 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 weirdo + # self-intersecting contour; ignore it. + 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: - midStats = StatisticsPen(glyphset=None) - contour.replay(midStats) + expectedSize = sqrt(size0 * size1) - midVector = contour_vector_from_stats(midStats) - - m0Vec = ( - m0Vectors[ix] if not normalized else m0VectorsNormalized[ix] + log.debug( + "%s: actual size %g; threshold size %g, master sizes: %g, %g", + problem_type, + 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 - t = tolerance**power + size0, size1 = sorted((size0, size1)) - for overweight, problem_type in enumerate( - ("underweight", "overweight") + if ( + not overweight + and expectedSize * tolerance > midSize + 1e-5 + ) or ( + overweight and 1e-5 + expectedSize / tolerance < midSize ): - if overweight: - expectedSize = sqrt(size0 * size1) - expectedSize = (size0 + size1) - expectedSize - expectedSize = size1 + (midSize - size1) - continue - else: - expectedSize = sqrt(size0 * size1) - - log.debug( - "%s: actual size %g; threshold size %g, master sizes: %g, %g", - problem_type, - midSize, - expectedSize, - size0, - size1, + 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, + }, ) - 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 # From 1130fd799f3563ac751604abed098ef3ceea6b81 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Sun, 3 Dec 2023 20:25:35 -0500 Subject: [PATCH 5/5] [interpolatable] Black --- Lib/fontTools/varLib/interpolatable.py | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/Lib/fontTools/varLib/interpolatable.py b/Lib/fontTools/varLib/interpolatable.py index 0c66532d7..ae2de2a19 100644 --- a/Lib/fontTools/varLib/interpolatable.py +++ b/Lib/fontTools/varLib/interpolatable.py @@ -390,12 +390,8 @@ def test_gen( midVector = contour_vector_from_stats(midStats) - m0Vec = ( - m0Vectors[ix] if not normalized else m0VectorsNormalized[ix] - ) - m1Vec = ( - m1Vectors[ix] if not normalized else m1VectorsNormalized[ix] - ) + 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] @@ -426,11 +422,8 @@ def test_gen( size0, size1 = sorted((size0, size1)) if ( - not overweight - and expectedSize * tolerance > midSize + 1e-5 - ) or ( - overweight and 1e-5 + expectedSize / tolerance < midSize - ): + not overweight and expectedSize * tolerance > midSize + 1e-5 + ) or (overweight and 1e-5 + expectedSize / tolerance < midSize): try: if overweight: this_tolerance = (expectedSize / midSize) ** (