Created
March 2, 2018 21:43
-
-
Save foone/bb2feb15a222fd3f5ed5f19d09f1ee52 to your computer and use it in GitHub Desktop.
variant on mTesseracted's voxel script
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
''' | |
===================================== | |
Rotating 3D voxel animation of PYTHON | |
===================================== | |
Demonstrates using ``ax.voxels`` with uneven coordinates | |
''' | |
import matplotlib.pyplot as plt | |
import numpy as np | |
from mpl_toolkits.mplot3d import Axes3D | |
import matplotlib.animation as manimation | |
from math import copysign | |
def explode(data): | |
size = np.array(data.shape)*2 | |
data_e = np.zeros(size - 1, dtype=data.dtype) | |
data_e[::2, ::2, ::2] = data | |
return data_e | |
def voxel_face(corns, dm, nf): | |
''' | |
Grab the corner coordinates of one voxel face | |
Parameters | |
---------- | |
corns : np.indices array of corners for one voxel | |
dm : (dimension), values can be 0(x), 1(y), 2(z) | |
nf : (near/far face), values can be 0(near), 1(far) | |
''' | |
lc = corns.copy() #local copy so we don't swap original | |
if dm == 1 : #swap y into x and correct ordering | |
lc[0], lc[1] = corns[1].transpose(1,0,2), corns[0].transpose(1,0,2) | |
if dm == 2 : #swap z into x and correct ordering | |
lc[0], lc[2] = corns[2].transpose(2,1,0), corns[0].transpose(2,1,0) | |
ret = np.zeros((3,2,2)) | |
xc1 = lc[0,nf,0,0] #hold x dim constant | |
ret[0,:] = np.array([[xc1, xc1], [xc1, xc1]]) | |
yc1, yc2 = lc[1,0,0:2,0] | |
ret[1,:] = np.array([[yc1, yc2], [yc1, yc2]]) | |
zc1, zc2 = lc[2,0,0,0:2] | |
ret[2,:] = np.array([[zc1, zc1], [zc2, zc2]]) | |
if dm != 0 : #swap x back into desired dimension | |
ret[0], ret[dm] = ret[dm].copy(), ret[0].copy() | |
return ret | |
COLORS={ | |
'on': '#FF55FF', | |
'off': '#55FFFF', | |
'shade': '#000000', | |
'bg': '#000000' | |
} | |
plt.rcParams['axes.facecolor'] = COLORS['bg'] | |
def create_face(voxels, side, facestr): | |
if side == 'A': | |
start=[0,0,4] | |
for_x=[1,0,0] | |
for_y=[0,0,-1] | |
elif side == 'B': | |
start=[-1,0,4] | |
for_x=[0,1,0] | |
for_y=[0,0,-1] | |
elif side == 'C': | |
start=[3,0,-1] | |
for_x=[-1,0,0] | |
for_y=[0,1,0] | |
elif side == 'D': | |
start=[0,3,4] | |
for_x=[0,-1,0] | |
for_y=[0,0,-1] | |
elif side == 'E': | |
start=[0,3,0] | |
for_x=[0,-1,0] | |
for_y=[1,0,0] | |
elif side == 'F': | |
start=[3,-1,4] | |
for_x=[-1,0,0] | |
for_y=[0,0,-1] | |
x,y,z=start | |
for out_y, line in enumerate(facestr.strip('\r\n').splitlines()): | |
for out_x,char in enumerate(line.strip()): | |
x=start[0] + for_x[0]*out_x + for_y[0]*out_y | |
y=start[1] + for_x[1]*out_x + for_y[1]*out_y | |
z=start[2] + for_x[2]*out_x + for_y[2]*out_y | |
#print out_x,out_y,x,y,z,char | |
voxels[x,y,z] = (char == '#') | |
# build PYTHON letters | |
n_voxels = np.zeros((4, 4, 5), dtype=bool) | |
letters = [None]*6 | |
letter_faces = np.zeros((6,2),dtype=int) | |
#First face | |
create_face(n_voxels,'A',""" | |
#### | |
#___ | |
###_ | |
#___ | |
#___ | |
""") | |
letters[0] = np.array(np.where(n_voxels)).T | |
letter_faces[0] = [1, 0] #close y face | |
n_voxels[...] = False | |
#Y | |
create_face(n_voxels,'B',""" | |
____ | |
_##_ | |
#__# | |
#__# | |
_##_ | |
""") | |
letters[1] = np.array(np.where(n_voxels)).T | |
letter_faces[1] = [0, 1] #far x face | |
n_voxels[...] = False | |
#T | |
create_face(n_voxels,'C',""" | |
_##_ | |
#__# | |
#__# | |
_##_ | |
""") | |
letters[2] = np.array(np.where(n_voxels)).T | |
letter_faces[2] = [2, 1] #far z face | |
n_voxels[...] = False | |
#H | |
create_face(n_voxels,'D',""" | |
____ | |
##__ | |
#_#_ | |
#_#_ | |
#_#_ | |
""") | |
letters[3] = np.array(np.where(n_voxels)).T | |
letter_faces[3] = [0, 0] #close x face | |
n_voxels[...] = False | |
#O | |
create_face(n_voxels,'E',""" | |
_##_ | |
#_#_ | |
##__ | |
_##_ | |
""") | |
letters[4] = np.array(np.where(n_voxels)).T | |
letter_faces[4] = [2, 0] #close z face | |
n_voxels[...] = False | |
#N | |
create_face(n_voxels,'F',""" | |
____ | |
____ | |
____ | |
____ | |
____ | |
""") | |
letters[5] = np.array(np.where(n_voxels)).T | |
letter_faces[5] = [1, 1] #far y face | |
n_voxels[...] = False | |
fcol = np.full(n_voxels.shape, COLORS['on']) | |
ecol = np.full(n_voxels.shape, COLORS['shade']) | |
filled = np.ones(n_voxels.shape) | |
# upscale the above voxel image, leaving gaps | |
filled_2 = explode(filled) | |
fcolors_2 = explode(fcol) | |
ecolors_2 = explode(ecol) | |
# Shrink the gaps | |
corn = np.indices(np.array(filled_2.shape) + 1).astype(float) // 2 | |
ccorn = 0.05 #close corner | |
fcorn = 1.0 - ccorn | |
corn[0,0::2, :, :] += ccorn | |
corn[1,:, 0::2, :] += ccorn | |
corn[2,:, :, 0::2] += ccorn | |
corn[0,1::2, :, :] += fcorn | |
corn[1,:, 1::2, :] += fcorn | |
corn[2,:, :, 1::2] += fcorn | |
fig = plt.figure(figsize=(2,2)) | |
ax = fig.gca(projection='3d') | |
ax.axis("off") | |
#Plot the voxels | |
x, y, z = corn | |
ax.voxels(x, y, z, filled_2, facecolors=fcolors_2, edgecolors=ecolors_2) | |
#Plot the letter square faces | |
jj=0 | |
for j in [x for x in letters if x is not None]: | |
locf = np.empty((j.shape[0],3,2,2)) #local face | |
ji = 0 | |
for i in j: | |
i = i * 2 #skip empty voxels | |
loc = corn[:,i[0]:i[0]+2,i[1]:i[1]+2,i[2]:i[2]+2] #local corners | |
locf[ji] = voxel_face(loc, letter_faces[jj,0], letter_faces[jj,1]) | |
ax.plot_surface(locf[ji,0],locf[ji,1],locf[ji,2],color=COLORS['off'], | |
shade=False) | |
ji += 1 | |
jj += 1 | |
#Views: PY, P, Y, T, H, O, N, PY | |
view_elev = [ 5, 0, 0, 90, 0, -90, 0, 5] | |
view_azim = [-60, -90, 0, 90, 180, 180, 90, -60] | |
#''' | |
FFMpegWriter = manimation.writers['ffmpeg'] | |
metadata = dict(title='Movie Test', artist='Matplotlib', | |
comment='Movie support!') | |
writer = FFMpegWriter(fps=15, metadata=metadata) | |
with writer.saving(fig, "pythonRot2.mp4", 100): | |
for j in range(20): | |
ax.view_init(view_elev[0], view_azim[0]) | |
plt.draw() | |
writer.grab_frame() | |
for i in range(1,len(view_elev)): | |
de = (view_elev[i] - view_elev[i-1]) | |
da = (view_azim[i] - view_azim[i-1]) | |
if abs(da) >= 180 : #unecessary in this config | |
da -= copysign(360, da) | |
if abs(de) >= 180 : | |
de -= copysign(360, de) | |
if i != 1 : | |
steps = 60 | |
else : | |
steps = 10 | |
da = da / float(steps) | |
de = de / float(steps) | |
for j in range(10): #Pause on direct view of a letter | |
ax.view_init(view_elev[i-1], view_azim[i-1]) | |
plt.draw() | |
writer.grab_frame() | |
for j in range(steps): #Rotate to next letter | |
ax.view_init(view_elev[i-1] + j*de, | |
view_azim[i-1] + j*da) | |
plt.draw() | |
writer.grab_frame() | |
#''' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment