-
Notifications
You must be signed in to change notification settings - Fork 66
Expand file tree
/
Copy pathsnmm035
More file actions
138 lines (115 loc) · 4.25 KB
/
snmm035
File metadata and controls
138 lines (115 loc) · 4.25 KB
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
!pip install apng
import os, IPython
from PIL import Image, ImageDraw, ImageFont
from apng import APNG
class AStudio(object):
bodies: list
frame: int
def __init__(self, width=400, height=300, background='white'):
self.width = width
self.height = height
self.background = background
self.bodies = []
self.files = []
self.frame = 0
def append(self, shape):
self.bodies.append(shape) # 被写体を追加する
return shape
def render(self, caption=''):
# 新しいキャンバスを作る
canvas = ACanvas(self.width, self.height, self.background)
# リスト上の形状を順番に描画する
for body in self.bodies:
body.render(canvas, self.frame)
# PNG画像に保存する
filename = f'frame{self.frame}.png'
canvas.image.save(filename)
self.files.append(filename)
self.frame += 1
def create_anime(self, filename='anime.png', delay=100):
APNG.from_files(self.files, delay=int(delay)).save(filename)
for image in self.files:
os.remove(image) # 不要なファイルは消す
self.files = []
return filename
class ACanvas(object):
width:int #横幅
height:int #高さ
background: any #背景色
image: Image
draw: ImageDraw
def __init__(self, width=400, height=300, background='white'):
self.width = width
self.height = height
self.background = background
self.image = Image.new('RGBA', (self.width, self.height), self.background)
self.draw = ImageDraw.Draw(self.image, 'RGBA')
class AShape(object):
width:int #幅
height:int #高さ
cx:int #中心点のx
cy:int #中心点のy
scale:float # スケール
def __init__(self, width, height=None, cx=None, cy=None):
self.width = width
self.height = width if height is None else height
self.cx = self.width / 2 if cx is None else cx
self.cy = self.height / 2 if cy is None else cy
self.scale = 1.0
def bounds(self): # 左上の座標と幅,高さを計算する
w = self.width * self.scale
h = self.height * self.scale
ox = self.cx - w/2
oy = self.cy - h/2
return ox, oy, w, h
def render(self, canvas: ACanvas, tick: int):
ox, oy, w, h = self.bounds()
canvas.draw.rectangle((ox, oy, ox+w, oy+h), fill=(127,127,127))
class ARectangle(AShape):
color: any
def __init__(self, width=50, height=50, cx=None, cy=None, color=None):
# スーパークラスのコンストラクタを呼ぶ
AShape.__init__(self, width, height, cx, cy)
self.color = get_color(color)
def render(self, canvas: ACanvas, tick: int):
ox, oy, w, h = self.bounds()
canvas.draw.rectangle((ox, oy, ox+w, oy+h), fill=self.color)
## カラーテーマ
import random
ColorTheme = [
'#de9610', '#c93a40', '#fff001', '#d06d8c', '#65ace4', '#a0c238',
'#56a764', '#d16b16', '#cc528b', '#9460a0', '#f2cf01', '#0074bf',
]
def get_color(c): # カラーテーマからランダムに選ぶ
if c is None:
return random.choice(ColorTheme)
if isinstance(c, int):
return ColorTheme[c % len(ColorTheme)]
return c
class APolygon(AShape):
color: any
slope: float # 最初の傾き
def __init__(self, width=100, height=None, cx=None, cy=None, N=3, slope=0.0, color=None):
AShape.__init__(self, width, height, cx, cy)
self.N = N
self.slope = slope
self.color = get_color(color)
def render(self, canvas: ACanvas, tick: int):
theta = math.pi * 2 / self.N
# 半径
r = min(self.width, self.height)/2
# 頂点の数だけ頂点の座標を計算する
points = []
for i in range(self.N):
x = self.cx + r * math.cos(theta*i + self.slope)
y = self.cy + r * math.sin(theta*i + self.slope)
points.append((x, y))
canvas.draw.polygon(points, fill=self.color)
import math
studio = AStudio()
shape = APolygon(width=150, N=5)
studio.append(shape)
for i in range(60):
shape.slope = (2 * math.pi * i) / 10
studio.render()
IPython.display.Image(studio.create_anime())