diff --git a/2025/sketch_2025_08_15/cell.py b/2025/sketch_2025_08_15/cell.py new file mode 100644 index 00000000..99383618 --- /dev/null +++ b/2025/sketch_2025_08_15/cell.py @@ -0,0 +1,141 @@ +from math import sqrt +from py5 import * +from functools import cache + + +SIN_60 = sqrt(3) * 0.5 # sin(radians(60)) + +class mock_cell: + state = 1 + gen = 0 + +class Cell(): + """Main cellular automaton class""" + board = dict() + EVN_NBS = ((0, 1), (0, -1), (-1, 0), (1, 0), (-1, -1), (1, -1)) + ODD_NBS = ((0, 1), (0, -1), (-1, 0), (1, 0), (-1, 1), (1, 1)) + W = 8 + H = SIN_60 * W + last_clicked = None + + def __init__(self, i, j, rnd=False): + self.board[(i, j)] = self + self.i = i + self.j = j + self.x = i * self.W * 1.5 + self.W + if i % 2 == 0: + self.y = j * self.H * 2 + self.H + else: + self.y = j * self.H * 2 + self.H * 2 + if rnd: + self.state = 1 if random(50000 / (1 + j)) < 1 else 0 + else: + self.state = 0 + self.next_state = self.state + self.gen = 0 + self.nbs = 0 + + def display(self): + if self.state: + with push_matrix(): + translate(self.x, self.y) + d = (4 + 4 * sin(self.gen / 5.0)) + #stroke(d * 16, 200, 200) #42 * self.nbs) + stroke(42 * self.nbs, 200, 200) + stroke_weight(2 + d * self.W / 6) + point(0, 0) + + def calc_live_nbs(self): + nbs = self.EVN_NBS if self.i % 2 == 0 else self.ODD_NBS + return sum(self.get_neighbour(i_offset, j_offset).state + for i_offset, j_offset in nbs) + + def get_neighbour(self, i_offset, j_offset): + return self.board.get((self.i + i_offset, + self.j + j_offset), + mock_cell()) + + def get_live_nbs(self): + nbs = self.EVN_NBS if self.i % 2 == 0 else self.ODD_NBS + return [self.get_neighbour(i_offset, j_offset) + for i_offset, j_offset in nbs + if self.get_neighbour(i_offset, j_offset).state] + + def calc_next_state(self): + self.nbs = nbs = self.calc_live_nbs() + # muito bom + if nbs == 2 and self.state == 0: + self.next_state = 1 + lnbs = self.get_live_nbs() + if lnbs: + self.gen = lnbs[-1].gen + 1 + elif nbs > 4: + self.next_state = 0 + else: + self.next_state = self.state + +# # muito bom +# if nbs == 2 and self.state == 0: +# self.next_state = 1 +# lnbs = self.get_live_nbs() +# if lnbs: +# self.gen = lnbs[-1].gen + 1 +# elif nbs > 4: +# self.next_state = 0 +# else: +# self.next_state = self.state +# +# if nbs == 2 and self.state == 0: +# self.next_state = 1 +# lnbs = self.get_live_nbs() +# if lnbs: +# self.gen = lnbs[-1].gen + 1 +# elif nbs not in (5, 4, 3, 2): +# self.next_state = 0 +# else: +# self.next_state = self.state + + def check_click(self): + if self.__class__.last_clicked != self: + self.state ^= 1 + self.__class__.last_clicked = self + + + @staticmethod + def hexagon(w): + h = SIN_60 * w + with begin_shape(): + vertex(-w, 0) + vertex(-w / 2, -h) + vertex(w / 2, -h) + vertex(w, 0) + vertex(w - w / 2, h) + vertex(-w / 2, h) + vertex(-w, 0) + + @classmethod + def next_board(cls): + for cell in cls.board.values(): + cell.calc_next_state() + for cell in cls.board.values(): + cell.state = cell.next_state + + @classmethod + def toggle(cls, mx, my): + i = (mx - cls.W) // (cls.W * 1.5) + if i % 2 == 0: + j = (my - cls.H) // (cls.H * 2) + else: + j = (my - cls.H * 2) // (cls.H * 2) + try: + cell = cls.board[(i, j)] + cell.check_click() + except KeyError: + pass + + @classmethod + def init_board(cls, cols, rows, rnd=False): + for i in range(cols): + for j in range(rows): + cls(i, j, rnd) + diff --git a/2025/sketch_2025_08_15/sketch_2025_08_15.png b/2025/sketch_2025_08_15/sketch_2025_08_15.png new file mode 100644 index 00000000..70d9f758 Binary files /dev/null and b/2025/sketch_2025_08_15/sketch_2025_08_15.png differ diff --git a/2025/sketch_2025_08_15/sketch_2025_08_15.py b/2025/sketch_2025_08_15/sketch_2025_08_15.py new file mode 100644 index 00000000..5309ffad --- /dev/null +++ b/2025/sketch_2025_08_15/sketch_2025_08_15.py @@ -0,0 +1,55 @@ +# based on sketch_2022_05_12hex_cells + +import py5 +from cell import Cell + +play = former_play_state = False +sample_rate = 1 + +def setup(): + global cols, rows, board + py5.size(800, 800) + py5.color_mode(py5.HSB) + cols = int(py5.width / Cell.W * 1.5) + rows = int(py5.height / Cell.H * 2) - 1 + Cell.init_board(cols, rows) + + +def draw(): + py5.background(0) + for cell in Cell.board.values(): + cell.display() + if play and py5.frame_count % sample_rate == 0: + Cell.next_board() + + +def key_pressed(): + global play + if py5.key == 'e': + Cell.init_board(cols, rows) + elif py5.key == 'r': + Cell.init_board(cols, rows, rnd=True) + elif py5.key == ' ': + play = not play + elif py5.key == 's': + py5.save_frame('####.png') + + +def mouse_dragged(): + Cell.toggle(py5.mouse_x, py5.mouse_y) + + +def mouse_pressed(): + global play, former_play_state + former_play_state = play + play = False + Cell.toggle(py5.mouse_x, py5.mouse_y) + + +def mouse_released(): + global play + play = former_play_state + Cell.last_clicked = None + +py5.run_sketch(block=False) + diff --git a/docs/README.md b/docs/README.md index a57056a1..9eccae1a 100644 --- a/docs/README.md +++ b/docs/README.md @@ -26,6 +26,16 @@ If you appreciate what I have been doing, you may also support my artistic work, +### sketch_2025_08_15 + +![sketch_2025_08_15](https://raw.githubusercontent.com/villares/sketch-a-day/main/2025/sketch_2025_08_15/sketch_2025_08_15.png) + +[sketch_2025_08_15](https://github.com/villares/sketch-a-day/tree/main/2025/sketch_2025_08_15) [[py5](https://py5coding.org/)] + + + +--- + ### sketch_2025_08_14 ![sketch_2025_08_14](https://raw.githubusercontent.com/villares/sketch-a-day/main/2025/sketch_2025_08_14/sketch_2025_08_14.png)