def imagem(X1,X2,Y1,Y2,M,N,padrão,expressão):
""" Gera uma imagem percorrendo o retângulo [X1,X2)x[Y1,Y2)
discretizado com M pontos na largura e N pontos na altura,
avaliando uma expressão que determina a cor do pixel (x,y).
A string padrão ("RGB" ou "cinza") determina se a imagem
será em nÃveis de cinza ou colorida.
"""
# gera o cabeçalho do arquivo PNM (=PBM, PGM ou PPM)
if padrão=="RGB": print("P3")
else: print("P2")
print(M,N)
print(255)
# o laço externo percorre os valores de Y em ordem decrescente
for n in range(N):
y = Y2-n*(Y2-Y1)/N
# o laço interno percorre os valores de X em ordem crescente
for m in range(M):
x = X1+m*(X2-X1)/M
# imprime o pixel correspondente
print(eval(expressão),end=" ")
# pula a linha para o próximo valor de y
print("")
imagem(-2,2,-4,4,N,N,"cinza","round(255*(1-abs(y-x**3+x)/14)**10)")
def Newton(z0,função,derivada):
""" Calcula a raiz de uma função f(z) de uma variável complexa z
a partir de z0 (=x+y*1j) pelo método de Newton, devolvendo uma
estimativa da raiz (z) e o número de iterações (k) do método.
As strings função e derivada contém as representações analÃticas
de f(z) e f'(z). O método usa tolerância de 1e-8 para a parada e
um limite de 255 iterações. Caso f'(z)=0 em algum momento, o
método para e devolve z = 0 e k = maxiter.
"""
eps = 1e-8
maxiter = 255
# inicialização do método
z = z0
# testa viabilidade da iteração de Newton (f'(z)!=0)
if abs(eval(derivada))<eps: return 0,maxiter
# aplica o passo de Newton a partir de z0
z = z0-eval(função)/eval(derivada)
k=1 # contador de iterações
while abs(z-z0)>eps and k<maxiter:
z0 = z # guarda iterado anterior
if abs(eval(derivada))<eps: return 0,maxiter # testa viabilidade
z = z0-eval(função)/eval(derivada) # aplica o passo de Newton
k += 1 # mais uma iteração
return z,k
def mapadecinzas(f,fmin,fmax):
""" Converte o valor f (teoricamente entre fmin e fmax) em uma
representação de um pixel no modelo "cinza", cuidando para que
os valores produzidos sejam sempre inteiros entre 0 e 255.
"""
# faz um mapeamento linear do valor f em [fmin,fmax] no intervalo [0,1]
x = (f-fmin)/(fmax-fmin)
# cuida do caso em que f não estava no intervalo
if x<0: x = 0
if x>1: x = 1
# leva para o intervalo [0,255]
pix = round(255*x)
return pix
def exemplo1(x,y):
""" Calcula uma raiz quadrada de 1 pelo método de Newton
a partir do ponto inicial z0=x+yi.
Usada como expressão na função imagem.
"""
função = "z**2-1"
derivada = "2*z"
z,k = Newton(x+y*1j,função,derivada)
fmin, fmax = -1, +1 # limites dos valores encontrados
pix = mapadecinzas(z.real,fmin,fmax)
return pix
imagem(-1.5,1.5,-1.5,1.5,N,N,"cinza","exemplo1(x,y)")
def exemplo2(x,y):
""" Calcula uma raiz quadrada de 1 pelo método de Newton
a partir do ponto inicial z0=x+yi.
Usada como expressão na função imagem.
"""
função = "z**2-1"
derivada = "2*z"
z,k = Newton(x+y*1j,função,derivada)
fmin, fmax = -1, +1 # limites dos valores encontrados
pix = mapadecinzas(z.real*2**(-k/10),fmin,fmax)
return pix
imagem(-1.5,1.5,-1.5,1.5,N,N,"cinza","exemplo2(x,y)")
def mapadecores(f,fmin,fmax,a):
""" Converte o valor f (teoricamente entre fmin e fmax) em uma
representação de um pixel em um espaço de cor "RGB".
O parâmetro a é usado para escurecer a cor no modelo "RGB".
"""
# mapeia o valor f em [fmin,fmax] no intervalo [0,1]
x = (f-fmin)/(fmax-fmin)
if x<0: x=0
if x>1: x=1
# localiza esse número em um espaço RGB por interpolação
# linear, a partir dos pontos de controle:
# 0 = R, 0.16 = R+G, 0.33 = G, 0.5 = G+B, 0.66 = B, 0.83 = B+R, 1 = R
R = round(a*255*min(1,max(0,2-6*x,6*x-4)))
G = round(a*255*max(0,min(1,4-6*x,6*x)))
B = round(a*255*max(0,min(1,6-6*x,6*x-2)))
return str(R)+" "+str(G)+" "+str(B)
from cmath import pi,phase
def exemplo3(x,y):
""" Calcula raÃzes quartas de 1.
"""
função = "z**4-1"
derivada = "4*z**3"
z,k = Newton(x+y*1j,função,derivada)
fmin, fmax = -pi, +pi
pix = mapadecores(phase(z),fmin,fmax,1) # usa a=1 para não considerar o k
return pix
imagem(-1.5,1.5,-1.5,1.5,N,N,"RGB","exemplo3(x,y)")
def exemplo4(x,y):
""" calcula raÃzes quartas de 1, atribuindo profundidade de cor aos pixels
conforme o número de iterações até a convergência do método de Newton.
"""
função = "z**4-1"
derivada = "4*z**3"
z,k = Newton(x+y*1j,função,derivada)
fmin, fmax = -pi, +pi
pix = mapadecores(phase(z),fmin,fmax,2**(-k/10))
return pix
imagem(-1.5,1.5,-1.5,1.5,N,N,"RGB","exemplo4(x,y)")
def exemplo5(x,y):
""" Calcula raÃzes nonas de 1, relacionando
o número de iterações à profundidade de cor.
"""
função = "z**9-1"
derivada = "9*z**8"
z,k = Newton(x+y*1j,função,derivada)
fmin, fmax = -pi, +pi
pix = mapadecores(phase(z),fmin,fmax,2**(-k/10))
return pix
imagem(-1.5,1.5,-1.5,1.5,N,N,"RGB","exemplo5(x,y)")
imagem(0.5,1.0,1.0,1.5,N,N,"RGB","exemplo5(x,y)")
imagem(0.75,0.875,1.25,1.375,N,N,"RGB","exemplo5(x,y)")
def itera(z0,iterador,condição):
""" Aplica o iterador a partir de z0 e enquanto satisfizer a condição.
Devolve o Ãndice da iteração que violou a condição, ou maxiter
se a condição não foi violada até esse limite de iterações.
"""
maxiter = 255 # se chegar nesse número de iterações, interrompe a sequência.
z = z0 # ponto inicial
k = 0 # contador de iterações
while eval(condição) and k<maxiter:
z = eval(iterador)
k += 1
return k
def exemplo8(x,y):
""" Aplica iterador "z**2+z0" com z0 = x+y*1j, testando quantas
iterações leva até a sequência sair do cÃrculo de raio 2.
"""
z0 = x+y*1j
iterador = "z**2+z0"
condição = "abs(z)<=2"
k = itera(z0,iterador,condição)
fmin, fmax = 0, (255**0.5)*6/4 # força o mapeamento entre vermelho e azul
pix = mapadecores(k**0.5,fmin,fmax,1)
return pix
imagem(-2,1.5,-1.5,1.5,N,N,"RGB","exemplo8(x,y)")
imagem(-2,-0.5,-0.75,0.75,N,N,"RGB","exemplo8(x,y)")
imagem(-2,-1.25,-0.375,0.375,N,N,"RGB","exemplo8(x,y)")
imagem(-1.82,-1.72,-0.05,0.05,N,N,"RGB","exemplo8(x,y)")
imagem(-0.65,-0.4,0.45,0.7,N,N,"RGB","exemplo8(x,y)")