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.
Files changed (101) hide show
  1. assets/bullet_hell/enemy.py +130 -0
  2. assets/bullet_hell/enemy_bullets.py +230 -0
  3. assets/bullet_hell/main.py +11 -0
  4. assets/bullet_hell/old_verisons/bullet_hell.py +379 -0
  5. assets/bullet_hell/old_verisons/enemy.py +226 -0
  6. assets/bullet_hell/old_verisons/game_start.py +6 -0
  7. assets/bullet_hell/old_verisons/main.py +50 -0
  8. assets/bullet_hell/old_verisons/player.py +76 -0
  9. assets/bullet_hell/player.py +89 -0
  10. assets/bullet_hell/player_bullets.py +34 -0
  11. assets/bullet_hell/setting.py +33 -0
  12. examples/animated_sprite/main.py +7 -0
  13. examples/animated_sprite/my_sprite.py +79 -0
  14. examples/bullet_hell/enemy.py +152 -0
  15. examples/bullet_hell/enemy_bullet.py +88 -0
  16. examples/bullet_hell/main.py +17 -0
  17. examples/bullet_hell/player.py +39 -0
  18. examples/bullet_hell/player_bullet.py +31 -0
  19. examples/doodle_jump/main.py +9 -0
  20. examples/doodle_jump/platforms.py +51 -0
  21. examples/doodle_jump/player.py +52 -0
  22. examples/fish/assets/bullet_hell/enemy.py +130 -0
  23. examples/fish/assets/bullet_hell/enemy_bullets.py +230 -0
  24. examples/fish/assets/bullet_hell/main.py +11 -0
  25. examples/fish/assets/bullet_hell/old_verisons/bullet_hell.py +379 -0
  26. examples/fish/assets/bullet_hell/old_verisons/enemy.py +226 -0
  27. examples/fish/assets/bullet_hell/old_verisons/game_start.py +6 -0
  28. examples/fish/assets/bullet_hell/old_verisons/main.py +50 -0
  29. examples/fish/assets/bullet_hell/old_verisons/player.py +76 -0
  30. examples/fish/assets/bullet_hell/player.py +89 -0
  31. examples/fish/assets/bullet_hell/player_bullets.py +34 -0
  32. examples/fish/assets/bullet_hell/setting.py +33 -0
  33. examples/fish/fish.py +67 -0
  34. examples/fish/main.py +4 -0
  35. examples/getting-started/step 1 - create a sprite/main.py +11 -0
  36. examples/getting-started/step 1 - create a sprite/player.py +5 -0
  37. examples/getting-started/step 2 - control a sprite/main.py +11 -0
  38. examples/getting-started/step 2 - control a sprite/player.py +42 -0
  39. examples/getting-started/step 3 - backdrops/main.py +17 -0
  40. examples/getting-started/step 3 - backdrops/player.py +33 -0
  41. examples/getting-started/step 4 - clone a sprite/enemy.py +53 -0
  42. examples/getting-started/step 4 - clone a sprite/main.py +17 -0
  43. examples/getting-started/step 4 - clone a sprite/player.py +32 -0
  44. examples/getting-started/step 4 - clone a sprite (simple)/enemy.py +42 -0
  45. examples/getting-started/step 4 - clone a sprite (simple)/main.py +17 -0
  46. examples/getting-started/step 4 - clone a sprite (simple)/player.py +32 -0
  47. examples/getting-started/step 5 - local variables/enemy.py +52 -0
  48. examples/getting-started/step 5 - local variables/main.py +17 -0
  49. examples/getting-started/step 5 - local variables/player.py +49 -0
  50. examples/getting-started/step 6 - shared variables/enemy.py +64 -0
  51. examples/getting-started/step 6 - shared variables/main.py +17 -0
  52. examples/getting-started/step 6 - shared variables/player.py +80 -0
  53. examples/getting-started/step 7 - Referencing other sprites/enemy.py +83 -0
  54. examples/getting-started/step 7 - Referencing other sprites/hearts.py +39 -0
  55. examples/getting-started/step 7 - Referencing other sprites/main.py +23 -0
  56. examples/getting-started/step 7 - Referencing other sprites/player.py +59 -0
  57. examples/getting-started/step 8 - sprite variables/enemy.py +98 -0
  58. examples/getting-started/step 8 - sprite variables/hearts.py +39 -0
  59. examples/getting-started/step 8 - sprite variables/main.py +22 -0
  60. examples/getting-started/step 8 - sprite variables/player.py +63 -0
  61. examples/getting-started/step 9 - messages/enemy.py +98 -0
  62. examples/getting-started/step 9 - messages/hearts.py +39 -0
  63. examples/getting-started/step 9 - messages/main.py +23 -0
  64. examples/getting-started/step 9 - messages/player.py +78 -0
  65. examples/perspective_background/main.py +14 -0
  66. examples/perspective_background/player.py +52 -0
  67. examples/perspective_background/trees.py +39 -0
  68. examples/simple_pong/ball.py +72 -0
  69. examples/simple_pong/left_paddle.py +36 -0
  70. examples/simple_pong/main.py +21 -0
  71. examples/simple_pong/right_paddle.py +37 -0
  72. examples/simple_pong/score_display.py +54 -0
  73. pyscratch/__init__.py +48 -0
  74. pyscratch/event.py +263 -0
  75. pyscratch/game_module.py +1589 -0
  76. pyscratch/helper.py +561 -0
  77. pyscratch/sprite.py +1920 -0
  78. pyscratch/tools/sprite_preview/left_panel/frame_preview_card.py +238 -0
  79. pyscratch/tools/sprite_preview/left_panel/frame_preview_panel.py +42 -0
  80. pyscratch/tools/sprite_preview/main.py +18 -0
  81. pyscratch/tools/sprite_preview/main_panel/animation_display.py +77 -0
  82. pyscratch/tools/sprite_preview/main_panel/frame_bin.py +33 -0
  83. pyscratch/tools/sprite_preview/main_panel/play_edit_ui.py +64 -0
  84. pyscratch/tools/sprite_preview/main_panel/set_as_sprite_folder.py +22 -0
  85. pyscratch/tools/sprite_preview/main_panel/sprite_edit_ui.py +174 -0
  86. pyscratch/tools/sprite_preview/main_panel/warning_message.py +25 -0
  87. pyscratch/tools/sprite_preview/right_panel/back_button.py +35 -0
  88. pyscratch/tools/sprite_preview/right_panel/cut_button.py +32 -0
  89. pyscratch/tools/sprite_preview/right_panel/cut_parameter_fitting.py +152 -0
  90. pyscratch/tools/sprite_preview/right_panel/cut_parameters.py +42 -0
  91. pyscratch/tools/sprite_preview/right_panel/file_display.py +84 -0
  92. pyscratch/tools/sprite_preview/right_panel/file_display_area.py +57 -0
  93. pyscratch/tools/sprite_preview/right_panel/spritesheet_view.py +262 -0
  94. pyscratch/tools/sprite_preview/right_panel/ss_select_corner.py +208 -0
  95. pyscratch/tools/sprite_preview/settings.py +14 -0
  96. pyscratch/tools/sprite_preview/utils/input_box.py +235 -0
  97. pyscratch/tools/sprite_preview/utils/render_wrapped_file_name.py +86 -0
  98. pyscratch_pysc-1.0.3.dist-info/METADATA +37 -0
  99. pyscratch_pysc-1.0.3.dist-info/RECORD +101 -0
  100. pyscratch_pysc-1.0.3.dist-info/WHEEL +5 -0
  101. pyscratch_pysc-1.0.3.dist-info/top_level.txt +3 -0
@@ -0,0 +1,35 @@
1
+ from pathlib import Path
2
+ from typing import List, Tuple
3
+ import pyscratch as pysc
4
+ from pyscratch.sprite import Sprite
5
+ from settings import *
6
+ from .file_display import FileDisplay
7
+ w, h = 50*1.62, 50
8
+ colour = (127, 127, 127)
9
+ button = pysc.create_rect_sprite(colour, w, h)
10
+ button.set_xy((900, 550))
11
+ pysc.game.change_layer(button, 1)
12
+
13
+ #button.when_game_start().add_handler(lambda: button.lock_to(pysc.game.shared_data['folder_navigation'], offset=(0,0)))
14
+
15
+
16
+
17
+ def on_click():
18
+ if pysc.game.shared_data['cut_or_nav_mode'] == 'nav':
19
+ pysc.game.broadcast_message('back_nav', None)
20
+ else:
21
+ #pysc.game.broadcast_message('cut', None)
22
+ pysc.game.broadcast_message('cut_or_nav_mode_change', 'nav')
23
+
24
+ button.when_this_sprite_clicked().add_handler(on_click)
25
+
26
+
27
+ def on_msg_mode_change(mode):
28
+ pysc.game.shared_data['cut_or_nav_mode'] = mode
29
+ if mode == 'nav':
30
+ button.write_text("Back", DEFAULT_FONT24, offset=(w/2, h/2))
31
+ else:
32
+ button.write_text("Back", DEFAULT_FONT24, offset=(w/2, h/2))
33
+
34
+
35
+ button.when_receive_message('cut_or_nav_mode_change').add_handler(on_msg_mode_change)
@@ -0,0 +1,32 @@
1
+ from pathlib import Path
2
+ from typing import List, Tuple
3
+ import pyscratch as pysc
4
+ from pyscratch.sprite import Sprite
5
+ from settings import *
6
+ from .file_display import FileDisplay
7
+ w, h = 50*1.62, 50
8
+ colour = (127, 127, 127)
9
+ button = pysc.create_rect_sprite(colour, w, h)
10
+ button.set_xy((1100, 550))
11
+ pysc.game.change_layer(button, 1)
12
+
13
+ def on_click():
14
+ if pysc.game.shared_data['cut_or_nav_mode'] == 'nav':
15
+ #pysc.game.broadcast_message('cut_or_nav_mode_change', 'cut')
16
+ pass
17
+ else:
18
+ pysc.game.broadcast_message('cut', None)
19
+ #pysc.game.broadcast_message('cut_or_nav_mode_change', 'nav')
20
+
21
+ button.when_this_sprite_clicked().add_handler(on_click)
22
+
23
+
24
+ def on_msg_mode_change(mode):
25
+ pysc.game.shared_data['cut_or_nav_mode'] = mode
26
+ if mode == 'nav':
27
+ button.write_text("...", DEFAULT_FONT24, offset=(w/2, h/2))
28
+ else:
29
+ button.write_text("Cut", DEFAULT_FONT24, offset=(w/2, h/2))
30
+
31
+
32
+ button.when_receive_message('cut_or_nav_mode_change').add_handler(on_msg_mode_change)
@@ -0,0 +1,152 @@
1
+ import pygame
2
+ import numpy as np
3
+ from settings import *
4
+ import pyscratch as pysc
5
+ game = pysc.game
6
+ xpos2 = 1050
7
+
8
+
9
+ w, h = 50*1.62, 40
10
+ colour = (255, 127, 127)
11
+ button = pysc.create_rect_sprite(colour, w, h)
12
+ button.set_xy((1200, 650))
13
+ button.write_text("Fit", DEFAULT_FONT24, offset=(w/2, h/2))
14
+ pysc.game.change_layer(button, 1)
15
+
16
+
17
+
18
+ def on_click():
19
+ if not 'image_on_right_display' in pysc.game.shared_data:
20
+ pysc.game.broadcast_message('warning', 'image not selected' )
21
+ return
22
+
23
+ if game['offset_x'] is None: return
24
+ if game['offset_y'] is None: return
25
+ if game['size_x'] is None: return
26
+ if game['size_y'] is None: return
27
+
28
+
29
+ img: pygame.Surface = game['image_on_right_display']
30
+
31
+
32
+ fitter = ParamsFitter(img)
33
+
34
+ n_col, pixel_x, offset_x = fitter.find_best_x(game['offset_x'], game['limit_x'])
35
+ n_row, pixel_y, offset_y = fitter.find_best_y(game['offset_y'], game['limit_y'])
36
+
37
+ if (n_col and pixel_x and (not offset_x is None)):
38
+ game['n_col'] = n_col
39
+ game['pixel_x'] = pixel_x
40
+ game['offset_x'] = offset_x
41
+ game['size_x'] = min(game['limit_x'] - game['offset_x']+1, game['size_x'])
42
+
43
+ if (n_row and pixel_y and (not offset_y is None)):
44
+ game['n_row'] = n_row
45
+ game['pixel_y'] = pixel_y
46
+ game['offset_y'] = offset_y
47
+ game['offset_y'] = offset_y
48
+ game['size_y'] = min(game['limit_y'] - game['offset_y']+1, game['size_y'])
49
+
50
+
51
+ #game.broadcast_message('n_row_change')
52
+ #game.broadcast_message('n_col_change')
53
+
54
+ #game.broadcast_message('pixel_x_change')
55
+
56
+ #game.broadcast_message('pixel_y_change')
57
+
58
+ #game.broadcast_message('offset_x_change')
59
+ #game.broadcast_message('offset_y_change')
60
+
61
+
62
+
63
+
64
+ button.when_this_sprite_clicked().add_handler(on_click)
65
+
66
+ from itertools import product
67
+ class ParamsFitter:
68
+
69
+ def __init__(self, surface):
70
+
71
+ self.surface = surface
72
+ self.arr = pygame.surfarray.pixels_alpha(surface)
73
+
74
+ self.yslits = ParamsFitter.get_slit_values(self.arr.mean(0))
75
+ self.xslits = ParamsFitter.get_slit_values(self.arr.mean(1))
76
+
77
+ def num_frames_x(self, offset, limit):
78
+ # TODO: think about it: is the boundary correct?
79
+ return ParamsFitter.get_num_frames(self.xslits[offset: limit+2])
80
+
81
+ def num_frames_y(self, offset, limit):
82
+ return ParamsFitter.get_num_frames(self.yslits[offset: limit+2])
83
+
84
+ def find_best_x(self, offset_centre: int, limit:int):
85
+ offset_centre = int(offset_centre)
86
+ limit = int(limit)
87
+
88
+ return ParamsFitter.search_best(self.xslits[: limit+2], offset_centre)
89
+
90
+ def find_best_y(self, offset_centre: int, limit:int):
91
+ offset_centre = int(offset_centre)
92
+ limit = int(limit)
93
+ return ParamsFitter.search_best(self.yslits[: limit+2], offset_centre)
94
+
95
+ @staticmethod
96
+ def search_best(slit_values, centre_offset, step_range_ratio=0.1, offset_range_ratio=0.1):
97
+ #print(len(slit_values))
98
+ n_frame = int(ParamsFitter.get_num_frames(slit_values[centre_offset:]))
99
+ print(ParamsFitter.get_num_frames_rank(slit_values[centre_offset:]))
100
+
101
+ # TODO: possibly DIV BY ZERO?
102
+ step_size = max(1, (len(slit_values[centre_offset:])-1)/n_frame )
103
+
104
+ step_range = round(step_size*step_range_ratio)
105
+ offset_range = round(step_size*offset_range_ratio)
106
+
107
+ step_size = round(step_size)
108
+
109
+ # TODO: very easy to go out of boundary
110
+ step_sizes = range(step_size+step_range+1, max(0, step_size-step_range), -1)
111
+ offsets = range(max(0,centre_offset-offset_range), centre_offset+offset_range+1)
112
+
113
+ vmin = np.inf
114
+ best_ss = None
115
+ best_os = None
116
+ for ss, os in product(step_sizes, offsets):
117
+
118
+ n, v = ParamsFitter.eval_cut(slit_values, ss, os)
119
+ #print(f"trying: pixel_sz:{ss}, offset:{os} -> {n, v}")
120
+ if not n == n_frame: continue
121
+ if v < vmin:
122
+ best_ss = ss
123
+ best_os = os
124
+ vmin = v
125
+ #print(ss, os, v)
126
+
127
+
128
+
129
+ print(f"n_frame: {n_frame}, pixel_sz:{best_ss}, offset:{best_os} -> {vmin}")
130
+ return n_frame, best_ss, best_os
131
+
132
+ @staticmethod
133
+ def get_num_frames(slit_values):
134
+ return np.argmax(np.abs(np.fft.fft(slit_values))[1:len(slit_values)//2])+1
135
+
136
+ @staticmethod
137
+ def get_num_frames_rank(slit_values):
138
+ return np.argsort(np.abs(np.fft.fft(slit_values))[1:len(slit_values)//2])+1
139
+
140
+
141
+ @staticmethod
142
+ def get_rough_est_num_pixel(slit_values):
143
+ return (len(slit_values)-1)/ParamsFitter.get_num_frames(slit_values)
144
+
145
+ @staticmethod
146
+ def eval_cut(slit_values, step_size, offset=0):
147
+ cut_indices = np.arange(offset, len(slit_values), step_size)
148
+ return len(cut_indices)-1, slit_values[cut_indices].mean()
149
+
150
+ @staticmethod
151
+ def get_slit_values(axis_mean):
152
+ return np.r_[[0], (axis_mean[1:]+axis_mean[:-1]), [0]]
@@ -0,0 +1,42 @@
1
+ from utils.input_box import IntegerInputBox, FloatInputBoxHoriLabel
2
+ import pyscratch as pysc
3
+ from settings import *
4
+
5
+ xpos1 = 830
6
+ xpos2 = 1050
7
+
8
+ offset_x_input = IntegerInputBox('offset_x', 'offset_x_change', "0", 80)
9
+ offset_x_input.set_xy((xpos1, 605))
10
+
11
+
12
+ offset_y_input = IntegerInputBox('offset_y', 'offset_y_change', "0", 80)
13
+ offset_y_input.set_xy((xpos2, 605))
14
+
15
+ pysc.game['limit_x'] = None
16
+ pysc.game['limit_y'] = None
17
+
18
+ pysc.game['size_x'] = None
19
+ pysc.game['size_y'] = None
20
+
21
+
22
+
23
+ row_input = IntegerInputBox('n_row', "n_row_change", "", 80)
24
+ row_input.set_xy((xpos1, 650))
25
+
26
+ col_input = IntegerInputBox('n_col', "n_col_change", "", 80)
27
+ col_input.set_xy((xpos2, 650))
28
+
29
+
30
+ pixel_x_input = IntegerInputBox('pixel_x', 'pixel_x_change', "0", 80)
31
+ pixel_x_input.set_xy((xpos1, 695))
32
+
33
+ pixel_y_input = IntegerInputBox('pixel_y', 'pixel_y_change', "0", 80)
34
+ pixel_y_input.set_xy((xpos2, 695))
35
+
36
+
37
+ pysc.game.change_layer(offset_x_input, 1)
38
+ pysc.game.change_layer(offset_y_input, 1)
39
+ pysc.game.change_layer(row_input, 1)
40
+ pysc.game.change_layer(col_input, 1)
41
+ pysc.game.change_layer(pixel_x_input, 1)
42
+ pysc.game.change_layer(pixel_y_input, 1)
@@ -0,0 +1,84 @@
1
+ from pathlib import Path
2
+ from typing import Tuple
3
+ import pyscratch as pysc
4
+
5
+ from utils.render_wrapped_file_name import render_wrapped_file_name
6
+ from settings import *
7
+
8
+ width, height = 100, 140
9
+ colour = (130, 130, 130)
10
+
11
+ spacing = 10
12
+ n_cols = 5
13
+
14
+ def try_load_image(path):
15
+ try:
16
+ return pysc.load_image(path)
17
+ except:
18
+ return None
19
+
20
+
21
+
22
+ def FileDisplay(path: Path, order: int, panel_top_left):
23
+ sprite = pysc.create_rect_sprite(colour, width, height)
24
+
25
+ # set the position
26
+
27
+ panel_x, panel_y = panel_top_left
28
+ sprite.x = spacing+(order%n_cols)*(width+spacing)+panel_x +width/2
29
+ sprite.y = spacing+(order//n_cols)*(height+spacing)+panel_y +height/2
30
+
31
+ # set the display
32
+
33
+ surface = try_load_image(path)
34
+ if not surface:
35
+ # TODO: messy af
36
+ if path.is_file():
37
+ sprite.remove()
38
+ return None
39
+
40
+ text = render_wrapped_file_name(path.name+'/', 8, DEFAULT_FONT24)
41
+ sprite.draw(text, offset=(width/2, height/2) )
42
+ #sprite.write_text(path.name+'/', DEFAULT_FONT24, offset=(width/2, height/2))
43
+
44
+ else:
45
+ image_margin = 20
46
+ text_height = 20
47
+ fit = "horizontal" if surface.get_width() >= surface.get_height() else "vertical"
48
+ surface = pysc.scale_to_fit_aspect(surface, (width-image_margin, height-text_height-image_margin ), fit)
49
+ sprite.draw(surface, (width/2, (height-text_height)/2))
50
+
51
+ text = render_wrapped_file_name(path.name, 8, DEFAULT_FONT24, color= (255, 255, 255), max_lines=2)
52
+ sprite.draw(text,offset=(width/2, height-20), reset=False)
53
+
54
+
55
+
56
+ sprite.sprite_data['is_file'] = path.is_file()
57
+
58
+
59
+ def on_click():
60
+ if not path.is_file():
61
+ pysc.game.broadcast_message('folder_update', path)
62
+ else:
63
+ pysc.game.broadcast_message('image_selected', path)
64
+ #pysc.game.broadcast_message('cut_or_nav_mode_change', 'cut')
65
+
66
+
67
+
68
+ sprite.when_this_sprite_clicked().add_handler(on_click)
69
+
70
+
71
+ def on_msg_mode_change(mode):
72
+ if mode == 'nav':
73
+ sprite.show()
74
+ #pysc.game.bring_to_front(sprite)
75
+ else:
76
+ sprite.hide()
77
+
78
+ sprite.when_receive_message('cut_or_nav_mode_change').add_handler(on_msg_mode_change)
79
+
80
+
81
+
82
+
83
+ return sprite
84
+
@@ -0,0 +1,57 @@
1
+ from pathlib import Path
2
+ from typing import List, Tuple, cast
3
+ import pyscratch as pysc
4
+ from pyscratch.sprite import Sprite
5
+ from settings import *
6
+ from .file_display import FileDisplay
7
+
8
+ width, height = RIGHT_PANEL_WIDTH, SCREEN_HEIGHT - PANEL_MARGIN - BOTTOM_RIGHT_PANEL_HEIGHT
9
+
10
+ colour = 255, 255, 255
11
+ folder_navigation = pysc.create_rect_sprite(colour, width, height)
12
+
13
+ folder_navigation.set_xy((SCREEN_WIDTH - PANEL_MARGIN - RIGHT_PANEL_WIDTH/2, SCREEN_HEIGHT/2-BOTTOM_RIGHT_PANEL_HEIGHT/2))
14
+
15
+ topleft = folder_navigation.x-width/2, folder_navigation.y-height/2
16
+
17
+ folder_navigation['file_display_list'] = []
18
+
19
+ def on_msg_folder_update(path: Path):
20
+ #folder_navigation.sprite_data['path'] = path
21
+ pysc.game.shared_data['path'] = path
22
+
23
+ for fdisp in folder_navigation['file_display_list']:
24
+ fdisp.remove()
25
+
26
+ folder_navigation['file_display_list'] = []
27
+ file_display_list = folder_navigation['file_display_list']
28
+
29
+ c = 0
30
+ for f in path.iterdir():
31
+ fdisp = FileDisplay(f, c, topleft)
32
+ if fdisp:
33
+ c+=1
34
+ file_display_list.append(fdisp)
35
+
36
+ folder_navigation.when_receive_message('folder_update').add_handler(on_msg_folder_update)
37
+
38
+
39
+ def on_msg_back_nav(_):
40
+ path: Path = pysc.game['path']
41
+ pysc.game.broadcast_message('folder_update', path.parent)
42
+
43
+
44
+
45
+ folder_navigation.when_receive_message('back_nav').add_handler(on_msg_back_nav)
46
+
47
+
48
+
49
+ def on_msg_mode_change(mode):
50
+ if mode == 'nav':
51
+ folder_navigation.show()
52
+ pysc.game.move_to_back(folder_navigation)
53
+ else:
54
+ folder_navigation.hide()
55
+
56
+ folder_navigation.when_receive_message('cut_or_nav_mode_change').add_handler(on_msg_mode_change)
57
+ pysc.game['folder_navigation'] = folder_navigation
@@ -0,0 +1,262 @@
1
+ """
2
+ pysc.game.broadcast_message
3
+ ("cut_sprite_frame_drop", dict(sprite=sprite, position=pysc.get_mouse_pos()))
4
+
5
+ """
6
+ from pathlib import Path
7
+ from typing import List, Tuple, cast
8
+
9
+ import numpy as np
10
+ from pygame import Surface
11
+ import pyscratch as pysc
12
+ from settings import *
13
+ game = pysc.game
14
+ width, height = RIGHT_PANEL_WIDTH, SCREEN_HEIGHT - PANEL_MARGIN - BOTTOM_RIGHT_PANEL_HEIGHT
15
+
16
+ colour = 255, 255, 255, 255
17
+ ss_view = pysc.create_rect_sprite(colour, width, height)
18
+
19
+ ss_view.set_xy((SCREEN_WIDTH - PANEL_MARGIN - RIGHT_PANEL_WIDTH/2, SCREEN_HEIGHT/2-BOTTOM_RIGHT_PANEL_HEIGHT/2))
20
+ #ss_view.set_draggable(False)
21
+ #topleft = ss_view.x-width/2, ss_view.y-height/2
22
+ pysc.game['ss_view'] = ss_view
23
+
24
+ def topleft():
25
+ return ss_view.x-width/2, ss_view.y-height/2
26
+ ss_view['frame_list'] = []
27
+
28
+ pysc.game['ss_view_topleft'] = topleft()
29
+ pysc.game['ss_view_buttom_right'] = ss_view.x+width/2, ss_view.y+height/2
30
+
31
+
32
+
33
+ ss_view['spritesheet_sprite'] = None
34
+ pysc.game['ss_sprite'] = None
35
+ def on_msg_mode_change(mode):
36
+ if not mode == 'nav':
37
+ ss_view.show()
38
+ pysc.game.move_to_back(ss_view)
39
+ for f in ss_view.sprite_data['frame_list']:
40
+ f.show()
41
+
42
+ else:
43
+ ss_view.hide()
44
+ for f in ss_view.sprite_data['frame_list']:
45
+ f.hide()
46
+ if ss_sprite := ss_view['spritesheet_sprite']:
47
+ ss_sprite.hide()
48
+
49
+
50
+ ss_view.when_receive_message('cut_or_nav_mode_change').add_handler(on_msg_mode_change)
51
+
52
+ def try_load_image(path):
53
+ try:
54
+ return pysc.load_image(path)
55
+ except:
56
+ return None
57
+
58
+
59
+ def on_msg_image_selected(path):
60
+
61
+ for f in ss_view['frame_list']:
62
+ f.remove()
63
+ ss_view['frame_list'] = []
64
+
65
+ img = try_load_image(path)
66
+ if img:
67
+ pysc.game.shared_data['image_on_right_display'] = img
68
+
69
+ if ss_sprite := ss_view['spritesheet_sprite']:
70
+ ss_sprite.remove()
71
+
72
+ ss_view['spritesheet_sprite'] = pysc.Sprite({'a':[img]})
73
+
74
+ ss_sprite:pysc.Sprite = ss_view['spritesheet_sprite']
75
+
76
+ pysc.game['ss_sprite'] = ss_sprite
77
+ ss_sprite.oob_limit=np.inf
78
+ ss_sprite['original_width'] = img.get_width()
79
+ ss_sprite['original_height'] = img.get_height()
80
+ game.change_layer(ss_sprite, 0)
81
+
82
+
83
+ #ss_sprite.lock_to(ss_view, offset=(0,0))
84
+ #ss_sprite.set_xy((0,0))
85
+
86
+ ss_sprite.set_xy((ss_view.rect.centerx, ss_view.rect.centery))
87
+ ss_sprite.set_draggable(True) #TODO: dragging a locked sprite leads to unexpected behaviour
88
+
89
+ ss_sprite._drawing_manager.set_mask_threshold(-1)
90
+
91
+
92
+ pysc.game.broadcast_message('cut_or_nav_mode_change', 'cut')
93
+ pass
94
+
95
+ ss_view.when_receive_message('image_selected').add_handler(on_msg_image_selected)
96
+ from itertools import product
97
+ def on_msg_cut(_):
98
+ ss_view.draw(pysc.create_rect((0,0,0,0), 1, 1)) # reset
99
+
100
+ if not 'image_on_right_display' in pysc.game.shared_data:
101
+ pysc.game.broadcast_message('warning', 'image not selected' )
102
+ return
103
+
104
+ for f in ss_view['frame_list']:
105
+ f.remove()
106
+ ss_view['frame_list'] = []
107
+
108
+
109
+ if ss_sprite := ss_view['spritesheet_sprite']:
110
+ ss_sprite.hide()
111
+
112
+
113
+ spritesheet: pygame.Surface = pysc.game.shared_data['image_on_right_display']
114
+
115
+ n_row = pysc.game.shared_data['n_row']
116
+ n_col = pysc.game.shared_data['n_col']
117
+
118
+ if not n_row:
119
+ print('invalid n_row')
120
+ pysc.game.broadcast_message('warning', 'invalid n_row' )
121
+ return
122
+
123
+ if not n_col:
124
+ print('invalid n_col')
125
+ pysc.game.broadcast_message('warning', 'invalid n_col' )
126
+ return
127
+ n_row = int(n_row)
128
+ n_col = int(n_col)
129
+ print(game['offset_x'], game['offset_y'], game['size_x'], game['size_y'])
130
+ spritesheet_to_cut = spritesheet.subsurface(game['offset_x'], game['offset_y'], game['size_x'], game['size_y'])
131
+
132
+ for i, (r, c) in enumerate(product(range(n_row), range(n_col))):
133
+ frame = pysc.get_frame_from_sprite_sheet_by_frame_size(spritesheet_to_cut, game['pixel_x'], game['pixel_y'], c, r)
134
+ sprite = SpriteFrameAfterCut(frame, i, ss_sprite.scale_factor, n_col)
135
+ ss_view['frame_list'].append(sprite)
136
+
137
+ ss_view.when_receive_message('cut').add_handler(on_msg_cut)
138
+
139
+
140
+
141
+ def SpriteFrameAfterCut(surface: Surface, order, scale_factor, n_col):
142
+ w, h = surface.get_width()*scale_factor, surface.get_height()*scale_factor
143
+ sprite = pysc.Sprite({'always':[surface]})
144
+ sprite.oob_limit = np.inf
145
+ sprite.scale_by(scale_factor)
146
+
147
+
148
+ spacing = 0
149
+ lt = topleft()
150
+ x = spacing+(order%n_col)*(w + spacing)+lt[0] +w/2
151
+ y = spacing+(order//n_col)*(h+spacing)+lt[1] +h/2
152
+
153
+ sprite['x'] = x
154
+ sprite['y'] = y
155
+
156
+ sprite.set_xy((x,y))
157
+ sprite.set_draggable(True)
158
+
159
+
160
+
161
+ def on_mouse_release():
162
+
163
+ pysc.game.broadcast_message("cut_sprite_frame_drop", dict(surface=surface, sprite=sprite, position=pysc.get_mouse_pos()))
164
+ pass
165
+
166
+
167
+
168
+ pysc.game.when_this_sprite_click_released(sprite).add_handler(on_mouse_release)
169
+
170
+
171
+ return sprite
172
+
173
+
174
+
175
+ # TODO: The scrolling becomes hard to use because touching only happens in the non-transparent pixels
176
+
177
+
178
+ def on_scroll(updown):
179
+ #if updown.
180
+ ss_sprite:pysc.Sprite = ss_view['spritesheet_sprite']
181
+ if not ss_sprite: return
182
+ if not ss_sprite.is_touching_mouse():
183
+ return
184
+
185
+ if updown == 'up':
186
+ ss_sprite.scale_by(1.05)
187
+ else:
188
+ ss_sprite.scale_by(1/1.05)
189
+
190
+ pysc.game.broadcast_message("ss_sprite_scale_change")
191
+
192
+ pysc.game.when_mouse_scroll([ss_view]).add_handler(on_scroll)
193
+
194
+
195
+
196
+ def draw_cutting_rect(surface, colour, x0, y0, x1, y1, n_row, n_col):
197
+
198
+ n_row = 1 if not n_row else n_row
199
+ n_col = 1 if not n_col else n_col
200
+
201
+ x0, x1 = min(x0, x1), max(x0, x1)
202
+ y0, y1 = min(y0, y1), max(y0, y1)
203
+
204
+ n_line_h = n_row - 1
205
+ n_line_v = n_col - 1
206
+
207
+
208
+ pygame.draw.lines(surface, colour, True, [(x0, y0), (x0, y1), (x1, y1), (x1, y0)])
209
+
210
+ x_step = (x1 - x0)/n_col # vertical lines
211
+ y_step = (y1 - y0)/n_row # horizontal lines
212
+ #
213
+ for lx in range(int(n_line_v)):
214
+ xpos = (1+lx)*x_step
215
+ xpos += x0
216
+ pygame.draw.line(surface, colour, (xpos, y0), (xpos, y1))
217
+
218
+ for ly in range(int(n_line_h)):
219
+ ypos = (1+ly)*y_step
220
+ ypos += y0
221
+ pygame.draw.line(surface, colour, (x0, ypos), (x1, ypos))
222
+
223
+ return x_step, y_step
224
+
225
+
226
+
227
+
228
+ def draw_selection_rect():
229
+ cir0, cir1 = pysc.game['c0'], pysc.game['c1']
230
+ while True:
231
+ yield 1/30
232
+ ss_sprite:pysc.Sprite = ss_view['spritesheet_sprite']
233
+ if not ss_sprite:
234
+ continue
235
+
236
+ if game['pixel_x'] is None: continue
237
+ if game['pixel_y'] is None: continue
238
+
239
+ ss_view._drawing_manager.frames[0].fill((255,255,255))
240
+
241
+ n_col = 1 if not pysc.game['n_col'] else pysc.game['n_col']
242
+ n_row = 1 if not pysc.game['n_row'] else pysc.game['n_row']
243
+
244
+
245
+ # top left of the selected rect
246
+ gx0 = game['offset_x']*ss_sprite.scale_factor + ss_sprite.rect.left - ss_view.rect.left
247
+ gy0 = game['offset_y']*ss_sprite.scale_factor + ss_sprite.rect.top - ss_view.rect.top
248
+
249
+ gx1 = gx0 + game['pixel_x']*n_col*ss_sprite.scale_factor
250
+ gy1 = gy0 + game['pixel_y']*n_row*ss_sprite.scale_factor
251
+
252
+
253
+ #pygame.draw.lines(ss_view._drawing_manager.frames[0], (0,0,0), True, [(gx0, gy0), (gx0, gy1), (gx1, gy1), (gx1, gy0)])
254
+
255
+ draw_cutting_rect(
256
+ ss_view._drawing_manager.frames[0],
257
+ (0,0,0),
258
+ gx0, gy0, gx1, gy1,
259
+ pysc.game['n_row'], pysc.game['n_col']
260
+ )
261
+
262
+ ss_view.when_game_start().add_handler(draw_selection_rect)