Another lovely programming exercise. I took the task one step further and created an interactive explorer of variants of the Koch curve. Some of them are very interesting. Koch curve

It should be easy to expand it to work with more complicated L-systems.

import pygame
import os
import time
import math

def rot(v, a):
    sa = math.sin(a)
    ca = math.cos(a)
    return (v[0] * ca - v[1] * sa, v[0] * sa + v[1] * ca)

def vsum(x, y):
    return (x[0] + y[0], x[1] + y[1])

def process(rule, point, length, angle, level, max_level, surface, color = (0, 0, 0)):
    if level == max_level:
        new_point = vsum(point, rot((length, 0), angle))
        pygame.draw.aaline(surface, color, point, new_point)
        return new_point, angle
    s = rule[1]
    new_length = length / rule[3]
    if level == 0:
        s = rule[0]
        new_length = length

    for ch in s:
        if ch == '+':
            angle += rule[2]
        elif ch == '-':
            angle -= rule[2]
        else:
            point, angle = process(rule, point, new_length, angle, level + 1, max_level, surface)
            
    return point, angle
        

# math
start_point = (120, 100)
len0 = 300
start_angle = 0.0

systems = [
("F++F++F", "F-F++F-F", 2 * math.pi / 360.0 * 60.0, 3),
("F+F+F+F", "F-F+F+F-F", 2 * math.pi / 360.0 * 90.0, 3),
("F+F+F+F+F", "F-F+F+F-F", 2 * math.pi / 360.0 * 72.0, 3.62),
("F+F+F+F", "F-F+F+F-", 2 * math.pi / 360.0 * 90.0, 2),
("F++F++F", "-F++FF--F+", 2 * math.pi / 360.0 * 60.0, 2)
]
system_id = 0

# pygame boilerplate code

os.environ['SDL_VIDEO_CENTERED'] = '1'

pygame.init()

mysurf = pygame.Surface((640, 480), depth=32)
myrect = mysurf.get_rect()
clock = pygame.time.Clock()
running = True

level = 1

try:
    screen = pygame.display.set_mode((640, 480))
 
    pygame.display.flip()

    mysurf.fill((255, 255, 255))
    process(systems[system_id], start_point, len0, start_angle, 0, level, mysurf)
 
    while running:

        clock.tick(100)

        changed = False

        for evt in pygame.event.get():
            if evt.type == pygame.KEYDOWN and evt.key == pygame.K_ESCAPE or \
               evt.type == pygame.QUIT:
                running = False
                break
            
            if evt.type == pygame.KEYDOWN:
                if evt.key == pygame.K_UP:
                    level += 1
                    changed = True
                if evt.key == pygame.K_DOWN and level > 1:
                    level -= 1
                    changed = True
                if evt.key == pygame.K_RIGHT:
                    system_id = (system_id + 1) % len(systems)
                    changed = True
                if evt.key == pygame.K_LEFT:
                    system_id = (system_id + len(systems) - 1) % len(systems)
                    changed = True
                if evt.key == pygame.K_PAGEUP:
                    len0 *= 2
                    changed = True
                if evt.key == pygame.K_PAGEDOWN:
                    len0 /= 2
                    changed = True
        if changed:
            mysurf.fill((255, 255, 255))
            process(systems[system_id], start_point, len0, start_angle, 0, level, mysurf)            
                
        screen.blit(mysurf, (0, 0))
        pygame.display.flip()
finally:
    pygame.quit()  

About these ads