Skip to content

Commit a25228a

Browse files
authored
Merge pull request #80 from tmontes/canvas-y-is-up
Canvas y is up
2 parents 28e8f06 + 0dd03c2 commit a25228a

File tree

9 files changed

+400
-75
lines changed

9 files changed

+400
-75
lines changed

src/aturtle/canvas.py

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# ----------------------------------------------------------------------------
2+
# Python A-Turtle
3+
# ----------------------------------------------------------------------------
4+
# Copyright (c) Tiago Montes.
5+
# See LICENSE for details.
6+
# ----------------------------------------------------------------------------
7+
8+
import tkinter
9+
10+
11+
12+
class InvertedYCanvas:
13+
14+
"""
15+
Behaves like a tkinter.Canvas widget, where Y coordinates are negated such
16+
that larger values are at the top of the screen.
17+
"""
18+
19+
def __init__(self, master, background):
20+
"""
21+
Creates a Canvas widget with parent `master` and the given `background`
22+
color. Sets the underlying tkinter.Canvas widget's highlightthickness
23+
to zero such that no border/outline is visible.
24+
"""
25+
self._canvas = tkinter.Canvas(
26+
master,
27+
highlightthickness=0,
28+
background=background,
29+
)
30+
31+
def _inverted_y(self, coords):
32+
33+
# `coords` is an iterable of (x0, y0, x1, y0, ..., xn, yn) numbers,
34+
# where the ones in odd offsets/indices are Y values that are negated.
35+
36+
return [
37+
-value if offset % 2 else value
38+
for offset, value in enumerate(coords)
39+
]
40+
41+
42+
def create_polygon(self, coords, *, fill, outline, width):
43+
"""
44+
Creates a polygon item with given `coords` and visual attributes.
45+
"""
46+
return self._canvas.create_polygon(
47+
self._inverted_y(coords),
48+
fill=fill,
49+
outline=outline,
50+
width=width,
51+
)
52+
53+
54+
def create_image(self, x, y, *, image, anchor):
55+
"""
56+
Creates an image item at the given (`x`, `y`) position with the `image`
57+
achored at `anchor`.
58+
"""
59+
return self._canvas.create_image(x, -y, image=image, anchor=anchor)
60+
61+
62+
def create_line(self, coords, *, fill, width, capstyle):
63+
"""
64+
Creates a line item with given `coords` and visual attributes.
65+
"""
66+
return self._canvas.create_line(
67+
self._inverted_y(coords),
68+
fill=fill,
69+
width=width,
70+
capstyle=capstyle,
71+
)
72+
73+
74+
def move(self, item_id, dx, dy):
75+
"""
76+
Moves the item identified by `item_id` relatively by (`dx`, `dy`).
77+
"""
78+
return self._canvas.move(item_id, dx, -dy)
79+
80+
81+
def coords(self, item_id, coords):
82+
"""
83+
Sets the coordinates of the item identified by `item_id` to `coords`.
84+
"""
85+
return self._canvas.coords(item_id, self._inverted_y(coords))
86+
87+
88+
def __getattr__(self, name):
89+
"""
90+
Delegates attribute access to the wrapped tkinter.Canvas object.
91+
"""
92+
return getattr(self._canvas, name)

src/aturtle/shapes/bitmap.py

+7-7
Original file line numberDiff line numberDiff line change
@@ -106,20 +106,20 @@ def _rotated_tkinter(self, image, around, step, rotations):
106106

107107
ax, ay = around
108108

109-
neg_theta = -math.pi * 2 * step / rotations
109+
theta = math.pi * 2 * step / rotations
110110

111111
pixel_rows = []
112112
transparency = {}
113113

114114
for y in range(h):
115115
pixel_row = []
116116
for x in range(w):
117-
cos_neg_theta = math.cos(neg_theta)
118-
sin_neg_theta = math.sin(neg_theta)
117+
cos_theta = math.cos(theta)
118+
sin_theta = math.sin(theta)
119119
off_x = x - ax
120120
off_y = y - ay
121-
src_x = int(off_x * cos_neg_theta - off_y * sin_neg_theta) + ax
122-
src_y = int(off_x * sin_neg_theta + off_y * cos_neg_theta) + ay
121+
src_x = int(off_x * cos_theta - off_y * sin_theta) + ax
122+
src_y = int(off_x * sin_theta + off_y * cos_theta) + ay
123123
if (0 <= src_x < w) and (0 <= src_y < h):
124124
red, green, blue = image.get(src_x, src_y)
125125
transp = image.transparency_get(src_x, src_y)
@@ -142,10 +142,10 @@ def _rotated_pil(self, pil_image, around, step, total):
142142

143143
# PIL-based image rotation.
144144

145-
neg_theta = -360 * step / total
145+
theta = 360 * step / total
146146

147147
rotated_pil_image = pil_image.rotate(
148-
neg_theta,
148+
theta,
149149
resample=Image.BICUBIC,
150150
center=around,
151151
)

src/aturtle/sprites/bitmap.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def __init__(self, canvas, shape, *, anchor=(0, 0), angle=0, speed=360,
2727
sprite_x - shape_x,
2828
sprite_y - shape_y,
2929
image=shape[angle],
30-
anchor='nw',
30+
anchor='sw',
3131
)
3232

3333

src/aturtle/turtle.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ async def async_left(self, angle, *, speed=None, easing=None, fps=None,
176176
underlying Sprite's animated movement operation.
177177
"""
178178
await self._sprite.async_rotate(
179-
-angle,
179+
angle,
180180
speed=speed,
181181
easing=easing,
182182
fps=fps,
@@ -193,7 +193,7 @@ async def async_right(self, angle, *, speed=None, easing=None, fps=None,
193193
underlying Sprite's animated movement operation.
194194
"""
195195
await self._sprite.async_rotate(
196-
angle,
196+
-angle,
197197
speed=speed,
198198
easing=easing,
199199
fps=fps,

src/aturtle/window.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
import tkinter
99

10+
from . import canvas
11+
1012

1113

1214
class Window:
@@ -18,7 +20,8 @@ class Window:
1820
_windows = []
1921

2022
def __init__(self, width=320, height=320, x=None, y=None,
21-
fill_color='white', title='A-Turtle'):
23+
fill_color='white', title='A-Turtle',
24+
canvas_factory=canvas.InvertedYCanvas):
2225
"""
2326
Initialize a Window with the given `width` and `height`, filled in
2427
`fill_color`, with the given `title`.
@@ -47,9 +50,8 @@ def __init__(self, width=320, height=320, x=None, y=None,
4750
y = (tk_window.winfo_screenheight() - height) // 2 if y is None else y
4851

4952
tk_window.geometry(f'{width}x{height}+{x}+{y}')
50-
canvas = tkinter.Canvas(
53+
canvas = canvas_factory(
5154
tk_window,
52-
highlightthickness=0,
5355
background=fill_color,
5456
)
5557
canvas.pack(expand=True, fill='both')

tests/test_bitmap_sprites.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ def test_create_calls_canvas_create_image(self):
5656
-10,
5757
-10,
5858
image='image-at-angle-0',
59-
anchor='nw',
59+
anchor='sw',
6060
)
6161

6262

0 commit comments

Comments
 (0)