1from __future__ import print_function, division, absolute_import
2from fontTools.misc.py23 import *
3from fontTools.misc.bezierTools import (
4    calcQuadraticBounds, calcCubicBounds, splitLine, splitQuadratic,
5    splitCubic, splitQuadraticAtT, splitCubicAtT, solveCubic)
6import pytest
7
8
9def test_calcQuadraticBounds():
10    assert calcQuadraticBounds(
11        (0, 0), (50, 100), (100, 0)) == (0, 0, 100, 50.0)
12    assert calcQuadraticBounds(
13        (0, 0), (100, 0), (100, 100)) == (0.0, 0.0, 100, 100)
14
15
16def test_calcCubicBounds():
17    assert calcCubicBounds(
18        (0, 0), (25, 100), (75, 100), (100, 0)) == ((0, 0, 100, 75.0))
19    assert calcCubicBounds(
20        (0, 0), (50, 0), (100, 50), (100, 100)) == (0.0, 0.0, 100, 100)
21    assert calcCubicBounds(
22        (50, 0), (0, 100), (100, 100), (50, 0)
23    ) == pytest.approx((35.566243, 0.000000, 64.433757, 75.000000))
24
25
26def test_splitLine():
27    assert splitLine(
28        (0, 0), (100, 100), where=50, isHorizontal=True
29    ) == [((0, 0), (50.0, 50.0)), ((50.0, 50.0), (100, 100))]
30    assert splitLine(
31        (0, 0), (100, 100), where=100, isHorizontal=True
32    ) == [((0, 0), (100, 100))]
33    assert splitLine(
34        (0, 0), (100, 100), where=0, isHorizontal=True
35    ) == [((0, 0), (0, 0)), ((0, 0), (100, 100))]
36    assert splitLine(
37        (0, 0), (100, 100), where=0, isHorizontal=False
38    ) == [((0, 0), (0, 0)), ((0, 0), (100, 100))]
39    assert splitLine(
40        (100, 0), (0, 0), where=50, isHorizontal=False
41    ) == [((100, 0), (50, 0)), ((50, 0), (0, 0))]
42    assert splitLine(
43        (0, 100), (0, 0), where=50, isHorizontal=True
44    ) == [((0, 100), (0, 50)), ((0, 50), (0, 0))]
45    assert splitLine(
46        (0, 100), (100, 100), where=50, isHorizontal=True
47    ) == [((0, 100), (100, 100))]
48
49
50def assert_curves_approx_equal(actual_curves, expected_curves):
51    assert len(actual_curves) == len(expected_curves)
52    for acurve, ecurve in zip(actual_curves, expected_curves):
53        assert len(acurve) == len(ecurve)
54        for apt, ept in zip(acurve, ecurve):
55            assert apt == pytest.approx(ept)
56
57
58def test_splitQuadratic():
59    assert splitQuadratic(
60        (0, 0), (50, 100), (100, 0), where=150, isHorizontal=False
61    ) == [((0, 0), (50, 100), (100, 0))]
62    assert splitQuadratic(
63        (0, 0), (50, 100), (100, 0), where=50, isHorizontal=False
64    ) == [((0, 0), (25, 50), (50, 50)),
65          ((50, 50), (75, 50), (100, 0))]
66    assert splitQuadratic(
67        (0, 0), (50, 100), (100, 0), where=25, isHorizontal=False
68    ) == [((0, 0), (12.5, 25), (25, 37.5)),
69          ((25, 37.5), (62.5, 75), (100, 0))]
70    assert_curves_approx_equal(
71        splitQuadratic(
72            (0, 0), (50, 100), (100, 0), where=25, isHorizontal=True),
73        [((0, 0), (7.32233, 14.64466), (14.64466, 25)),
74         ((14.64466, 25), (50, 75), (85.3553, 25)),
75         ((85.3553, 25), (92.6777, 14.64466), (100, -7.10543e-15))])
76    # XXX I'm not at all sure if the following behavior is desirable
77    assert splitQuadratic(
78        (0, 0), (50, 100), (100, 0), where=50, isHorizontal=True
79    ) == [((0, 0), (25, 50), (50, 50)),
80          ((50, 50), (50, 50), (50, 50)),
81          ((50, 50), (75, 50), (100, 0))]
82
83
84def test_splitCubic():
85    assert splitCubic(
86        (0, 0), (25, 100), (75, 100), (100, 0), where=150, isHorizontal=False
87    ) == [((0, 0), (25, 100), (75, 100), (100, 0))]
88    assert splitCubic(
89        (0, 0), (25, 100), (75, 100), (100, 0), where=50, isHorizontal=False
90    ) == [((0, 0), (12.5, 50), (31.25, 75), (50, 75)),
91          ((50, 75), (68.75, 75), (87.5, 50), (100, 0))]
92    assert_curves_approx_equal(
93        splitCubic(
94            (0, 0), (25, 100), (75, 100), (100, 0), where=25,
95            isHorizontal=True),
96        [((0, 0), (2.293792, 9.17517), (4.798045, 17.5085), (7.47414, 25)),
97         ((7.47414, 25), (31.2886, 91.6667), (68.7114, 91.6667),
98          (92.5259, 25)),
99         ((92.5259, 25), (95.202, 17.5085), (97.7062, 9.17517),
100          (100, 1.77636e-15))])
101
102
103def test_splitQuadraticAtT():
104    assert splitQuadraticAtT(
105        (0, 0), (50, 100), (100, 0), 0.5
106    ) == [((0, 0), (25, 50), (50, 50)),
107          ((50, 50), (75, 50), (100, 0))]
108    assert splitQuadraticAtT(
109        (0, 0), (50, 100), (100, 0), 0.5, 0.75
110    ) == [((0, 0), (25, 50), (50, 50)),
111          ((50, 50), (62.5, 50), (75, 37.5)),
112          ((75, 37.5), (87.5, 25), (100, 0))]
113
114
115def test_splitCubicAtT():
116    assert splitCubicAtT(
117        (0, 0), (25, 100), (75, 100), (100, 0), 0.5
118    ) == [((0, 0), (12.5, 50), (31.25, 75), (50, 75)),
119          ((50, 75), (68.75, 75), (87.5, 50), (100, 0))]
120    assert splitCubicAtT(
121        (0, 0), (25, 100), (75, 100), (100, 0), 0.5, 0.75
122    ) == [((0, 0), (12.5, 50), (31.25, 75), (50, 75)),
123          ((50, 75), (59.375, 75), (68.75, 68.75), (77.34375, 56.25)),
124          ((77.34375, 56.25), (85.9375, 43.75), (93.75, 25), (100, 0))]
125
126
127def test_solveCubic():
128    assert solveCubic(1, 1, -6, 0) == [-3.0, -0.0, 2.0]
129    assert solveCubic(-10.0, -9.0, 48.0, -29.0) == [-2.9, 1.0, 1.0]
130    assert solveCubic(-9.875, -9.0, 47.625, -28.75) == [-2.911392, 1.0, 1.0]
131    assert solveCubic(1.0, -4.5, 6.75, -3.375) == [1.5, 1.5, 1.5]
132    assert solveCubic(-12.0, 18.0, -9.0, 1.50023651123) == [0.5, 0.5, 0.5]
133    assert solveCubic(9.0, 0.0, 0.0, -7.62939453125e-05) == [-0.0, -0.0, -0.0]
134