Skip to content

Instantly share code, notes, and snippets.

@drslump
Created April 18, 2012 20:54
Show Gist options
  • Save drslump/2416484 to your computer and use it in GitHub Desktop.
Save drslump/2416484 to your computer and use it in GitHub Desktop.
Fire model effect for ansi terminals
#!/usr/bin/env python
# coding: utf-8
"""
Fire model effect for ansi terminals.
Model based on http://freespace.virgin.net/hugo.elias/models/m_fire.htm
"""
__author__ = "Iván -DrSlump- Montes"
__copyright__ = "Public domain"
import os, sys, math, signal
from random import randint
from time import sleep
# black, red, light-red, yellow
COLORS = ( '0;30', '0;31', '1;31', '0;33' )
DITHS = ( '░', '▒', '█', )
# Build a palette from the colors and dithering characters list
palette = [' ']
for c in COLORS:
for d in DITHS:
palette.append( '\x1b[%sm%s' % (c, d) )
#print ''.join(palette)
#sys.exit()
# Obtain size form the terminal
rows, cols = os.popen('stty size', 'r').read().split()
rows = int(rows) + 2 # two rows at bottom are only used to generate heat
cols = int(cols)
# Initialize buffers
buffer_a = bytearray(rows*cols)
buffer_b = bytearray(rows*cols)
map_rows = rows * 3
map_cols = cols
map_buff = bytearray(map_rows*map_cols)
def box_blur(src, rows, cols, steps = 25):
""" Simple box blur implementation """
dst = bytearray(rows*cols)
for s in range(steps):
for y in range(1, rows-1):
ofs = y * cols
for x in range(1, cols-1):
n1 = src[ ofs + x-1 ]
n2 = src[ ofs + x+1 ]
n3 = src[ ofs-cols + x ]
n4 = src[ ofs+cols + x ]
p = (n1+n2+n3+n4) // 4
dst[ofs + x] = p
src, dst = dst, src
return dst
def coolit(from_buf, to_buf, map, map_ofs = 0):
for y in range(1, rows-1):
ofs = y * cols
for x in range(1, cols-1):
n1 = from_buf[ ofs + x-1 ]
n2 = from_buf[ ofs + x+1 ]
n3 = from_buf[ ofs-cols + x ]
n4 = from_buf[ ofs+cols + x ]
mofs = (map_ofs + y) % map_rows
p = (n1+n2+n3+n4) // 4
p -= map[mofs*map_cols + x] / 12
if p < 0: p = 0
to_buf[ (y-1)*cols + x ] = int(p)
def render(buf):
# Calculate a conversion factor between the buffer and the palette
factor = math.ceil(256 / len(palette))
sys.stdout.write('\x1b[2J')
for y in range(rows-2):
s = ''
for x in range(cols):
# Obtain the level and convert it to the palette scale
level = buf[ y * cols + x ]
level = max(1, math.floor(level / factor)) - 1
s += palette[int(level)]
sys.stdout.write(s + '\n')
sys.stdout.write('\x1b[0m')
sys.stdout.flush()
# Configure the displacement map
for y in range(map_rows):
for x in range(map_cols):
map_buff[y * map_cols + x] = randint(0, 255)
map_buff = box_blur(map_buff, map_rows, map_cols, 15)
#render(map_buff)
#sys.exit()
def sigint_handler(signal, frame):
sys.exit()
signal.signal(signal.SIGINT, sigint_handler)
frame_cnt = 0
while True:
# Heat up the bottom lines
for x in range(10):
col = randint(0, cols-1)
p = randint(220, 255)
buffer_a[ (rows-2)*cols + col] = p
buffer_a[ (rows-1)*cols + col] = p
# Cool the fire a bit and render it
coolit(buffer_a, buffer_b, map_buff, frame_cnt)
render(buffer_b)
# swap buffers
buffer_a, buffer_b = buffer_b, buffer_a
sleep(0.10)
frame_cnt += 1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment