PyScrapers

From XPUB & Lens-Based wiki
Revision as of 13:47, 9 December 2008 by Michael Murtaugh (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Build a city in Python using just while loops and the PIL? Yes, and along the way some basic principles of programming visualisations and simple 3D with isometric projection.

You start by establishing a grid (based in this case on a single variable, gridsize, which determines the width in height, in pixels, of each cell of the grid).

Nested while loops create a two-dimensional iterator that draws the grid cell by cell.

#!python numbers=off
import math, random	     ## standard Python modules
import Image, [[ImageDraw]]	     ## PIL modules

width = 480
height = 360
im = Image.new("RGB", (width, height))
draw = [[ImageDraw]].Draw(im)

gridsize = 24
cols = width / gridsize
rows = height / gridsize
print "drawing with", cols, "columns, and", rows, "rows"

r = 0
while r < rows:
	c = 0
	while c < cols:
		left = c*gridsize
		top = r*gridsize
		right = left + gridsize
		bottom = top + gridsize
		draw.rectangle((left, top, right, bottom))
		c += 1
	
	r += 1

im.save("pyscrapers01.png", "png")

attachment:pyscrapers01.png

Now, you can use one of the loop variables, say the columns, or c, to determine a color to use in filling each cell. Here, we use some basic maths to translate the column index to an RGB value. Notice the use of 1.0 to force Python to use precise (floating point) math, and then the int() function to finally turn the result back into a whole number (as needed for the color specification for the PIL).

#!python numbers=off
colorcomp = int((1.0*c/cols) * 255)

The PIL accepts color defined as text string in the form "rgb(red, green, blue)" where red, green, and blue are (whole) numbers from 0 to 255, each specifying the amount of each color. To translate the colorcomp variable to a PIL-format string, you could use. Notice again how you need to use str() to convert the integer to a string type:

#!python numbers=off
color="rgb("+str(colorcomp)+",0,0)"

Here's the full code, and resulting image, using this colorcomp variable as the red component of the cell color:

#!python numbers=off
import math, random
import Image, [[ImageDraw]]

width = 480
height = 360
im = Image.new("RGB", (width, height))
draw = [[ImageDraw]].Draw(im)

gridsize = 24
cols = width / gridsize
rows = height / gridsize
print "drawing with", cols, "columns, and", rows, "rows"

r = 0
while r < rows:
	c = 0
	while c < cols:
		colorcomp = int((1.0*c/cols)*255)
		color = "rgb("+str(colorcomp)+",0,0)"
		left = c*gridsize
		top = r*gridsize
		right = left + gridsize
		bottom = top + gridsize
		draw.rectangle((left, top, right, bottom), fill=color)
		c += 1
	
	r += 1

im.save("pyscrapers02.png", "png")

attachment:pyscrapers02.png

You could use the same technique to translate the row to the level of blue in the fill color:

#!python numbers=off
import math, random
import Image, [[ImageDraw]]

width = 480
height = 360
im = Image.new("RGB", (width, height))
draw = [[ImageDraw]].Draw(im)

gridsize = 24
cols = width / gridsize
rows = height / gridsize
print "drawing with", cols, "columns, and", rows, "rows"

r = 0
while r < rows:
	c = 0
	while c < cols:
		redcomp = int((1.0*c/cols)*255)
		bluecomp = int((1.0*r/rows)*255)
		color = "rgb("+str(redcomp)+",0,"+str(bluecomp)+")"
		left = c*gridsize
		top = r*gridsize
		right = left + gridsize
		bottom = top + gridsize
		draw.rectangle((left, top, right, bottom), fill=color)
		c += 1
	
	r += 1

im.save("pyscrapers03.png", "png")

attachment:pyscrapers03.png

The PIL also allows color to specified as HSL (hue-saturation-lightness) triplets. The format is "hsl(hue,sat%,lightness%)", where the hue is a (whole) number from 0 to 360 (degrees on a color wheel, where 0 is red), and saturation and lightness are percentages (whole numbers from 0 to 100). Read more color models supported by the PIL: http://www.pythonware.com/library/pil/handbook/imagecolor.htm

You could apply the same idea above, now mapping column to saturation, and row to lightness. The hue in this case is a constant, set in this case to 0:

#!python numbers=off
import math, random
import Image, [[ImageDraw]]

width = 480
height = 360
im = Image.new("RGB", (width, height))
draw = [[ImageDraw]].Draw(im)

gridsize = 24
cols = width / gridsize
rows = height / gridsize
print "drawing with", cols, "columns, and", rows, "rows"

r = 0
while r < rows:
	c = 0
	while c < cols:
		hue = 0
		satcomp = int((1.0*c/cols)*100)
		lcomp = int((1.0*r/rows)*100)
		color = "hsl("+str(hue)+","+str(satcomp)+"%,"+str(lcomp)+"%)"
		left = c*gridsize
		top = r*gridsize
		right = left + gridsize
		bottom = top + gridsize
		draw.rectangle((left, top, right, bottom), fill=color)
		c += 1
	
	r += 1

im.save("pyscrapers04.png", "png")

attachment:pyscrapers04.png

HSL is often a very useful color model for creating visualisations, as it allows you to use variations of brightness / saturation while preserving a particular color (hue). For drawing PyScrapers, HSL will be useful to simulate some basic lighting / shadow effects.

On to PyScrapers2...