Commits

Anonymous committed a1dbd84

Initial commit.

  • Participants

Comments (0)

Files changed (1)

+from __future__ import division
+
+from math import cos, sin, pi
+import pygame
+from random import randrange, random
+import sys
+import time
+
+WINDOW_SIZE = (640, 480)
+WINDOW_CAPTION = 'Swirls'
+
+class Object(object):
+    pass
+
+SINGULARITY = Object()
+SINGULARITY.START_SPEED = 10
+SINGULARITY.COUNT = 3
+SINGULARITY.VISIBLE = False
+
+FLARE = Object()
+FLARE.START_SPEED = 80
+FLARE.COUNT = 16
+FLARE.MAX_SPEED = 10000
+FLARE.FADE_TO_WHITE = True
+
+GRAVITY_CONSTANT = 1000000.
+NEUTRAL_DISTANCE = 20
+HUE_CHANGE_RATE = 0.02
+NUM_HEAD_SEGMENTS = 100
+COOLING_FACTOR = 0.25
+DIM_HALFLIFE = 30
+WALL_DAMPING = 0.75
+MAX_FRAME_RATE = 50
+
+display = Object()
+world = Object()
+
+def main():
+    initialise()
+    while True:
+        handle_events()
+        advance_time()
+        update_display()
+
+def initialise():
+    initialise_display()
+    initialise_world()
+
+def initialise_display():
+    pygame.init()
+    display.screen = pygame.display.set_mode(WINDOW_SIZE)
+    pygame.display.set_caption(WINDOW_CAPTION)
+    display.rect = pygame.Rect((0, 0), WINDOW_SIZE)
+
+    display.canvas = pygame.Surface(WINDOW_SIZE)
+    display.blank = pygame.Surface(WINDOW_SIZE)
+    pygame.draw.rect(display.blank, (0, 0, 0), display.rect)
+    display.dim_level = 1
+
+def initialise_world():
+    world.singularities = initialise_singularities()
+    world.flares = initialise_flares()
+    world.time = time.time()
+    world.hue = random()
+    world.mousedown = False
+
+def initialise_flares():
+    result = []
+    for i in xrange(FLARE.COUNT):
+        f = create_flare()
+        result.append(f)
+    return result
+
+def create_flare():
+    f = Object()
+    f.last_pos = f.pos = centre_position()
+    f.vel = random_start_velocity(FLARE)
+    f.head_segments = []
+    return f
+
+def centre_position():
+    width, height = WINDOW_SIZE
+    return vector(width / 2, height / 2)
+
+def initialise_singularities():
+    result = []
+    for i in xrange(SINGULARITY.COUNT):
+        s = create_singularity()
+        result.append(s)
+    return result
+
+def create_singularity():
+    s = Object()
+    s.pos = random_position()
+    s.vel = random_start_velocity(SINGULARITY)
+    return s
+
+def random_position():
+    width, height = WINDOW_SIZE
+    return vector(
+        randrange(width),
+        randrange(height))
+
+def vector(x, y):
+    return [x, y]
+
+def polar_vector(magnitude, angle):
+    return [
+        magnitude * cos(angle),
+        magnitude * sin(angle)
+    ]
+
+def random_start_velocity(kind):
+    angle = random() * 2 * pi
+    magnitude = kind.START_SPEED
+    return polar_vector(magnitude, angle)
+    
+def handle_events():
+    for event in pygame.event.get():
+        process_event(event)
+
+def process_event(event):
+    try:
+        handler = EVENT_HANDLERS[event.type]
+    except KeyError:
+        return
+    handler(event)
+
+def process_quit_event(event):
+    sys.exit(0)
+
+def process_mousebuttondown_event(event):
+    if event.button == 1:
+        world.mousedown = True
+
+def process_mousebuttonup_event(event):
+    if event.button == 1:
+        world.mousedown = False
+    
+EVENT_HANDLERS = {
+    pygame.QUIT: process_quit_event,
+    pygame.MOUSEBUTTONDOWN: process_mousebuttondown_event,
+    pygame.MOUSEBUTTONUP: process_mousebuttonup_event,
+}
+
+def advance_time():
+    now = time.time()
+    delta = now - world.time
+    if delta < 1 / MAX_FRAME_RATE:
+        time.sleep(1/MAX_FRAME_RATE - delta)
+        now = time.time()
+        delta = now - world.time
+    world.time = now
+    colour = make_colour(world.hue)
+    
+    dim_canvas(delta)
+    advance_hue(delta)
+    for s in world.singularities:
+        advance_singularity(s, delta)
+    for f in world.flares:
+        advance_flare(f, delta, colour)
+
+def advance_hue(t):
+    world.hue = (world.hue + t * HUE_CHANGE_RATE) % 1
+
+def advance_flare(f, t, colour):
+    acc = get_flare_acceleration(f)
+    f.vel = add_vectors(f.vel, scalar_multiply(acc, t))
+    f.vel = cap_magnitude(f.vel, FLARE.MAX_SPEED)
+    advance_flare_to(f, add_vectors(f.pos, scalar_multiply(f.vel, t)), colour, t)
+    if out_of_universe_left(f.pos):
+        f.vel = make_rightwards(f.vel)
+        f.vel = scalar_multiply(f.vel, WALL_DAMPING)
+    elif out_of_universe_right(f.pos):
+        f.vel = make_leftwards(f.vel)
+        f.vel = scalar_multiply(f.vel, WALL_DAMPING)
+    if out_of_universe_top(f.pos):
+        f.vel = make_downwards(f.vel)
+        f.vel = scalar_multiply(f.vel, WALL_DAMPING)
+    elif out_of_universe_bottom(f.pos):
+        f.vel = make_upwards(f.vel)
+        f.vel = scalar_multiply(f.vel, WALL_DAMPING)
+
+def advance_flare_to(f, new_pos, colour, t):
+    f.last_pos = f.pos
+    f.pos = new_pos
+    for segment in f.head_segments:
+        diminish_segment_temperature(segment, t)
+    add_flare_head_segment(f, f.last_pos, f.pos, colour)
+
+def diminish_segment_temperature(segment, t):
+    segment.temperature *= (COOLING_FACTOR ** t)
+
+def add_flare_head_segment(f, start, end, colour):
+    s = Object()
+    s.temperature = 1
+    s.start = start
+    s.end = end
+    s.colour = colour
+    f.head_segments.append(s)
+    f.head_segments = f.head_segments[-NUM_HEAD_SEGMENTS:]
+
+def cap_magnitude(vec, limit):
+    m = vector_magnitude(vec)
+    if m <= limit:
+        return vec
+    return scalar_multiply(vec, (limit / m))
+
+def get_flare_acceleration(f):
+    total = vector(0, 0)
+    for s in world.singularities:
+        a = get_acceleration_to(f, s.pos)
+        total = add_vectors(total, a)
+    if world.mousedown:
+        a = get_acceleration_to(f, pygame.mouse.get_pos())
+        a = scalar_multiply(a, 2)
+        total = add_vectors(total, a)
+    return total
+
+def get_acceleration_to(f, pos):
+    disp = subtract_vectors(pos, f.pos)
+    dist = vector_magnitude(disp)
+
+    if dist > NEUTRAL_DISTANCE:
+        acc = GRAVITY_CONSTANT / (dist ** 2)
+    else:
+        acc = 0
+    
+    return scalar_multiply(disp, acc / dist)
+
+def advance_singularity(s, t):
+    s.pos = add_vectors(s.pos, scalar_multiply(s.vel, t))
+    if out_of_window_left(s.pos):
+        s.vel = make_rightwards(s.vel)
+    elif out_of_window_right(s.pos):
+        s.vel = make_leftwards(s.vel)
+    if out_of_window_top(s.pos):
+        s.vel = make_downwards(s.vel)
+    elif out_of_window_bottom(s.pos):
+        s.vel = make_upwards(s.vel)
+
+def add_vectors(vec1, vec2):
+    x1, y1 = vec1
+    x2, y2 = vec2
+    return vector(x1 + x2, y1 + y2)
+
+def subtract_vectors(vec1, vec2):
+    x1, y1 = vec1
+    x2, y2 = vec2
+    return vector(x1 - x2, y1 - y2)
+
+def vector_magnitude(vec):
+    x, y = vec
+    return (x ** 2 + y ** 2) ** 0.5
+
+def scalar_multiply(vec, t):
+    x, y = vec
+    return vector(x * t, y * t)
+
+def out_of_window_left(pos):
+    return pos[0] < 0
+
+def out_of_window_right(pos):
+    return pos[0] >= WINDOW_SIZE[0]
+
+def out_of_window_top(pos):
+    return pos[1] < 0
+
+def out_of_window_bottom(pos):
+    return pos[1] >= WINDOW_SIZE[1]
+
+def out_of_universe_left(pos):
+    return pos[0] < -WINDOW_SIZE[0]/2
+
+def out_of_universe_right(pos):
+    return pos[0] >= WINDOW_SIZE[0]*3/2
+
+def out_of_universe_top(pos):
+    return pos[1] < -WINDOW_SIZE[1]/2
+
+def out_of_universe_bottom(pos):
+    return pos[1] >= WINDOW_SIZE[1]*3/2
+
+def make_rightwards(vel):
+    x, y = vel
+    return vector(abs(x), y)
+
+def make_leftwards(vel):
+    x, y = vel
+    return vector(-abs(x), y)
+
+def make_downwards(vel):
+    x, y = vel
+    return vector(x, abs(y))
+
+def make_upwards(vel):
+    x, y = vel
+    return vector(x, -abs(y))
+
+def update_display():
+    clear_display()
+    draw_world()
+    show_display()
+
+def clear_display():
+    #pygame.draw.rect(display.screen, (0, 0, 0), display.rect)
+    pass
+
+def draw_world():
+    draw_to_canvas()
+    display.screen.blit(display.canvas, (0, 0))
+    draw_temporaries()
+
+def dim_canvas(t):
+    display.dim_level *= 0.5 ** (t/DIM_HALFLIFE)
+    alpha = 255 - int(256 * display.dim_level)
+    if alpha > 0:
+        display.blank.set_alpha(alpha)
+        display.canvas.blit(display.blank, (0, 0))
+        display.dim_level = 1
+
+def draw_temporaries():
+    for f in world.flares:
+        draw_flare_head(f)
+    for s in world.singularities:
+        draw_singularity(s)
+
+def draw_to_canvas():
+    colour = make_colour(world.hue)
+    for f in world.flares:
+        draw_flare_trail(f, colour)
+
+def draw_singularity(s):
+    if SINGULARITY.VISIBLE:
+        pygame.draw.circle(display.screen, (255, 128, 0),
+            screen_position(s.pos), 5)
+
+def draw_flare_trail(f, colour):
+    pygame.draw.line(display.canvas, colour,
+            screen_position(f.last_pos), screen_position(f.pos), 2)
+
+def draw_flare_head(f):
+    for segment in f.head_segments:
+        draw_flare_head_segment(segment)
+
+def draw_flare_head_segment(segment):
+    colour = scale_segment_colour(segment.colour, segment.temperature)
+    pygame.draw.line(display.screen, colour, screen_position(segment.start),
+            screen_position(segment.end), 2)
+
+def scale_segment_colour(base_colour, temperature):
+    if FLARE.FADE_TO_WHITE:
+        return scale_colour_towards_white(base_colour, temperature)
+    return scale_colour_towards_black(base_colour, temperature)
+
+def scale_colour_towards_white(base_colour, temperature):
+    r, g, b = base_colour
+    return (
+        r + temperature * (255 - r),
+        g + temperature * (255 - g),
+        b + temperature * (255 - b),
+    )
+
+def scale_colour_towards_black(base_colour, temperature):
+    r, g, b = base_colour
+    return (
+        r * (1 - temperature),
+        g * (1 - temperature),
+        b * (1 - temperature),
+    )
+
+def make_colour(hue):
+    region, remainder = divmod(hue, 1/6)
+    midbit = 6 * remainder
+    if region == 0:
+        r = (1, midbit, 0)
+    elif region == 1:
+        r = (1-midbit, 1, 0)
+    elif region == 2:
+        r = (0, 1, midbit)
+    elif region == 3:
+        r = (0, 1-midbit, 1)
+    elif region == 4:
+        r = (midbit, 0, 1)
+    elif region == 5:
+        r = (1, 0, 1-midbit)
+    return tuple(int(255*i) for i in r)
+
+def screen_position(pos):
+    x, y = pos
+    return (int(x), int(y))
+
+def show_display():
+    pygame.display.flip()
+
+if __name__ == '__main__':
+    main()