diff --git a/s092/graphs.py b/s092/graphs.py index 28915eaf..05b9ed24 100644 --- a/s092/graphs.py +++ b/s092/graphs.py @@ -7,7 +7,7 @@ class Ponto(): " Pontos num grafo, VEL_MAX inicial sorteada, criam Arestas com outros Pontos " - def __init__(self, x, y, NUM_CONNECT, cor=color(0)): + def __init__(self, x, y, cor=color(0)): VEL_MAX = Ponto.VEL_MAX self.x = x self.y = y @@ -19,7 +19,7 @@ class Ponto(): random(128, 255), # G random(128, 255), # B 128) # Alpha ~50% - self.cria_arestas(NUM_CONNECT) + self.cria_arestas() def desenha(self): if self.sel: @@ -40,8 +40,7 @@ class Ponto(): self.vx = self.limitar(self.vx, VEL_MAX) self.vy = self.limitar(self.vy, VEL_MAX) - def cria_arestas(self, NUM_CONNECT): - for _ in range(NUM_CONNECT): + def cria_arestas(self): lista_pontos = list(Ponto.SET) if lista_pontos: nova_aresta = Aresta(rnd_choice(lista_pontos), self) @@ -64,16 +63,12 @@ class Aresta(): def __init__(self, p1, p2): self.p1 = p1 self.p2 = p2 - self.sel = False def desenha(self): - if self.sel: - stroke(0) - else: - stroke(255) + stroke(0) line(self.p1.x, self.p1.y, self.p2.x, self.p2.y) noStroke() - fill(255) + fill(0) ellipse(self.p1.x, self.p1.y, TAM_PONTO / 6, TAM_PONTO / 6) ellipse(self.p2.x, self.p2.y, TAM_PONTO / 6, TAM_PONTO / 6) diff --git a/s092/s091B.py b/s092/s091B.py deleted file mode 100644 index c5d138b3..00000000 --- a/s092/s091B.py +++ /dev/null @@ -1,80 +0,0 @@ -# Alexandre B A Villares - https://abav.lugaralgum.com/sketch-a-day -SKETCH_NAME = "s091B" # 180401 - -add_library('serial') # import processing.serial.*; -add_library('arduino') # import cc.arduino.*; -add_library('gifAnimation') - -from gif_exporter import gif_export -from shapes import * -from parameters import * - -SHAPES = [circle, # defined in shapes.py - square, - exes, - losang] - -aY, bY, cY, dY = 0, 0, 0, 0 - -def setup(): - size(600, 600) - frameRate(30) - background(0) - global A, B, C, D - # Ask user for Arduino port, cancel will return `None` - port = Inputs.select_source(Arduino) - # `None` will activate Sliders - A, B, C, D = Inputs.setup_inputs(port) - -def draw(): - global aY, bY, cY, dY - # fill(0, 2) - # rect(0, 0, width, height) - - a = A.val / 4 - b = B.val / 4 - c = C.val / 4 - d = D.val / 4 - noFill() - stroke(255, 255, 255) - ellipse(4 * width / 5, aY, a, a) - stroke(0, 0, 255) - ellipse(3 * width / 5, bY, b, b) - stroke(0, 255, 0) - ellipse(2 * width / 5, cY, c, c) - stroke(255, 0, 0) - ellipse(1 * width / 5, dY, d, d) - - if Inputs.TILT: - background(0) - - # uncomment next lines to export GIF - if not frameCount % 30: - gif_export(GifMaker, - frames=2000, - delay=500, - filename=SKETCH_NAME) - - # Updates reading or draws sliders and checks mouse dragging / keystrokes - Inputs.update_inputs() - - aY += a / 16 - if aY > height + a: - aY = -a - - bY += b / 16 - if bY > height + a: - bY = -a - - cY += c / 16 - if cY > height + a: - cY = -a - - dY += d / 16 - if dY > height + a: - dY = -a - - -def rnd_choice(collection): - i = int(random(len(collection))) - return collection[i] diff --git a/s092/s092.gif b/s092/s092.gif new file mode 100644 index 00000000..10f3316d Binary files /dev/null and b/s092/s092.gif differ diff --git a/s092/s092.pyde b/s092/s092.pyde index 0c70885c..c47bbffa 100644 --- a/s092/s092.pyde +++ b/s092/s092.pyde @@ -1,5 +1,5 @@ # Alexandre B A Villares - https://abav.lugaralgum.com/sketch-a-day -SKETCH_NAME = "s091" # 180401 +SKETCH_NAME = "s092" # 180402 add_library('serial') # import processing.serial.*; add_library('arduino') # import cc.arduino.*; @@ -25,7 +25,7 @@ def draw(): TAM_BARRA = A.val / 4 NUM_PONTOS = int(B.val / 4) VEL_MAX = C.val / 128 - NUM_CONNECT = 1 + int(D.val / 256) + NUM_CONNECT = 1+ int(D.val / 256) # % of connections # para cada ponto for ponto in Ponto.SET: @@ -41,19 +41,24 @@ def draw(): aresta.puxa_empurra(TAM_BARRA) # altera a velocidade dos pontos # atualiza número de pontos if NUM_PONTOS > len(Ponto.SET): - Ponto.SET.add(Ponto(random(width), random(height), NUM_CONNECT)) + Ponto.SET.add(Ponto(random(width), random(height))) elif NUM_PONTOS < len(Ponto.SET): Ponto.SET.remove(rnd_choice(list(Ponto.SET))) - + # atualiza número de arestas + if NUM_PONTOS * NUM_CONNECT > len(Aresta.ARESTAS): + rnd_choice(list(Ponto.SET)).cria_arestas() + elif NUM_PONTOS * NUM_CONNECT < len(Aresta.ARESTAS): + Ponto.SET.remove(rnd_choice(list(Ponto.SET))) + if Inputs.TILT: Ponto.SET = set() # uncomment next lines to export GIF - # if not frameCount % 30: - # gif_export(GifMaker, - # frames=2000, - # delay=500, - # filename=SKETCH_NAME) + if not frameCount % 30: + gif_export(GifMaker, + frames=2000, + delay=500, + filename=SKETCH_NAME) # Updates reading or draws sliders and checks mouse dragging / keystrokes Inputs.update_inputs() diff --git a/s093/gif_exporter.py b/s093/gif_exporter.py new file mode 100644 index 00000000..dbb58014 --- /dev/null +++ b/s093/gif_exporter.py @@ -0,0 +1,36 @@ +""" +Alexandre B A Villares http://abav.lugaralgum.com - GPL v3 + +A helper for the Processing gifAnimation library (https://github.com/jordanorelli) +ported to Processing 3 by 01010101 (https://github.com/01010101) +Download the library from https://github.com/01010101/GifAnimation/archive/master.zip +This helper was inspired by an example by Art Simon https://github.com/APCSPrinciples/AnimatedGIF/ + +Put at the start of your sketch: + add_library('gifAnimation') + from gif_exporter import gif_export +and 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=32, # quality range 0 - 255 + delay=170, # this is quick + frames=0): # 0 will stop on keyPressed or frameCount >= 100000 + 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 and keyPressed or frameCount >= 100000) \ + or (frames != 0 and frameCount >= frames): + gifExporter.finish() + print("gif saved") + exit() diff --git a/s093/graphs.py b/s093/graphs.py new file mode 100644 index 00000000..05b9ed24 --- /dev/null +++ b/s093/graphs.py @@ -0,0 +1,87 @@ +# -*- coding: utf-8 -*- +TAM_PONTO = 50 # TAM_PONTO dos Pontos + +class Ponto(): + VEL_MAX = 5 + SET = set() + + " Pontos num grafo, VEL_MAX inicial sorteada, criam Arestas com outros Pontos " + + def __init__(self, x, y, cor=color(0)): + VEL_MAX = Ponto.VEL_MAX + self.x = x + self.y = y + self.z = 0 # para compatibilidade com PVector... + self.vx = random(-VEL_MAX, VEL_MAX) + self.vy = random(-VEL_MAX, VEL_MAX) + self.sel = False # se está selecionado, começa sem seleção + self.cor = color(random(128, 255), # R + random(128, 255), # G + random(128, 255), # B + 128) # Alpha ~50% + self.cria_arestas() + + def desenha(self): + if self.sel: + stroke(0) + else: + noStroke() + fill(self.cor) + ellipse(self.x, self.y, TAM_PONTO, TAM_PONTO) + + def move(self, VEL_MAX): + Ponto.VEL_MAX = VEL_MAX + self.x += self.vx + self.y += self.vy + if not (0 < self.x < width): + self.vx = -self.vx + if not (0 < self.y < height): + self.vy = -self.vy + self.vx = self.limitar(self.vx, VEL_MAX) + self.vy = self.limitar(self.vy, VEL_MAX) + + def cria_arestas(self): + lista_pontos = list(Ponto.SET) + if lista_pontos: + nova_aresta = Aresta(rnd_choice(lista_pontos), self) + Aresta.ARESTAS.append(nova_aresta) + + def limitar(self, v, v_max): + if v > v_max: + return v_max + elif v < -v_max: + return -v_max + else: + return v + +class Aresta(): + + """ Arestas contém só dois Pontos e podem ou não estar selecionadas """ + + ARESTAS = [] + + def __init__(self, p1, p2): + self.p1 = p1 + self.p2 = p2 + + def desenha(self): + stroke(0) + line(self.p1.x, self.p1.y, self.p2.x, self.p2.y) + noStroke() + fill(0) + ellipse(self.p1.x, self.p1.y, TAM_PONTO / 6, TAM_PONTO / 6) + ellipse(self.p2.x, self.p2.y, TAM_PONTO / 6, TAM_PONTO / 6) + + def puxa_empurra(self, TAM_BARRA): + d = dist(self.p1.x, self.p1.y, self.p2.x, self.p2.y) + delta = TAM_BARRA - d + dir = PVector.sub(self.p1, self.p2) + dir.mult(delta / 1000) + self.p1.vx = self.p1.vx + dir.x + self.p1.vy = self.p1.vy + dir.y + self.p2.vx = self.p2.vx - dir.x + self.p2.vy = self.p2.vy - dir.y + +def rnd_choice(collection): + i = int(random(len(collection))) + return collection[i] diff --git a/s093/parameters.py b/s093/parameters.py new file mode 100644 index 00000000..62e4c945 --- /dev/null +++ b/s093/parameters.py @@ -0,0 +1,177 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +""" +This will hpefully switch between Arduino (Firmata) variable input and +nice sliders based on Peter Farell's Sliders htts://twitter.com/hackingmath +https://github.com/hackingmath/python-sliders http://farrellpolymath.com/ +""" +class Inputs: + TILT = None + + @staticmethod + def select_source(Arduino): + Inputs.Arduino = Arduino # to make available on this module + port_list = [str(num) + ": " + port for num, port + in enumerate(Arduino.list()) + if ("usb" in port.lower() + or "COM" in port)] + if not port_list: + port_list.append(None) + user_input = option_pane("Is your Arduino connected?", + "Choose the port or, Cancel\nto use sliders:", + port_list, + -1) # index for default option + return user_input + + @staticmethod + def setup_inputs(port): + + if port == None: + # start, end, default, + key, - key + A = Slider(0, 1023, 10, 'q', 'a') + B = Slider(0, 1023, 10, 'w', 's') + C = Slider(0, 1023, 10, 'e', 'd') + D = Slider(0, 1023, 10, 'r', 'f') + + A.position(40, height - 70) + B.position(40, height - 30) + C.position(width - 140, height - 70) + D.position(width - 140, height - 30) + + @staticmethod + def update(): + Slider.update_all() + Inputs.TILT = (keyPressed and key == ' ') + + else: + arduino = Inputs.Arduino(this, Inputs.Arduino.list()[port], 57600) + + A = Analog_input(arduino, 1) + B = Analog_input(arduino, 2) + C = Analog_input(arduino, 3) + D = Analog_input(arduino, 4) + + @staticmethod + def update(): + Analog_input.update_all() + Inputs.TILT = arduino.digitalRead(13) == Inputs.Arduino.HIGH + + Inputs.update_inputs = update + return A, B, C, D + + +def option_pane(title, message, options, default=None, index_only=True): + from javax.swing import JOptionPane + + if default == None: + default = options[0] + elif index_only: + default = options[default] + + selection = JOptionPane.showInputDialog( + frame, + message, + title, + JOptionPane.INFORMATION_MESSAGE, + None, # for Java null + options, + default) # must be in options, otherwise 1st is shown + if selection: + if index_only: + return options.index(selection) + else: + return selection + + +class Slider: + + SLIDERS = [] + + def __init__(self, low, high, default, more_key, less_key): + '''slider has range from low to high + and is set to default''' + self.low = low + self.high = high + self.val = default + self.clicked = False + self.more = more_key + self.less = less_key + Slider.SLIDERS.append(self) + + def position(self, x, y): + '''slider's position on screen''' + self.x = x + self.y = y + # the position of the rect you slide: + self.rectx = self.x + map(self.val, self.low, self.high, 0, 120) + self.recty = self.y - 10 + + def update(self): + '''updates the slider''' + pushStyle() + rectMode(CENTER) + # black translucid rect behind slider + fill(0, 100) + noStroke() + rect(self.x + 60, self.y, 130, 20) + # gray line behind slider + strokeWeight(4) + stroke(200) + line(self.x, self.y, self.x + 120, self.y) + # press mouse to move slider + if (self.x < mouseX < self.x + 120 and + self.y < mouseY < self.y + 20): + fill(250) + textSize(10) + text(str(int(self.val)), self.rectx, self.recty + 35) + if mousePressed: + self.rectx = mouseX + # key usage + if keyPressed: + if key == self.more: + self.rectx += 1 + if key == self.less: + self.rectx -= 1 + # constrain rectangle + self.rectx = constrain(self.rectx, self.x, self.x + 120) + # draw rectangle + strokeWeight(1) + fill(255) + rect(self.rectx, self.recty + 10, 10, 20) + self.val = map(self.rectx, self.x, self.x + 120, self.low, self.high) + popStyle() + + def value(self): + ''' backwards compatible method... ''' + self.update() + return self.val + + @classmethod + def update_all(cls): + for slider in Slider.SLIDERS: + slider.update() + +class Analog_input: + + INPUTS_LIST = [] + + def __init__(self, board, pin): + self.board = board + self.pin = pin + self.val = 100 + Analog_input.INPUTS_LIST.append(self) + + def update(self): + arduino = self.board + self.val = arduino.analogRead(self.pin) + + def value(self): + self.update() + return self.val + + @classmethod + def update_all(cls): + for reader in Analog_input.INPUTS_LIST: + reader.update() + diff --git a/s093/s093.pyde b/s093/s093.pyde new file mode 100644 index 00000000..dd9cdbc7 --- /dev/null +++ b/s093/s093.pyde @@ -0,0 +1,72 @@ +# Alexandre B A Villares - https://abav.lugaralgum.com/sketch-a-day +SKETCH_NAME = "s091" # 180401 + +add_library('serial') # import processing.serial.*; +add_library('arduino') # import cc.arduino.*; +add_library('gifAnimation') + +from gif_exporter import gif_export +from graphs import * +from parameters import * + +def setup(): + frameRate(30) + global A, B, C, D + # Ask user for Arduino port, cancel will return `None` + port = Inputs.select_source(Arduino) + # `None` will activate Sliders + A, B, C, D = Inputs.setup_inputs(port) + + size(400, 400) + +def draw(): + background(128) + + TAM_BARRA = A.val / 4 + NUM_PONTOS = int(B.val / 4) + VEL_MAX = C.val / 128 + NUM_CONNECT = 1+ int(D.val / 256) # % of connections + + # para cada ponto + for ponto in Ponto.SET: + ponto.desenha() # desenha + ponto.move(VEL_MAX) # atualiza posição + # para cada aresta + # checa se há Arestas com Pontos já removidos + for aresta in Aresta.ARESTAS: + if (aresta.p1 not in Ponto.SET) or (aresta.p2 not in Ponto.SET): + Aresta.ARESTAS.remove(aresta) # nesse caso remove a Aresta também + else: # senão + aresta.desenha() # desenha a linha + aresta.puxa_empurra(TAM_BARRA) # altera a velocidade dos pontos + # atualiza número de pontos + if NUM_PONTOS > len(Ponto.SET): + Ponto.SET.add(Ponto(random(width), random(height))) + elif NUM_PONTOS < len(Ponto.SET): + Ponto.SET.remove(rnd_choice(list(Ponto.SET))) + # atualiza número de arestas + if NUM_PONTOS * NUM_CONNECT > len(Aresta.ARESTAS): + rnd_choice(list(Ponto.SET)).cria_arestas() + elif NUM_PONTOS * NUM_CONNECT < len(Aresta.ARESTAS): + Ponto.SET.remove(rnd_choice(list(Ponto.SET))) + + if Inputs.TILT: + Ponto.SET = set() + + # uncomment next lines to export GIF + if not frameCount % 30: + gif_export(GifMaker, + frames=2000, + delay=500, + filename=SKETCH_NAME) + + # Updates reading or draws sliders and checks mouse dragging / keystrokes + Inputs.update_inputs() + +def mouseDragged(): # quando o mouse é arrastado + for ponto in Ponto.SET: # para cada Ponto checa distância do mouse + if dist(mouseX, mouseY, ponto.x, ponto.y) < TAM_PONTO / 2: + # move o Ponto para posição do mouse + ponto.x, ponto.y = mouseX, mouseY + ponto.vx = 0 + ponto.vy = 0