summaryrefslogtreecommitdiffstats
path: root/share/extensions/tests/test_inkex_tween.py
blob: f1eabf112b39b2f7f89475ca8ede34b1ad487a5f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
# coding=utf-8
#
# Copyright (C) 2020-2021 Jonathan Neuhauser, jonathan.neuhauser@outlook.com
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
#
"""Test interpolation inkex module functionality"""
from inkex.tester import TestCase

import inkex
import inkex.tween as tween
import pytest
import numpy as np


class TweenTest(TestCase):
    """Unit tests for the Inkscape inkex tween library"""

    black = inkex.Color("#000000")
    grey50 = inkex.Color("#080808")
    white = inkex.Color("#111111")

    def test_interpcoord(self):
        val = tween.interpcoord(0, 1, 0.5)
        assert val == pytest.approx(0.5, 1e-3)

    def test_interppoints(self):
        val = tween.interppoints((0, 0), (1, 1), 0.5)
        assert val == pytest.approx((0.5, 0.5), (1e-3, 1e-3))

    def initialize_gradient(self, lg, stoparray):
        for key, value in stoparray.items():
            stop = inkex.Stop()
            stop.style = inkex.Style()
            stop.style["stop-color"] = value
            stop.offset = key
            lg.add(stop)

    def initialize_linear_gradient(self, bounding_box, stoparray):
        lg = inkex.LinearGradient()
        self.initialize_gradient(lg, stoparray)
        lg.set("x1", bounding_box.left)
        lg.set("x2", bounding_box.right)
        lg.set("y1", bounding_box.center.y)
        lg.set("y2", bounding_box.center.y)
        return lg

    def initialize_radial_gradient(self, bounding_box, stoparray):
        grad = inkex.RadialGradient()
        self.initialize_gradient(grad, stoparray)
        grad.set("cx", bounding_box.center.x)
        grad.set("cy", bounding_box.center.y)
        grad.set("fx", bounding_box.center.x)
        grad.set("fy", bounding_box.center.y)
        grad.set("r", bounding_box.right - bounding_box.center.x)
        return grad

    def test_fill_interpolator(self):
        svg = inkex.SvgDocumentElement()
        p1 = inkex.PathElement(
            d="M 5.23564,33.586285 46.410969,-0.94834 89.873815,35.045501 Z"
        )
        p2 = inkex.PathElement(
            d="m 136.32372,31.390088 c 0,0 -65.213764,-4.27631 18.17433,-22.4506304 83.38809,-18.17434 64.14469,35.2795704 64.14469,35.2795704 z"
        )
        p3 = inkex.Rectangle()
        svg.add(p1, p2, p3)
        g1 = self.initialize_linear_gradient(
            p1.bounding_box(), {0: "#ff0000", 0.5: "#00ff00", 1: "#0000ff"}
        )
        g2 = self.initialize_linear_gradient(
            p2.bounding_box(),
            {0: "#ff0000", 0.25: "#ff0000", 0.75: "#0000ff", 1: "#0000ff"},
        )
        g3 = self.initialize_radial_gradient(
            p2.bounding_box(), {0: "#ff0000", 0.5: "#00ff00", 1: "#0000ff"}
        )
        grad1 = tween.GradientInterpolator.append_to_doc(svg, g1)
        grad2 = tween.GradientInterpolator.append_to_doc(svg, g2)
        grad3 = tween.GradientInterpolator.append_to_doc(svg, g3)
        expected = [
            [
                {"fill": "#640000"},
                {"fill": "#320000"},
                {"fill": [75, 0, 0]},
            ],  # both only fill
            [
                {"fill": "none"},
                {"fill": "#320000"},
                {"fill": [50, 0, 0], "fill-opacity": 0.5},
            ],  # interpolate via fill-opacity
            [{"fill": "none"}, {}, {"fill": [0, 0, 0]}],  # only one fill set to None
            [
                {"fill": "#00ff00"},
                {"fill": grad1},
                {
                    "fill/grad/stops": [
                        [0, "#7f7f00"],
                        [0.5, "#00ff00"],
                        [1, "#007f7f"],
                    ],
                    "fill/grad/x1": "5.23564px",
                },
            ],
            [
                {"fill": "#00ff00"},
                {"fill": grad3},
                {
                    "fill/grad/stops": [
                        [0, "#7f7f00"],
                        [0.5, "#00ff00"],
                        [1, "#007f7f"],
                    ],
                    "fill/grad/cx": "106.932px",
                },
            ],
            [{"fill": grad2}, {"fill": grad3}, {"fill": grad2}],
            [
                {"fill": grad1},
                {"fill": grad2},
                {
                    "fill/grad/stops": [
                        [0, "#ff0000"],
                        [0.25, "#bf3f00"],
                        [0.5, "#3f7f3f"],
                        [0.75, "#003fbf"],
                        [1, "#0000ff"],
                    ]
                },
            ],
            [
                {"fill": "none"},
                {"fill": grad1},
                {
                    "fill/grad/stops": [
                        [0, "#ff0000"],
                        [0.5, "#00ff00"],
                        [1, "#0000ff"],
                    ],
                    "fill/grad/x1": "5.23564px",
                },
            ],
        ]

        for sstyle, estyle, interpstyle in expected:
            p1.style = inkex.Style()
            p1.style.update(sstyle)
            p2.style = inkex.Style()
            p2.style.update(estyle)

            interpolator = tween.StyleInterpolator(p1, p2)
            p3.style = interpolator.interpolate(0.5)
            for key, value in interpstyle.items():
                keys = key.split("/")
                if keys[0] == "fill":
                    newfill = p3.style("fill")
                    if key == "fill":
                        assert newfill == value
                    elif keys[1] == "grad":
                        assert isinstance(
                            newfill, (inkex.LinearGradient, inkex.RadialGradient)
                        )
                        if keys[2] == "stops":
                            assert len(value) == len(newfill.stops)
                            for idx, _ in enumerate(value):
                                assert (
                                    newfill.stops[idx].style["stop-color"]
                                    == value[idx][1]
                                )
                                self.assertAlmostEqual(
                                    float(newfill.stop_offsets[idx]),
                                    value[idx][0],
                                    1e-3,
                                )
                        else:
                            assert newfill.get(keys[2]) == value
                else:
                    assert p3.style(key) == value

    def test_path_interpolation(self):
        p1 = inkex.Path("M 5.23564,33.586285 46.410969,-0.94834 89.873815,35.045501 Z")
        p2 = inkex.Path(
            "m 136.32372,31.390088 c 0,0 -65.213764,-4.27631 18.17433,-22.4506304 83.38809,-18.17434 64.14469,35.2795704 64.14469,35.2795704 z"
        )
        pel1 = inkex.PathElement()
        pel2 = inkex.PathElement()
        pel1.path = p1
        pel2.path = p2
        result_method_1 = "M 70.7798 32.4882 C 70.7798 32.4882 58.7606 13.0827 100.455 3.99558 C 142.149 -5.09157 154.258 39.6323 154.258 39.6323 Z"
        result_method_2 = "M 70.7798 32.4882 C 70.7798 32.4882 59.6785 13.1429 98.726 4.37785 C 99.2875 4.25181 100.254 4.44741 101.52 4.87808 C 126.751 3.94755 151.064 21.8659 153.864 27.4179 C 156.66 32.9612 150.143 39.5613 144.479 39.4637 C 131.98 39.2482 70.7798 32.4882 70.7798 32.4882"
        calls = [
            [None, result_method_1],
            [tween.FirstNodesInterpolator, result_method_1],
            [tween.EqualSubsegmentsInterpolator, result_method_2],
        ]

        for arg, expected in calls:
            interp = tween.AttributeInterpolator.create_from_attribute(
                pel1, pel2, "d", method=arg
            )
            result = interp.interpolate(0.5)
            print(result)
            assert np.allclose(result, inkex.CubicSuperPath(inkex.Path(expected)))