* [varLib.models] Add test for modeling rounding error Tests https://github.com/fonttools/fonttools/pull/2214 If you flip demo to True, it does a slower test and demos the new error as well as the error the old code was producing (ie. rounding deltas post-modeling). Indeed, the new error is always capped by 0.5 as expected, whereas the old one was unbounded. Here's the worst-case error of the bad code: ... 240 0.42 4.8 ... 240 is just the line number. 0.42 is new error. 4.8 is old error. * turn test_modeling_error into a parametrized pytest test Like the other test methods in the same module, all those whose name starts with 'test_' are automatically discovered and run by pytest which is our default test runner. So there is no need to call the test method itself in the top-level module scope. One simply runs the test via pytest. To execute this specific test method one can do 'pytest Tests/varLib/models_test.py::test_modeling_error'. * use pytest markers to mark specific test as 'slow' So that one can optionally deselect tests marked with specific marker by passing -m option (e.g. to deselect 'slow' tests, pytest -m 'not slow' ...). https://docs.pytest.org/en/stable/mark.html#registering-marks https://docs.pytest.org/en/stable/example/parametrize.html#set-marks-or-test-id-for-individual-parametrized-test * [varLib/models_test] Comment out non-test code Co-authored-by: Cosimo Lupo <clupo@google.com>
189 lines
6.7 KiB
Python
189 lines
6.7 KiB
Python
from fontTools.varLib.models import (
|
|
normalizeLocation, supportScalar, VariationModel, VariationModelError)
|
|
import pytest
|
|
|
|
|
|
def test_normalizeLocation():
|
|
axes = {"wght": (100, 400, 900)}
|
|
assert normalizeLocation({"wght": 400}, axes) == {'wght': 0.0}
|
|
assert normalizeLocation({"wght": 100}, axes) == {'wght': -1.0}
|
|
assert normalizeLocation({"wght": 900}, axes) == {'wght': 1.0}
|
|
assert normalizeLocation({"wght": 650}, axes) == {'wght': 0.5}
|
|
assert normalizeLocation({"wght": 1000}, axes) == {'wght': 1.0}
|
|
assert normalizeLocation({"wght": 0}, axes) == {'wght': -1.0}
|
|
|
|
axes = {"wght": (0, 0, 1000)}
|
|
assert normalizeLocation({"wght": 0}, axes) == {'wght': 0.0}
|
|
assert normalizeLocation({"wght": -1}, axes) == {'wght': 0.0}
|
|
assert normalizeLocation({"wght": 1000}, axes) == {'wght': 1.0}
|
|
assert normalizeLocation({"wght": 500}, axes) == {'wght': 0.5}
|
|
assert normalizeLocation({"wght": 1001}, axes) == {'wght': 1.0}
|
|
|
|
axes = {"wght": (0, 1000, 1000)}
|
|
assert normalizeLocation({"wght": 0}, axes) == {'wght': -1.0}
|
|
assert normalizeLocation({"wght": -1}, axes) == {'wght': -1.0}
|
|
assert normalizeLocation({"wght": 500}, axes) == {'wght': -0.5}
|
|
assert normalizeLocation({"wght": 1000}, axes) == {'wght': 0.0}
|
|
assert normalizeLocation({"wght": 1001}, axes) == {'wght': 0.0}
|
|
|
|
|
|
def test_supportScalar():
|
|
assert supportScalar({}, {}) == 1.0
|
|
assert supportScalar({'wght':.2}, {}) == 1.0
|
|
assert supportScalar({'wght':.2}, {'wght':(0,2,3)}) == 0.1
|
|
assert supportScalar({'wght':2.5}, {'wght':(0,2,4)}) == 0.75
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"numLocations, numSamples", [
|
|
pytest.param(127, 509, marks=pytest.mark.slow),
|
|
(31, 251),
|
|
]
|
|
)
|
|
def test_modeling_error(numLocations, numSamples):
|
|
# https://github.com/fonttools/fonttools/issues/2213
|
|
locations = [{'axis': float(i)/numLocations} for i in range(numLocations)]
|
|
masterValues = [100. if i else 0. for i in range(numLocations)]
|
|
|
|
model = VariationModel(locations)
|
|
|
|
for i in range(numSamples):
|
|
loc = {'axis': float(i)/numSamples}
|
|
scalars = model.getScalars(loc)
|
|
|
|
deltas_float = model.getDeltas(masterValues)
|
|
deltas_round = model.getDeltas(masterValues, round=round)
|
|
|
|
expected = model.interpolateFromDeltasAndScalars(deltas_float, scalars)
|
|
actual = model.interpolateFromDeltasAndScalars(deltas_round, scalars)
|
|
|
|
err = abs(actual - expected)
|
|
assert err <= .5, (i, err)
|
|
|
|
# This is how NOT to round deltas.
|
|
#deltas_late_round = [round(d) for d in deltas_float]
|
|
#bad = model.interpolateFromDeltasAndScalars(deltas_late_round, scalars)
|
|
#err_bad = abs(bad - expected)
|
|
#if err != err_bad:
|
|
# print("{:d} {:.2} {:.2}".format(i, err, err_bad))
|
|
|
|
|
|
class VariationModelTest(object):
|
|
|
|
@pytest.mark.parametrize(
|
|
"locations, axisOrder, sortedLocs, supports, deltaWeights",
|
|
[
|
|
(
|
|
[
|
|
{'wght': 0.55, 'wdth': 0.0},
|
|
{'wght': -0.55, 'wdth': 0.0},
|
|
{'wght': -1.0, 'wdth': 0.0},
|
|
{'wght': 0.0, 'wdth': 1.0},
|
|
{'wght': 0.66, 'wdth': 1.0},
|
|
{'wght': 0.66, 'wdth': 0.66},
|
|
{'wght': 0.0, 'wdth': 0.0},
|
|
{'wght': 1.0, 'wdth': 1.0},
|
|
{'wght': 1.0, 'wdth': 0.0},
|
|
],
|
|
["wght"],
|
|
[
|
|
{},
|
|
{'wght': -0.55},
|
|
{'wght': -1.0},
|
|
{'wght': 0.55},
|
|
{'wght': 1.0},
|
|
{'wdth': 1.0},
|
|
{'wdth': 1.0, 'wght': 1.0},
|
|
{'wdth': 1.0, 'wght': 0.66},
|
|
{'wdth': 0.66, 'wght': 0.66}
|
|
],
|
|
[
|
|
{},
|
|
{'wght': (-1.0, -0.55, 0)},
|
|
{'wght': (-1.0, -1.0, -0.55)},
|
|
{'wght': (0, 0.55, 1.0)},
|
|
{'wght': (0.55, 1.0, 1.0)},
|
|
{'wdth': (0, 1.0, 1.0)},
|
|
{'wdth': (0, 1.0, 1.0), 'wght': (0, 1.0, 1.0)},
|
|
{'wdth': (0, 1.0, 1.0), 'wght': (0, 0.66, 1.0)},
|
|
{'wdth': (0, 0.66, 1.0), 'wght': (0, 0.66, 1.0)}
|
|
],
|
|
[
|
|
{},
|
|
{0: 1.0},
|
|
{0: 1.0},
|
|
{0: 1.0},
|
|
{0: 1.0},
|
|
{0: 1.0},
|
|
{0: 1.0,
|
|
4: 1.0,
|
|
5: 1.0},
|
|
{0: 1.0,
|
|
3: 0.7555555555555555,
|
|
4: 0.24444444444444444,
|
|
5: 1.0,
|
|
6: 0.66},
|
|
{0: 1.0,
|
|
3: 0.7555555555555555,
|
|
4: 0.24444444444444444,
|
|
5: 0.66,
|
|
6: 0.43560000000000006,
|
|
7: 0.66}
|
|
]
|
|
),
|
|
(
|
|
[
|
|
{},
|
|
{'bar': 0.5},
|
|
{'bar': 1.0},
|
|
{'foo': 1.0},
|
|
{'bar': 0.5, 'foo': 1.0},
|
|
{'bar': 1.0, 'foo': 1.0},
|
|
],
|
|
None,
|
|
[
|
|
{},
|
|
{'bar': 0.5},
|
|
{'bar': 1.0},
|
|
{'foo': 1.0},
|
|
{'bar': 0.5, 'foo': 1.0},
|
|
{'bar': 1.0, 'foo': 1.0},
|
|
],
|
|
[
|
|
{},
|
|
{'bar': (0, 0.5, 1.0)},
|
|
{'bar': (0.5, 1.0, 1.0)},
|
|
{'foo': (0, 1.0, 1.0)},
|
|
{'bar': (0, 0.5, 1.0), 'foo': (0, 1.0, 1.0)},
|
|
{'bar': (0.5, 1.0, 1.0), 'foo': (0, 1.0, 1.0)},
|
|
],
|
|
[
|
|
{},
|
|
{0: 1.0},
|
|
{0: 1.0},
|
|
{0: 1.0},
|
|
{0: 1.0, 1: 1.0, 3: 1.0},
|
|
{0: 1.0, 2: 1.0, 3: 1.0},
|
|
],
|
|
)
|
|
]
|
|
)
|
|
def test_init(
|
|
self, locations, axisOrder, sortedLocs, supports, deltaWeights
|
|
):
|
|
model = VariationModel(locations, axisOrder=axisOrder)
|
|
|
|
assert model.locations == sortedLocs
|
|
assert model.supports == supports
|
|
assert model.deltaWeights == deltaWeights
|
|
|
|
def test_init_duplicate_locations(self):
|
|
with pytest.raises(VariationModelError, match="Locations must be unique."):
|
|
VariationModel(
|
|
[
|
|
{"foo": 0.0, "bar": 0.0},
|
|
{"foo": 1.0, "bar": 1.0},
|
|
{"bar": 1.0, "foo": 1.0},
|
|
]
|
|
)
|