xref: /aosp_15_r20/external/fonttools/Tests/varLib/models_test.py (revision e1fe3e4ad2793916b15cccdc4a7da52a7e1dd0e9)
1*e1fe3e4aSElliott Hughesfrom fontTools.varLib.models import (
2*e1fe3e4aSElliott Hughes    normalizeLocation,
3*e1fe3e4aSElliott Hughes    supportScalar,
4*e1fe3e4aSElliott Hughes    VariationModel,
5*e1fe3e4aSElliott Hughes    VariationModelError,
6*e1fe3e4aSElliott Hughes)
7*e1fe3e4aSElliott Hughesimport pytest
8*e1fe3e4aSElliott Hughes
9*e1fe3e4aSElliott Hughes
10*e1fe3e4aSElliott Hughesdef test_normalizeLocation():
11*e1fe3e4aSElliott Hughes    axes = {"wght": (100, 400, 900)}
12*e1fe3e4aSElliott Hughes    assert normalizeLocation({"wght": 400}, axes) == {"wght": 0.0}
13*e1fe3e4aSElliott Hughes    assert normalizeLocation({"wght": 100}, axes) == {"wght": -1.0}
14*e1fe3e4aSElliott Hughes    assert normalizeLocation({"wght": 900}, axes) == {"wght": 1.0}
15*e1fe3e4aSElliott Hughes    assert normalizeLocation({"wght": 650}, axes) == {"wght": 0.5}
16*e1fe3e4aSElliott Hughes    assert normalizeLocation({"wght": 1000}, axes) == {"wght": 1.0}
17*e1fe3e4aSElliott Hughes    assert normalizeLocation({"wght": 0}, axes) == {"wght": -1.0}
18*e1fe3e4aSElliott Hughes
19*e1fe3e4aSElliott Hughes    axes = {"wght": (0, 0, 1000)}
20*e1fe3e4aSElliott Hughes    assert normalizeLocation({"wght": 0}, axes) == {"wght": 0.0}
21*e1fe3e4aSElliott Hughes    assert normalizeLocation({"wght": -1}, axes) == {"wght": 0.0}
22*e1fe3e4aSElliott Hughes    assert normalizeLocation({"wght": 1000}, axes) == {"wght": 1.0}
23*e1fe3e4aSElliott Hughes    assert normalizeLocation({"wght": 500}, axes) == {"wght": 0.5}
24*e1fe3e4aSElliott Hughes    assert normalizeLocation({"wght": 1001}, axes) == {"wght": 1.0}
25*e1fe3e4aSElliott Hughes
26*e1fe3e4aSElliott Hughes    axes = {"wght": (0, 1000, 1000)}
27*e1fe3e4aSElliott Hughes    assert normalizeLocation({"wght": 0}, axes) == {"wght": -1.0}
28*e1fe3e4aSElliott Hughes    assert normalizeLocation({"wght": -1}, axes) == {"wght": -1.0}
29*e1fe3e4aSElliott Hughes    assert normalizeLocation({"wght": 500}, axes) == {"wght": -0.5}
30*e1fe3e4aSElliott Hughes    assert normalizeLocation({"wght": 1000}, axes) == {"wght": 0.0}
31*e1fe3e4aSElliott Hughes    assert normalizeLocation({"wght": 1001}, axes) == {"wght": 0.0}
32*e1fe3e4aSElliott Hughes
33*e1fe3e4aSElliott Hughes
34*e1fe3e4aSElliott Hughes@pytest.mark.parametrize(
35*e1fe3e4aSElliott Hughes    "axes, location, expected",
36*e1fe3e4aSElliott Hughes    [
37*e1fe3e4aSElliott Hughes        # lower != default != upper
38*e1fe3e4aSElliott Hughes        ({"wght": (100, 400, 900)}, {"wght": 1000}, {"wght": 1.2}),
39*e1fe3e4aSElliott Hughes        ({"wght": (100, 400, 900)}, {"wght": 900}, {"wght": 1.0}),
40*e1fe3e4aSElliott Hughes        ({"wght": (100, 400, 900)}, {"wght": 650}, {"wght": 0.5}),
41*e1fe3e4aSElliott Hughes        ({"wght": (100, 400, 900)}, {"wght": 400}, {"wght": 0.0}),
42*e1fe3e4aSElliott Hughes        ({"wght": (100, 400, 900)}, {"wght": 250}, {"wght": -0.5}),
43*e1fe3e4aSElliott Hughes        ({"wght": (100, 400, 900)}, {"wght": 100}, {"wght": -1.0}),
44*e1fe3e4aSElliott Hughes        ({"wght": (100, 400, 900)}, {"wght": 25}, {"wght": -1.25}),
45*e1fe3e4aSElliott Hughes        # lower == default != upper
46*e1fe3e4aSElliott Hughes        (
47*e1fe3e4aSElliott Hughes            {"wght": (400, 400, 900), "wdth": (100, 100, 150)},
48*e1fe3e4aSElliott Hughes            {"wght": 1000, "wdth": 200},
49*e1fe3e4aSElliott Hughes            {"wght": 1.2, "wdth": 2.0},
50*e1fe3e4aSElliott Hughes        ),
51*e1fe3e4aSElliott Hughes        (
52*e1fe3e4aSElliott Hughes            {"wght": (400, 400, 900), "wdth": (100, 100, 150)},
53*e1fe3e4aSElliott Hughes            {"wght": 25, "wdth": 25},
54*e1fe3e4aSElliott Hughes            {"wght": -0.75, "wdth": -1.5},
55*e1fe3e4aSElliott Hughes        ),
56*e1fe3e4aSElliott Hughes        # lower != default == upper
57*e1fe3e4aSElliott Hughes        (
58*e1fe3e4aSElliott Hughes            {"wght": (100, 400, 400), "wdth": (50, 100, 100)},
59*e1fe3e4aSElliott Hughes            {"wght": 700, "wdth": 150},
60*e1fe3e4aSElliott Hughes            {"wght": 1.0, "wdth": 1.0},
61*e1fe3e4aSElliott Hughes        ),
62*e1fe3e4aSElliott Hughes        (
63*e1fe3e4aSElliott Hughes            {"wght": (100, 400, 400), "wdth": (50, 100, 100)},
64*e1fe3e4aSElliott Hughes            {"wght": -50, "wdth": 25},
65*e1fe3e4aSElliott Hughes            {"wght": -1.5, "wdth": -1.5},
66*e1fe3e4aSElliott Hughes        ),
67*e1fe3e4aSElliott Hughes        # degenerate case with lower == default == upper, normalized location always 0
68*e1fe3e4aSElliott Hughes        ({"wght": (400, 400, 400)}, {"wght": 100}, {"wght": 0.0}),
69*e1fe3e4aSElliott Hughes        ({"wght": (400, 400, 400)}, {"wght": 400}, {"wght": 0.0}),
70*e1fe3e4aSElliott Hughes        ({"wght": (400, 400, 400)}, {"wght": 700}, {"wght": 0.0}),
71*e1fe3e4aSElliott Hughes    ],
72*e1fe3e4aSElliott Hughes)
73*e1fe3e4aSElliott Hughesdef test_normalizeLocation_extrapolate(axes, location, expected):
74*e1fe3e4aSElliott Hughes    assert normalizeLocation(location, axes, extrapolate=True) == expected
75*e1fe3e4aSElliott Hughes
76*e1fe3e4aSElliott Hughes
77*e1fe3e4aSElliott Hughesdef test_supportScalar():
78*e1fe3e4aSElliott Hughes    assert supportScalar({}, {}) == 1.0
79*e1fe3e4aSElliott Hughes    assert supportScalar({"wght": 0.2}, {}) == 1.0
80*e1fe3e4aSElliott Hughes    assert supportScalar({"wght": 0.2}, {"wght": (0, 2, 3)}) == 0.1
81*e1fe3e4aSElliott Hughes    assert supportScalar({"wght": 2.5}, {"wght": (0, 2, 4)}) == 0.75
82*e1fe3e4aSElliott Hughes    assert supportScalar({"wght": 3}, {"wght": (0, 2, 2)}) == 0.0
83*e1fe3e4aSElliott Hughes    assert (
84*e1fe3e4aSElliott Hughes        supportScalar(
85*e1fe3e4aSElliott Hughes            {"wght": 3},
86*e1fe3e4aSElliott Hughes            {"wght": (0, 2, 2)},
87*e1fe3e4aSElliott Hughes            extrapolate=True,
88*e1fe3e4aSElliott Hughes            axisRanges={"wght": (0, 2)},
89*e1fe3e4aSElliott Hughes        )
90*e1fe3e4aSElliott Hughes        == 1.5
91*e1fe3e4aSElliott Hughes    )
92*e1fe3e4aSElliott Hughes    assert (
93*e1fe3e4aSElliott Hughes        supportScalar(
94*e1fe3e4aSElliott Hughes            {"wght": -1},
95*e1fe3e4aSElliott Hughes            {"wght": (0, 2, 2)},
96*e1fe3e4aSElliott Hughes            extrapolate=True,
97*e1fe3e4aSElliott Hughes            axisRanges={"wght": (0, 2)},
98*e1fe3e4aSElliott Hughes        )
99*e1fe3e4aSElliott Hughes        == -0.5
100*e1fe3e4aSElliott Hughes    )
101*e1fe3e4aSElliott Hughes    assert (
102*e1fe3e4aSElliott Hughes        supportScalar(
103*e1fe3e4aSElliott Hughes            {"wght": 3},
104*e1fe3e4aSElliott Hughes            {"wght": (0, 1, 2)},
105*e1fe3e4aSElliott Hughes            extrapolate=True,
106*e1fe3e4aSElliott Hughes            axisRanges={"wght": (0, 2)},
107*e1fe3e4aSElliott Hughes        )
108*e1fe3e4aSElliott Hughes        == -1.0
109*e1fe3e4aSElliott Hughes    )
110*e1fe3e4aSElliott Hughes    assert (
111*e1fe3e4aSElliott Hughes        supportScalar(
112*e1fe3e4aSElliott Hughes            {"wght": -1},
113*e1fe3e4aSElliott Hughes            {"wght": (0, 1, 2)},
114*e1fe3e4aSElliott Hughes            extrapolate=True,
115*e1fe3e4aSElliott Hughes            axisRanges={"wght": (0, 2)},
116*e1fe3e4aSElliott Hughes        )
117*e1fe3e4aSElliott Hughes        == -1.0
118*e1fe3e4aSElliott Hughes    )
119*e1fe3e4aSElliott Hughes    assert (
120*e1fe3e4aSElliott Hughes        supportScalar(
121*e1fe3e4aSElliott Hughes            {"wght": 2},
122*e1fe3e4aSElliott Hughes            {"wght": (0, 0.75, 1)},
123*e1fe3e4aSElliott Hughes            extrapolate=True,
124*e1fe3e4aSElliott Hughes            axisRanges={"wght": (0, 1)},
125*e1fe3e4aSElliott Hughes        )
126*e1fe3e4aSElliott Hughes        == -4.0
127*e1fe3e4aSElliott Hughes    )
128*e1fe3e4aSElliott Hughes    with pytest.raises(TypeError):
129*e1fe3e4aSElliott Hughes        supportScalar(
130*e1fe3e4aSElliott Hughes            {"wght": 2}, {"wght": (0, 0.75, 1)}, extrapolate=True, axisRanges=None
131*e1fe3e4aSElliott Hughes        )
132*e1fe3e4aSElliott Hughes
133*e1fe3e4aSElliott Hughes
134*e1fe3e4aSElliott Hughesdef test_model_extrapolate():
135*e1fe3e4aSElliott Hughes    locations = [{}, {"a": 1}, {"b": 1}, {"a": 1, "b": 1}]
136*e1fe3e4aSElliott Hughes    model = VariationModel(locations, extrapolate=True)
137*e1fe3e4aSElliott Hughes    masterValues = [100, 200, 300, 400]
138*e1fe3e4aSElliott Hughes    testLocsAndValues = [
139*e1fe3e4aSElliott Hughes        ({"a": -1, "b": -1}, -200),
140*e1fe3e4aSElliott Hughes        ({"a": -1, "b": 0}, 0),
141*e1fe3e4aSElliott Hughes        ({"a": -1, "b": 1}, 200),
142*e1fe3e4aSElliott Hughes        ({"a": -1, "b": 2}, 400),
143*e1fe3e4aSElliott Hughes        ({"a": 0, "b": -1}, -100),
144*e1fe3e4aSElliott Hughes        ({"a": 0, "b": 0}, 100),
145*e1fe3e4aSElliott Hughes        ({"a": 0, "b": 1}, 300),
146*e1fe3e4aSElliott Hughes        ({"a": 0, "b": 2}, 500),
147*e1fe3e4aSElliott Hughes        ({"a": 1, "b": -1}, 0),
148*e1fe3e4aSElliott Hughes        ({"a": 1, "b": 0}, 200),
149*e1fe3e4aSElliott Hughes        ({"a": 1, "b": 1}, 400),
150*e1fe3e4aSElliott Hughes        ({"a": 1, "b": 2}, 600),
151*e1fe3e4aSElliott Hughes        ({"a": 2, "b": -1}, 100),
152*e1fe3e4aSElliott Hughes        ({"a": 2, "b": 0}, 300),
153*e1fe3e4aSElliott Hughes        ({"a": 2, "b": 1}, 500),
154*e1fe3e4aSElliott Hughes        ({"a": 2, "b": 2}, 700),
155*e1fe3e4aSElliott Hughes    ]
156*e1fe3e4aSElliott Hughes    for loc, expectedValue in testLocsAndValues:
157*e1fe3e4aSElliott Hughes        assert expectedValue == model.interpolateFromMasters(loc, masterValues)
158*e1fe3e4aSElliott Hughes
159*e1fe3e4aSElliott Hughes
160*e1fe3e4aSElliott Hughes@pytest.mark.parametrize(
161*e1fe3e4aSElliott Hughes    "numLocations, numSamples",
162*e1fe3e4aSElliott Hughes    [
163*e1fe3e4aSElliott Hughes        pytest.param(127, 509, marks=pytest.mark.slow),
164*e1fe3e4aSElliott Hughes        (31, 251),
165*e1fe3e4aSElliott Hughes    ],
166*e1fe3e4aSElliott Hughes)
167*e1fe3e4aSElliott Hughesdef test_modeling_error(numLocations, numSamples):
168*e1fe3e4aSElliott Hughes    # https://github.com/fonttools/fonttools/issues/2213
169*e1fe3e4aSElliott Hughes    locations = [{"axis": float(i) / numLocations} for i in range(numLocations)]
170*e1fe3e4aSElliott Hughes    masterValues = [100.0 if i else 0.0 for i in range(numLocations)]
171*e1fe3e4aSElliott Hughes
172*e1fe3e4aSElliott Hughes    model = VariationModel(locations)
173*e1fe3e4aSElliott Hughes
174*e1fe3e4aSElliott Hughes    for i in range(numSamples):
175*e1fe3e4aSElliott Hughes        loc = {"axis": float(i) / numSamples}
176*e1fe3e4aSElliott Hughes        scalars = model.getScalars(loc)
177*e1fe3e4aSElliott Hughes
178*e1fe3e4aSElliott Hughes        deltas_float = model.getDeltas(masterValues)
179*e1fe3e4aSElliott Hughes        deltas_round = model.getDeltas(masterValues, round=round)
180*e1fe3e4aSElliott Hughes
181*e1fe3e4aSElliott Hughes        expected = model.interpolateFromDeltasAndScalars(deltas_float, scalars)
182*e1fe3e4aSElliott Hughes        actual = model.interpolateFromDeltasAndScalars(deltas_round, scalars)
183*e1fe3e4aSElliott Hughes
184*e1fe3e4aSElliott Hughes        err = abs(actual - expected)
185*e1fe3e4aSElliott Hughes        assert err <= 0.5, (i, err)
186*e1fe3e4aSElliott Hughes
187*e1fe3e4aSElliott Hughes        # This is how NOT to round deltas.
188*e1fe3e4aSElliott Hughes        # deltas_late_round = [round(d) for d in deltas_float]
189*e1fe3e4aSElliott Hughes        # bad = model.interpolateFromDeltasAndScalars(deltas_late_round, scalars)
190*e1fe3e4aSElliott Hughes        # err_bad = abs(bad - expected)
191*e1fe3e4aSElliott Hughes        # if err != err_bad:
192*e1fe3e4aSElliott Hughes        #    print("{:d}	{:.2}	{:.2}".format(i, err, err_bad))
193*e1fe3e4aSElliott Hughes
194*e1fe3e4aSElliott Hughes
195*e1fe3e4aSElliott HugheslocationsA = [{}, {"wght": 1}, {"wdth": 1}]
196*e1fe3e4aSElliott HugheslocationsB = [{}, {"wght": 1}, {"wdth": 1}, {"wght": 1, "wdth": 1}]
197*e1fe3e4aSElliott HugheslocationsC = [
198*e1fe3e4aSElliott Hughes    {},
199*e1fe3e4aSElliott Hughes    {"wght": 0.5},
200*e1fe3e4aSElliott Hughes    {"wght": 1},
201*e1fe3e4aSElliott Hughes    {"wdth": 1},
202*e1fe3e4aSElliott Hughes    {"wght": 1, "wdth": 1},
203*e1fe3e4aSElliott Hughes]
204*e1fe3e4aSElliott Hughes
205*e1fe3e4aSElliott Hughes
206*e1fe3e4aSElliott Hughesclass VariationModelTest(object):
207*e1fe3e4aSElliott Hughes    @pytest.mark.parametrize(
208*e1fe3e4aSElliott Hughes        "locations, axisOrder, sortedLocs, supports, deltaWeights",
209*e1fe3e4aSElliott Hughes        [
210*e1fe3e4aSElliott Hughes            (
211*e1fe3e4aSElliott Hughes                [
212*e1fe3e4aSElliott Hughes                    {"wght": 0.55, "wdth": 0.0},
213*e1fe3e4aSElliott Hughes                    {"wght": -0.55, "wdth": 0.0},
214*e1fe3e4aSElliott Hughes                    {"wght": -1.0, "wdth": 0.0},
215*e1fe3e4aSElliott Hughes                    {"wght": 0.0, "wdth": 1.0},
216*e1fe3e4aSElliott Hughes                    {"wght": 0.66, "wdth": 1.0},
217*e1fe3e4aSElliott Hughes                    {"wght": 0.66, "wdth": 0.66},
218*e1fe3e4aSElliott Hughes                    {"wght": 0.0, "wdth": 0.0},
219*e1fe3e4aSElliott Hughes                    {"wght": 1.0, "wdth": 1.0},
220*e1fe3e4aSElliott Hughes                    {"wght": 1.0, "wdth": 0.0},
221*e1fe3e4aSElliott Hughes                ],
222*e1fe3e4aSElliott Hughes                ["wght"],
223*e1fe3e4aSElliott Hughes                [
224*e1fe3e4aSElliott Hughes                    {},
225*e1fe3e4aSElliott Hughes                    {"wght": -0.55},
226*e1fe3e4aSElliott Hughes                    {"wght": -1.0},
227*e1fe3e4aSElliott Hughes                    {"wght": 0.55},
228*e1fe3e4aSElliott Hughes                    {"wght": 1.0},
229*e1fe3e4aSElliott Hughes                    {"wdth": 1.0},
230*e1fe3e4aSElliott Hughes                    {"wdth": 1.0, "wght": 1.0},
231*e1fe3e4aSElliott Hughes                    {"wdth": 1.0, "wght": 0.66},
232*e1fe3e4aSElliott Hughes                    {"wdth": 0.66, "wght": 0.66},
233*e1fe3e4aSElliott Hughes                ],
234*e1fe3e4aSElliott Hughes                [
235*e1fe3e4aSElliott Hughes                    {},
236*e1fe3e4aSElliott Hughes                    {"wght": (-1.0, -0.55, 0)},
237*e1fe3e4aSElliott Hughes                    {"wght": (-1.0, -1.0, -0.55)},
238*e1fe3e4aSElliott Hughes                    {"wght": (0, 0.55, 1.0)},
239*e1fe3e4aSElliott Hughes                    {"wght": (0.55, 1.0, 1.0)},
240*e1fe3e4aSElliott Hughes                    {"wdth": (0, 1.0, 1.0)},
241*e1fe3e4aSElliott Hughes                    {"wdth": (0, 1.0, 1.0), "wght": (0, 1.0, 1.0)},
242*e1fe3e4aSElliott Hughes                    {"wdth": (0, 1.0, 1.0), "wght": (0, 0.66, 1.0)},
243*e1fe3e4aSElliott Hughes                    {"wdth": (0, 0.66, 1.0), "wght": (0, 0.66, 1.0)},
244*e1fe3e4aSElliott Hughes                ],
245*e1fe3e4aSElliott Hughes                [
246*e1fe3e4aSElliott Hughes                    {},
247*e1fe3e4aSElliott Hughes                    {0: 1.0},
248*e1fe3e4aSElliott Hughes                    {0: 1.0},
249*e1fe3e4aSElliott Hughes                    {0: 1.0},
250*e1fe3e4aSElliott Hughes                    {0: 1.0},
251*e1fe3e4aSElliott Hughes                    {0: 1.0},
252*e1fe3e4aSElliott Hughes                    {0: 1.0, 4: 1.0, 5: 1.0},
253*e1fe3e4aSElliott Hughes                    {
254*e1fe3e4aSElliott Hughes                        0: 1.0,
255*e1fe3e4aSElliott Hughes                        3: 0.7555555555555555,
256*e1fe3e4aSElliott Hughes                        4: 0.24444444444444444,
257*e1fe3e4aSElliott Hughes                        5: 1.0,
258*e1fe3e4aSElliott Hughes                        6: 0.66,
259*e1fe3e4aSElliott Hughes                    },
260*e1fe3e4aSElliott Hughes                    {
261*e1fe3e4aSElliott Hughes                        0: 1.0,
262*e1fe3e4aSElliott Hughes                        3: 0.7555555555555555,
263*e1fe3e4aSElliott Hughes                        4: 0.24444444444444444,
264*e1fe3e4aSElliott Hughes                        5: 0.66,
265*e1fe3e4aSElliott Hughes                        6: 0.43560000000000004,
266*e1fe3e4aSElliott Hughes                        7: 0.66,
267*e1fe3e4aSElliott Hughes                    },
268*e1fe3e4aSElliott Hughes                ],
269*e1fe3e4aSElliott Hughes            ),
270*e1fe3e4aSElliott Hughes            (
271*e1fe3e4aSElliott Hughes                [
272*e1fe3e4aSElliott Hughes                    {},
273*e1fe3e4aSElliott Hughes                    {"bar": 0.5},
274*e1fe3e4aSElliott Hughes                    {"bar": 1.0},
275*e1fe3e4aSElliott Hughes                    {"foo": 1.0},
276*e1fe3e4aSElliott Hughes                    {"bar": 0.5, "foo": 1.0},
277*e1fe3e4aSElliott Hughes                    {"bar": 1.0, "foo": 1.0},
278*e1fe3e4aSElliott Hughes                ],
279*e1fe3e4aSElliott Hughes                None,
280*e1fe3e4aSElliott Hughes                [
281*e1fe3e4aSElliott Hughes                    {},
282*e1fe3e4aSElliott Hughes                    {"bar": 0.5},
283*e1fe3e4aSElliott Hughes                    {"bar": 1.0},
284*e1fe3e4aSElliott Hughes                    {"foo": 1.0},
285*e1fe3e4aSElliott Hughes                    {"bar": 0.5, "foo": 1.0},
286*e1fe3e4aSElliott Hughes                    {"bar": 1.0, "foo": 1.0},
287*e1fe3e4aSElliott Hughes                ],
288*e1fe3e4aSElliott Hughes                [
289*e1fe3e4aSElliott Hughes                    {},
290*e1fe3e4aSElliott Hughes                    {"bar": (0, 0.5, 1.0)},
291*e1fe3e4aSElliott Hughes                    {"bar": (0.5, 1.0, 1.0)},
292*e1fe3e4aSElliott Hughes                    {"foo": (0, 1.0, 1.0)},
293*e1fe3e4aSElliott Hughes                    {"bar": (0, 0.5, 1.0), "foo": (0, 1.0, 1.0)},
294*e1fe3e4aSElliott Hughes                    {"bar": (0.5, 1.0, 1.0), "foo": (0, 1.0, 1.0)},
295*e1fe3e4aSElliott Hughes                ],
296*e1fe3e4aSElliott Hughes                [
297*e1fe3e4aSElliott Hughes                    {},
298*e1fe3e4aSElliott Hughes                    {0: 1.0},
299*e1fe3e4aSElliott Hughes                    {0: 1.0},
300*e1fe3e4aSElliott Hughes                    {0: 1.0},
301*e1fe3e4aSElliott Hughes                    {0: 1.0, 1: 1.0, 3: 1.0},
302*e1fe3e4aSElliott Hughes                    {0: 1.0, 2: 1.0, 3: 1.0},
303*e1fe3e4aSElliott Hughes                ],
304*e1fe3e4aSElliott Hughes            ),
305*e1fe3e4aSElliott Hughes            (
306*e1fe3e4aSElliott Hughes                [
307*e1fe3e4aSElliott Hughes                    {},
308*e1fe3e4aSElliott Hughes                    {"foo": 0.25},
309*e1fe3e4aSElliott Hughes                    {"foo": 0.5},
310*e1fe3e4aSElliott Hughes                    {"foo": 0.75},
311*e1fe3e4aSElliott Hughes                    {"foo": 1.0},
312*e1fe3e4aSElliott Hughes                    {"bar": 0.25},
313*e1fe3e4aSElliott Hughes                    {"bar": 0.75},
314*e1fe3e4aSElliott Hughes                    {"bar": 1.0},
315*e1fe3e4aSElliott Hughes                ],
316*e1fe3e4aSElliott Hughes                None,
317*e1fe3e4aSElliott Hughes                [
318*e1fe3e4aSElliott Hughes                    {},
319*e1fe3e4aSElliott Hughes                    {"bar": 0.25},
320*e1fe3e4aSElliott Hughes                    {"bar": 0.75},
321*e1fe3e4aSElliott Hughes                    {"bar": 1.0},
322*e1fe3e4aSElliott Hughes                    {"foo": 0.25},
323*e1fe3e4aSElliott Hughes                    {"foo": 0.5},
324*e1fe3e4aSElliott Hughes                    {"foo": 0.75},
325*e1fe3e4aSElliott Hughes                    {"foo": 1.0},
326*e1fe3e4aSElliott Hughes                ],
327*e1fe3e4aSElliott Hughes                [
328*e1fe3e4aSElliott Hughes                    {},
329*e1fe3e4aSElliott Hughes                    {"bar": (0.0, 0.25, 1.0)},
330*e1fe3e4aSElliott Hughes                    {"bar": (0.25, 0.75, 1.0)},
331*e1fe3e4aSElliott Hughes                    {"bar": (0.75, 1.0, 1.0)},
332*e1fe3e4aSElliott Hughes                    {"foo": (0.0, 0.25, 1.0)},
333*e1fe3e4aSElliott Hughes                    {"foo": (0.25, 0.5, 1.0)},
334*e1fe3e4aSElliott Hughes                    {"foo": (0.5, 0.75, 1.0)},
335*e1fe3e4aSElliott Hughes                    {"foo": (0.75, 1.0, 1.0)},
336*e1fe3e4aSElliott Hughes                ],
337*e1fe3e4aSElliott Hughes                [
338*e1fe3e4aSElliott Hughes                    {},
339*e1fe3e4aSElliott Hughes                    {0: 1.0},
340*e1fe3e4aSElliott Hughes                    {0: 1.0, 1: 0.3333333333333333},
341*e1fe3e4aSElliott Hughes                    {0: 1.0},
342*e1fe3e4aSElliott Hughes                    {0: 1.0},
343*e1fe3e4aSElliott Hughes                    {0: 1.0, 4: 0.6666666666666666},
344*e1fe3e4aSElliott Hughes                    {0: 1.0, 4: 0.3333333333333333, 5: 0.5},
345*e1fe3e4aSElliott Hughes                    {0: 1.0},
346*e1fe3e4aSElliott Hughes                ],
347*e1fe3e4aSElliott Hughes            ),
348*e1fe3e4aSElliott Hughes            (
349*e1fe3e4aSElliott Hughes                [
350*e1fe3e4aSElliott Hughes                    {},
351*e1fe3e4aSElliott Hughes                    {"foo": 0.25},
352*e1fe3e4aSElliott Hughes                    {"foo": 0.5},
353*e1fe3e4aSElliott Hughes                    {"foo": 0.75},
354*e1fe3e4aSElliott Hughes                    {"foo": 1.0},
355*e1fe3e4aSElliott Hughes                    {"bar": 0.25},
356*e1fe3e4aSElliott Hughes                    {"bar": 0.75},
357*e1fe3e4aSElliott Hughes                    {"bar": 1.0},
358*e1fe3e4aSElliott Hughes                ],
359*e1fe3e4aSElliott Hughes                None,
360*e1fe3e4aSElliott Hughes                [
361*e1fe3e4aSElliott Hughes                    {},
362*e1fe3e4aSElliott Hughes                    {"bar": 0.25},
363*e1fe3e4aSElliott Hughes                    {"bar": 0.75},
364*e1fe3e4aSElliott Hughes                    {"bar": 1.0},
365*e1fe3e4aSElliott Hughes                    {"foo": 0.25},
366*e1fe3e4aSElliott Hughes                    {"foo": 0.5},
367*e1fe3e4aSElliott Hughes                    {"foo": 0.75},
368*e1fe3e4aSElliott Hughes                    {"foo": 1.0},
369*e1fe3e4aSElliott Hughes                ],
370*e1fe3e4aSElliott Hughes                [
371*e1fe3e4aSElliott Hughes                    {},
372*e1fe3e4aSElliott Hughes                    {"bar": (0, 0.25, 1.0)},
373*e1fe3e4aSElliott Hughes                    {"bar": (0.25, 0.75, 1.0)},
374*e1fe3e4aSElliott Hughes                    {"bar": (0.75, 1.0, 1.0)},
375*e1fe3e4aSElliott Hughes                    {"foo": (0, 0.25, 1.0)},
376*e1fe3e4aSElliott Hughes                    {"foo": (0.25, 0.5, 1.0)},
377*e1fe3e4aSElliott Hughes                    {"foo": (0.5, 0.75, 1.0)},
378*e1fe3e4aSElliott Hughes                    {"foo": (0.75, 1.0, 1.0)},
379*e1fe3e4aSElliott Hughes                ],
380*e1fe3e4aSElliott Hughes                [
381*e1fe3e4aSElliott Hughes                    {},
382*e1fe3e4aSElliott Hughes                    {0: 1.0},
383*e1fe3e4aSElliott Hughes                    {0: 1.0, 1: 0.3333333333333333},
384*e1fe3e4aSElliott Hughes                    {0: 1.0},
385*e1fe3e4aSElliott Hughes                    {0: 1.0},
386*e1fe3e4aSElliott Hughes                    {0: 1.0, 4: 0.6666666666666666},
387*e1fe3e4aSElliott Hughes                    {0: 1.0, 4: 0.3333333333333333, 5: 0.5},
388*e1fe3e4aSElliott Hughes                    {0: 1.0},
389*e1fe3e4aSElliott Hughes                ],
390*e1fe3e4aSElliott Hughes            ),
391*e1fe3e4aSElliott Hughes        ],
392*e1fe3e4aSElliott Hughes    )
393*e1fe3e4aSElliott Hughes    def test_init(self, locations, axisOrder, sortedLocs, supports, deltaWeights):
394*e1fe3e4aSElliott Hughes        model = VariationModel(locations, axisOrder=axisOrder)
395*e1fe3e4aSElliott Hughes
396*e1fe3e4aSElliott Hughes        assert model.locations == sortedLocs
397*e1fe3e4aSElliott Hughes        assert model.supports == supports
398*e1fe3e4aSElliott Hughes        assert model.deltaWeights == deltaWeights
399*e1fe3e4aSElliott Hughes
400*e1fe3e4aSElliott Hughes    def test_init_duplicate_locations(self):
401*e1fe3e4aSElliott Hughes        with pytest.raises(VariationModelError, match="Locations must be unique."):
402*e1fe3e4aSElliott Hughes            VariationModel(
403*e1fe3e4aSElliott Hughes                [
404*e1fe3e4aSElliott Hughes                    {"foo": 0.0, "bar": 0.0},
405*e1fe3e4aSElliott Hughes                    {"foo": 1.0, "bar": 1.0},
406*e1fe3e4aSElliott Hughes                    {"bar": 1.0, "foo": 1.0},
407*e1fe3e4aSElliott Hughes                ]
408*e1fe3e4aSElliott Hughes            )
409*e1fe3e4aSElliott Hughes
410*e1fe3e4aSElliott Hughes    @pytest.mark.parametrize(
411*e1fe3e4aSElliott Hughes        "locations, axisOrder, masterValues, instanceLocation, expectedValue, masterScalars",
412*e1fe3e4aSElliott Hughes        [
413*e1fe3e4aSElliott Hughes            (
414*e1fe3e4aSElliott Hughes                [
415*e1fe3e4aSElliott Hughes                    {},
416*e1fe3e4aSElliott Hughes                    {"axis_A": 1.0},
417*e1fe3e4aSElliott Hughes                    {"axis_B": 1.0},
418*e1fe3e4aSElliott Hughes                    {"axis_A": 1.0, "axis_B": 1.0},
419*e1fe3e4aSElliott Hughes                    {"axis_A": 0.5, "axis_B": 1.0},
420*e1fe3e4aSElliott Hughes                    {"axis_A": 1.0, "axis_B": 0.5},
421*e1fe3e4aSElliott Hughes                ],
422*e1fe3e4aSElliott Hughes                ["axis_A", "axis_B"],
423*e1fe3e4aSElliott Hughes                [
424*e1fe3e4aSElliott Hughes                    0,
425*e1fe3e4aSElliott Hughes                    10,
426*e1fe3e4aSElliott Hughes                    20,
427*e1fe3e4aSElliott Hughes                    70,
428*e1fe3e4aSElliott Hughes                    50,
429*e1fe3e4aSElliott Hughes                    60,
430*e1fe3e4aSElliott Hughes                ],
431*e1fe3e4aSElliott Hughes                {
432*e1fe3e4aSElliott Hughes                    "axis_A": 0.5,
433*e1fe3e4aSElliott Hughes                    "axis_B": 0.5,
434*e1fe3e4aSElliott Hughes                },
435*e1fe3e4aSElliott Hughes                37.5,
436*e1fe3e4aSElliott Hughes                [0.25, 0.0, 0.0, -0.25, 0.5, 0.5],
437*e1fe3e4aSElliott Hughes            ),
438*e1fe3e4aSElliott Hughes        ],
439*e1fe3e4aSElliott Hughes    )
440*e1fe3e4aSElliott Hughes    def test_interpolation(
441*e1fe3e4aSElliott Hughes        self,
442*e1fe3e4aSElliott Hughes        locations,
443*e1fe3e4aSElliott Hughes        axisOrder,
444*e1fe3e4aSElliott Hughes        masterValues,
445*e1fe3e4aSElliott Hughes        instanceLocation,
446*e1fe3e4aSElliott Hughes        expectedValue,
447*e1fe3e4aSElliott Hughes        masterScalars,
448*e1fe3e4aSElliott Hughes    ):
449*e1fe3e4aSElliott Hughes        model = VariationModel(locations, axisOrder=axisOrder)
450*e1fe3e4aSElliott Hughes
451*e1fe3e4aSElliott Hughes        interpolatedValue = model.interpolateFromMasters(instanceLocation, masterValues)
452*e1fe3e4aSElliott Hughes        assert interpolatedValue == expectedValue
453*e1fe3e4aSElliott Hughes
454*e1fe3e4aSElliott Hughes        assert masterScalars == model.getMasterScalars(instanceLocation)
455*e1fe3e4aSElliott Hughes
456*e1fe3e4aSElliott Hughes        assert model.interpolateFromValuesAndScalars(
457*e1fe3e4aSElliott Hughes            masterValues, masterScalars
458*e1fe3e4aSElliott Hughes        ) == pytest.approx(interpolatedValue)
459*e1fe3e4aSElliott Hughes
460*e1fe3e4aSElliott Hughes    @pytest.mark.parametrize(
461*e1fe3e4aSElliott Hughes        "masterLocations, location, expected",
462*e1fe3e4aSElliott Hughes        [
463*e1fe3e4aSElliott Hughes            (locationsA, {"wght": 0, "wdth": 0}, [1, 0, 0]),
464*e1fe3e4aSElliott Hughes            (
465*e1fe3e4aSElliott Hughes                locationsA,
466*e1fe3e4aSElliott Hughes                {"wght": 0.5, "wdth": 0},
467*e1fe3e4aSElliott Hughes                [0.5, 0.5, 0],
468*e1fe3e4aSElliott Hughes            ),
469*e1fe3e4aSElliott Hughes            (locationsA, {"wght": 1, "wdth": 0}, [0, 1, 0]),
470*e1fe3e4aSElliott Hughes            (
471*e1fe3e4aSElliott Hughes                locationsA,
472*e1fe3e4aSElliott Hughes                {"wght": 0, "wdth": 0.5},
473*e1fe3e4aSElliott Hughes                [0.5, 0, 0.5],
474*e1fe3e4aSElliott Hughes            ),
475*e1fe3e4aSElliott Hughes            (locationsA, {"wght": 0, "wdth": 1}, [0, 0, 1]),
476*e1fe3e4aSElliott Hughes            (locationsA, {"wght": 1, "wdth": 1}, [-1, 1, 1]),
477*e1fe3e4aSElliott Hughes            (
478*e1fe3e4aSElliott Hughes                locationsA,
479*e1fe3e4aSElliott Hughes                {"wght": 0.5, "wdth": 0.5},
480*e1fe3e4aSElliott Hughes                [0, 0.5, 0.5],
481*e1fe3e4aSElliott Hughes            ),
482*e1fe3e4aSElliott Hughes            (
483*e1fe3e4aSElliott Hughes                locationsA,
484*e1fe3e4aSElliott Hughes                {"wght": 0.75, "wdth": 0.75},
485*e1fe3e4aSElliott Hughes                [-0.5, 0.75, 0.75],
486*e1fe3e4aSElliott Hughes            ),
487*e1fe3e4aSElliott Hughes            (
488*e1fe3e4aSElliott Hughes                locationsB,
489*e1fe3e4aSElliott Hughes                {"wght": 1, "wdth": 1},
490*e1fe3e4aSElliott Hughes                [0, 0, 0, 1],
491*e1fe3e4aSElliott Hughes            ),
492*e1fe3e4aSElliott Hughes            (
493*e1fe3e4aSElliott Hughes                locationsB,
494*e1fe3e4aSElliott Hughes                {"wght": 0.5, "wdth": 0},
495*e1fe3e4aSElliott Hughes                [0.5, 0.5, 0, 0],
496*e1fe3e4aSElliott Hughes            ),
497*e1fe3e4aSElliott Hughes            (
498*e1fe3e4aSElliott Hughes                locationsB,
499*e1fe3e4aSElliott Hughes                {"wght": 1, "wdth": 0.5},
500*e1fe3e4aSElliott Hughes                [0, 0.5, 0, 0.5],
501*e1fe3e4aSElliott Hughes            ),
502*e1fe3e4aSElliott Hughes            (
503*e1fe3e4aSElliott Hughes                locationsB,
504*e1fe3e4aSElliott Hughes                {"wght": 0.5, "wdth": 0.5},
505*e1fe3e4aSElliott Hughes                [0.25, 0.25, 0.25, 0.25],
506*e1fe3e4aSElliott Hughes            ),
507*e1fe3e4aSElliott Hughes            (
508*e1fe3e4aSElliott Hughes                locationsC,
509*e1fe3e4aSElliott Hughes                {"wght": 0.5, "wdth": 0},
510*e1fe3e4aSElliott Hughes                [0, 1, 0, 0, 0],
511*e1fe3e4aSElliott Hughes            ),
512*e1fe3e4aSElliott Hughes            (
513*e1fe3e4aSElliott Hughes                locationsC,
514*e1fe3e4aSElliott Hughes                {"wght": 0.25, "wdth": 0},
515*e1fe3e4aSElliott Hughes                [0.5, 0.5, 0, 0, 0],
516*e1fe3e4aSElliott Hughes            ),
517*e1fe3e4aSElliott Hughes            (
518*e1fe3e4aSElliott Hughes                locationsC,
519*e1fe3e4aSElliott Hughes                {"wght": 0.75, "wdth": 0},
520*e1fe3e4aSElliott Hughes                [0, 0.5, 0.5, 0, 0],
521*e1fe3e4aSElliott Hughes            ),
522*e1fe3e4aSElliott Hughes            (
523*e1fe3e4aSElliott Hughes                locationsC,
524*e1fe3e4aSElliott Hughes                {"wght": 0.5, "wdth": 1},
525*e1fe3e4aSElliott Hughes                [-0.5, 1, -0.5, 0.5, 0.5],
526*e1fe3e4aSElliott Hughes            ),
527*e1fe3e4aSElliott Hughes            (
528*e1fe3e4aSElliott Hughes                locationsC,
529*e1fe3e4aSElliott Hughes                {"wght": 0.75, "wdth": 1},
530*e1fe3e4aSElliott Hughes                [-0.25, 0.5, -0.25, 0.25, 0.75],
531*e1fe3e4aSElliott Hughes            ),
532*e1fe3e4aSElliott Hughes        ],
533*e1fe3e4aSElliott Hughes    )
534*e1fe3e4aSElliott Hughes    def test_getMasterScalars(self, masterLocations, location, expected):
535*e1fe3e4aSElliott Hughes        model = VariationModel(masterLocations)
536*e1fe3e4aSElliott Hughes        assert model.getMasterScalars(location) == expected
537