AcidWarpy
From Noah.org
Python is the new C
I originally wrote AcidWarp in C. To this day, 20 years later, people keep bugging me about AcidWarp!
I decided it would be fun to play around with the old functions so I decided to see if it was possible to resurrect the code to run on a modern machine. I decided to use Python since that is my language of choice in the 21st century.
This version requires PyGame. This is trivial to install on Ubuntu Linux (sudo aptitude install python-pygame) and Windows. It's probably easy on other platforms.
To run AcidWarpy you will need the main acidwarpy.py code below as well as the image plugins. You will need at least one plugin. Acidwarpy will scan the current directory for all plugins and automatically add them to the list of images that can be displayed.
Click here to download: acidwarpy.py
Click here to download: image1_plugin.py
Click here to download: image2_plugin.py
Click here to download: image3_plugin.py
Click here to download: image4_plugin.py
Click here to download: image5_plugin.py
Click here to download: image6_plugin.py
Click here to download: image7_plugin.py
#!/usr/bin/env python """ SYNOPSIS acidwarpy [-h] [-v,--verbose] [--version] [--full] DESCRIPTION A port of AcidWarp to Python. AUTHOR Noah Spurrier <noah@noah.org> LICENSE This script is in the public domain, free from copyrights or restrictions. VERSION $Id: acidwarpy.py 220 2008-02-20 19:55:50Z noah $ """ try: import pygame, pygame.image from pygame.locals import * except ImportError, e: raise ImportError (str(e) + """ A critical module was not found. This program requires "PyGame" to be installed. See http://www.pygame.org/ for more information. On Ubuntu you can type "sudo aptitude install python-pygame" to satisfy this requirement.""") import os, sys, time, random, threading, traceback, optparse from math import * #from palette import palette_manager class roller (threading.Thread): """This runs a function in a loop in a thread.""" def __init__(self, interval, function, args=[], kwargs={}): """The interval parameter defines time between each call to the function. """ threading.Thread.__init__(self) self.interval = interval self.function = function self.args = args self.kwargs = kwargs self.finished = threading.Event() def cancel(self): """Stop the roller.""" self.finished.set() def run(self): while not self.finished.isSet(): self.finished.wait(self.interval) self.function(*self.args, **self.kwargs) def distance (x, y): """This returns the distance from (0,0) to (x,y).""" return sqrt(x*x + y*y) # The ANGLE_UNIT is the number of angle units in a circle. 2PI for radians. ANGLE_UNIT= 3.1415926535897932384626433832795 * 2.0 # 2*PI, PI/2, PI/4, etc. ANGLE_UNIT_2 = (ANGLE_UNIT*2.0) ANGLE_UNIT_HALF = (ANGLE_UNIT/2.0) ANGLE_UNIT_EIGHTH = (ANGLE_UNIT/8.0) ANGLE_UNIT_QUART = (ANGLE_UNIT/4.0) ANGLE_UNIT_THREE_QUARTERS = (ANGLE_UNIT*3.0/4.0) def angle (x, y): """Get and preserve quadrant info before we convert everything into quadrant one for processing.""" quadrant = 1 if (x*y) > 0: if (x < 0): quadrant = 3 x = -x y = -y else: if (x < 0): quadrant = 2 swap = -x x = y y = swap if (y < 0): quadrant = 4 swap = -y y = x x = swap if (x < y): if (y != 0): angle = ANGLE_UNIT_QUART - atan (float(x)/float(y)) #Frame_Edge_Angle[FRAME_SIZE * x / y] - 1 else: angle = ANGLE_UNIT_QUART else: if (x != 0): angle = atan(float(y)/float(x)) # Frame_Edge_Angle[FRAME_SIZE * y / x] else: angle = 0.0 # Now put the angle in the proper quadrant if quadrant == 1: return angle elif quadrant == 2: return angle + ANGLE_UNIT_QUART elif quadrant == 3: return angle + ANGLE_UNIT_HALF elif quadrant == 4: return angle + ANGLE_UNIT_THREE_QUARTERS return -1; def build_palette (step): """Build a palette -- a list with 256 RGB triplets.""" loop = range(256) # First we create a 256-element array. it goes from 0, to 255, and back to 0 ramp = [abs((x+step*3)%511-255) for x in loop] #using the previous ramp and some other crude math, we make some different #values for each R, G, and B color planes return [(ramp[x], ramp[(x+32)%256], (x+step)%256) for x in loop] def make_image_from_plugin (surf, size, plugin_func): """This iterates over all the x,y coordinates on the given surface with the given size. For each x,y coordinate the plugin_function will be called. The current state is passed. This includes the current x,y coordinate as well as some extra useful information such as the angle from the current x,y point to the center of the surface. The plugin could calculate these values for itself, but these values are use so often that it's useful to recalculate them for all plugins. """ center_x = size[0] / 2 center_y = size[1] / 2 for y in range(size[1]): for x in range (size[0]): dx = x - center_x; dy = y - center_y; d = distance(dx,dy) a = angle(dx,dy) + ANGLE_UNIT_QUART state = {"max_x":size[0], "max_y":size[1], "x":x, "y":y, "center_x":center_x, "center_y":center_y, "dx":dx, "dy":dy, "d":d, "a":a} color = plugin_func(state) color = color % 255 pal_color = surf.get_palette_at(int(color)) surf.set_at ((x,y), pal_color ) return surf def _get_func (plugin_filename): source = file(plugin_filename, "rb").read() c = compile (source, '', "exec") exec c in globals(), locals() aFunc = eval ("func", globals(), locals()) assert callable(aFunc), u"%s is not callable." % fullFuncName return aFunc def rotate_screen_palette (screen, palette, r_dir, g_dir, b_dir): palette.rotate_palette (r_dir, g_dir, b_dir) screen.set_palette (palette.get_array()) class palette_manager: RGBW=0 WHITE=1 WHITE_HALF=2 PASTEL=3 RGBW_LIGHTNING=4 WHITE_LIGHTNING=5 WHITE_HALF_LIGHTNING=6 PASTEL_LIGHTNING=7 ALL_BLACK=8 ALL_WHITE=9 def __init__ (self): self.palette_array = [(0,0,0)] * 256 def init_type (self, pal_type): if pal_type == self.RGBW: self.set_rgbw () return if pal_type == self.WHITE: self.set_white () return if pal_type == self.WHITE_HALF: self.set_white_half () return if pal_type == self.PASTEL: self.set_pastel () return if pal_type == self.RGBW_LIGHTNING: self.set_rgbw () self.add_sparkles_to_palette (9) return if pal_type == self.WHITE_LIGHTNING: self.set_white () self.add_sparkles_to_palette (9) return if pal_type == self.WHITE_HALF_LIGHTNING: self.set_white_half () self.add_sparkles_to_palette (9) return if pal_type == self.PASTEL_LIGHTNING: self.set_pastel () self.add_sparkles_to_palette (9) return if pal_type == self.ALL_BLACK: self.set_all_black() return if pal_type == self.ALL_WHITE: self.set_all_white() return return def get_array (self): return self.palette_array def add_sparkles_to_palette (self, sparkle_amount): for index in range (1,256,4): self.palette_array[index] = (min(63, self.palette_array[index][0] + sparkle_amount), min(63, self.palette_array[index][1] + sparkle_amount), min(63, self.palette_array[index][2] + sparkle_amount) ) def set_all_black (self): for index in range (0,256): self.palette_array[index] = (0,0,0) def set_all_white (self): for index in range (0,256): self.palette_array[index] = (255,255,255) def set_rgbw (self): for index in range (0, 32): self.palette_array[index] = (index * 2,0,0) self.palette_array[index + 64] = (0,index * 2,0) self.palette_array[index + 128] = (0,0,index * 2) self.palette_array[index + 192] = (index * 2,index * 2,index * 2) for index in range (32, 64): self.palette_array[index] = ((63 - index)*2,0,0) self.palette_array[index + 64] = (0,(63 - index)*2,0) self.palette_array[index + 128] = (0,0,(63 - index)*2) self.palette_array[index + 192] = ((63 - index)*2,(63 - index)*2,(63 - index)*2) def set_white (self): for index in range (0, 128): self.palette_array[index] = (index/2,index/2,index/2) for index in range (128, 256): self.palette_array[index] = ((255 - index)/2,(255 - index)/2,(255 - index)/2) def set_white_half (self): for index in range (0, 64): self.palette_array[index] = (index,index,index) self.palette_array[index + 64] = (63 - index,63 - index,63 - index) for index in range (128, 256): self.palette_array[index] = (0,0,0) def set_pastel (self): for index in range (0, 128): self.palette_array[index] = (128 + index,128 + index,128 + index) self.palette_array[index + 128] = (127 + (127 - index),127 + (127 - index),127 + (127 - index)) def rotate_palette (self, r_step, g_step, b_step): pa = zip(*self.palette_array) # unzip pa = map(list,pa) self.palette_array = zip(*(tuple(self._rotate(pa[0],r_step)),tuple(self._rotate(pa[1],g_step)),tuple(self._rotate(pa[2],b_step)))) def fade_to_black (self): pa = zip (*self.palette_array) pa = map(list,pa) all_black = 1 for i in range (0, len(pa[0])): if pa[0][i] > 0: all_black = 0 pa[0][i] = pa[0][i] - 1 if pa[1][i] > 0: all_black = 0 pa[1][i] = pa[1][i] - 1 if pa[2][i] > 0: all_black = 0 pa[2][i] = pa[2][i] - 1 self.palette_array = zip(*(tuple(pa[0]),tuple(pa[1]),tuple(pa[2])) ) return all_black def fade_to_other_palette (self, p): all_equal = 1 for i in range (0, len(self.palette_array)): if self.palette_array[i] != p.palette_array[i]: all_equal = 0 self.palette_array[i] = self._move_toward (self.palette_array[i],p.palette_array[i]) return all_equal def _move_toward (self, tuple1, tuple2): if tuple1[0] < tuple2[0]: t0 = tuple1[0] + 1 if tuple1[0] > tuple2[0]: t0 = tuple1[0] - 1 if tuple1[1] < tuple2[1]: t1 = tuple1[1] + 1 if tuple1[1] > tuple2[1]: t1 = tuple1[1] - 1 if tuple1[2] < tuple2[2]: t2 = tuple1[2] + 1 if tuple1[2] > tuple2[2]: t2 = tuple1[2] - 1 return (t0,t1,t2) def _rotate (self, seq, n=0): if n == 0: return seq n = n % len(seq) return seq[n:] + seq[:n] def main(): global options, args # Load the functions -- these are stored in external python files. # This is a fancy import mechanism. This imports all files with names # that end in *plugin.py. plugins = [] plugin_names = [x for x in os.listdir(".") if x[-9:]=='plugin.py'] plugin_names.sort() plugin_names.reverse() for wrp in plugin_names: plugins.append (_get_func (wrp)) pygame.init() pygame.mouse.set_visible(False) if options.full: screen = pygame.display.set_mode((640, 480), HWSURFACE|HWPALETTE|FULLSCREEN, 8) else: screen = pygame.display.set_mode((640, 480), 0, 8) pygame.display.set_caption ("AcidWarpy") p = palette_manager () p.init_type(palette_manager.PASTEL) screen.set_palette(p.get_array()) image = screen.convert() plugin_index = random.randrange (len(plugins)) make_image_from_plugin (image, (640,480), plugins[plugin_index]) screen.blit(image, (0,0)) p.init_type(palette_manager.PASTEL) screen.set_palette(p.get_array()) # r = roller (0.01, rotate_screen_palette, (screen, p, 1, 0, -1)) # r.start() # screen.set_palette(p.get_array()) plugin_index = random.randrange (len(plugins)) start_time = time.time() showstoppers = (QUIT, KEYDOWN, MOUSEBUTTONDOWN) while pygame.event.poll().type not in showstoppers: time.sleep (0.01) p.rotate_palette (1,0,-1) screen.set_palette (p.get_array()) if time.time() - start_time > 5: plugin_index = plugin_index + 1 if plugin_index >= len(plugins): plugin_index = 0 plugin_index = random.randrange (len(plugins)) make_image_from_plugin (image, (640,480), plugins[plugin_index]) while p.fade_to_black() != 1: screen.set_palette (p.get_array()) time.sleep (0.01) p_target = palette_manager () p_target.init_type(palette_manager.PASTEL) p.init_type(palette_manager.PASTEL) screen.set_palette(p.get_array()) screen.blit(image, (0,0)) p.init_type(palette_manager.ALL_BLACK) screen.set_palette(p.get_array()) while p.fade_to_other_palette(p_target) != 1: screen.set_palette (p.get_array()) time.sleep (0.01) start_time = time.time() # r.cancel() pygame.quit() if __name__ == '__main__': try: start_time = time.time() parser = optparse.OptionParser(formatter=optparse.TitledHelpFormatter(), usage=globals()['__doc__'], version='$Id: acidwarpy.py 220 2008-02-20 19:55:50Z noah $') parser.add_option ('-v', '--verbose', action='store_true', default=False, help='verbose output') parser.add_option ('--full', action='store_true', default=False, help='Full screen mode') (options, args) = parser.parse_args() #if len(args) < 1: # parser.error ('missing argument') if options.verbose: print time.asctime() main() if options.verbose: print time.asctime() if options.verbose: print 'TOTAL TIME IN MINUTES:', if options.verbose: print (time.time() - start_time) / 60.0 sys.exit(0) except KeyboardInterrupt, e: # Ctrl-C raise e except SystemExit, e: # sys.exit() raise e except Exception, e: print 'ERROR, UNEXPECTED EXCEPTION' print str(e) traceback.print_exc() os._exit(1)
