File: test_image.py

package info (click to toggle)
python-svg.path 7.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky
  • size: 300 kB
  • sloc: python: 2,456; makefile: 37; sh: 2
file content (143 lines) | stat: -rw-r--r-- 4,824 bytes parent folder | download
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
import unittest
import os
from typing import Tuple
from PIL import Image, ImageDraw, ImageColor, ImageChops
from math import sqrt

from svg.path.path import CubicBezier, QuadraticBezier, Line, Arc, PathSegment
from .font import get_better_than_nothing_font


RED = ImageColor.getrgb("red")
GREEN = ImageColor.getrgb("limegreen")
BLUE = ImageColor.getrgb("cornflowerblue")
YELLOW = ImageColor.getrgb("yellow")
CYAN = ImageColor.getrgb("cyan")
WHITE = ImageColor.getrgb("white")
BLACK = ImageColor.getrgb("black")

DOT = 4 + 4j  # x+y radius of dot


def c2t(c: complex) -> Tuple[float, float]:
    """Make a complex number into a tuple"""
    return c.real, c.imag


def magnitude(c: complex) -> float:
    return sqrt(c.real**2 + c.imag**2)


class ImageTest(unittest.TestCase):
    """Creates a PNG image and compares with a correct PNG"""

    def setUp(self) -> None:
        self.image = Image.new(mode="RGB", size=(500, 1200))
        self.draw = ImageDraw.Draw(self.image)

    def draw_path(self, path: PathSegment) -> None:
        lines = [c2t(path.point(x * 0.01)) for x in range(1, 101)]
        self.draw.line(lines, fill=WHITE, width=2)

        p = path.point(0)
        self.draw.ellipse([c2t(p - DOT), c2t(p + DOT)], fill=BLUE)
        p = path.point(1)
        self.draw.ellipse([c2t(p - DOT), c2t(p + DOT)], fill=GREEN)

    def draw_tangents(self, path: PathSegment, count: int) -> None:
        count += 1
        for i in range(1, count):
            p = path.point(i / count)
            t = path.tangent(i / count)
            self.draw.line([c2t(p), c2t(p + t)], fill=RED, width=1)

            # And a nice 90 angle
            tt = complex(t.imag, -t.real)
            # scale it to always be 20px
            tt *= 20 / magnitude(tt)

            self.draw.line([c2t(p), c2t(tt + p)], fill=YELLOW, width=1)

    def test_image(self) -> None:
        font = get_better_than_nothing_font()
        self.draw.text((10, 10), "This is an SVG line:", font=font)
        self.draw.text(
            (10, 100),
            "The red line is a tangent, and the yellow is 90 degrees from that.",
            font=font,
        )

        line1 = Line(40 + 60j, 200 + 80j)
        self.draw_path(line1)
        self.draw_tangents(line1, 1)

        self.draw.text(
            (10, 140), "This is an Arc segment, almost a whole circle:", font=font
        )
        arc1 = Arc(260 + 320j, 100 + 100j, 0, 1, 1, 260 + 319j)
        self.draw_path(arc1)
        self.draw_tangents(arc1, 5)
        self.draw.text((10, 460), "With five tangents.", font=font)

        self.draw.text(
            (10, 500),
            "Next we have a quadratic bezier curve, with one tangent:",
            font=font,
        )
        start = 30 + 600j
        control = 400 + 540j
        end = 260 + 650j
        qbez1 = QuadraticBezier(start, control, end)
        self.draw_path(qbez1)
        self.draw.ellipse([c2t(control - DOT), c2t(control + DOT)], fill=WHITE)
        self.draw.line([c2t(start), c2t(control), c2t(end)], fill=CYAN)
        self.draw_tangents(qbez1, 1)
        self.draw.text(
            (10, 670),
            "The white dot is the control point, and the cyan lines are ",
            font=font,
        )
        self.draw.text(
            (10, 690), "illustrating the how the control point works.", font=font
        )

        self.draw.text(
            (10, 730),
            "Lastly is a cubic bezier, with 2 tangents, and 2 control points:",
            font=font,
        )

        start = 30 + 800j
        control1 = 400 + 780j
        control2 = 50 + 900j
        end = 300 + 980j
        cbez1 = CubicBezier(start, control1, control2, end)
        self.draw_path(cbez1)
        self.draw.ellipse([c2t(control1 - DOT), c2t(control1 + DOT)], fill=WHITE)
        self.draw.ellipse([c2t(control2 - DOT), c2t(control2 + DOT)], fill=WHITE)
        self.draw.line(
            [
                c2t(start),
                c2t(control1),
            ],
            fill=CYAN,
        )
        self.draw.line([c2t(control2), c2t(end)], fill=CYAN)
        self.draw_tangents(cbez1, 2)

        # self.image.show()  # Useful when debugging

        filename = os.path.join(os.path.split(__file__)[0], "test_image.png")

        # If you have made intentional changes to the test_image.png, save it
        # by uncommenting these lines. Don't forget to comment them out again,
        # or the test will always pass
        # with open(filename, "wb") as fp:
        #     self.image.save(fp, format="PNG")

        with open(filename, "rb") as fp:
            test_image = Image.open(fp, mode="r")
            diff = ImageChops.difference(test_image, self.image)
            self.assertFalse(
                diff.getbbox(), "The resulting image is different from test_image.png"
            )