diff --git a/2019/sketch_190118a/arcs.py b/2019/sketch_190118a/arcs.py new file mode 100644 index 00000000..32896ba2 --- /dev/null +++ b/2019/sketch_190118a/arcs.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- + +ROTATION = {0 : 0, + BOTTOM : 0, + DOWN : 0, + 1 : HALF_PI, + LEFT : HALF_PI, + 2 : PI, + TOP : PI, + UP : PI, + 3 : PI + HALF_PI, + RIGHT: PI + HALF_PI, + BOTTOM + RIGHT : 0, + DOWN + RIGHT : 0, + DOWN + LEFT : HALF_PI, + BOTTOM + LEFT : HALF_PI, + TOP + LEFT : PI, + UP + LEFT : PI, + TOP + RIGHT: PI + HALF_PI, + UP + RIGHT: PI + HALF_PI, + } + +def quarter_circle(x, y, radius, quadrant): + circle_arc(x, y, radius, ROTATION[quadrant], HALF_PI) + +def half_circle(x, y, radius, quadrant): + circle_arc(x, y, radius, ROTATION[quadrant], PI) + +def circle_arc(x, y, radius, start_ang, sweep_ang): + npoints = 2. + angle = sweep_ang/npoints + a = start_ang + while a < start_ang + sweep_ang: + sx = x + cos(a) * radius + sy = y + sin(a) * radius + line(0, 0, sx, sy) + a += angle + + #arc(x, y, radius * 2, radius * 2, start_ang, start_ang + sweep_ang) + +def bar(x1, y1, x2, y2, thickness=None, shorter=0, ends=(1,1)): + """ + O código para fazer as barras, dois pares (x, y), + um parâmetro de encurtamento: shorter + """ + L = dist(x1, y1, x2, y2) + if not thickness: + thickness = 10 + with pushMatrix(): + translate(x1, y1) + angle = atan2(x1 - x2, y2 - y1) + rotate(angle) + offset = shorter / 2 + line(thickness/2, offset, thickness/2, L - offset) + line(-thickness/2, offset, -thickness/2, L - offset) + if ends[0]: + half_circle(0, offset, thickness/2, UP) + if ends[1]: + half_circle(0, L - offset, thickness/2, DOWN) diff --git a/2019/sketch_190118a/cell.py b/2019/sketch_190118a/cell.py new file mode 100644 index 00000000..5f5a0fc8 --- /dev/null +++ b/2019/sketch_190118a/cell.py @@ -0,0 +1,250 @@ +# -*- coding: utf-8 -*- +from random import choice +from arcs import quarter_circle, half_circle, circle_arc, bar + +class Cell(): + grid = dict() + debug_mode = False + # constants + variations = "abcde" + N, A, I, T, L, C, E = type_names = "NAITLCE" + module_types = {"11111": A, # All neighbours on + "00100": N, # No neighbours on - isolated + "01111": T, # T-shaped (three neighbours) + "11110": T, + "11101": T, + "10111": T, + "10101": I, # I - Up & down or Left and Right + "01110": I, + "01100": C, # Cap - single neighbour + "00110": C, + "00101": C, + "10100": C, + "01101": L, # L-shaped (two neighbours) + "10110": L, + "00111": L, + "11100": L, + "00000": E, # Empty - not used at this point + # "10000": E, + # "01000": E, + # "00010": E + } + + # neighbours list + NL = ((-1, -1), (+0, -1), (+1, -1), + (-1, +0), (+0, +0), (+1, +0), + (-1, +1), (+0, +1), (+1, +1)) + # ortho neighbours + ONL = ((+0, -1), + (-1, +0), (+0, +0), + (+1, +0), + (+0, +1)) + # diagonal neighbours + DNL = ((-1, -1), (+1, -1), + (+0, +0), + (-1, +1), (+1, +1)) + + def __init__(self, index, cell_size, state=False, border=None): + self.index = index + self.state = state + self.size_ = cell_size + self.mouse_down = False + self.variation = "a" + self.ang = choice((0, 1, 2, 3)) + self.border = border + self.calculate_pos() + + def calculate_pos(self): + i, j = self.index + if self.border == None: + self.border = self.size_ + self.pos = PVector(self.border + self.size_ / 2 + i * self.size_ - width/2, + self.border + self.size_ / 2 + j * self.size_ - height/2) + + def update(self, mx, my): + # mouse over & selection treatment + hs = self.size_ / 2 + px, py = self.pos.x, self.pos.y + self.mouse_on = (px - hs < mx < px + hs and + py - hs < my < py + hs) + if self.mouse_on and mousePressed: + self.mouse_down = True + + elif self.mouse_down: + self.state = not self.state + self.mouse_down = False + + self.identify_module(Cell.ONL) + self.type = Cell.module_types.get(self.module, "") + + def plot(self, mode): + quarter = self.size_ / 4. # - 1 + rnd = choice(Cell.variations) + mode_variation = {1: "a", + 2: "b", + 3: "c", + 4: "d", + 5: "e", + 6: Cell.variation_dict.get(Cell.module_types.get(self.module)), + 7: rnd + } + self.draw_node() + if self.state: + strokeWeight(1) + if 1 <= mode <= len(mode_variation): + self.variation = mode_variation[mode] + #self.draw_node() + if mode == -1: + fill(100, 100) + noStroke() + rect(self.pos.x, self.pos.y, self.size_, self.size_) + noFill() + + # diagonal mode + if mode == 8: + i, j = self.index + for (ni, nj) in Cell.DNL: + nb = Cell.grid.get((i + ni, j + nj), None) + if nb and nb.state: + for ii in range(Cell.step_start - 2, + Cell.step_end - 2, + Cell.step): # (-28, 29, 7): + stroke(64 - ii * 8, 255, 255) + if ni <> 0 and nj <> 0: + bar(self.pos.x, self.pos.y, + nb.pos.x, nb.pos.y, + (quarter + ii) * 2, ends=(0, 1)) + # else: + # rect(self.pos.x, self.pos.y, + # (a + ii) * 2, (a + ii) * 2) + + def draw_node(self): + """ draws node """ + siz = self.size_ + l = siz / 2. + a = l / 2. # - 1 + c = l / 2. # + 1 + with pushMatrix(): + translate(self.pos.x, self.pos.y) + if Cell.debug_mode: + fill(0) + text(self.type, 0, 0) + noFill() # stroke(0) + rotation = {"11110": PI, + "10110": PI, + "00101": PI, + "11101": HALF_PI, + "01110": HALF_PI, + "11100": HALF_PI, + "00110": HALF_PI, + "11111": HALF_PI * self.ang, + "10111": PI + HALF_PI, + "00111": PI + HALF_PI, + "01100": PI + HALF_PI + } + # rotation appropriate for each type + rotate(rotation.get(self.module, 0)) + + for i in range(Cell.step_start, + Cell.step_end, + Cell.step): # (-28, 29, 7): + stroke(16 + i * 8, 255, 255) + + if self.type == Cell.A: + if self.variation in "bd": + quarter_circle(l, l, c + i, TOP + LEFT) + quarter_circle(-l, -l, c + i, BOTTOM + RIGHT) + quarter_circle(-l, l, c + i, TOP + RIGHT) + quarter_circle(l, -l, c + i, BOTTOM + LEFT) + if self.variation in "de": + rect(0, 0, (a - i) * 2, (a - i) * 2) + if self.variation in "ae": + # rect(0, 0, (a + i) * 2, (a + i) * 2) + half_circle(-l, 0, a - i, RIGHT) + half_circle(l, 0, a - i, LEFT) + half_circle(0, l, a - i, TOP) + half_circle(0, -l, a - i, BOTTOM) + if self.variation == "c": + line(+a - i, -l, +a - i, l) + line(-a + i, -l, -a + i, l) + half_circle(-l, 0, a - i, RIGHT) + half_circle(l, 0, a - i, LEFT) + # if keyPressed: rect(0, 0, (a - i) * 2, (a - i) * 2) + + elif self.type == Cell.T: + if self.variation in "bde": + line(-l, -a + i, l, -a + i) + quarter_circle(l, l, c + i, TOP + LEFT) + quarter_circle(-l, l, c + i, TOP + RIGHT) + elif self.variation == "c": + pass + half_circle(-l, 0, a - i, RIGHT) + half_circle(l, 0, a - i, LEFT) + half_circle(0, l, a - i, TOP) + if self.variation in "ce": + box( (a - i) * 2) + if self.variation == "a": + line(-l, -a + i, l, -a + i) + half_circle(-l, 0, a - i, RIGHT) + half_circle(l, 0, a - i, LEFT) + half_circle(0, l, a - i, TOP) + + elif self.type == Cell.I: + if self.variation == "ce": + box( (a - i) * 2) + if self.variation in "abde": + line(+a - i, -l, +a - i, l) + line(-a + i, -l, -a + i, l) + if self.variation in "ca": + half_circle(0, l, a - i, TOP) + half_circle(0, -l, a - i, BOTTOM) + + elif self.type == Cell.L: + if self.variation in "a": + quarter_circle(-l, l, siz - c - i, TOP + RIGHT) + elif self.variation == "b": + quarter_circle(-l, l, siz - c - i, TOP + RIGHT) + i *= -1 + quarter_circle(-l, l, c - i, TOP + RIGHT) + elif self.variation in "ce": + box((a - i) * 2) + if self.variation in "acde": + half_circle(-l, 0, a - i, RIGHT) + half_circle(0, l, a - i, TOP) + + elif self.type == Cell.C: + if self.variation in "ac": + half_circle(0, -l, a - i, BOTTOM) + if self.variation in "abe": + half_circle(0, 0, a - i, BOTTOM) + if self.variation in "abde": + line(+a - i, -l, +a - i, 0) + line(-a + i, -l, -a + i, 0) + if self.variation in "dc": + box((a - i) * 2) + if self.variation == "e": + half_circle(0, -l/2, a - i, TOP) + + elif self.type == Cell.N: + box( (a - i) * 2) + + # else: # self.type == Cell.E: + # stroke(0) + # rect(0, 0, self.size_, self.size_) + + def identify_module(self, nbs): + i, j = self.index[0], self.index[1] + self.module = "" + for (ni, nj) in nbs: + nb = Cell.grid.get((i + ni, j + nj), None) + if nb and nb.state: + self.module += "1" + else: + self.module += "0" + + @staticmethod + def variation_choices(): + Cell.variation_dict = dict() + for t in Cell.type_names: + Cell.variation_dict[t] = choice(Cell.variations) + diff --git a/2019/sketch_190118a/gif_exporter.py b/2019/sketch_190118a/gif_exporter.py new file mode 100644 index 00000000..33e28fa6 --- /dev/null +++ b/2019/sketch_190118a/gif_exporter.py @@ -0,0 +1,40 @@ +""" +Alexandre B A Villares http://abav.lugaralgum.com - GPL v3 + +A helper for the Processing gifAnimation library https://github.com/extrapixel/gif-animation/tree/3.0 +Download from https://github.com/villares/processing-play/blob/master/export_GIF/unzip_and_move_to_libraries_GifAnimation.zip +This helper was inspired by an example by Art Simon https://github.com/APCSPrinciples/AnimatedGIF/ + +# add at the start of your sketch: + add_library('gifAnimation') + from gif_exporter import gif_export +# add at the end of draw(): + gif_export(GifMaker) +""" + +def gif_export(GifMaker, # gets a reference to the library + filename="exported", # .gif will be added + repeat=0, # 0 makes it an "endless" animation + quality=255, # quality range 0 - 255 + delay=900, # this is quick + frames=0, # 0 will stop on keyPressed or frameCount >= 100000 + finish=False): # force stop + global gifExporter + try: + gifExporter + except NameError: + gifExporter = GifMaker(this, filename + ".gif") + gifExporter.setRepeat(repeat) + gifExporter.setQuality(quality) + gifExporter.setDelay(delay) + + gifExporter.addFrame() + + if frames == 0: + if keyPressed and key == "e": + finish = True + + if finish: + gifExporter.finish() + print("gif saved") + exit() diff --git a/2019/sketch_190118a/sketch_190118a.gif b/2019/sketch_190118a/sketch_190118a.gif new file mode 100644 index 00000000..d20c622d Binary files /dev/null and b/2019/sketch_190118a/sketch_190118a.gif differ diff --git a/2019/sketch_190118a/sketch_190118a.pyde b/2019/sketch_190118a/sketch_190118a.pyde new file mode 100644 index 00000000..b8fec4fa --- /dev/null +++ b/2019/sketch_190118a/sketch_190118a.pyde @@ -0,0 +1,110 @@ +# Alexandre B A Villares - https://abav.lugaralgum.com/sketch-a-day +SKETCH_NAME, OUTPUT = "sketch_190118a", ".gif" # find sketch name yourself! +# mode 6 creates a dict of variations for each type +# 'R' randomizes the type-variation dict + +from cell import Cell +from random import choice +add_library('GifAnimation') +add_library('peasycam') +from gif_exporter import gif_export + +CELL_SIZE = 20 +Cell.step_start = -2 +Cell.step_end = 3 +Cell.step = 2 +modulus = 3 +mode = 0 +save_frame = False +def setup(): + size(600, 600, P3D) + colorMode(HSB) + global grid_size + grid_size = width / CELL_SIZE + rectMode(CENTER) + strokeCap(SQUARE) + Cell.variation_choices() + cam = PeasyCam(this, 200) + + +def init_grid(f=None): + # default grid is with random state for cells + if f == None: + f = lambda i, j: choice((True, False)) + # number of collums and rows -2 for default cell sized border + w = int(width // CELL_SIZE) # - 2 + h = int(height // CELL_SIZE) # - 2 + for i in range(w): + for j in range(h): + # default Cell constructor has border=CELL_SIZE + Cell.grid[(i, j)] = Cell((i, j), CELL_SIZE, f(i, j), border=0) + +def draw(): + global save_frame + background(0) + for c in Cell.grid.values(): + c.update(mouseX, mouseY) + for c in Cell.grid.values(): + c.plot(mode) + + if save_frame: + save_frame = False + gif_export(GifMaker, SKETCH_NAME) + +def keyPressed(): + global mode, modulus, save_frame + if key == "g" or key == "G": + save_frame = True + if key == "s" or key == "S": + saveFrame(SKETCH_NAME + "_#######.png") + if key != CODED and key in "01234567789": + mode = int(key) + if key == "-": + mode = -1 + if key == " ": + t = lambda i, j: True + f = lambda i, j: False + init_grid(choice((t, f))) + if key == "r": + init_grid() + if key == "R": + Cell.variation_choices() + if key == "x": + init_grid(lambda i, j: (i + j) % modulus) + if key == "<" and modulus > 2: + modulus -= 1 + if key == ">": + modulus += 1 + if key == "z": + move_grid() + if keyCode == RIGHT: + move_grid(x=1, y=0) + if keyCode == LEFT: + move_grid(x=-1, y=0) + if keyCode == UP: + move_grid(x=0, y=-1) + if keyCode == DOWN: + move_grid(x=0, y=1) + +def move_grid(x=1, y=1): + w, h = width // CELL_SIZE, height // CELL_SIZE + new_grid = dict() + for i in range(w): + for j in range(h): + c = Cell.grid.get((i, j), None) + if c: + c.index = ((i + x) % w, (j + y) % h) + c.calculate_pos() + new_grid[c.index] = c + Cell.grid = new_grid + + +# print text to add to the project's README.md +def settings(): + println( + """ +![{0}](2019/{0}/{0}{1}) + +[{0}](https://github.com/villares/sketch-a-day/tree/master/2019/{0}) [[Py.Processing](https://villares.github.io/como-instalar-o-processing-modo-python/index-EN)] +""".format(SKETCH_NAME, OUTPUT) + ) diff --git a/README.md b/README.md index 08b3bfaf..5c4a0ad5 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,12 @@ Subscribe to sort-of-weekly newsletter: [[sketch-mail](https://tinyletter.com/v ## 2019 +--- + +![sketch_190118a](2019/sketch_190118a/sketch_190118a.gif) + +[sketch_190118a](https://github.com/villares/sketch-a-day/tree/master/2019/sketch_190118a) [[Py.Processing](https://villares.github.io/como-instalar-o-processing-modo-python/index-EN)] + --- ![sketch_190117b](2019/sketch_190117b/sketch_190117b.gif)