Non grid-constrained automata
So far we've looked at multiple forms of cellular automata that connect the a tile to its neighbors, in a single array of tiles, or in a 2D grid of tiles. We could even expand the dimensionality of our programs to include 3, or even higher dimensions!
However, not all automata are restricted to grids, at least not in the screen. We will introduce the
movement of free particles based on forces of attraction and repulsion. This job has been pioneered by
Jeffrey Ventrella, a procedural artist and scientist who created an algorithm known simply as clusters.
To generate clusters, first we need to understand the nature of the forces applied to particles. A
particle will simply be a dot in the screen. The presence of other particles will change the direction
of our particle by a force that is correlated to the distance between the two particles.
The parameters of the Algorithm
- RES: int - PARTICLE size
- SCREEN: (int,int) - Dimensions of the screen in pixels
- N: int - Total number of particles of each type
- RMIN: int - The minimum distance between PARTICLEs. Below this value a PARTICLE rejects another (too close)
- RMAX: int - The maximum distance between PARTICLEs. Beyond this value a PARTICLE doesn't see another (too far)
- RMID: int - The middle distance between PARTICLEs. Around this value a PARTICLE's behavior is strongly dictated by a COEFFICIENT
- PARTICLE: Particle(int,int,str) - A particle of a given color that lives somewhere in the screen
- LIST_OF_PARTICLE: [PARTICLE, ... n=N] - all the particles in the screen with a given color
- PARTICLE_GRID: [LIST_OF_PARTICLE, ... n= No. colors] - all the particles in the screen
- MATRIX_COEF: [[int,int,int], ..., n=3] - The matrix that summarizes the forces applied between particles of different colors: red, green, blue
# MODULES import pygame import random from random import randint as rn from random import uniform as uf import math # DD RES = 5 SCREEN = (1020,800) display = pygame.display.set_mode(SCREEN) N = 100 RMIN = 40 RMAX = 150 RMID = (RMAX-RMIN)/2 + RMIN # DD. PARTICLE # particle = Particle() # interp. a particle in the particle life algorithm class Particle(pygame.sprite.Sprite): def __init__(self,x,y,color): super(Particle,self).__init__() self.x = x self.y = y self.color = color self.size = RES self.dx = 0 self.dy = 0 def draw(self,display): pygame.draw.circle(display,self.color,(self.x,self.y),self.size) particle0 = Particle(100,130,"red") particle1 = Particle(50,10,"green") particle2 = Particle(63,82,"blue") # TEMPLATE FOR PARTICLE # ... particle.x # ... particle.y # ... particle.color # ... particle.size # ... particle.draw(display) # DD. LIST_OF_PARTICLE # lop_% = [PARTICLE, ...n =N] # interp. the total number of particles of a given color lop_red = [] lop_green = [] lop_blue = [] for p in range(N): particle_red = Particle(rn(10,SCREEN[0]),rn(10,SCREEN[1]),"red"); lop_red.append(particle_red) particle_green = Particle(rn(10,SCREEN[0]),rn(10,SCREEN[1]),"green"); lop_green.append(particle_green) particle_blue = Particle(rn(10,SCREEN[0]),rn(10,SCREEN[1]),"blue"); lop_blue.append(particle_blue) # TEMPLATE FOR LIST_OF_PARTICLE # for particle in lop: # ... particle # ... particle.x # ... particle.y # ... particle.color # ... particle.size # ... particle.draw(display) # DD. PARTICLE_GRID # grid = [LIST_OF_PARTICLE, n=3] # interp. a collection of particles of multiple colors # INVARIANT: color sequence has to match the order of rows and columns in MATRIX_COEFFICIENTS grid = [lop_red,lop_green,lop_blue] # TEMPLATE FOR PARTICLE_GRID # for lop in grid: # ... lop # for particle in lop: # ... particle # ... particle.x # ... particle.y # ... particle.color # ... particle.size # ... particle.draw(display) def fn_for_grid(fn,*args): for lop in grid: for particle in lop: fn (particle,*args) # DD. MATRIX_COEFFICIENTS # mcoef = [[float,n=3],n=3] # interp. the 2D array of coefficients to map the forc applied to the particle # RED GREEN BLUE # RED X X X # GREEN X X X # BLUE X X X mcoef = [[uf(-4,4) for i in range(3)], [uf(-4,4) for i in range(3)], [uf(-4,4) for i in range(3)]] mcoef = [ [1.56,-1.37,1.23], [-1.2,-0.96,3.63], [2.19,1.310,2.95] ]
The Algorithm
- For every PARTICLE_A in PARTICLE_GRID:
- For every PARTICLE_B in PARTICLE_GRID:
- if PARTICLE_A is not PARTICLE_B:
- Calculate distance between particles and use it to get the force applied to PARTICLE_A
- Calculate angle to apply the force, then add that force to x and y of PARTICLE_A