Primeira vez aqui? Seja bem vindo e cheque o FAQ!
x

Como construir uma classe similar a ”Circle“ apresentada na Listing 6.4 e gerar as figuras 6.4 e 6.5 do livro Generative Art: A Practical Guide Using Processing, de Matt Pearson?

+1 voto
36 visitas
perguntada Jun 14 em Ciência da Computação por claudiaeirado (46 pontos)  
editado Jun 14 por claudiaeirado

A imagem será apresentada aqui.

Compartilhe

1 Resposta

+1 voto
respondida Jun 16 por claudiaeirado (46 pontos)  
editado Jun 16 por claudiaeirado

Eis o programa desenvolvido no livro Generative Art: A Practical Guide Using Processing , do Listing 6.4:

A imagem será apresentada aqui.
A imagem será apresentada aqui.
A imagem será apresentada aqui.

Exercício Figura 6.4

Basicamente, o programa gera um array de 10 novas instâncias de círculos agrupados (2 a 2, círculos concêntricos: um grande com raio variável, colorido e sem borda e outro menor de raio 10, sem preenchimento, e com borda colorida, ambos com o mesmo centro (x,y)) que se movem aleatoriamente na tela. Quando se clica na tela ele gera mais 10 novos círculos e assim por diante. Compilando o programa em Processing, obtem-se o seguinte resultado para a Figura 6.4:

A imagem será apresentada aqui.

Implementação em Python:

Em Python 3.7, utilizou-se as bibliotecas Random, Time e Tkinter para desenvolver o algoritmo:

import random as rd
import tkinter as tk
import time as tm

O construtor da classe "Circle" gera os centros dos círculos com as coordenadas (x,y), raios e cores aleatórias por meio da função randint (biblioteca random) e desenha dois tipos de círculos com a função create_oval (biblioteca tkinter). O primeiro círculo possui preenchimento colorido, sem bordas e raio variável com centro (x,y). O segundo círculo é plotado conjuntamente com o primeiro sem preenchimento, bordas coloridas e mesmo centro (x,y).

class Circle:
 def __init__(self,width,height,canvas,root):
     self.width=width
     self.height=height
     self.x = rd.randint(0,self.width)
     self.y = rd.randint(0,self.height)
     self.radius = rd.randint(0,100) + 10
     self.xmove = rd.randint(0,10) 
     self.ymove = rd.randint(0,10) 
     self.root = root
     self.root.title('circle')
     self.canvas = canvas
     self.canvas.pack()
     self.random_color= rd.randint(0,0x1000000)
     self.color = '{:06x}'.format(self.random_color)
     self.random_color2= rd.randint(0,0x1000000)
     self.color2 = '{:06x}'.format(self.random_color2)
     self.x0 = self.x - self.radius
     self.y0 = self.y - self.radius
     self.x1 = self.x + self.radius
     self.y1 = self.y + self.radius
     self.my_circle = self.canvas.create_oval(self.x0, self.y0, self.x1, self.y1,\
              fill = ('#'+ self.color), 
              outline = "", tags="circle")
     self.center_circle = self.canvas.create_oval(self.x-10, self.y-10, self.x+10, self.y+10,\
              fill = "", 
              outline = ('#'+ self.color2), tags="centercircle")

Os métodos iter e next abaixo da classe "Circle" tornam o objeto iterável e retorna o próximo objeto da lista:

def __iter__(self):
     # Retorna o objeto iterável (ele próprio: self)
     return self

 def __next__(self):
     return self

O método updateMe da classe "Circle" move os objetos conjuntamente (dois círculos concêntricos) dentro da tela por meio da função move (biblioteca tkinter):

     def updateMe(self):
         self.canvas.move(self.my_circle, self.xmove, self.ymove)
         self.canvas.move(self.center_circle, self.xmove, self.ymove)
         self.position=self.canvas.coords(self.my_circle)
         if self.position[2]>=self.width+self.radius or self.position[0]<=0:
             self.xmove=-self.xmove
         if self.position[3]>=self.height+self.radius or self.position[1]<=0:
             self.ymove=-self.ymove

Para executar a figura 6.4 foi utilizado o script abaixo. Foi criada a classe "DrawandMove" que gera um Array de 10 objetos (2 círculos concêntricos de cada vez, totalizando 20 círculos ao todo). O método release cria mais 10 objetos ao clicar na tela e assim por diante.

   class DrawandMove():
        def __init__(self,root,width,height,canvas):
            self.root=root
            self.width=width
            self.height=height
            self.canvas=canvas
            self.canvas.bind("<ButtonRelease-1>", self.release)
            self.circleArr= []

        def drawCircles(self):
            for i in range(0,10):
                self.thisCirc = object.__new__(Circle)
                Circle.__init__(self.thisCirc,self.width,self.height,self.canvas,self.root)
                self.circleArr.append(self.thisCirc)

        def release(self,event):
                a= event.x
                b=event.y
                self.drawCircles()

        def draw(self):
                for circle in self.circleArr:
                    circle.updateMe()
                self.root.after(25,self.draw)


    if __name__ == '__main__':        
        width,height=1000,600
        root=tk.Tk()
        root.wm_attributes('-alpha', 0.9)
        canvas=tk.Canvas(root,width=1000,height=600,bg='white')
        l=object.__new__(DrawandMove)
        DrawandMove.__init__(l,root,width,height,canvas)   
        l.drawCircles()
        l.draw()
        root.mainloop()

Uma outra forma de executar a Figura 6.4 sem utilizar o recurso de criar a classe "DrawandMove", seria utilizando uma variável global para o Array dos círculos:

    if __name__ == '__main__':
    global circleArr
    circleArr= []
    def drawCircles(canvas,width,height,root):
        for i in range(0,10):
            thisCirc = object.__new__(Circle)
            Circle.__init__(thisCirc,width,height,canvas,root)
            circleArr.append(thisCirc)

    def release(event):
        a= event.x
        b=event.y
        drawCircles(canvas,width,height,root)

    def draw():      
        for circle in circleArr:
            circle.updateMe()
        root.after(25,draw)


    width=1000
    height=600
    root=tk.Tk()
    root.wm_attributes('-alpha', 0.90)
    canvas=tk.Canvas(root,width=1000,height=600,bg='white')
    drawCircles(canvas,width,height,root)
    draw()
    canvas.bind("<ButtonRelease-1>", release)
    root.update()
    tm.sleep(0.05)
    root.mainloop()

Segue a versão completa do programa, com o uso de duas classes:

import random as rd
import tkinter as tk

class Circle:
     def __init__(self,width,height,canvas,root):
         self.width=width
         self.height=height
         self.x = rd.randint(0,self.width)
         self.y = rd.randint(0,self.height)
         self.radius = rd.randint(0,100) + 10
         self.xmove = rd.randint(0,10) 
         self.ymove = rd.randint(0,10) 
         self.root = root
         self.root.title('circle')
         self.canvas = canvas
         self.canvas.pack()
         self.random_color= rd.randint(0,0x1000000)
         self.color = '{:06x}'.format(self.random_color)
         self.random_color2= rd.randint(0,0x1000000)
         self.color2 = '{:06x}'.format(self.random_color2)
         self.x0 = self.x - self.radius
         self.y0 = self.y - self.radius
         self.x1 = self.x + self.radius
         self.y1 = self.y + self.radius
         self.my_circle = self.canvas.create_oval(self.x0, self.y0, self.x1, self.y1,\
                  fill = ('#'+ self.color), 
                  outline = "", tags="circle")
         self.center_circle = self.canvas.create_oval(self.x-10, self.y-10, self.x+10, self.y+10,\
                  fill = "", 
                  outline = ('#'+ self.color2), tags="centercircle")

     def __iter__(self):
         # Retorna o objeto iterável (ele próprio: self)
         return self

     def __next__(self):
         return self

     def updateMe(self):
             self.canvas.move(self.my_circle, self.xmove, self.ymove)
             self.canvas.move(self.center_circle, self.xmove, self.ymove)
             self.position=self.canvas.coords(self.my_circle)
             if self.position[2]>=self.width+self.radius or self.position[0]<=0:
                 self.xmove=-self.xmove
             if self.position[3]>=self.height+self.radius or self.position[1]<=0:
                 self.ymove=-self.ymove



class DrawandMove():
    def __init__(self,root,width,height,canvas):
        self.root=root
        self.width=width
        self.height=height
        self.canvas=canvas
        self.canvas.bind("<ButtonRelease-1>", self.release)
        self.circleArr= []

    def drawCircles(self):
        for i in range(0,10):
            self.thisCirc = object.__new__(Circle)
            Circle.__init__(self.thisCirc,self.width,self.height,self.canvas,self.root)
            self.circleArr.append(self.thisCirc)

    def release(self,event):
            a= event.x
            b=event.y
            self.drawCircles()

    def draw(self):
            for circle in self.circleArr:
                circle.updateMe()
            self.root.after(25,self.draw)


if __name__ == '__main__':        
    width,height=1000,600
    root=tk.Tk()
    root.wm_attributes('-alpha', 0.9)
    canvas=tk.Canvas(root,width=1000,height=600,bg='white')
    l=object.__new__(DrawandMove)
    DrawandMove.__init__(l,root,width,height,canvas)   
    l.drawCircles()
    l.draw()
    root.mainloop()

Eis a Figura 6,4, gerada pelo Python:

A imagem será apresentada aqui.

Exercício Figura 6.5

Para a Figura 6.5, tem-se a a modificação do programa para que, se quaisquer dois círculos interceptarem-se, apagam-se os círculos maiores e mantém-se os círculos de raio 10. No caso dos círculos não se cruzarem, e ficarem livres, devem ficar mais opacos (mais claros). De forma, que, ao longo do tempo os círculos maiores e coloridos vão esmaecendo e só sobram os círculos de raio 10. Essa é a modificação, na linguagem Processing, proposta pelo livro no método updateMe da classe Circle:

A imagem será apresentada aqui.
A imagem será apresentada aqui.

A figura gerada no Processing é mostrada a seguir:

A imagem será apresentada aqui.

Implementação em Python:

O módulo Tkinter não possui uma entrada RGBA (RGB + alpha), com o valor para alpha que trata da opacidade (transparência) da figura. Para tanto, foi utilizado um artifício da biblioteca PIL para possibilitar habilitar o alpha e assim, conseguir adaptar o programa. Em Python, segue o programa:

import tkinter as tk
import random as rd
import math as mt
from PIL import Image, ImageTk,ImageDraw

class Circle:
     def __init__(self,width,height,canvas,root,circleArr):
         self.circleArr=circleArr
         self.width=width

         self.height=height
         self.x = rd.randint(0,self.width)/2
         self.y = rd.randint(0,self.height)/2
         self.radius = rd.randint(10,100) + 10
         self.xmove = rd.randint(0,10) - 5
         self.ymove = rd.randint(0,10) - 5
         self.alpha = rd.randint(0,255)
         self.root = root
         self.root.title('circle')
         self.canvas = canvas
         self.canvas.pack()
         self.fillcol = (rd.randint(0,255), rd.randint(0,255), rd.randint(0,255))
         self.fillcol2 = (rd.randint(0,255), rd.randint(0,255), rd.randint(0,255))

     def __iter__(self):
         # Retorna o objeto iterável (ele próprio: self)
         return self

     def __next__(self):
         return self

     def create_circle(self,a,b,c,d, **kwargs):
        if 'alpha' in kwargs:
             alpha = kwargs.pop('alpha')
             fill = kwargs.pop('fill')
             fill = fill + (alpha,)
             outline = kwargs.pop('outline')
             self.image = Image.new('RGBA', (self.width,self.height),color=0)
             self.draw = ImageDraw.Draw(self.image)
             self.draw.ellipse([a,b,c,d],fill,outline)
             self.images.append(ImageTk.PhotoImage(self.image))
        return self.canvas.create_image((a+c)/2,(b+d)/2, image=self.images[-1], anchor='nw')



     def drawMe(self):
         self.images = []
         self.x0 = self.x - self.radius
         self.y0 = self.y - self.radius
         self.x1 = self.x + self.radius
         self.y1 = self.y + self.radius
         self.my_circle = self.create_circle(self.x0, self.y0, self.x1, self.y1,fill=self.fillcol,outline=None,alpha=self.alpha)
         self.center_circle= self.create_circle(self.x-10, self.y-10, self.x+10, self.y+10,fill=(255,255,255),outline=self.fillcol2,alpha=0)




     def updateMe(self):
             self.x += self.xmove
             self.y += self.ymove
             self.canvas.move(self.my_circle, self.xmove, self.ymove)
             self.canvas.move(self.center_circle, self.xmove, self.ymove)
             self.position=self.canvas.bbox(self.my_circle)
             if self.position[0]>=(0.5*self.width) or self.position[0]<=0:
                 self.xmove=-self.xmove
             if self.position[1]>=(0.5*self.height) or self.position[1]<=0:
                 self.ymove=-self.ymove
             touching = False   
             for i in range(0,len(self.circleArr)):
                 otherCirc = self.circleArr[i]
                 coord = self.canvas.bbox(self.my_circle)
                 coord1 = self.canvas.bbox(otherCirc.my_circle)
                 if(otherCirc != self):
                     dis= mt.sqrt((coord[0]-coord1[0])**2+(coord[1]-coord1[1])**2)
                     rule= dis - self.radius - otherCirc.radius
                     if (rule < 0):
                         touching = True
                         break
             if (touching):
                 if (self.alpha > 0):
                     self.alpha =0
                     self.fill = self.fillcol + (self.alpha,)
                     self.outline =self.fillcol2
                     self.drawMe()

             else:
                 if (self.alpha <= 255):
                     self.alpha -= 2
                     self.fill = self.fillcol + (self.alpha,)
                     self.outline =self.fillcol2
                     self.drawMe()
             return            




class DrawandMove():
    def __init__(self,root,width,height,canvas):
        self.root=root
        self.width=width
        self.height=height
        self.canvas=canvas
        self.canvas.bind("<ButtonRelease-1>", self.release)
        self.circleArr= []


    def drawCircles(self):
        for i in range(0,10):
            self.thisCirc = object.__new__(Circle)
            Circle.__init__(self.thisCirc,self.width,self.height,self.canvas,self.root,self.circleArr)
            self.thisCirc.drawMe()
            self.circleArr.append(self.thisCirc)

    def release(self,event):
            a= event.x
            b=event.y
            self.drawCircles()

    def draw(self):
            for circle in self.circleArr:
                circle.updateMe()
            self.root.after(25,self.draw)


if __name__ == '__main__':        
    width,height=1000,600
    root=tk.Tk()
    canvas=tk.Canvas(root,width=1000,height=600,bg='white')
    l=object.__new__(DrawandMove)
    DrawandMove.__init__(l,root,width,height,canvas)
    l.drawCircles()
    l.draw()
    root.mainloop()

Essa é a sequência de evolução da Figura 6.5, gerada no Python:

A imagem será apresentada aqui.
A imagem será apresentada aqui.

...