Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Tree: 83679c5733
Fetching contributors…

Cannot retrieve contributors at this time

executable file 182 lines (146 sloc) 5.315 kB
#!/usr/bin/env python
# life.py: Play Conway's Game of Life on your image.
# Copyright 2010 by Akkana Peck, http://www.shallowsky.com/software/
# You may use and distribute this plug-in under the terms of the GPL.
#
# Thanks to Joao Bueno for some excellent tips on speeding up
# pixel ops in gimp-python!
import math
import time
from gimpfu import *
from array import array
import sys # only needed for debugging
# FACTOR should be 256. * 3. if you want to use only black and white.
# Needs to be adjusted for colors, though.
# And also for image type (currently assumes nchannels == 3).
#FACTOR = 255 * 3.
# Life rules, adjusted for RGB (each neighbor can contribute up to 255*3)
NOTALIVE = 600
LONELY = 1230
NEWBORN = 1600
OVERCROWDED = 2295
NEWPXL = array("B", [ 180, 255, 180 ])
DYINGPXL = array("B", [ 80, 30, 30 ])
BLACK = array("B", [ 0, 0, 0 ])
def CA_rule(pixels, x, y, pos, width, height, nchannels=3) :
oldval = pixels[pos : pos + nchannels]
sumoldval = float(sum(oldval))
#print pos, sumoldval
# Calculate new value based on Life rules
neighbors = 0
for xx in range(x-1, x+2) :
for yy in range(y-1, y+2) :
if xx == x and yy == y : continue # don't include self in sum
dpos = (xx + width * yy) * nchannels
s = int(sum(pixels[dpos : dpos + nchannels]) + .5)
neighbors += s
#print "(%d, %d): [%-3d %-3d %-3d]=%-3d, neighbors %-4d" % \
# (x, y, oldval[0], oldval[1], oldval[2], sumoldval, neighbors)
#sys.stdout.flush()
if neighbors < LONELY or neighbors > OVERCROWDED :
# Pixel dies
if sumoldval > NOTALIVE :
return DYINGPXL
else :
# It is so ridiculous that python arrays can't handle
# mathematical ops like oldval / 2
return array("B", [ oldval[0]/2, oldval[1]/2, oldval[2]/2 ])
elif sumoldval < NOTALIVE and neighbors > NEWBORN :
# New one born
return NEWPXL
else :
# pixel stays the same
return oldval
def python_life_step(img, layer) :
sys.stdout.flush()
gimp.progress_init("Playing life ...")
pdb.gimp_image_undo_group_start(img)
width, height = layer.width, layer.height
src_rgn = layer.get_pixel_rgn(0, 0, width, height, False, False)
nchannels = len(src_rgn[0,0])
src_pixels = array("B", src_rgn[0:width, 0:height])
dst_rgn = layer.get_pixel_rgn(0, 0, width, height, True, True)
dst_pixels = array("B", "\x00" * (width * height * nchannels))
# Loop over the region:
for x in xrange(0, width - 1) :
for y in xrange(0, height) :
pos = (x + width * y) * nchannels
dst_pixels[pos : pos + nchannels] = CA_rule(src_pixels, x, y, pos,
width, height,
nchannels)
progress = float(x)/layer.width
if (int(progress * 100) % 20 == 0) :
gimp.progress_update(progress)
# Copy the whole array back to the pixel region:
dst_rgn[0:width, 0:height] = dst_pixels.tostring()
layer.flush()
layer.merge_shadow(True)
layer.update(0, 0, width, height)
pdb.gimp_image_undo_group_end(img)
pdb.gimp_progress_end()
pdb.gimp_displays_flush()
import gtk
class LifeWindow(gtk.Window):
def __init__ (self, img, layer, *args):
self.running = False
self.img = img
self.layer = layer
win = gtk.Window.__init__(self, *args)
self.set_border_width(10)
# Obey the window manager quit signal:
self.connect("destroy", gtk.main_quit)
# Make the UI
vbox = gtk.VBox(spacing=10, homogeneous=True)
self.add(vbox)
label = gtk.Label("Conway's Game of Life")
vbox.add(label)
label.show()
hbox = gtk.HBox(spacing=20)
btn = gtk.Button("Run")
hbox.add(btn)
btn.show()
btn.connect("clicked", self.runstoplife)
btn = gtk.Button("Single step")
hbox.add(btn)
btn.show()
btn.connect("clicked", self.steplife)
btn = gtk.Button("Cancel")
hbox.add(btn)
btn.show()
btn.connect("clicked", gtk.main_quit)
vbox.add(hbox)
hbox.show()
vbox.show()
self.show()
return win
def steplife(self, widget) :
python_life_step(self.img, self.layer)
def runstoplife(self, widget) :
if self.running :
widget.set_label("Run")
self.running = False
return
self.running = True
widget.set_label("Stop")
while self.running :
python_life_step(self.img, self.layer)
# Handle any button presses that have happened while we were busy
while gtk.events_pending() :
gtk.main_iteration()
def python_life(img, layer) :
l = LifeWindow(img, layer)
gtk.main()
register(
"python_fu_life",
"Conway's game of Life",
"Conway's game of Life",
"Akkana Peck",
"Akkana Peck",
"2010",
"<Image>/Filters/Map/Life...",
"RGB", # Adjust for other types when code is more flexible
[
],
[],
python_life)
main()
Jump to Line
Something went wrong with that request. Please try again.