Personagens Modulares
Escrito em Fevereiro de 2011, por dardna em Design de Comunicação (gráfico), Grafismos Especializados.
Numa primeira abordagem pareceu-me importante o desafio de pensar uma alternativa para o sistema de Identicons como forma de reconhecimento visual cujos resultados permitam identificação (no seu sentido total). Assim, mais do que pensar numa solução que resolvesse 300000 situações, prefiro não encontrar um limite tangível (apontar para o infinito?).
Os insectos surgiram rapidamente como o personagem que naturalmente oferece mais variações: são conhecidas milhares de espécies e outras tantas estão por descobrir. Por outro lado, no contexto digital/informático, estão sempre em relação com a ideia de “bug” (falha). Lembrei-me pouco depois que não era completamente inocente esta solução pois já tinha uma imagem em mente que acabei por identificar.
Avancei então com um gerador de insectos (ou antes, insectos e aranhas, que não são bem a mesma coisa). Tencionava também incluir estruturas semelhantes a minhocas, larvas e centopeias mas não tive tempo (ainda) e teria de contornar uma ou outra dificuldade no código (points e contours de path no shoebot).
Código nodebox/shoebot:
""" Aranha (2 partes) ou Insecto (3 partes) de tamanho variável 6 ou 8 patas variáveis (8 se for aranha) 2 olhos variáveis ou mais se for aranha 1 a 3 pares variáveis de mandibulas 0 a 2 pares variáveis de antenas se for insecto 0 a 2 pares variáveis de asas se for insecto """ # devolve valor relativo a proporção (importado do Processing) def map(value,istart,istop,ostart,ostop): return ostart+(ostop-ostart)*((value-istart)/(istop-istart)) # devolve a distancia entre dois pontos def distance(x0,y0,x1,y1): from math import sqrt,pow return sqrt(pow(x1-x0,2)+pow(y1-y0,2)) # devolve coordenadas de um ponto a uma distancia e angulo de outro def rel_coordinates(x0,y0,distance,angle): from math import radians,sin,cos x1=x0+cos(radians(angle))*distance y1=y0+sin(radians(angle))*distance return x1,y1 # devolve o angulo entre dois pontos def angle(x0,y0,x1,y1): from math import degrees,atan2 a=degrees( atan2(y1-y0,x1-x0) ) return a # oval a partir do centro def oval_from_center(x,y,w,h,drawing=True): x=x-w/2 y=y-h/2 if drawing==True: oval(x,y,w,h) else: return oval(x,y,w,h,draw=False) # devolve lista de partes, proporção (tamanho) de cada parte def parts(parts_number,start_x,start_y,length,angle): parts=list() # (size_x,size_y,rad_size,brightness) head_size=random(.15,.45)/(parts_number-1) head_width=random(.9,1.1)*head_size head_rad=random(2.1,3.) # falsos radius (menores) parts.append((head_size,head_width,head_rad,random(-.25,.25))) tor_size=0 if parts_number==3: tor_size=random(.1,.45) tor_width=random(.85,1.5)*head_size tor_rad=random(2.1,3.) parts.append((tor_size,tor_width,tor_rad,random(-.25,.25))) abd_size=1-head_size-tor_size abd_width=random(1.25*head_size,abd_size*.75) abd_rad=random(2.1,3.) parts.append((abd_size,abd_width,abd_rad,random(-.25,.25))) parts.reverse() # tails first return parts size(400,400) colormode(HSB) background(1) # Cores ternárias main_color=random(0.,1.) alt_color=main_color+.33 if main_color+.33<1 else main_color-.66 alt_color_2=main_color-.33 if main_color-.33>0 else main_color+.66 # Inicio e fim do insecto, opções body_start=(random(0,WIDTH),random(0,HEIGHT)) body_end=None while body_end==None or distance(body_start[0],body_start[1],body_end[0],body_end[1])<(WIDTH/3): body_end=(random(WIDTH/4,WIDTH*3/4),random(HEIGHT/4,HEIGHT*3/4)) parts_number=random(2,3) # Dados principais body_angle=angle(body_start[0],body_start[1],body_end[0],body_end[1]) body_length=distance(body_start[0],body_start[1],body_end[0],body_end[1]) parts=parts(parts_number,body_start[0],body_start[1],body_length,body_angle) #PERNAS stroke(main_color,0.5,0.1) translate(body_start[0],body_start[1]) leg_center_part=1 leg_pairs_number=4 if len(parts)==2 else random(3,4) leg_center=rel_coordinates(0,0,((2*((parts[0][0]*body_length)/parts[0][2]))+(parts[leg_center_part][0]*body_length)/parts[leg_center_part][2]),body_angle) start_angle=body_angle+random(-100.,-80.) leg_angle=random(30.,50.) leg_angle_wide=leg_angle+random(30.,90.) leg_angle_increment=leg_angle/(leg_pairs_number-1) leg_angle_increment_wide=(leg_angle_wide/(leg_pairs_number-1)) leg_size=random(.8,1.25)*parts[leg_center_part-1][0]*body_length leg_ratio=random(2.5,3.5) leg_width=random(1,2)*3 for i in range(2): for j in range(leg_pairs_number): angle=(start_angle-leg_angle/2)+j*(leg_angle_increment*random(.9,1.1)) angle_wide=(start_angle-leg_angle_wide/2)+j*(leg_angle_increment_wide*random(.9,1.1)) angle_middle=angle+(angle_wide-angle)/random(1.9,2.1) leg_end_0=rel_coordinates(leg_center[0],leg_center[1],leg_size/3,angle) leg_end_1=rel_coordinates(leg_center[0],leg_center[1],leg_size*2/3,angle_middle) leg_end_2=rel_coordinates(leg_center[0],leg_center[1],leg_size,angle_wide) strokewidth(leg_width) line(leg_center[0],leg_center[1],leg_end_0[0],leg_end_0[1]) strokewidth(leg_width*2/3) line(leg_end_0[0],leg_end_0[1],leg_end_1[0],leg_end_1[1]) strokewidth(leg_width/3) line(leg_end_1[0],leg_end_1[1],leg_end_2[0],leg_end_2[1]) start_angle+=180 #CORPO nostroke() reset() transform(mode=CENTER) translate(body_start[0],body_start[1]) for part in parts: part_rad=rel_coordinates(0,0,(part[0]*body_length)/part[2],body_angle) translate(part_rad[0],part_rad[1]) push() rotate(-body_angle) fill(main_color,.3,.5+part[3]) oval_from_center(0,0,part[0]*body_length,part[1]*body_length) pop() translate(part_rad[0],part_rad[1]) parts.reverse() #HEAD head_center=rel_coordinates(0,0,(parts[0][0]*body_length)/parts[0][2],180+body_angle) translate(head_center[0],head_center[1]) #MANDIBULAS stroke(main_color,.1,.1) jaw_pairs=random(1,3) for i in range(jaw_pairs): m=1 jaws_angle=random(5.,40.) jaws_angle_inner=random(.2,.7)*jaws_angle jaws_size=random(.55,.7)*parts[0][1]*body_length jaws_closure=random(.75,1.25) for j in range(2): jaw_0=rel_coordinates(0,0,parts[0][0]*body_length/2,body_angle+m*jaws_angle/2) jaw_1=rel_coordinates(0,0,jaws_size*jaws_closure,body_angle+m*jaws_angle/2) jaw_2=rel_coordinates(0,0,jaws_size,body_angle+m*jaws_angle_inner/2) strokewidth(2) line(jaw_0[0],jaw_0[1],jaw_1[0],jaw_1[1]) strokewidth(1) line(jaw_1[0],jaw_1[1],jaw_2[0],jaw_2[1]) m*=-1 #OLHOS nostroke() fill(alt_color,.5,.5) eye_pairs=random(2,8) if len(parts)==2 else 1 eye_size=(random(.1,.45)*parts[0][1]*body_length)/eye_pairs for i in range(eye_pairs): eye_angle=random(20.,160.) eye_d=random(.25,.5)*parts[0][1]*body_length eye_l=rel_coordinates(0,0,eye_d,body_angle+eye_angle/2) eye_r=rel_coordinates(0,0,eye_d,body_angle-eye_angle/2) oval_from_center(eye_l[0],eye_l[1],eye_size,eye_size) oval_from_center(eye_r[0],eye_r[1],eye_size,eye_size) #ANTENAS if len(parts)==3: stroke(main_color,.1,.1) nofill() antenna=random(0,1) if antenna==1: antenna_pairs=random(0,2) for i in range(antenna_pairs): antenna_d=random(.1,.2)*parts[0][1]*body_length antenna_size=random(.15,.4)*body_length antenna_angle=random(60.,100.) antenna_angle_2=antenna_angle+random(-20.,20.) antenna_ratio=random(.3,.8) m=1 for j in range(2): antenna_0=rel_coordinates(0,0,antenna_d,body_angle+m*120) antenna_1=rel_coordinates(0,0,antenna_size*antenna_ratio,body_angle+m*antenna_angle_2/2) antenna_2=rel_coordinates(0,0,antenna_size,body_angle+m*antenna_angle/2) autoclosepath(False) path = findpath([(antenna_0[0],antenna_0[1]),(antenna_1[0],antenna_1[1]),(antenna_2[0],antenna_2[1])]) drawpath(path) m*=-1 #ASAS nostroke() fill(alt_color_2,.2,1,.5) if len(parts)==3 and random(0,1)==1: reset() translate(body_start[0]+leg_center[0],body_start[1]+leg_center[1]) wing_pairs=random(1,2) for i in range(wing_pairs): wing_d=random(.1,.2)*parts[1][1]*body_length wing_size=random(.75,1.1)*body_length wing_width=random(.3,.6)*wing_size wing_angle=random(-20,70) m=1 for j in range(2): wing_0=rel_coordinates(0,0,wing_size/2+wing_d,body_angle+m*90+m*wing_angle) push() translate(wing_0[0],wing_0[1]) rotate(-body_angle+90-m*wing_angle) oval_from_center(0,0,wing_size,wing_width) pop() m*=-1