pyscratch-pysc 1.0.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- assets/bullet_hell/enemy.py +130 -0
- assets/bullet_hell/enemy_bullets.py +230 -0
- assets/bullet_hell/main.py +11 -0
- assets/bullet_hell/old_verisons/bullet_hell.py +379 -0
- assets/bullet_hell/old_verisons/enemy.py +226 -0
- assets/bullet_hell/old_verisons/game_start.py +6 -0
- assets/bullet_hell/old_verisons/main.py +50 -0
- assets/bullet_hell/old_verisons/player.py +76 -0
- assets/bullet_hell/player.py +89 -0
- assets/bullet_hell/player_bullets.py +34 -0
- assets/bullet_hell/setting.py +33 -0
- examples/animated_sprite/main.py +7 -0
- examples/animated_sprite/my_sprite.py +79 -0
- examples/bullet_hell/enemy.py +152 -0
- examples/bullet_hell/enemy_bullet.py +88 -0
- examples/bullet_hell/main.py +17 -0
- examples/bullet_hell/player.py +39 -0
- examples/bullet_hell/player_bullet.py +31 -0
- examples/doodle_jump/main.py +9 -0
- examples/doodle_jump/platforms.py +51 -0
- examples/doodle_jump/player.py +52 -0
- examples/fish/assets/bullet_hell/enemy.py +130 -0
- examples/fish/assets/bullet_hell/enemy_bullets.py +230 -0
- examples/fish/assets/bullet_hell/main.py +11 -0
- examples/fish/assets/bullet_hell/old_verisons/bullet_hell.py +379 -0
- examples/fish/assets/bullet_hell/old_verisons/enemy.py +226 -0
- examples/fish/assets/bullet_hell/old_verisons/game_start.py +6 -0
- examples/fish/assets/bullet_hell/old_verisons/main.py +50 -0
- examples/fish/assets/bullet_hell/old_verisons/player.py +76 -0
- examples/fish/assets/bullet_hell/player.py +89 -0
- examples/fish/assets/bullet_hell/player_bullets.py +34 -0
- examples/fish/assets/bullet_hell/setting.py +33 -0
- examples/fish/fish.py +67 -0
- examples/fish/main.py +4 -0
- examples/getting-started/step 1 - create a sprite/main.py +11 -0
- examples/getting-started/step 1 - create a sprite/player.py +5 -0
- examples/getting-started/step 2 - control a sprite/main.py +11 -0
- examples/getting-started/step 2 - control a sprite/player.py +42 -0
- examples/getting-started/step 3 - backdrops/main.py +17 -0
- examples/getting-started/step 3 - backdrops/player.py +33 -0
- examples/getting-started/step 4 - clone a sprite/enemy.py +53 -0
- examples/getting-started/step 4 - clone a sprite/main.py +17 -0
- examples/getting-started/step 4 - clone a sprite/player.py +32 -0
- examples/getting-started/step 4 - clone a sprite (simple)/enemy.py +42 -0
- examples/getting-started/step 4 - clone a sprite (simple)/main.py +17 -0
- examples/getting-started/step 4 - clone a sprite (simple)/player.py +32 -0
- examples/getting-started/step 5 - local variables/enemy.py +52 -0
- examples/getting-started/step 5 - local variables/main.py +17 -0
- examples/getting-started/step 5 - local variables/player.py +49 -0
- examples/getting-started/step 6 - shared variables/enemy.py +64 -0
- examples/getting-started/step 6 - shared variables/main.py +17 -0
- examples/getting-started/step 6 - shared variables/player.py +80 -0
- examples/getting-started/step 7 - Referencing other sprites/enemy.py +83 -0
- examples/getting-started/step 7 - Referencing other sprites/hearts.py +39 -0
- examples/getting-started/step 7 - Referencing other sprites/main.py +23 -0
- examples/getting-started/step 7 - Referencing other sprites/player.py +59 -0
- examples/getting-started/step 8 - sprite variables/enemy.py +98 -0
- examples/getting-started/step 8 - sprite variables/hearts.py +39 -0
- examples/getting-started/step 8 - sprite variables/main.py +22 -0
- examples/getting-started/step 8 - sprite variables/player.py +63 -0
- examples/getting-started/step 9 - messages/enemy.py +98 -0
- examples/getting-started/step 9 - messages/hearts.py +39 -0
- examples/getting-started/step 9 - messages/main.py +23 -0
- examples/getting-started/step 9 - messages/player.py +78 -0
- examples/perspective_background/main.py +14 -0
- examples/perspective_background/player.py +52 -0
- examples/perspective_background/trees.py +39 -0
- examples/simple_pong/ball.py +72 -0
- examples/simple_pong/left_paddle.py +36 -0
- examples/simple_pong/main.py +21 -0
- examples/simple_pong/right_paddle.py +37 -0
- examples/simple_pong/score_display.py +54 -0
- pyscratch/__init__.py +48 -0
- pyscratch/event.py +263 -0
- pyscratch/game_module.py +1589 -0
- pyscratch/helper.py +561 -0
- pyscratch/sprite.py +1920 -0
- pyscratch/tools/sprite_preview/left_panel/frame_preview_card.py +238 -0
- pyscratch/tools/sprite_preview/left_panel/frame_preview_panel.py +42 -0
- pyscratch/tools/sprite_preview/main.py +18 -0
- pyscratch/tools/sprite_preview/main_panel/animation_display.py +77 -0
- pyscratch/tools/sprite_preview/main_panel/frame_bin.py +33 -0
- pyscratch/tools/sprite_preview/main_panel/play_edit_ui.py +64 -0
- pyscratch/tools/sprite_preview/main_panel/set_as_sprite_folder.py +22 -0
- pyscratch/tools/sprite_preview/main_panel/sprite_edit_ui.py +174 -0
- pyscratch/tools/sprite_preview/main_panel/warning_message.py +25 -0
- pyscratch/tools/sprite_preview/right_panel/back_button.py +35 -0
- pyscratch/tools/sprite_preview/right_panel/cut_button.py +32 -0
- pyscratch/tools/sprite_preview/right_panel/cut_parameter_fitting.py +152 -0
- pyscratch/tools/sprite_preview/right_panel/cut_parameters.py +42 -0
- pyscratch/tools/sprite_preview/right_panel/file_display.py +84 -0
- pyscratch/tools/sprite_preview/right_panel/file_display_area.py +57 -0
- pyscratch/tools/sprite_preview/right_panel/spritesheet_view.py +262 -0
- pyscratch/tools/sprite_preview/right_panel/ss_select_corner.py +208 -0
- pyscratch/tools/sprite_preview/settings.py +14 -0
- pyscratch/tools/sprite_preview/utils/input_box.py +235 -0
- pyscratch/tools/sprite_preview/utils/render_wrapped_file_name.py +86 -0
- pyscratch_pysc-1.0.3.dist-info/METADATA +37 -0
- pyscratch_pysc-1.0.3.dist-info/RECORD +101 -0
- pyscratch_pysc-1.0.3.dist-info/WHEEL +5 -0
- pyscratch_pysc-1.0.3.dist-info/top_level.txt +3 -0
pyscratch/helper.py
ADDED
@@ -0,0 +1,561 @@
|
|
1
|
+
"""
|
2
|
+
Everything in this module is directly under the pyscratch namespace.
|
3
|
+
For example, instead of doing `pysc.helper.random_number`,
|
4
|
+
you can also directly do `pysc.random_number`
|
5
|
+
"""
|
6
|
+
|
7
|
+
|
8
|
+
from functools import cache
|
9
|
+
from itertools import product
|
10
|
+
from pathlib import Path
|
11
|
+
from typing import Dict, List, Tuple, Union
|
12
|
+
import random
|
13
|
+
|
14
|
+
import pygame
|
15
|
+
import numpy as np
|
16
|
+
|
17
|
+
|
18
|
+
def cap(v:float, min_v:float, max_v:float)->float:
|
19
|
+
"""
|
20
|
+
Cap a value to a range between `min_v` and `max_v`.
|
21
|
+
|
22
|
+
Example
|
23
|
+
```python
|
24
|
+
cap(5, 1, 10) # returns 5
|
25
|
+
cap(15, 1, 10) # returns 10
|
26
|
+
cap(-10, 1, 10) # returns 1
|
27
|
+
|
28
|
+
# a more realistic example usage: keep the sprite within the screen.
|
29
|
+
my_sprite.x = cap(my_sprite.x, 0, SCREEN_WIDTH)
|
30
|
+
my_sprite.y = cap(my_sprite.y, 0, SCREEN_HEIGHT)
|
31
|
+
```
|
32
|
+
"""
|
33
|
+
return max(min(max_v, v), min_v)
|
34
|
+
|
35
|
+
def random_number(min_v:float, max_v:float) -> float:
|
36
|
+
"""
|
37
|
+
Returns a random number between `min_v` and `max_v`
|
38
|
+
"""
|
39
|
+
return random.random()*(max_v-min_v)+min_v
|
40
|
+
|
41
|
+
@cache
|
42
|
+
def load_image(path: str) -> pygame.Surface:
|
43
|
+
"""
|
44
|
+
Return the `pygame.Surface` given the path to an image.
|
45
|
+
"""
|
46
|
+
return pygame.image.load(path).convert_alpha()
|
47
|
+
|
48
|
+
|
49
|
+
def get_frame_from_sprite_sheet(
|
50
|
+
sheet: pygame.Surface,
|
51
|
+
columns:int, rows:int, index:int,
|
52
|
+
spacing:int=0, margin:int=0, inset:int=0):
|
53
|
+
"""
|
54
|
+
Extract a specific frame from a sprite sheet.
|
55
|
+
|
56
|
+
*You will not need to use this function directly.*
|
57
|
+
|
58
|
+
THIS FUNCTION IS WRITTEN BY AN AI.
|
59
|
+
|
60
|
+
|
61
|
+
The index starts from the top-left as 0,
|
62
|
+
and increments left-to-right before up-to-down.
|
63
|
+
For example, for a 3x3 sheet, the index would be
|
64
|
+
```
|
65
|
+
0 1 2
|
66
|
+
3 4 5
|
67
|
+
6 7 8
|
68
|
+
```
|
69
|
+
"""
|
70
|
+
sheet_rect = sheet.get_rect()
|
71
|
+
|
72
|
+
total_spacing_x = spacing * (columns - 1)
|
73
|
+
total_spacing_y = spacing * (rows - 1)
|
74
|
+
|
75
|
+
total_margin_x = margin * 2
|
76
|
+
total_margin_y = margin * 2
|
77
|
+
|
78
|
+
frame_width = (sheet_rect.width - total_spacing_x - total_margin_x) // columns
|
79
|
+
frame_height = (sheet_rect.height - total_spacing_y - total_margin_y) // rows
|
80
|
+
|
81
|
+
col = index % columns
|
82
|
+
row = index // columns
|
83
|
+
|
84
|
+
x = margin + col * (frame_width + spacing)
|
85
|
+
y = margin + row * (frame_height + spacing)
|
86
|
+
|
87
|
+
# Apply internal cropping (inset) from all sides
|
88
|
+
cropped_rect = pygame.Rect(
|
89
|
+
x + inset,
|
90
|
+
y + inset,
|
91
|
+
frame_width - 2 * inset,
|
92
|
+
frame_height - 2 * inset
|
93
|
+
)
|
94
|
+
|
95
|
+
return sheet.subsurface(cropped_rect)
|
96
|
+
|
97
|
+
def get_frame_from_sprite_sheet_by_frame_size(
|
98
|
+
sheet: pygame.Surface,
|
99
|
+
size_x: int, size_y: int,
|
100
|
+
c:int, r: int):
|
101
|
+
"""
|
102
|
+
Extract a specific frame from a sprite sheet.
|
103
|
+
|
104
|
+
*You will not need to use this function directly.*
|
105
|
+
|
106
|
+
|
107
|
+
Parameters
|
108
|
+
---
|
109
|
+
sheet: pygame.Surface
|
110
|
+
The sprite sheet you want to cut
|
111
|
+
|
112
|
+
size_x: int
|
113
|
+
The x pixel size of the frame
|
114
|
+
|
115
|
+
size_y: int
|
116
|
+
The y pixel size of the frame
|
117
|
+
|
118
|
+
c: int
|
119
|
+
The column position of the frame
|
120
|
+
|
121
|
+
r: int
|
122
|
+
The row position of the frame
|
123
|
+
"""
|
124
|
+
crop_rect = pygame.Rect(
|
125
|
+
c*size_x,
|
126
|
+
r*size_y,
|
127
|
+
size_x,
|
128
|
+
size_y
|
129
|
+
)
|
130
|
+
|
131
|
+
return sheet.subsurface(crop_rect)
|
132
|
+
|
133
|
+
from PIL import Image, ImageSequence
|
134
|
+
import pygame
|
135
|
+
|
136
|
+
@cache
|
137
|
+
def load_gif_frames(path) -> List[pygame.Surface]:
|
138
|
+
"""
|
139
|
+
Load a gif file as a list of images (as `pygame.Surface`)
|
140
|
+
|
141
|
+
*You will not need to use this function directly.*
|
142
|
+
|
143
|
+
THIS FUNCTION IS WRITTEN BY AN AI.
|
144
|
+
|
145
|
+
"""
|
146
|
+
pil = Image.open(path)
|
147
|
+
frames = []
|
148
|
+
for frame in ImageSequence.Iterator(pil):
|
149
|
+
mode = frame.convert("RGBA")
|
150
|
+
data = mode.tobytes()
|
151
|
+
size = mode.size
|
152
|
+
surf = pygame.image.frombuffer(data, size, "RGBA").convert_alpha()
|
153
|
+
frames.append(surf)
|
154
|
+
return frames
|
155
|
+
|
156
|
+
|
157
|
+
@cache
|
158
|
+
def load_frames_from_gif_folder(folder_path: Path):
|
159
|
+
"""
|
160
|
+
Creates a `frame_dict` that the Sprite constructor takes using
|
161
|
+
the gif files inside a folder.
|
162
|
+
|
163
|
+
The folder should be organised in the following way:
|
164
|
+
```
|
165
|
+
├─ player/
|
166
|
+
├─ walking.gif
|
167
|
+
├─ idling.gif
|
168
|
+
├─ jumping.gif
|
169
|
+
...
|
170
|
+
```
|
171
|
+
|
172
|
+
*You will not need to use this function directly.*
|
173
|
+
|
174
|
+
|
175
|
+
"""
|
176
|
+
frame_dict = {}
|
177
|
+
for f in folder_path.iterdir():
|
178
|
+
if f.suffix != '.gif':
|
179
|
+
print(f"ignoring {f.name} (not a gif)")
|
180
|
+
continue
|
181
|
+
|
182
|
+
frame_dict[f.stem] = load_gif_frames(f)
|
183
|
+
|
184
|
+
return frame_dict
|
185
|
+
|
186
|
+
|
187
|
+
|
188
|
+
|
189
|
+
def cut_sprite_sheet(
|
190
|
+
sheet: pygame.Surface,
|
191
|
+
columns, rows,
|
192
|
+
spacing=0, margin=0, inset=0,
|
193
|
+
folder_path='.',
|
194
|
+
suffix='png'):
|
195
|
+
"""
|
196
|
+
Split a sprite sheet so each frame is its own image file.
|
197
|
+
|
198
|
+
*You probably won't need to use this function directly.*
|
199
|
+
|
200
|
+
Parameters
|
201
|
+
---
|
202
|
+
sheet: pygame.Surface
|
203
|
+
The sprite sheet image
|
204
|
+
columns: int
|
205
|
+
The total number of columns of the sheet
|
206
|
+
rows: int
|
207
|
+
The total number of rows of the sheet
|
208
|
+
spacing: int
|
209
|
+
Keep it as zero.
|
210
|
+
margin: int
|
211
|
+
Keep it as zero.
|
212
|
+
inset: int
|
213
|
+
Keep it as zero.
|
214
|
+
folder_path: str
|
215
|
+
The path of the destination folder to put the splitted images.
|
216
|
+
suffix: str
|
217
|
+
The file extension
|
218
|
+
"""
|
219
|
+
|
220
|
+
folder_path = Path(folder_path)
|
221
|
+
if not folder_path.exists():
|
222
|
+
folder_path.mkdir()
|
223
|
+
for i in range(columns*rows):
|
224
|
+
f = get_frame_from_sprite_sheet(sheet, columns, rows, i, spacing, margin, inset)
|
225
|
+
pygame.image.save(f, folder_path/f"{i}.{suffix}")
|
226
|
+
|
227
|
+
def _get_frame_sequence(sheet:pygame.Surface, columns:int, rows:int, indices:List[int], spacing:int, margin:int, inset:int):
|
228
|
+
"""
|
229
|
+
**You will not need to use this function directly.**
|
230
|
+
"""
|
231
|
+
return [get_frame_from_sprite_sheet(sheet, columns, rows, i, spacing, margin, inset) for i in indices]
|
232
|
+
|
233
|
+
def _get_frame_dict(
|
234
|
+
sheet:pygame.Surface, columns:int, rows:int,
|
235
|
+
indices_dict: Dict[str, List[int]],
|
236
|
+
spacing:int=0, margin:int=0, inset:int=0):
|
237
|
+
"""
|
238
|
+
**You will not need to use this function directly.**
|
239
|
+
"""
|
240
|
+
frame_dict = {}
|
241
|
+
for k, v in indices_dict.items():
|
242
|
+
assert isinstance(v, list) or isinstance(v, tuple)
|
243
|
+
|
244
|
+
frame_dict[k] = _get_frame_sequence(sheet, columns, rows, v, spacing, margin, inset)
|
245
|
+
|
246
|
+
return frame_dict
|
247
|
+
|
248
|
+
@cache
|
249
|
+
def load_frames_from_folder(folder_path: Union[Path, str]):
|
250
|
+
"""
|
251
|
+
Creates a `frame_dict` that the Sprite constructor takes using
|
252
|
+
the images inside a folder.
|
253
|
+
You can use `sprite.create_animated_sprite` instead for convenience.
|
254
|
+
|
255
|
+
The folder should be organised in one of these two ways:
|
256
|
+
|
257
|
+
**Option 1**: Only one frame mode. *Note that the images must be numbered.*
|
258
|
+
```
|
259
|
+
├─ player/
|
260
|
+
├─ 0.png
|
261
|
+
├─ 1.png
|
262
|
+
├─ 2.png
|
263
|
+
...
|
264
|
+
```
|
265
|
+
|
266
|
+
**Option 2**: Multiple frame modes. *Note that the images must be numbered.*
|
267
|
+
```
|
268
|
+
├─ player/
|
269
|
+
├─ walking/
|
270
|
+
├─ 0.png
|
271
|
+
├─ 1.png
|
272
|
+
...
|
273
|
+
├─ idling/
|
274
|
+
├─ 0.png
|
275
|
+
├─ 1.png
|
276
|
+
...
|
277
|
+
...
|
278
|
+
```
|
279
|
+
Example
|
280
|
+
```python
|
281
|
+
frame_dict = load_frames_from_folder("assets/player")
|
282
|
+
my_sprite1 = Sprite(frame_dict)
|
283
|
+
```
|
284
|
+
"""
|
285
|
+
return _load_frames_from_folder_uncached(folder_path)
|
286
|
+
|
287
|
+
|
288
|
+
|
289
|
+
def _load_frames_from_folder_uncached(folder_path: Union[Path, str]):
|
290
|
+
|
291
|
+
def extract_images(path: Path):
|
292
|
+
index2image: Dict[int, pygame.Surface] = {}
|
293
|
+
|
294
|
+
for f in path.iterdir():
|
295
|
+
if f.is_dir():
|
296
|
+
continue
|
297
|
+
|
298
|
+
if not f.stem.isdigit():
|
299
|
+
print(f'skipping: {f.name}')
|
300
|
+
continue
|
301
|
+
index2image[int(f.stem)] = pygame.image.load(f).convert_alpha()
|
302
|
+
|
303
|
+
return [index2image[i] for i in sorted(index2image.keys())]
|
304
|
+
|
305
|
+
|
306
|
+
path = Path(folder_path)
|
307
|
+
|
308
|
+
folder_seen = False
|
309
|
+
file_seen = False
|
310
|
+
|
311
|
+
frame_dict = {}
|
312
|
+
for f in path.iterdir():
|
313
|
+
if f.is_dir():
|
314
|
+
folder_seen = True
|
315
|
+
assert not file_seen
|
316
|
+
frame_dict[f.stem] = extract_images(f)
|
317
|
+
else:
|
318
|
+
file_seen = True
|
319
|
+
assert not folder_seen
|
320
|
+
|
321
|
+
if not folder_seen:
|
322
|
+
frame_dict[path.stem] = extract_images(path)
|
323
|
+
|
324
|
+
return frame_dict
|
325
|
+
|
326
|
+
#RGBType = Tuple[int, int, int]
|
327
|
+
#RGBAType = Tuple[int, int, int, int]
|
328
|
+
#ColourType = Union[RGBAType, RGBType]
|
329
|
+
|
330
|
+
def create_circle(colour, radius: float) -> pygame.Surface:
|
331
|
+
"""
|
332
|
+
Create a circle image (pygame.Surface) given the colour and radius.
|
333
|
+
|
334
|
+
Parameters
|
335
|
+
---
|
336
|
+
colour: Tuple[int, int, int] or Tuple[int, int, int, int]
|
337
|
+
The colour of the rectangle in RGB or RGBA. Value range: [0-255].
|
338
|
+
radius: float
|
339
|
+
the radius of the cirlce.
|
340
|
+
"""
|
341
|
+
surface = pygame.Surface((radius*2, radius*2), pygame.SRCALPHA)
|
342
|
+
pygame.draw.circle(surface, colour, (radius, radius), radius)
|
343
|
+
return surface
|
344
|
+
|
345
|
+
|
346
|
+
def create_rect(colour, width: float, height: float) -> pygame.Surface:
|
347
|
+
"""
|
348
|
+
Create a rectangle image (pygame.Surface) given the colour, width and height
|
349
|
+
|
350
|
+
Parameters
|
351
|
+
---
|
352
|
+
colour: Tuple[int, int, int] or Tuple[int, int, int, int]
|
353
|
+
The colour of the rectangle in RGB or RGBA. Value range: [0-255].
|
354
|
+
width: float
|
355
|
+
the width (x length) of the rectangle.
|
356
|
+
height: float
|
357
|
+
the height (y length) of the rectangle.
|
358
|
+
"""
|
359
|
+
|
360
|
+
surface = pygame.Surface((width, height), pygame.SRCALPHA)
|
361
|
+
pygame.draw.rect(surface, colour, surface.get_rect())
|
362
|
+
return surface
|
363
|
+
|
364
|
+
def scale_and_tile(image, screen_size, scale_factor):
|
365
|
+
"""
|
366
|
+
Scale an image by a factor and tile it to fill the screen.
|
367
|
+
|
368
|
+
THIS FUNCTION IS WRITTEN BY AN AI.
|
369
|
+
|
370
|
+
Parameters
|
371
|
+
----------
|
372
|
+
image : pygame.Surface
|
373
|
+
The image to scale and tile.
|
374
|
+
screen_size : tuple of int
|
375
|
+
The width and height of the target surface.
|
376
|
+
scale_factor : float
|
377
|
+
Factor by which to scale the image.
|
378
|
+
"""
|
379
|
+
|
380
|
+
img_w, img_h = image.get_size()
|
381
|
+
new_img = pygame.transform.smoothscale(image, (int(img_w * scale_factor), int(img_h * scale_factor)))
|
382
|
+
new_img_w, new_img_h = new_img.get_size()
|
383
|
+
|
384
|
+
tiled_surface = pygame.Surface(screen_size)
|
385
|
+
|
386
|
+
for y in range(0, screen_size[1], new_img_h):
|
387
|
+
for x in range(0, screen_size[0], new_img_w):
|
388
|
+
tiled_surface.blit(new_img, (x, y))
|
389
|
+
|
390
|
+
return tiled_surface
|
391
|
+
|
392
|
+
|
393
|
+
def scale_to_fill(image:pygame.Surface, new_size:Tuple[int, int]):
|
394
|
+
"""
|
395
|
+
Scale an image to a new size without preserving aspect ratio.
|
396
|
+
|
397
|
+
Parameters
|
398
|
+
----------
|
399
|
+
image : pygame.Surface
|
400
|
+
The image to scale.
|
401
|
+
new_size : tuple of int
|
402
|
+
Target width and height of the image.
|
403
|
+
"""
|
404
|
+
return pygame.transform.smoothscale(image, new_size)
|
405
|
+
|
406
|
+
|
407
|
+
def scale_to_fit_aspect(image:pygame.Surface, new_size:Tuple[int, int], fit='horizontal'):
|
408
|
+
"""
|
409
|
+
Scale an image to fit a rect while preserving aspect ratio.
|
410
|
+
|
411
|
+
THIS FUNCTION IS WRITTEN BY AN AI.
|
412
|
+
|
413
|
+
Parameters
|
414
|
+
----------
|
415
|
+
image : pygame.Surface
|
416
|
+
The image to scale.
|
417
|
+
new_size : tuple of int
|
418
|
+
The width and height of the target rect.
|
419
|
+
fit : 'horizontal' or 'vertical'
|
420
|
+
Axis to fit the image against. Default is 'horizontal'.
|
421
|
+
"""
|
422
|
+
img_rect = image.get_rect()
|
423
|
+
screen_w, screen_h = new_size
|
424
|
+
img_w, img_h = img_rect.size
|
425
|
+
|
426
|
+
if fit == 'horizontal':
|
427
|
+
scale_factor = screen_w / img_w
|
428
|
+
elif fit == 'vertical':
|
429
|
+
scale_factor = screen_h / img_h
|
430
|
+
else:
|
431
|
+
raise ValueError("fit must be either 'horizontal' or 'vertical'")
|
432
|
+
|
433
|
+
new_size = (int(img_w * scale_factor), int(img_h * scale_factor))
|
434
|
+
return pygame.transform.smoothscale(image, new_size)
|
435
|
+
|
436
|
+
|
437
|
+
def _set_transparency(image, factor):
|
438
|
+
"""
|
439
|
+
Set the transparency of an image.
|
440
|
+
|
441
|
+
THIS FUNCTION IS WRITTEN BY AN AI.
|
442
|
+
|
443
|
+
***IMCOMPLETE IMPLEMENTATION***:
|
444
|
+
The transparency of the transparent background of the image is also changed
|
445
|
+
|
446
|
+
|
447
|
+
Parameters
|
448
|
+
----------
|
449
|
+
image : pygame.Surface
|
450
|
+
The image to adjust.
|
451
|
+
factor : float
|
452
|
+
Transparency level from 0.0 (fully transparent) to 1.0 (fully opaque).
|
453
|
+
"""
|
454
|
+
new_image = image.copy()
|
455
|
+
new_image.set_alpha(int(factor*255))
|
456
|
+
return new_image
|
457
|
+
|
458
|
+
def set_transparency(image: pygame.Surface, factor):
|
459
|
+
"""
|
460
|
+
Get a new copy of the image with the transparency applied.
|
461
|
+
|
462
|
+
Parameters
|
463
|
+
----------
|
464
|
+
image : pygame.Surface
|
465
|
+
The image to adjust.
|
466
|
+
factor : float
|
467
|
+
Transparency level from 0.0 (fully transparent) to 1.0 (fully opaque).
|
468
|
+
"""
|
469
|
+
w = image.get_width()
|
470
|
+
h = image.get_height()
|
471
|
+
|
472
|
+
sur = pygame.Surface((w, h)).convert_alpha()
|
473
|
+
|
474
|
+
sur.fill((255,255,255,int(factor*255) ))
|
475
|
+
#sur.set_alpha(int(factor*255))
|
476
|
+
|
477
|
+
new_image = image.copy()
|
478
|
+
new_image.blit(sur, (0,0), special_flags=pygame.BLEND_RGBA_MULT)
|
479
|
+
return new_image
|
480
|
+
|
481
|
+
|
482
|
+
def adjust_brightness(image, factor):
|
483
|
+
"""
|
484
|
+
Get a new copy of the image with the brightness changes applied.
|
485
|
+
|
486
|
+
THIS FUNCTION IS WRITTEN BY AN AI.
|
487
|
+
|
488
|
+
Parameters
|
489
|
+
----------
|
490
|
+
image : pygame.Surface
|
491
|
+
The image to adjust.
|
492
|
+
factor : float
|
493
|
+
Brightness multiplier. Values < 1.0 darken, values > 1.0 brighten.
|
494
|
+
|
495
|
+
"""
|
496
|
+
new_image = image.copy()
|
497
|
+
brightness_surface = pygame.Surface(image.get_size()).convert_alpha()
|
498
|
+
brightness_surface.fill((255, 255, 255, 0)) # Start with no change
|
499
|
+
|
500
|
+
# Per-pixel operation is slow. Instead, we use special_flags to modulate brightness.
|
501
|
+
if factor < 1:
|
502
|
+
# Darken using multiply blend mode
|
503
|
+
darken_surface = pygame.Surface(image.get_size()).convert_alpha()
|
504
|
+
value = int(255 * factor)
|
505
|
+
darken_surface.fill((value, value, value))
|
506
|
+
new_image.blit(darken_surface, (0, 0), special_flags=pygame.BLEND_RGBA_MULT)
|
507
|
+
elif factor > 1:
|
508
|
+
# Brighten by blending in white
|
509
|
+
value = int(255 * (factor - 1))
|
510
|
+
brighten_surface = pygame.Surface(image.get_size()).convert_alpha()
|
511
|
+
brighten_surface.fill((value, value, value))
|
512
|
+
new_image.blit(brighten_surface, (0, 0), special_flags=pygame.BLEND_RGBA_ADD)
|
513
|
+
|
514
|
+
return new_image
|
515
|
+
|
516
|
+
|
517
|
+
def _draw_guide_lines(screen: pygame.Surface, font: pygame.font.Font, minor_grid, major_grid):
|
518
|
+
w, h = screen.get_width(), screen.get_height()
|
519
|
+
|
520
|
+
|
521
|
+
pygame.draw.lines(screen, (0,0,0), True, [(0,0), (0, h), (w, h), (w, 0)])
|
522
|
+
screen.blit(font.render(f"({0},{0})", True, (0,0,0)), (5,5))
|
523
|
+
for i in range(0, w, minor_grid):
|
524
|
+
lw = 1
|
525
|
+
v = 200
|
526
|
+
if i and (not i % major_grid):
|
527
|
+
lw = 1
|
528
|
+
v = 0
|
529
|
+
screen.blit(font.render(f"{i}", True, (0,0,0)), (i+5,5))
|
530
|
+
|
531
|
+
pygame.draw.line(screen, (v,v,v,255-v), (i, 0), (i, h), width=lw)
|
532
|
+
|
533
|
+
|
534
|
+
|
535
|
+
|
536
|
+
for i in range(0, h, minor_grid):
|
537
|
+
lw = 1
|
538
|
+
v = 200
|
539
|
+
if i and (not i % major_grid):
|
540
|
+
lw = 1
|
541
|
+
v = 0
|
542
|
+
screen.blit(font.render(f"{i}", True, (0,0,0)), (5,i+5))
|
543
|
+
pygame.draw.line(screen, (v,v,v,255-v), (0, i), (w, i), width=lw)
|
544
|
+
|
545
|
+
#text = font.render(f"({w},{h})", True, (0,0,0))
|
546
|
+
#screen.blit(text, (w-5-text.get_width(), h-5-text.get_height()))
|
547
|
+
|
548
|
+
|
549
|
+
for x, y in product(range(major_grid, w, major_grid), range(major_grid, h, major_grid)):
|
550
|
+
text = font.render(f"({x},{y})", True, (0,0,0))
|
551
|
+
screen.blit(text, (x+5, y+5))
|
552
|
+
|
553
|
+
|
554
|
+
|
555
|
+
def _show_mouse_position(screen, font):
|
556
|
+
w, h = screen.get_width(), screen.get_height()
|
557
|
+
|
558
|
+
mx, my = pygame.mouse.get_pos()
|
559
|
+
|
560
|
+
text = font.render(f"({mx},{my})", True, (0,0,0))
|
561
|
+
screen.blit(text, (w-5-text.get_width(), h-5-text.get_height()))
|