-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy path__main__.py
More file actions
161 lines (133 loc) · 4.85 KB
/
Copy path__main__.py
File metadata and controls
161 lines (133 loc) · 4.85 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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
"""2026-07-09
Recursive Squares 07
Desenha quadrados recursivamente
ericof.com
png
Sketch,py5,CreativeCoding
"""
from collections import deque
from sketches.utils.draw import canvas
from sketches.utils.draw.cores.paletas import gera_paleta
from sketches.utils.helpers import sketches as helpers
import py5
sketch = helpers.info_for_sketch(__file__, __doc__)
paleta = gera_paleta("mandarin-redux")
grade_n = 5
tamanho_quadrado = 160
def constroi_areas() -> list[tuple[float, float, int, list[str | int], int]]:
"""Monta a grade ``grade_n`` x ``grade_n`` de areas centradas nas celulas.
Cada area recebe o centro da sua celula no painel interno e o tamanho base
``tamanho_quadrado``. Posicionar pelo centro da celula mantem a grade valida
para qualquer ``grade_n``. O ``z_sinal`` alterna por anel concentrico
(distancia de Chebyshev ate a celula central ``grade_n // 2``): aneis pares
-- centro (perimetro 1), perimetro 5, ... -- empilham em +z; aneis impares
-- perimetro 3, 7, ... -- empilham em -z. O padrao e simetrico so em grades
impares.
:returns: Lista de ``(cx, cy, tamanho_quadrado, paleta, z_sinal)`` por celula.
"""
largura, altura = helpers.DIMENSOES.internal
meio = grade_n // 2
return [
(
largura * (col + 0.5) / grade_n,
altura * (lin + 0.5) / grade_n,
tamanho_quadrado,
paleta,
1 if max(abs(col - meio), abs(lin - meio)) % 2 == 0 else -1,
)
for lin in range(grade_n)
for col in range(grade_n)
]
areas = constroi_areas()
cor_fundo = py5.color(0)
z_base = -28
z_divisor = 2
z_passo = 8
rotacao_x = 45
rotacao_z = -5
traco = 4
def setup():
py5.size(*helpers.DIMENSOES.external, py5.P3D)
py5.color_mode(py5.HSB, 360, 100, 100)
py5.rect_mode(py5.CENTER)
def desenha_quadrados(
cx: float, cy: float, tamanho_base: float, paleta: deque[int], z_sinal: int
) -> None:
# Espelhamento pela POSICAO no painel (nao por idx): areas a direita do
# centro giram no sentido oposto (reflexo horizontal) e areas abaixo do
# centro sao refletidas verticalmente via scale(1, -1). Chavear por posicao
# evita o bug de ``idx // 2`` agrupar pela diagonal e misturar as linhas de
# cima e de baixo. O tamanho usa a magnitude do angulo (loop 0->70), entao
# espelho e reflexo so mudam a orientacao -- o tamanho aparente fica identico.
centro_x = helpers.DIMENSOES.internal[0] / 2
centro_y = helpers.DIMENSOES.internal[1] / 2
reflete_h = 1 if cx < centro_x else -1
reflete_v = cy > centro_y
z = -40
for ida, angulo in enumerate(range(0, 75, 3)):
tamanho = tamanho_base * (1 - (angulo / 70)) if ida else tamanho_base
cor = paleta[0]
paleta.rotate()
z = z_base + ((ida // z_divisor) * z_passo * z_sinal)
with py5.push():
py5.translate(cx, cy, z)
if reflete_v:
py5.scale(1, -1)
py5.rotate(py5.radians(reflete_h * angulo))
py5.stroke(cor)
py5.square(0, 0, tamanho)
def draw() -> None:
py5.background(cor_fundo)
py5.no_fill()
py5.stroke_weight(traco)
with py5.push():
tx = helpers.DIMENSOES.pos_interno[0]
ty = helpers.DIMENSOES.pos_interno[1] + 100
py5.translate(tx, ty, -420)
py5.rotate_x(py5.radians(rotacao_x))
py5.rotate_z(py5.radians(rotacao_z))
for cx, cy, tamanho_base, paleta, z_sinal in areas:
paleta_deque: deque[int] = deque(paleta)
desenha_quadrados(cx, cy, tamanho_base, paleta_deque, z_sinal)
msg = (
f"grade: {grade_n}x{grade_n} | tam: {tamanho_quadrado} | "
f"z_base: {z_base} | z_divisor: {z_divisor} | "
f"z_passo: {z_passo} | traco: {traco}"
)
# Credits and go
canvas.sketch_frame(
sketch,
cor_fundo,
"large_transparent_white",
"transparent_white",
version=2,
msg=msg,
)
def key_pressed():
global z_base, z_divisor, z_passo, traco, grade_n, tamanho_quadrado, areas
key = py5.key
match key:
case "w" | "s":
z_base += 2 if key == "w" else -2
case "a" | "d":
z_passo += 1 if key == "d" else -1
case ">" | "<":
if key == "<" and z_divisor <= 1:
return
z_divisor += 1 if key == ">" else -1
case "+" | "-":
traco += 1 if key == "+" else -1
case "g" | "h":
grade_n = max(1, grade_n + (1 if key == "g" else -1))
areas = constroi_areas()
case "o" | "l":
tamanho_quadrado = max(10, tamanho_quadrado + (10 if key == "o" else -10))
areas = constroi_areas()
case " ":
save_and_close()
def save_and_close():
py5.no_loop()
canvas.save_sketch_image(sketch)
py5.exit_sketch()
if __name__ == "__main__":
py5.run_sketch()