
# hilbert3d.py
# input: 2  or  3  or  4
'''
 * Draws a 3-Dimensional Hilbert curve, colored by the distance along 
 * the curve. The fractal d can be specified as an argument or 
 * changed with the '+' and '-' keys. The algorithm is adapted from 
 * Thomas Diewald's Processing sketch.

https:#introcs.cs.princeton.edu/java/stddraw3d/Hilbert3D.java.html
https:#introcs.cs.princeton.edu/java/stddraw3d/
https://openprocessing.org/sketch/49692
'''

import matplotlib.pyplot as plt



def hilbert3D(center, w, d, v0, v1, v2, v3, v4 ,v5, v6, v7):

  # Create the eight possible vertices of a gray code
  verts = [
      (center[0]-w/2, center[1]+w/2, center[2]-w/2),
      (center[0]-w/2, center[1]+w/2, center[2]+w/2),
      (center[0]-w/2, center[1]-w/2, center[2]+w/2),
      (center[0]-w/2, center[1]-w/2, center[2]-w/2),
      (center[0]+w/2, center[1]-w/2, center[2]-w/2),
      (center[0]+w/2, center[1]-w/2, center[2]+w/2),
      (center[0]+w/2, center[1]+w/2, center[2]+w/2),
      (center[0]+w/2, center[1]+w/2, center[2]-w/2)
    ]

  # Arranges the gray code by the specified arguments
  gray = [ verts[v0], verts[v1], verts[v2], verts[v3], verts[v4], 
           verts[v5], verts[v6], verts[v7] ]

  if d <= 1:
    return gray

  # Constructs the curve by concatenating vertices from recursive calls
  coords = \
    hilbert3D(gray[0],w/2,d-1, v0,v3,v4,v7,v6,v5,v2,v1) + \
    hilbert3D(gray[1],w/2,d-1, v0,v7,v6,v1,v2,v5,v4,v3) + \
    hilbert3D(gray[2],w/2,d-1, v0,v7,v6,v1,v2,v5,v4,v3) + \
    hilbert3D(gray[3],w/2,d-1, v2,v3,v0,v1,v6,v7,v4,v5) + \
    hilbert3D(gray[4],w/2,d-1, v2,v3,v0,v1,v6,v7,v4,v5) + \
    hilbert3D(gray[5],w/2,d-1, v4,v3,v2,v5,v6,v1,v0,v7) + \
    hilbert3D(gray[6],w/2,d-1, v4,v3,v2,v5,v6,v1,v0,v7) + \
    hilbert3D(gray[7],w/2,d-1, v6,v5,v2,v1,v0,v3,v4,v7)
  return coords



# -------------- main ------------------------



level = int(input("level? "))
width = 1
coords = hilbert3D((0,0,0), width, level, 0, 1, 2, 3, 4, 5, 6, 7)

# print(*coords)
xs, ys, zs = zip(*coords)

# Plot
plt.axes(projection ='3d')
plt.axis('off')   # comment this out to see the axes
plt.plot(xs, ys, zs)
plt.title("Hilbert 3d for level="+str(level))
plt.show()
