#!/usr/bin/env python

# Adapted from Paul Furber's Python version of Nehe's OpenGL Lesson 3.

from __future__ import division

from OpenGL.GL import *
from OpenGL.GLU import *
import pygame, random, sys, numpy
from pygame.locals import *
from itertools import count, chain, izip

class Triangle(object):
    def __init__(self, corners):
        self.__corners = tuple(tuple(float(comp) for comp in corner)
                               for corner in corners)

    def draw(self):
        glBegin(GL_TRIANGLES)
        for r, g, b, a, x, y in self:
            glColor(r, g, b, a)
            glVertex(-1 + 2 * x, -1 + 2 * y)
        glEnd()
        
    def __iter__(self):
        return iter(self.__corners)

class Image(object):
    def __init__(self, triangles):
        self.__triangles = tuple(Triangle(t) for t in triangles)

    def draw(self):
        for triangle in self.__triangles:
            triangle.draw()
    
    def __iter__(self):
        return iter(self.__triangles)

def init():
    glShadeModel(GL_SMOOTH)
    glClearColor(0, 0, 0, 0)
    glEnable(GL_BLEND)
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)

def redraw(image):
    glClear(GL_COLOR_BUFFER_BIT)
    image.draw()

def generate_triangle(random=random):
    return Triangle((random.random() for _ in xrange(6)) for _ in xrange(3))

def generate_image(triangle_count, random=random):
    return Image([generate_triangle() for _ in xrange(triangle_count)])

def adjust_triangle(triangle, random=random):
    comps = list(chain(*triangle))
    i = random.randrange(len(comps))
    comps[i] = random.normalvariate(comps[i], 0.1)
    return Triangle([comps[:6], comps[6:12], comps[12:]])

def mutate_image_adjust(image, random=random):
    triangles = list(image)
    i = random.randrange(len(triangles))
    triangles[i] = adjust_triangle(triangles[i], random)
    return Image(triangles)

def mutate_image_replace(image, random=random):
    triangles = list(image)
    i = random.randrange(len(triangles))
    triangles[i] = generate_triangle(random)
    return Image(triangles)

def mutate_image_swap(image, random=random):
    triangles = list(image)
    i = random.randrange(len(triangles))
    j = random.randrange(len(triangles))
    triangles[i], triangles[j] = triangles[j], triangles[i]
    return Image(triangles)

def mutate_image(image, random=random):
    mutations = [mutate_image_adjust, mutate_image_replace, mutate_image_swap]
    mutation = random.choice(mutations)
    return mutation(image, random)

def get_pixels(surface):
    width, height = surface.get_size()
    return numpy.array([[surface.get_at((x, height - y - 1))[:3]
                         for y in xrange(height)]
                        for x in xrange(width)])

def main():
    args = sys.argv[1:]
    if len(args) != 1:
        sys.stderr.write("Usage: tics <image>\n")
        sys.exit(1)
    pygame.init()
    goal_surface = pygame.image.load(args[0])
    width, height = size = goal_surface.get_size()
    goal_pixels = get_pixels(goal_surface)
    pygame.display.set_mode(size, OPENGL | DOUBLEBUF)
    init()
    best_image = generate_image(32)
    best_fitness = 1
    for generation in count():
        event = pygame.event.poll()
        if (event.type == QUIT or
            (event.type == KEYDOWN and event.key == K_ESCAPE)):
            break
        image = mutate_image(best_image)
        redraw(image)
        pygame.display.flip()
        image_pixels = glReadPixels(0, 0, width, height, GL_RGB, GL_BYTE)
        fitness = ((image_pixels - goal_pixels) ** 2).mean() / 255 ** 2
        if fitness < best_fitness:
            print "#%d: %.7f" % (generation, fitness)
            best_image = image
            best_fitness = fitness

if __name__ == '__main__':
    main()

@@ -274,16 +274,171 @@
 , izip%0A%0A
+def normalize_comp(comp):%0A    return float(max(0, min(comp, 1)))%0A%0Adef normalize_corner(corner):%0A    return tuple(normalize_comp(comp) for comp in corner)%0A%0A
 class Tr
@@ -521,76 +521,32 @@
 ple(
-tuple(float(comp) for comp in corner)%0A                              
+normalize_corner(corner)
  for

normalize triangles on creation