[instancer/L4] Misc fixes and fix tests

This commit is contained in:
Behdad Esfahbod 2023-06-21 14:47:52 -06:00
parent 43e5aae018
commit 204532aee3
4 changed files with 37 additions and 24 deletions

View File

@ -204,8 +204,8 @@ class AxisTriple(Sequence):
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)
@ -234,14 +234,14 @@ class AxisTriple(Sequence):
@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:
@ -256,15 +256,18 @@ class NormalizedAxisTriple(AxisTriple):
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
@ -285,6 +288,12 @@ class NormalizedAxisTriple(AxisTriple):
else:
vDistance = -v * distanceNegative + distancePositive * default
if totalDistance == 0:
# This happens
if default == 0:
return -v / lower
return 0 # Shouldn't happen
return -vDistance / totalDistance
@ -375,7 +384,7 @@ class AxisLimits(_BaseAxisLimits):
distancePositive = triple[2] - triple[1]
if self[axis_tag] is None:
normalizedLimits[axis_tag] = NormalizedAxisTriple(
normalizedLimits[axis_tag] = NormalizedAxisTripleAndDistances(
0, 0, 0, distanceNegative, distancePositive
)
continue
@ -386,7 +395,7 @@ class AxisLimits(_BaseAxisLimits):
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,
@ -402,7 +411,7 @@ class NormalizedAxisLimits(_BaseAxisLimits):
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
@ -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))
@ -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
@ -969,7 +978,7 @@ def instantiateAvar(varfont, axisLimits):
mappedMax = floatToFixedToFloat(
piecewiseLinearMap(axisRange.maximum, mapping), 14
)
mappedAxisLimit = NormalizedAxisTriple(
mappedAxisLimit = NormalizedAxisTripleAndDistances(
mappedMin,
mappedDef,
mappedMax,

View File

@ -1,5 +1,4 @@
from fontTools.ttLib.tables import otTables as ot
from fontTools.varLib.models import normalizeValue
from copy import deepcopy
import logging
@ -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(
@ -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

View File

@ -1950,7 +1950,7 @@ class LimitTupleVariationAxisRangesTest:
],
)
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(
@ -2029,7 +2029,7 @@ class LimitTupleVariationAxisRangesTest:
],
)
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)
@ -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
@ -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):
@ -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):

View File

@ -1,4 +1,5 @@
from fontTools.varLib.instancer import solver
from fontTools.varLib.instancer import NormalizedAxisTripleAndDistances
import pytest
@ -264,6 +265,8 @@ class RebaseTentTest(object):
],
)
def test_rebaseTent(self, tent, axisRange, expected):
axisRange = NormalizedAxisTripleAndDistances(*axisRange)
sol = solver.rebaseTent(tent, axisRange)
a = pytest.approx