Skip to content

Commit

Permalink
[instancer/L4] Misc fixes and fix tests
Browse files Browse the repository at this point in the history
  • Loading branch information
behdad committed Jun 21, 2023
1 parent ff3ff6b commit fbc7541
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 24 deletions.
35 changes: 22 additions & 13 deletions Lib/fontTools/varLib/instancer/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,8 +204,8 @@ def expand(
default = None
if n == 2:
minimum, maximum = v
elif n == 3:
minimum, default, maximum = v
elif n >= 3:
return cls(*v)
else:
raise ValueError(f"expected sequence of 2 or 3; got {n}: {v!r}")
return cls(minimum, default, maximum)
Expand Down Expand Up @@ -234,14 +234,14 @@ def limitRangeAndPopulateDefault(self, fvarTriple) -> "AxisTriple":


@dataclasses.dataclass(frozen=True, order=True, repr=False)
class NormalizedAxisTriple(AxisTriple):
class NormalizedAxisTripleAndDistances(AxisTriple):
"""A triple of (min, default, max) normalized axis values."""

minimum: float
default: float
maximum: float
distanceNegative: float
distancePositive: float
distanceNegative: Optional[float] = 1
distancePositive: Optional[float] = 1

def __post_init__(self):
if self.default is None:
Expand All @@ -256,15 +256,18 @@ def reverse_negate(self):
v = self
return self.__class__(-v[2], -v[1], -v[0], v[4], v[3])

def normalizeValue(self, v):
def normalizeValue(self, v, extrapolate=True):
lower, default, upper, distanceNegative, distancePositive = self
assert lower <= default <= upper

if not extrapolate:
v = max(lower, min(upper, v))

if v == default:
return 0

if default < 0:
return -self.reverse_negate().normalizeValue(-v)
return -self.reverse_negate().normalizeValue(-v, extrapolate=extrapolate)

# default >= 0 and v != default

Expand All @@ -285,6 +288,12 @@ def normalizeValue(self, v):
else:
vDistance = -v * distanceNegative + distancePositive * default

if totalDistance == 0:
# This happens
if default == 0:
return -v / lower
return 0 # Shouldn't happen

Check warning on line 295 in Lib/fontTools/varLib/instancer/__init__.py

View check run for this annotation

Codecov / codecov/patch

Lib/fontTools/varLib/instancer/__init__.py#L295

Added line #L295 was not covered by tests

return -vDistance / totalDistance


Expand Down Expand Up @@ -375,7 +384,7 @@ def normalize(self, varfont, usingAvar=True) -> "NormalizedAxisLimits":
distancePositive = triple[2] - triple[1]

if self[axis_tag] is None:
normalizedLimits[axis_tag] = NormalizedAxisTriple(
normalizedLimits[axis_tag] = NormalizedAxisTripleAndDistances(

Check warning on line 387 in Lib/fontTools/varLib/instancer/__init__.py

View check run for this annotation

Codecov / codecov/patch

Lib/fontTools/varLib/instancer/__init__.py#L387

Added line #L387 was not covered by tests
0, 0, 0, distanceNegative, distancePositive
)
continue
Expand All @@ -386,7 +395,7 @@ def normalize(self, varfont, usingAvar=True) -> "NormalizedAxisLimits":
defaultV = triple[1]

avarMapping = avarSegments.get(axis_tag, None)
normalizedLimits[axis_tag] = NormalizedAxisTriple(
normalizedLimits[axis_tag] = NormalizedAxisTripleAndDistances(
*(normalize(v, triple, avarMapping) for v in (minV, defaultV, maxV)),
distanceNegative,
distancePositive,
Expand All @@ -402,7 +411,7 @@ def __init__(self, *args, **kwargs):
self._data = data = {}
for k, v in dict(*args, **kwargs).items():
try:
triple = NormalizedAxisTriple.expand(v)
triple = NormalizedAxisTripleAndDistances.expand(v)
except ValueError as e:
raise ValueError(f"Invalid axis limits for {k!r}: {v!r}") from e
data[k] = triple
Expand Down Expand Up @@ -486,7 +495,7 @@ def changeTupleVariationsAxisLimits(variations, axisLimits):


def changeTupleVariationAxisLimit(var, axisTag, axisLimit):
assert isinstance(axisLimit, NormalizedAxisTriple)
assert isinstance(axisLimit, NormalizedAxisTripleAndDistances)

# Skip when current axis is missing (i.e. doesn't participate),
lower, peak, upper = var.axes.get(axisTag, (-1, 0, 1))
Expand Down Expand Up @@ -549,7 +558,7 @@ def _instantiateGvarGlyph(
"Instancing accross VarComposite axes with variation is not supported."
)
limits = axisLimits[tag]
loc = limits.normalizeValue(loc)
loc = limits.normalizeValue(loc, extrapolate=False)
newLocation[tag] = loc
component.location = newLocation

Expand Down Expand Up @@ -969,7 +978,7 @@ def instantiateAvar(varfont, axisLimits):
mappedMax = floatToFixedToFloat(
piecewiseLinearMap(axisRange.maximum, mapping), 14
)
mappedAxisLimit = NormalizedAxisTriple(
mappedAxisLimit = NormalizedAxisTripleAndDistances(
mappedMin,
mappedDef,
mappedMax,
Expand Down
9 changes: 5 additions & 4 deletions Lib/fontTools/varLib/instancer/featureVars.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from fontTools.ttLib.tables import otTables as ot
from fontTools.varLib.models import normalizeValue
from copy import deepcopy
import logging

Expand Down Expand Up @@ -41,7 +40,9 @@ def _limitFeatureVariationConditionRange(condition, axisLimit):
# condition invalid or out of range
return

return tuple(axisLimit.normalizeValue(v) for v in (minValue, maxValue))
return tuple(
axisLimit.normalizeValue(v, extrapolate=False) for v in (minValue, maxValue)
)


def _instantiateFeatureVariationRecord(
Expand All @@ -50,9 +51,9 @@ def _instantiateFeatureVariationRecord(
applies = True
shouldKeep = False
newConditions = []
from fontTools.varLib.instancer import NormalizedAxisTriple
from fontTools.varLib.instancer import NormalizedAxisTripleAndDistances

default_triple = NormalizedAxisTriple(-1, 0, +1, 0, 0)
default_triple = NormalizedAxisTripleAndDistances(-1, 0, +1, 0, 0)
for i, condition in enumerate(record.ConditionSet.ConditionTable):
if condition.Format == 1:
axisIdx = condition.AxisIndex
Expand Down
14 changes: 7 additions & 7 deletions Tests/varLib/instancer/instancer_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1950,7 +1950,7 @@ def check_limit_single_var_axis_range(self, var, axisTag, axisRange, expected):
],
)
def test_positive_var(self, var, axisTag, newMax, expected):
axisRange = instancer.NormalizedAxisTriple(0, 0, newMax)
axisRange = instancer.NormalizedAxisTripleAndDistances(0, 0, newMax, 1, 1)
self.check_limit_single_var_axis_range(var, axisTag, axisRange, expected)

@pytest.mark.parametrize(
Expand Down Expand Up @@ -2029,7 +2029,7 @@ def test_positive_var(self, var, axisTag, newMax, expected):
],
)
def test_negative_var(self, var, axisTag, newMin, expected):
axisRange = instancer.NormalizedAxisTriple(newMin, 0, 0)
axisRange = instancer.NormalizedAxisTripleAndDistances(newMin, 0, 0, 1, 1)
self.check_limit_single_var_axis_range(var, axisTag, axisRange, expected)


Expand All @@ -2052,7 +2052,7 @@ def test_limitFeatureVariationConditionRange(oldRange, newLimit, expected):
condition = featureVars.buildConditionTable(0, *oldRange)

result = instancer.featureVars._limitFeatureVariationConditionRange(
condition, instancer.NormalizedAxisTriple(*newLimit)
condition, instancer.NormalizedAxisTripleAndDistances(*newLimit, 1, 1)
)

assert result == expected
Expand Down Expand Up @@ -2094,9 +2094,9 @@ def test_parseLimits_invalid(limits):
@pytest.mark.parametrize(
"limits, expected",
[
({"wght": (100, 400)}, {"wght": (-1.0, 0, 0)}),
({"wght": (100, 400, 400)}, {"wght": (-1.0, 0, 0)}),
({"wght": (100, 300, 400)}, {"wght": (-1.0, -0.5, 0)}),
({"wght": (100, 400)}, {"wght": (-1.0, 0, 0, 300, 500)}),
({"wght": (100, 400, 400)}, {"wght": (-1.0, 0, 0, 300, 500)}),
({"wght": (100, 300, 400)}, {"wght": (-1.0, -0.5, 0, 300, 500)}),
],
)
def test_normalizeAxisLimits(varfont, limits, expected):
Expand All @@ -2113,7 +2113,7 @@ def test_normalizeAxisLimits_no_avar(varfont):
limits = instancer.AxisLimits(wght=(400, 400, 500))
normalized = limits.normalize(varfont)

assert normalized["wght"] == pytest.approx((0, 0, 0.2), 1e-4)
assert normalized["wght"] == pytest.approx((0, 0, 0.2, 300, 500), 1e-4)


def test_normalizeAxisLimits_missing_from_fvar(varfont):
Expand Down
5 changes: 5 additions & 0 deletions Tests/varLib/instancer/solver_test.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from fontTools.varLib.instancer import solver
from fontTools.varLib.instancer import NormalizedAxisTripleAndDistances
import pytest


Expand Down Expand Up @@ -245,6 +246,10 @@ class RebaseTentTest(object):
],
)
def test_rebaseTent(self, tent, axisRange, expected):
if len(axisRange) < 5:
axisRange += (1, 1)
axisRange = NormalizedAxisTripleAndDistances(*axisRange)

sol = solver.rebaseTent(tent, axisRange)

a = pytest.approx
Expand Down

0 comments on commit fbc7541

Please sign in to comment.