Skip to content

Instantly share code, notes, and snippets.

@gbromios
Created April 6, 2017 00:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gbromios/df3fe9b15e8dcde987ea1033d40f4181 to your computer and use it in GitHub Desktop.
Save gbromios/df3fe9b15e8dcde987ea1033d40f4181 to your computer and use it in GitHub Desktop.
#!/usr/local/bin/python3
'''
usage: $ ./rainbower.py [-t [FRAMES FPS]] [-v] [-f FORMAT] [-o OUTFILE] your text here
outputs html by default.
'''
import math
debug = False
__all__ = ['Colorizer']
class Colorizer:
def __init__(self, char_format, color_format, post_format = '{0}'):
self.char_format = char_format
self.color_format = color_format
self.post_format = post_format
def __call__(self, text):
# strip spaces TODO all whitespace/nonprint
n = len(text.replace(" ", ""))
out = ""
x = 0
for r, g, b in smooth_rgb(n):
color = "#{:02X}{:02X}{:02X}".format(r, g, b)
# skip whitespace TODO all ws/non printing
while text[x] == " ":
out += text[x]
x+= 1
out += self.char_format.format(COLOR=color, CHAR=text[x])
x += 1
return self.post_format.format(out)
def smooth_rgb(n):
assert(n > 1)
# sample cos from 0 to 2π inclusive, at n equally-spaced inputs, ensuring that
# the first and last values have equal inputs
dx = 2 * math.pi / (n - 1)
if debug: print("{} samples [0..2π], {} apart", n, dx)
for i in (float(i) for i in range(n)):
if debug: print(" {}: fancy_cos({:.4f}) = {:.4f}".format(
int(i),
(i * dx) / (2 * math.pi),
(math.cos(dx * i) + 1) / 2)
)
# sample cos(dx * i) three times, offset by 0, 1/3 and 2/3 of the period
r, g, b = (
(math.cos(dx * i) + 1) / 2,
(math.cos((dx * i) + 2 * math.pi / 3) + 1) / 2,
(math.cos((dx * i) + 4 * math.pi / 3) + 1) / 2,
)
# scale rgb values linearly such that at least one is at full blast
scale = 255 / max(r, g, b)
# clamp each value between 0..255 and cast to int
yield (
int(max(0, min(255, r * scale))),
int(max(0, min(255, g * scale))),
int(max(0, min(255, b * scale))),
)
def pygame_test(n, fps):
print("running test: {} frames at {} fps ({}s)".format(n, fps, n/fps))
import pygame
screen = pygame.display.set_mode((500, 500))
clock = pygame.time.Clock()
pygame.display.flip()
pls = None
for color in smooth_rgb(n):
screen.fill(color)
if debug: print ('\t', color)
if debug: print ()
pygame.display.flip()
if any(pygame.event.get(pygame.QUIT)):
raise SystemExit
clock.tick(fps)
# pause for a second at the end.
pygame.time.wait(1000)
def print_usage_and_exit():
print (__doc__)
raise SystemExit
def main(args):
global debug
run_test = False
frames, fps = 60, 1000
text = ''
format = None
outfile = None
while args:
arg = args[0]
if arg == '-v':
debug = True
args = args[1:]
elif arg == '-t':
run_test = True
try:
frames, fps = int(args[1]), float(args[2])
args = args[3:]
except IndexError:
args = args[1:]
elif arg == '-f':
try:
format = args[1]
args = args[2:]
except IndexError:
print_usage_and_exit()
elif arg == '-o':
try:
outfile = args[1]
args = args[2:]
except IndexError:
print_usage_and_exit()
else:
# out of options, combine the rest of the args
text = ' '.join(args)
break
if run_test:
pygame_test(frames, fps)
elif text:
#print(Colorizer(format='{{color:{COLOR}}}{CHAR}{{color}}', color_format="#{:02X}{:02X}{:02X}")(text)) # < jira
colorizer = Colorizer(
char_format = '<span style="color: {COLOR}">{CHAR}</span>',
color_format = '#{:02X}{:02X}{:02X}',
post_format = '<html><body style="background-color: #666; font-family: sans-serif; font-weight: bold;">{0}</body></html>'
)
output = colorizer(text)
if outfile:
with open(outfile, 'w') as of:
of.write(output)
else:
print(output)
else:
print_usage_and_exit()
if __name__ == '__main__':
import sys
main(sys.argv[1:])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment