Skip to content

Commit 3e0dadc

Browse files
refactor(rendering): shapes rendering rewrote
1 parent 8967305 commit 3e0dadc

21 files changed

+449
-528
lines changed

system/bytestream.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import io
2-
from typing import Literal, Optional
2+
from typing import Literal
33

44

55
class Reader(io.BytesIO):
@@ -68,7 +68,7 @@ def write_uint32(self, integer: int):
6868
def write_int32(self, integer: int):
6969
self.write_int(integer, 4, True)
7070

71-
def write_string(self, string: Optional["str"] = None):
71+
def write_string(self, string: str | None = None):
7272
if string is None:
7373
self.write_byte(255)
7474
return

system/lib/__init__.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -195,9 +195,9 @@ def refill_menu():
195195
Menu.Item(
196196
name=locale.clear_directories,
197197
description=locale.clean_dirs_description,
198-
handler=lambda: clear_directories()
199-
if Console.question(locale.clear_qu)
200-
else -1,
198+
handler=lambda: (
199+
clear_directories() if Console.question(locale.clear_qu) else -1
200+
),
201201
)
202202
)
203203
other.add(

system/lib/features/cut_sprites.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,9 @@ def render_objects(swf: SupercellSWF, output_folder: Path):
6767
region = shape.regions[region_index]
6868

6969
swf.xcod_writer.write_ubyte(region.texture_index)
70-
swf.xcod_writer.write_ubyte(region.get_points_count())
70+
swf.xcod_writer.write_ubyte(region.get_point_count())
7171

72-
for i in range(region.get_points_count()):
72+
for i in range(region.get_point_count()):
7373
swf.xcod_writer.write_uint16(int(region.get_u(i)))
7474
swf.xcod_writer.write_uint16(int(region.get_v(i)))
7575
swf.xcod_writer.write_ubyte(1 if region.is_mirrored else 0)

system/lib/features/place_sprites.py

Lines changed: 31 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import os
22
from pathlib import Path
3-
from typing import List
43

5-
from PIL import Image, ImageDraw
4+
from PIL import Image
65

76
from system.lib import Console
8-
from system.lib.helper import get_sides, get_size
9-
from system.lib.images import get_format_by_pixel_type
7+
from system.lib.images import create_filled_polygon_image, get_format_by_pixel_type
8+
from system.lib.math.polygon import get_rect
109
from system.lib.xcod import FileInfo
1110
from system.localization import locale
1211

@@ -15,7 +14,7 @@
1514

1615
def place_sprites(
1716
file_info: FileInfo, folder: Path, overwrite: bool = False
18-
) -> List[Image.Image]:
17+
) -> list[Image.Image]:
1918
files_to_overwrite = os.listdir(folder / ("overwrite" if overwrite else ""))
2019
texture_files = os.listdir(folder / "textures")
2120

@@ -40,46 +39,40 @@ def place_sprites(
4039
)
4140

4241
for region_index, region_info in enumerate(shape_info.regions):
43-
texture_size = (
44-
sheets[region_info.texture_id].width,
45-
sheets[region_info.texture_id].height,
46-
)
42+
texture_width = sheets[region_info.texture_id].width
43+
texture_height = sheets[region_info.texture_id].height
4744

4845
filename = f"shape_{shape_info.id}_{region_index}.png"
4946
if filename not in files_to_overwrite:
5047
continue
5148

52-
img_mask = Image.new("L", texture_size, 0)
53-
ImageDraw.Draw(img_mask).polygon(region_info.points, fill=MASK_COLOR)
54-
bbox = img_mask.getbbox()
55-
56-
if not bbox:
57-
min_x = min(i[0] for i in region_info.points)
58-
min_y = min(i[1] for i in region_info.points)
59-
max_x = max(i[0] for i in region_info.points)
60-
max_y = max(i[1] for i in region_info.points)
49+
rect = get_rect(region_info.points)
6150

62-
if max_y - min_y != 0:
63-
for _y in range(max_y - min_y):
64-
img_mask.putpixel((max_x - 1, min_y + _y - 1), MASK_COLOR)
51+
img_mask = create_filled_polygon_image(
52+
"L", texture_width, texture_height, region_info.points, MASK_COLOR
53+
)
6554

66-
elif max_x - min_x != 0:
67-
for _x in range(max_x - min_x):
68-
img_mask.putpixel((min_x + _x - 1, max_y - 1), MASK_COLOR)
55+
if rect.width + rect.height <= 2:
56+
if rect.height != 0:
57+
for _y in range(int(rect.height)):
58+
img_mask.putpixel(
59+
(int(rect.right - 1), int(rect.top + _y - 1)), MASK_COLOR
60+
)
61+
elif rect.width != 0:
62+
for _x in range(int(rect.width)):
63+
img_mask.putpixel(
64+
(int(rect.left + _x - 1), int(rect.bottom - 1)), MASK_COLOR
65+
)
6966
else:
70-
img_mask.putpixel((max_x - 1, max_y - 1), MASK_COLOR)
71-
72-
left, top, right, bottom = get_sides(region_info.points)
73-
if left == right:
74-
right += 1
75-
if top == bottom:
76-
bottom += 1
77-
78-
width, height = get_size(left, top, right, bottom)
79-
left = int(left)
80-
top = int(top)
67+
img_mask.putpixel(
68+
(int(rect.right - 1), int(rect.bottom - 1)), MASK_COLOR
69+
)
8170

82-
bbox = int(left), int(top), int(right), int(bottom)
71+
x = int(rect.left)
72+
y = int(rect.top)
73+
width = int(rect.width)
74+
height = int(rect.height)
75+
bbox = int(rect.left), int(rect.top), int(rect.right), int(rect.bottom)
8376

8477
tmp_region = Image.open(
8578
f'{folder}{"/overwrite" if overwrite else ""}/{filename}'
@@ -90,9 +83,9 @@ def place_sprites(
9083
tmp_region = tmp_region.resize((width, height), Image.Resampling.LANCZOS)
9184

9285
sheets[region_info.texture_id].paste(
93-
Image.new("RGBA", (width, height)), (left, top), img_mask.crop(bbox)
86+
Image.new("RGBA", (width, height)), (x, y), img_mask.crop(bbox)
9487
)
95-
sheets[region_info.texture_id].paste(tmp_region, (left, top), tmp_region)
88+
sheets[region_info.texture_id].paste(tmp_region, (x, y), tmp_region)
9689
print()
9790

9891
return sheets

system/lib/features/sc/__init__.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import struct
22
from pathlib import Path
3-
from typing import List
43

54
from loguru import logger
65
from PIL import Image
@@ -16,7 +15,7 @@
1615
def compile_sc(
1716
output_folder: Path,
1817
file_info: FileInfo,
19-
sheets: List[Image.Image],
18+
sheets: list[Image.Image],
2019
):
2120
sc = Writer()
2221

system/lib/helper.py

Lines changed: 0 additions & 45 deletions
This file was deleted.

system/lib/images.py

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import math
22

33
import PIL.PyAccess
4-
from PIL import Image
4+
from PIL import Image, ImageDraw
55

66
from system.bytestream import Reader, Writer
77
from system.lib.console import Console
8+
from system.lib.math.point import Point
9+
from system.lib.matrices import Matrix2x3
810
from system.lib.pixel_utils import (
911
get_channel_count_by_pixel_type,
1012
get_read_function,
@@ -17,7 +19,6 @@
1719

1820
def load_image_from_buffer(img: Image.Image) -> None:
1921
width, height = img.size
20-
# noinspection PyTypeChecker
2122
img_loaded: PIL.PyAccess.PyAccess = img.load() # type: ignore
2223

2324
with open("pixel_buffer", "rb") as pixel_buffer:
@@ -198,13 +199,13 @@ def transform_image(image, scale_x, scale_y, angle, x, y):
198199

199200
return image.transform(
200201
(translated_w, translated_h),
201-
Image.AFFINE,
202+
Image.Transform.AFFINE,
202203
(a, b, c, d, e, f),
203-
resample=Image.BILINEAR,
204+
resample=Image.Resampling.BILINEAR,
204205
)
205206

206207

207-
def translate_image(image, x, y):
208+
def translate_image(image, x: float, y: float) -> Image.Image:
208209
w, h = image.size
209210

210211
translated_w = int(math.ceil(w + math.fabs(x)))
@@ -216,23 +217,29 @@ def translate_image(image, x, y):
216217

217218
return image.transform(
218219
(translated_w, translated_h),
219-
Image.AFFINE,
220+
Image.Transform.AFFINE,
220221
(1, 0, -x, 0, 1, -y),
221-
resample=Image.BILINEAR,
222+
resample=Image.Resampling.BILINEAR,
222223
)
223224

224225

225-
def transform_image_by_matrix(image, matrix: list or tuple):
226-
scale_x, rotation_x, x = matrix[:3]
227-
rotation_y, scale_y, y = matrix[3:]
228-
return transform_image(
229-
image, scale_x, scale_y, math.atan2(rotation_x, rotation_y), x, y
226+
def transform_image_by_matrix(image: Image.Image, matrix: Matrix2x3):
227+
new_width = matrix.apply_x(image.width, image.height)
228+
new_height = matrix.apply_y(image.width, image.height)
229+
230+
return image.transform(
231+
(new_width, new_height),
232+
Image.Transform.AFFINE,
233+
(matrix.a, matrix.b, matrix.x, matrix.c, matrix.d, matrix.y),
234+
resample=Image.Resampling.BILINEAR,
230235
)
231236

232237

233-
if __name__ == "__main__":
234-
transform_image_by_matrix(
235-
Image.open("../../test_0.png"),
236-
[1.0458984375, 0.0, -127.65, 0.0, 1.0458984375, -700.0],
237-
).show()
238-
input()
238+
def create_filled_polygon_image(
239+
mode: str, width: int, height: int, polygon: list[Point], color: int
240+
) -> Image.Image:
241+
mask_image = Image.new(mode, (width, height), 0)
242+
drawable_image = ImageDraw.Draw(mask_image)
243+
drawable_image.polygon([point.as_tuple() for point in polygon], fill=color)
244+
245+
return mask_image

system/lib/math/__init__.py

Whitespace-only changes.

system/lib/objects/point.py renamed to system/lib/math/point.py

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
from typing import Tuple
2-
3-
41
class Point:
52
def __init__(self, x: float = 0, y: float = 0):
63
self.x: float = x
@@ -26,13 +23,7 @@ def __neg__(self):
2623
return self
2724

2825
def __repr__(self):
29-
return str(self.position)
26+
return str(self.as_tuple())
3027

31-
@property
32-
def position(self) -> Tuple[float, float]:
28+
def as_tuple(self) -> tuple[float, float]:
3329
return self.x, self.y
34-
35-
@position.setter
36-
def position(self, value: Tuple[float, float]):
37-
self.x = value[0]
38-
self.y = value[1]

system/lib/math/polygon.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
from math import atan2, degrees
2+
from typing import TypeAlias
3+
4+
from system.lib.math.point import Point
5+
from system.lib.math.rect import Rect
6+
7+
Polygon: TypeAlias = list[Point]
8+
9+
10+
def is_clockwise(polygon: Polygon) -> bool:
11+
"""Returns true if polygon points order is clockwise, otherwise false.
12+
13+
:param polygon:
14+
:return:
15+
"""
16+
points_sum = 0
17+
for i in range(len(polygon)):
18+
p1 = polygon[i]
19+
p2 = polygon[(i + 1) % len(polygon)]
20+
21+
points_sum += (p2.x - p1.x) * (p1.y + p2.y)
22+
return points_sum > 0
23+
24+
25+
def compare_polygons(
26+
polygon1: Polygon,
27+
polygon2: Polygon,
28+
round_to_nearest: bool = False,
29+
) -> tuple[int, bool]:
30+
"""Calculates rotation and if polygon is mirrored.
31+
32+
:param polygon1: shape polygon
33+
:param polygon2: sheet polygon
34+
:param round_to_nearest: should round to a multiple of 90
35+
:return: rotation degrees, is polygon mirrored
36+
"""
37+
38+
is_uv_clockwise = is_clockwise(polygon2)
39+
is_xy_clockwise = is_clockwise(polygon1)
40+
41+
mirroring = not (is_uv_clockwise == is_xy_clockwise)
42+
43+
dx = polygon1[1].x - polygon1[0].x
44+
dy = polygon1[1].y - polygon1[0].y
45+
du = polygon2[1].x - polygon2[0].x
46+
dv = polygon2[1].y - polygon2[0].y
47+
48+
angle_xy = degrees(atan2(dy, dx)) % 360
49+
angle_uv = degrees(atan2(dv, du)) % 360
50+
51+
angle = angle_xy - angle_uv
52+
53+
if mirroring:
54+
angle -= 180
55+
56+
angle = (angle + 360) % 360
57+
58+
if round_to_nearest:
59+
angle = round(angle / 90) * 90
60+
61+
return int(angle), mirroring
62+
63+
64+
def get_rect(polygon: list[Point]) -> Rect:
65+
"""Calculates polygon bounds and returns rect.
66+
67+
:param polygon: polygon points
68+
:return: Rect object
69+
"""
70+
71+
rect = Rect(left=100000, top=100000, right=-100000, bottom=-100000)
72+
73+
for point in polygon:
74+
rect.add_point(point.x, point.y)
75+
76+
return rect

0 commit comments

Comments
 (0)