How I created it:
- I found a Wavefront OBJ file defining a wireframe of a cow from somewhere unremembered on the internet. Here’s a large collection of downloadable OBJ files. An OBJ file first lists the vertices of the object in 3D space, then a list of the each face of the object.
- In Tableau, each edge of the object could be described as the endpoints of of a line, so Tableau needed as its source data an x, y, and z coordinate, and an edge name.
- I wrote this Python script to convert an OBJ file into a CSV file that Tableau would be able to load. The script is at the end of this post.
- From this Wikipedia page about the 3D Rotation Group I found the formulae to rotate an object around the x, y and z axis. In a Tableau calculated field, Here is how the x coordinate is changed by rotation on the y axis:
[X:RotateZ] * cos( ([ThetaY] / 360) * 2 * PI() ) - [Z] * sin( ([ThetaY] / 360) * 2 * PI() ) - If you download the workbook, it includes a rotatable dodecahedron on another tab. That one you can animate the rotation as there's a theta on the page shelf (as opposed to be a parameter). For the theta, for each row in the CSV file, I repeated the coordinate data with a different theta value at 5° intervals.
- The previous mentioned Python script to convert an OBJ file to a CSV file suitable for Tableau:
import sys
def print_line(vertex, edge):
sys.stdout.write(('%s, %s, %s, "%d"\n') % (vertex[0], vertex[1], vertex[2], edge))
# Column names
print "X, Y, Z, Edge"
lineNum = 0
doneWithV = False
faces = []
vertices = [[]]
f = open(sys.argv[1])
for line in f:
lineNum += 1
line = line.rstrip('#')
line = line.strip()
if len(line) == 0:
continue
vals = line.split()
if vals[0] == 'v':
if doneWithV:
print "V after F"
sys.exit(0)
if len(vals) != 4:
print "Invalid line #", lineNum
continue
vertices.append(vals[1:])
elif vals[0] == 'f':
doneWithV = True
faces.append(vals[1:])
edge = 0
for face in faces:
# Add the first vertex to the end of the list of vertices
# so there is a line from the last to the first vertex.
face += [face[0]]
for i in range(len(face) - 1):
print_line(vertices[int(face[i])], edge)
print_line(vertices[int(face[i + 1])], edge)
edge += 1;
I sent a link to this to my son Nick, who's launched into a computer science major this fall at Rochester Institute of Technology.
ReplyDeleteThis comment has been removed by a blog administrator.
ReplyDelete