Last active
May 7, 2019 18:05
-
-
Save jtickle/d4797e2b8866cbee9c2e399b5da8eedd to your computer and use it in GitHub Desktop.
Hilbert Terminal Filling Curve
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import shutil; | |
import collections; | |
import math; | |
import sys; | |
import argparse; | |
Point = collections.namedtuple('Point', ['x', 'y', 'n']) | |
HilbertAt = collections.namedtuple('HilbertAt', ['x', 'y', 'n']) | |
def hilbert (x, y, xi, xj, yi, yj, n): | |
if n <= 0: | |
yield Point(x = x + (xi + yi) / 2, y = y + (xj + yj) / 2, n=0) | |
else: | |
for i in hilbert(x, y, | |
yi/2, yj/2, xi/2, xj/2, n-1): | |
yield i | |
for i in hilbert(x + xi/2, y + xj/2, | |
xi/2, xj/2, yi/2, yj/2, n-1): | |
yield i | |
for i in hilbert(x + xi/2 + yi/2, y + xj/2 + yj/2, | |
xi/2, xj/2, yi/2, yj/2, n-1): | |
yield i | |
for i in hilbert(x + xi/2 + yi, y + xj/2 + yj, | |
-yi/2, -yj/2, -xi/2, -xj/2, n-1): | |
yield i | |
def roundPoints(p): | |
for pt in p: | |
yield Point(x=round(pt.x), y=round(pt.y), n=round(pt.n)) | |
def getHilbertLinePoints(hil): | |
prev = False | |
i = 0 | |
for cur in roundPoints(hil): | |
if prev: | |
if cur.x != prev.x: | |
neg = -1 if cur.x - prev.x < 0 else 1 | |
for x in range(prev.x + neg, cur.x, neg): | |
yield Point(x = x, y = cur.y, n=i) | |
elif cur.y != prev.y: | |
neg = -1 if cur.y - prev.y < 0 else 1 | |
for y in range(prev.y + neg, cur.y, neg): | |
yield Point(x = cur.x, y = y, n=i) | |
else: | |
print("Both X and Y changed") | |
exit(); | |
i+=1 | |
prev = cur | |
yield HilbertAt(**cur._asdict()) | |
def ESC(val): | |
print("\033[" + val, end="") | |
def putAt(L, C, s): | |
ESC("{0};{1}H{2}".format(L, C, s)) | |
def putOff(p, o, o2, s): | |
putAt(p.y + o.y + o2, p.x + o.x + o2, s) | |
def saveCursor(): | |
ESC("s") | |
def restoreCursor(): | |
ESC("u") | |
def clearScreen(): | |
ESC("2J") | |
alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'; | |
def base36char(n): | |
return alphabet[ n % len(alphabet) ] | |
def main(depth, label, square): | |
ts = shutil.get_terminal_size() | |
width = ts.columns - 1 | |
height = ts.lines - 4 | |
off = Point(x=0, y=0, n=0) | |
if(square): | |
if(width < height): | |
height = width | |
off = Point( | |
x=off.x, | |
y=math.floor(((ts.lines - 4) - height) / 2), | |
n=off.n) | |
if(width > height): | |
width = height | |
off = Point( | |
x=math.floor(((ts.columns - 1) - width) / 2), | |
y=off.y, | |
n=off.n) | |
gen = hilbert(0, 0, width, 0, 0, height, depth) | |
points = getHilbertLinePoints(gen) | |
clearScreen() | |
for i in points: | |
if isinstance(i, HilbertAt): | |
putOff(i, off, 0, "#") | |
if(label): | |
putOff(i, off, 1, "\\") | |
putOff(i, off, 2, "({0},{1})".format(i.x, i.y)) | |
else: | |
putOff(i, off, 0, base36char(i.n)) | |
putAt(ts.lines - 4, 0, "HILBERT SPACE FILLING FOR YOUR TERMINAL!") | |
print( "\n----------------------------------------") | |
if __name__ == "__main__": | |
parser = argparse.ArgumentParser( | |
description='Fill your terminal with a Hilbert curve') | |
parser.add_argument('depth', type=int, default=1, nargs='?', | |
help="Depth of the hilbert curve") | |
parser.add_argument('--label', '-l', dest='label', action='store_true') | |
parser.add_argument('--square', '-s', dest='square', action='store_true') | |
args = parser.parse_args() | |
main(args.depth, args.label, args.square) | |
if(len(sys.argv) < 2): | |
print("Pro Tip: specify a nonzero integer depth as an argument for") | |
print("more interesting space-filling. Also, try -l or -s.") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment