Randomness

July 6, 2016 ยท View on GitHub

Let's create some random stuff! We have several functions to simulate randomness. The simplest functions are random and it's bro random2. They create random values from the given interval:

random :: Float -> Pio Float
random maxValue = random (0, maxValue)

random2 :: (Float, Float) -> Pio Float
random2 (minValue, maxValue) = ...

Let's create a chaotic movement. This type of random behavior is called brownian motion:

import Graphics.Proc

main = runProc $ def 
	{ procSetup  = setup
	, procDraw   = draw
	, procUpdate = update }

width = 400
height = 400
center = (0.5 * width, 0.5 * height)

setup = do
	size (width, height)
	background (grey 0)	
	stroke (grey 255)
	strokeWeight 2
	frameRate 30
	return (center, center)

draw (p1, p2) = do
	line p1 p2

len = 5

update (_, p) = do
	x <- random2 (-len, len)
	y <- random2 (-len, len)
	return (p, p + (x, y))

We have functions to create ranfom points on the screen and colors: randomP2, randomCol, ranodimCola (color with transparency or alpha). Let's draw colored circles at random on the screen:

import Graphics.Proc

main = runProc $ def 
	{ procSetup  = setup
	, procDraw   = draw }

width = 400
height = 400
center = (0.5 * width, 0.5 * height)

setup = do
	size (width, height)
	background (grey 255)		
	frameRate 7

draw () = do
	drawRandomCircle

drawRandomCircle = do	
	noStroke
	fill =<< randomCola
	rad <- random2 (10, 40)
	circle rad =<< randomP2

Gaussian noise

With gaussian noise we can create values that most frequently fall to a certain value but often miss it by some degree. With this change we can spread all our circles around the center:

drawRandomGaussCircle = do	
	noStroke
	fill =<< randomCola
	rad <- random2 (10, 20)
	x <- randomGaussian
	y <- randomGaussian
	circle rad  (50 * (x, y) + center)

The function randomGaussian generates values that are spread around zero. Amount of spread equals to 1. We can add th value to change the center point and multiply the value to change the amount of spread.

Perlin noise

(right now it looks like haskell implementation of Perlin noise is not accurate)

Sometimes the random is way too random and randomGaussian is too focused. We can use the noise function to create "organic" chaotic structures. It implements the Perlin noise.

The noise is a function that maps values to random numbers. We can control the amount of spread by proximity of values of the argument. If values are close to each other then amount of noise is also small. It means that noise is a continuous function.

We have 1D, 2D and 3D noise functions:

noise1 :: Float -> Pio Float
noise2 :: P2 -> Pio Float
noise2 :: P3 -> Pio Float

Let's divide the screen in two halves with natural looking line:

import Graphics.Proc

main = runProc $ def 
	{ procSetup  = setup
	, procDraw   = draw }

width = 400
height = 400

setup = do
	size (width, height)	
	forM [0 .. width] $ \x -> do
		y <- noise1 (x / 100)
		return (x, (30 * y - 15) + 0.5 * height)
		
draw ps = do
	linePath ps		

Let's create a texture:

import Graphics.Proc

main = runProc $ def 
	{ procSetup  = setup
	, procDraw   = draw }

width = 400
height = 400

setup = do
	size (width, height)	
	noStroke
	forM [(x, y) | x <- [0, 5 .. width], y <- [0, 7 .. height]] $ \(x, y) -> do
		z <- noise2 (x / 100, y / 100)
		return ((x, y), 255 * z)
		
draw ps = do
	forM_ ps $ \(p, col) -> do
		fill (grey col)
		circle 5 p

Predictable noise

We can set the seed for random generators:

noiseSeed, randomSeed :: Float -> Pio ()

With fixed seed we can create reproducible noise behaviors. The randomSeed controls the random and gaussian noises at the same time.