Approximately 35 years ago I saw an early model of stereo lithographs while I was working at Computervision, then the leading CAD/CAM Company.. Then about 15 years ago, they started to become more affordable and common, so, I decided to make something... but what? So, another decade went by until the Goodnow Library setup a makerspace ... So, here we are. I added a tiny bit of documentation just incase anyone finds this interesting and perhaps, develops an enterest in Computer Graphics.
The first row of images are from the first version. See the RollingTetraDoDec.mov. The next version will rotate the dodec face down to be flat. Then I need to figure out how to build in the supports such that the cleanup is minimal. The second row is from meshlab.
Python Code
# tedoGen.py - generate a testrahedron trapped in a dodecahedron. jch.com/jch/art/platonic
#
# This version generates an .obj file. Use the 'meshlab' app to view it.
#
# Copyright 2018 YON Jan C. Hardenbergh - jch.com/
# License: Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) https://creativecommons.org/licenses/by-sa/4.0/
# Share - copy and redistribute the material in any medium or format
# Adapt - remix, transform, and build upon the materialfor any purpose, even commercially.
# This license is acceptable for Free Cultural Works.The licensor cannot revoke these freedoms as long as you follow the license terms.
#
# dodecGen.py - make a dodecahedron solid. - https://en.wikipedia.org/wiki/Dodecahedron
# 2018-07-15 T 18:25 started right after finishing dodec
# 2018-07-22 DONE! (almost, still need spandrels? tendrils?
# tetraGen.py - make a tetrahedron solid. - https://en.wikipedia.org/wiki/Tetrahedron
# 2018-07-08 - make triStrip take 4 points - spit out .svg to start debugging.
# 2018-07-15 - DONE! spit .ply - https://en.wikipedia.org/wiki/PLY_(file_format)
# 2018-07-26 - Added TrianguarPrism for 3D printing and change to .obj https://en.wikipedia.org/wiki/Wavefront_.obj_file
#
# Approximately 35 years ago I saw an early model of stereo lithographs while I was working at Computervision, then the
# leading CAD/CAM Company: https://en.wikipedia.org/wiki/Computervision. Then about 15 years ago, they started to become
# more affordable and common, so, I decided to make something... but what? So, another decade went by until the
# Goodnow Library setup a makerspace - https://goodnowlibrary.org/now-lab/ ... So, here we are. I added a tiny bit of
# documentation just incase anyone finds this interesting and perhaps, develops an enterest in Computer Graphics.
import math, sys, time
rad5 = math.sqrt(5)
kCNNN = 0
kCPNN = 1
kCNPN = 2
kCPPN = 3
kCNNP = 4
kCPNP = 5
kCNPP = 6
kCPPP = 7
kpXNN = 8
kpXPN = 9
kpXNP = 10
kpXPP = 11
kpZNN = 12
kpZPN = 13
kpZNP = 14
kpZPP = 15
kpYNN = 16
kpYPN = 17
kpYNP = 18
kpYPP = 19
################################### Point/Vector
class Point:
def __init__(self, value):
self.data = [value[0], value[1], value[2]]
def __getitem__(self, index):
return self.data[index]
def __add__(self, other):
return Point([self.data[0]+other[0], self.data[1]+other[1], self.data[2]+other[2]])
def __radd__(self, other):
return Point([self.data[0]+other[0], self.data[1]+other[1], self.data[2]+other[2]])
def __sub__(self, other):
return Point([self.data[0]-other[0], self.data[1]-other[1], self.data[2]-other[2]])
def __mul__(self, other):
return Point([self.data[0]*other, self.data[1]*other, self.data[2]*other])
def __rmul__(self, other):
return Point([self.data[0]*other, self.data[1]*other, self.data[2]*other])
def Dot(self, other):
return self.data[0]*other[0] + self.data[1]*other[1] + self.data[2]*other[2]
def Length(self):
sqr = self.data[0] * self.data[0] + self.data[1] * self.data[1] + self.data[2] * self.data[2]
#len = math.sqrt(sqr)
# print 'len', len, sqr, self.Print()
return math.sqrt(sqr)
def Norm(self):
len1 = self.Length()
if len1 < 0.000000001:
return Point([0,0,0])
wonOver = 1.0/len1
return self * wonOver
def Dot(self, other):
return self.data[0]*other[0] + self.data[1] * other[1] + self.data[2]*other[2]
def Print(self):
print "Point : %g, %g, %g" % (self.data[0], self.data[1], self.data[2])
def Cross(v1, v2):
return Point([v1[1]*v2[2] - v1[2]*v2[1], v1[2]*v2[0] - v1[0]*v2[2], v1[0]*v2[1] - v1[1]*v2[0]])
#######################################################################################################
#
# We are creating polygonal surfaces made of Indexed Triangles.
# These consist of a list of vertices and a list of triangles specified by 3 indices.
# To render a trianlge, you use the indices to pull out three points from the list of vertices.
# Here are the vertices and indices. While Kurt Akely would use indexes and vertexes, I stick to English (Latin?)
TriangleVertices = []
TriangleIndices = []
def getVertexIndex(point):
roundedPoint = Point([round(point[0],3),round(point[1],3),round(point[2],3)])
for idx in range(len(TriangleVertices)):
delta = roundedPoint - TriangleVertices[idx]
if delta.Length() < 0.001:
return idx
TriangleVertices.append(roundedPoint)
return len(TriangleVertices) - 1
def addTriStrip(ptLongSt, ptLongEnd, ptShortSt, ptShortEnd, count, red, green, blue ):
pt = ptLongSt # the longer line
ptB = ptShortSt # one less here
diffLong = ptLongEnd - ptLongSt
deltaLong = diffLong * (1.0/count)
diffShort = ptShortEnd - ptShortSt
deltaShort = diffShort * (1.0/(count -1))
pIdx0 = getVertexIndex(pt)
pIdx1 = pIdx2 = 0
for idx in range(count-1):
# print idx, pt[0], pt[1], pt[2], pIdx0
pIdx1 = getVertexIndex(ptB)
# print idx, ptB[0], ptB[1], ptB[2], pIdx1
pt0 = pt # first point in longer line
pt += deltaLong
pIdx2 = getVertexIndex(pt)
#print '[ %d %d %d ]'%(pIdx0, pIdx1, pIdx2) # 0, 1, 2
TriangleIndices.append([pIdx0, pIdx1, pIdx2, red, green, blue])
ptB0 = ptB
ptB += deltaShort
pIdx0 = getVertexIndex(ptB)
#print '[ %d %d %d ]'%(pIdx2, pIdx1, pIdx0) # 2, 1, 3
TriangleIndices.append([pIdx2, pIdx1, pIdx0, red, green, blue])
pIdx0 = pIdx2
pIdx1 = getVertexIndex(ptB)
# print idx, ptB[0], ptB[1], ptB[2], pIdx1
pt0 = pt # first point in longer line
pt += deltaLong
pIdx2 = getVertexIndex(pt)
#print '[ %d %d %d ]'%(pIdx0, pIdx1, pIdx2) # 0, 1, 2
TriangleIndices.append([pIdx0, pIdx1, pIdx2, red, green, blue])
def addQuadStrip( ptLongSt, ptLongEnd, ptShortSt, ptShortEnd, count, red, green, blue ):
pt = ptLongSt # the longer line
ptB = ptShortSt # one less here
diffLong = ptLongEnd - ptLongSt
deltaLong = diffLong * (1.0/count)
diffShort = ptShortEnd - ptShortSt
deltaShort = diffShort * (1.0/(count))
pIdx0 = getVertexIndex(pt)
pIdx1 = pIdx2 = 0
for idx in range(count):
# print idx, pt[0], pt[1], pt[2], pIdx0
pIdx1 = getVertexIndex(ptB)
# print idx, ptB[0], ptB[1], ptB[2], pIdx1
pt0 = pt # first point in longer line
pt += deltaLong
pIdx2 = getVertexIndex(pt)
TriangleIndices.append([pIdx0, pIdx1, pIdx2, red, green, blue])
ptB0 = ptB
ptB += deltaShort
pIdx0 = getVertexIndex(ptB)
#print '[ %d %d %d ]'%(pIdx2, pIdx1, pIdx0) # 2, 1, 3
TriangleIndices.append([pIdx2, pIdx1, pIdx0, red, green, blue])
pIdx0 = pIdx2
def addTriAngle(point1, point2, point3, red, green, blue ):
pIdx0 = getVertexIndex(point1)
pIdx1 = getVertexIndex(point2)
pIdx2 = getVertexIndex(point3)
TriangleIndices.append([pIdx0, pIdx1, pIdx2, red, green, blue])
def addPentagon(indices, red, green, blue ):
ptMiddle = Point([0,0,0])
for vIdx in range(5):
ptMiddle = ptMiddle + TriangleVertices[indices[vIdx]]
ptMiddle = ptMiddle * (1.0/5.0)
pIdxMiddle = getVertexIndex(ptMiddle)
for vIdx in range(5):
idx2 = (vIdx + 1) % 5
TriangleIndices.append([ pIdxMiddle, indices[idx2], indices[vIdx], red, green, blue])
# make a connector
# give a pentagon, create points that are towards the middle of the pentagon AND towards the center of the dodec
def addPentagonStrips(center, segmentLength, indices, red, green, blue ):
ptMiddle = Point([0,0,0])
for vIdx in range(5):
ptMiddle = ptMiddle + TriangleVertices[indices[vIdx]]
ptMiddle = ptMiddle * (1.0/5.0)
#pIdxMiddle = getVertexIndex(ptMiddle)
ptsToMiddle = []
ptsToCenter = []
for vIdx in range(5):
ptToMid = TriangleVertices[indices[vIdx]] + (ptMiddle - TriangleVertices[indices[vIdx]]) * segmentLength
ptToCtr = TriangleVertices[indices[vIdx]] + (center - TriangleVertices[indices[vIdx]]) * segmentLength
ptsToMiddle.append(ptToMid)
ptsToCenter.append(ptToCtr)
count = int(round(1.0/segmentLength))
for vIdx in range(5):
idx2 = (vIdx + 1) % 5
addQuadStrip( TriangleVertices[indices[vIdx]], TriangleVertices[indices[idx2]], ptsToMiddle[vIdx], ptsToMiddle[idx2], count, red, green, blue )
addQuadStrip( ptsToMiddle[vIdx], ptsToMiddle[idx2], ptsToCenter[vIdx], ptsToCenter[idx2], count, red, green, blue )
# Create a triangular prism between 2 points.
# Using vectors in clumsy way, but, it works.
def AddTriangularPrism(pt1, pt2, scale, red, green, blue):
delta = pt2 - pt1
shrinkPt1 = pt1 + delta * 0.05
shrinkPt2 = pt2 - delta * 0.05
norm0 = delta.Norm()
testPt = Point([norm0[1], -norm0[0], norm0[2]])
dot = norm0.Dot(testPt)
# testPt is just a guess, now make a point that is perpendicular to that w.r.t. delta
cross1 = Cross(norm0,testPt)
norm1 = cross1.Norm()
# now do the same thing to get the other perpendicular
cross2 = Cross(norm0, norm1)
norm2 = cross2.Norm()
# now make a third pont that forms an approximate equilateral triangle, get the mid vector and flip it
diff = norm2 - norm1
mid = norm1 + diff * 0.5
# TODO: -0.73 is a guess. We have a 90-45-45 triangle. We want to add 15 each 45 to get an =lat tri.
other = -0.73 * mid # and scale it when we flip it by an empirical amount.
end1Pt0 = shrinkPt1 + scale * norm1
end1Pt1 = shrinkPt1 + scale * norm2
end1Pt2 = shrinkPt1 + scale * other
end2Pt0 = shrinkPt2 + scale * norm1
end2Pt1 = shrinkPt2 + scale * norm2
end2Pt2 = shrinkPt2 + scale * other
addQuadStrip( end1Pt0, end2Pt0, end1Pt1, end2Pt1, 6, red, green, blue)
addQuadStrip( end1Pt1, end2Pt1, end1Pt2, end2Pt2, 6, red, green, blue)
addQuadStrip( end1Pt2, end2Pt2, end1Pt0, end2Pt0, 6, red, green, blue)
# Write .obj - file header https://en.wikipedia.org/wiki/Wavefront_.obj_file
def writeObjHeader(file, nVerts, nTris, timestamp):
file.write('# OBJ File Generated by tedoGen.py - see jch.com/jch/art/platonic\n')
file.write('# Vertices: %d\n'%(nVerts))
file.write('# Faces: %d\n'%(nTris))
# Write .ply file header - https://en.wikipedia.org/wiki/PLY_(file_format)
def writePlyHeader(file, nVerts, nTris, timestamp):
file.write('ply\n')
file.write('format ascii 1.0\n')
file.write('comment platonic solid art jch.com/jch/art/platonic\n')
file.write('element vertex %d\n'%( nVerts))
file.write('property float x\n')
file.write('property float y\n')
file.write('property float z\n')
file.write('element face %d\n'%( nTris))
file.write('property list uchar int vertex_indices\n')
file.write('property uchar red\n')
file.write('property uchar green\n')
file.write('property uchar blue\n')
file.write('end_header\n')
# main ***************************************************************
######################################################################
# dodecahedron
#
# This formulates the points of the dodec as a cube [+/-1, +/-1, +/-1] and 12 more = 20 total
# [ +/-1, +/-1, +/-1] kCNNN = [-1,-1,-1] ... kCPNP = [ 1, -1, 1] ... kCPPP = [1,1,1]
# The coordinates of the 12 vertices of the cross-edges are:
# [ 0, +/-hVal, +/-1/hVal ] kpXNN = [ 0, -1.618, -0.618 ] - points on the X = 0 plane
# [ +/-hVal, +/-1/hVal, 0 ] kpZNN .. kpZPP - points on the Z = 0 plane
# [ +/-1/hVal, 0, +/-hVal ] kpYNN .. kpYPP - points on the Y = 0 plane
# When hVal = ( 1+ sqrt(5) ) / 2, which is the inverse of the golden ratio, the result is a regular dodecahedron
# sqrt(5) = 2.236 and hVal = 1.618
rad5 = math.sqrt(5)
rad3 = math.sqrt(3)
def main():
timestamp = time.strftime("%Y-%m-%dT%H:%M:%S", time.localtime(time.time()))
print sys.argv[0], 'running at ', timestamp
# original points
hVal = (rad5 + 1.0)/2.0
hInv = 1.0/hVal
dodecPoints = [ Point([-1,-1,-1]), Point([ 1,-1,-1]), Point([-1, 1,-1]), Point([ 1, 1,-1]),
Point([-1,-1, 1]), Point([ 1,-1, 1]), Point([-1, 1, 1]), Point([ 1, 1, 1]),
Point([0,-hVal,-hInv]), Point([0, hVal,-hInv]),
Point([0,-hVal, hInv]), Point([0, hVal, hInv]),
Point([-hVal,-hInv, 0]), Point([ hVal,-hInv, 0]),
Point([-hVal, hInv, 0]), Point([ hVal, hInv, 0]),
Point([-hInv, 0, -hVal]), Point([ hInv, 0,-hVal]),
Point([-hInv, 0, hVal]), Point([ hInv, 0, hVal]) ]
dodecOffset = Point([0,0,0]) # [-0.5,-0.24*rad3,0])
dodecScale = 5.0
dodecSegLength = 0.15
orgIndices = [ kCNNN, kCPNN, kCNPN, kCPPN, kCNNP, kCPNP, kCNPP, kCPPP, kpXNN, kpXPN, kpXNP, kpXPP, kpZNN, kpZPN, kpZNP, kpZPP, kpYNN, kpYPN, kpYNP, kpYPP ]
orgNames = [ 'kCNNN', 'kCPNN', 'kCNPN', 'kCPPN', 'kCNNP', 'kCPNP', 'kCNPP', 'kCPPP', 'kpXNN', 'kpXPN', 'kpXNP', 'kpXPP', 'kpZNN', 'kpZPN', 'kpZNP', 'kpZPP', 'kpYNN', 'kpYPN', 'kpYNP', 'kpYPP' ]
# print 'dbg', len(orgIndices), len(dodecPoints)
dodecVerts = []
#for pt in dodecPoints:
for idx in range(len(dodecPoints)):
dodecVerts.append(dodecScale*(dodecPoints[idx]+dodecOffset))
vIdx = getVertexIndex(dodecVerts[-1])
# print '%2d %s %8.3f %8.3f %8.3f'%(idx, orgNames[idx], dodecVerts[-1][0], dodecVerts[-1][1], dodecVerts[-1][2])
addPentagonStrips(dodecOffset, dodecSegLength, [kCPPP, kpZPP, kpZPN, kCPNP, kpYPP], 255, 127, 127) # F0
addPentagonStrips(dodecOffset, dodecSegLength, [kCPPP, kpYPP, kpYNP, kCNPP, kpXPP], 127, 127, 127) # F1
addPentagonStrips(dodecOffset, dodecSegLength, [kCPPP, kpXPP, kpXPN, kCPPN, kpZPP], 127, 127, 127) # F2
addPentagonStrips(dodecOffset, dodecSegLength, [kCPNN, kpZPN, kpZPP, kCPPN, kpYPN], 127, 127, 127) # F3
addPentagonStrips(dodecOffset, dodecSegLength, [kCPNN, kpXNN, kpXNP, kCPNP, kpZPN], 127, 127, 127) # F4
addPentagonStrips(dodecOffset, dodecSegLength, [kCNNP, kpYNP, kpYPP, kCPNP, kpXNP], 127, 127, 127) # F5
addPentagonStrips(dodecOffset, dodecSegLength, [kCNNP, kpZNN, kpZNP, kCNPP, kpYNP], 127, 127, 127) # F6
addPentagonStrips(dodecOffset, dodecSegLength, [kCNPN, kpXPN, kpXPP, kCNPP, kpZNP], 127, 127, 127) # F7
addPentagonStrips(dodecOffset, dodecSegLength, [kCNPN, kpYNN, kpYPN, kCPPN, kpXPN], 127, 127, 127) # F8
addPentagonStrips(dodecOffset, dodecSegLength, [kCPNN, kpYPN, kpYNN, kCNNN, kpXNN], 127, 127, 127) # F9
addPentagonStrips(dodecOffset, dodecSegLength, [kCNNP, kpXNP, kpXNN, kCNNN, kpZNN], 127, 127, 127) # F10
addPentagonStrips(dodecOffset, dodecSegLength, [kCNPN, kpZNP, kpZNN, kCNNN, kpYNN], 127, 255, 127) # F11
##################################################
# tetrahedron - written before dodec, so, code is a bit rougher!
#
tetraSteps = 8
tetraSelLength = 1.0/tetraSteps # 0.125
tetraScale = 12.0
# original points
tetraPoints = [ Point([0,0,0]), Point([1,0,0]), Point([.5,.5*rad3,0]), Point([0.5, (0.5/3.0)*rad3, math.sqrt(1.0-(0.25 + 0.25*0.25*3.0))])]
tetraOffset = Point([-0.5, -0.4,-0.1]) # move it to be inside the dodec
tetraVerts = []
for pt in tetraPoints:
tetraVerts.append(pt+tetraOffset)
# print 'tetra %8.3f %8.3f %8.3f'%(tetraScale*tetraVerts[-1][0], tetraScale*tetraVerts[-1][1], tetraScale*tetraVerts[-1][2])
pt01 = tetraVerts[0] + (tetraVerts[1] - tetraVerts[0]) * tetraSelLength
pt21 = tetraVerts[2] - (tetraVerts[2] - tetraVerts[1]) * tetraSelLength
tetraVerts.append(pt01)
tetraVerts.append(pt21)
#print 'pt01 %6.3f %6.3f pt21 %6.3f %6.3f'%(pt21[0], pt21[1], pt01[0], pt01[1]) # , pt.Norm().Print(), pt.Print()
pt02 = tetraVerts[0] + (tetraVerts[2] - tetraVerts[0]) * tetraSelLength
pt12 = tetraVerts[1] - (tetraVerts[1] - tetraVerts[2]) * tetraSelLength
tetraVerts.append(pt02)
tetraVerts.append(pt12)
pt20 = tetraVerts[0] + (tetraVerts[2] - tetraVerts[0]) * (1.0-tetraSelLength)
pt10 = tetraVerts[0] + (tetraVerts[1] - tetraVerts[0]) * (1.0-tetraSelLength)
tetraVerts.append(pt20)
tetraVerts.append(pt10)
# tristrips
# facet 0, 1, 2
innerPt021 = pt01 + (tetraVerts[2] - tetraVerts[0]) * tetraSelLength
innerPt120 = pt10 + (tetraVerts[2] - tetraVerts[1]) * tetraSelLength
innerPt201 = pt20 - (tetraVerts[2] - tetraVerts[1]) * tetraSelLength
addTriStrip( tetraScale*tetraVerts[0], tetraScale*tetraVerts[1], tetraScale*pt02, tetraScale*pt12, tetraSteps, 200, 200, 200)
addTriStrip( tetraScale*tetraVerts[2], tetraScale*pt02, tetraScale*pt21, tetraScale*innerPt021, tetraSteps-1, 200, 200, 200)
addTriStrip( tetraScale*pt12, tetraScale*pt21, tetraScale*innerPt120, tetraScale*innerPt201, tetraSteps-2, 200, 200, 200)
pt03 = tetraVerts[0] + (tetraVerts[3] - tetraVerts[0]) * tetraSelLength
pt23 = tetraVerts[2] - (tetraVerts[2] - tetraVerts[3]) * tetraSelLength
pt13 = tetraVerts[1] - (tetraVerts[1] - tetraVerts[3]) * tetraSelLength
pt31 = tetraVerts[3] - (tetraVerts[3] - tetraVerts[1]) * tetraSelLength
pt30 = tetraVerts[3] - (tetraVerts[3] - tetraVerts[0]) * tetraSelLength
# facet 0, 1, 3
innerPt031 = pt01 + (tetraVerts[3] - tetraVerts[0]) * tetraSelLength
innerPt130 = pt10 - (tetraVerts[1] - tetraVerts[3]) * tetraSelLength
innerPt301 = pt30 - (tetraVerts[3] - tetraVerts[1]) * tetraSelLength
addTriStrip( tetraScale*tetraVerts[1], tetraScale*tetraVerts[0], tetraScale*pt13, tetraScale*pt03, tetraSteps, 180, 180, 180)
addTriStrip( tetraScale*pt03, tetraScale*tetraVerts[3], tetraScale*innerPt031, tetraScale*pt31, tetraSteps-1, 180, 180, 180)
addTriStrip( tetraScale*pt31, tetraScale*pt13, tetraScale*innerPt301, tetraScale*innerPt130, tetraSteps-2, 180, 180, 180)
# facet 0, 2, 3
pt32 = tetraVerts[3] + ((tetraVerts[2] - tetraVerts[3]) * tetraSelLength)
innerPt032 = pt02 + (tetraVerts[3] - tetraVerts[0]) * tetraSelLength
innerPt230 = pt20 + (tetraVerts[3] - tetraVerts[2]) * tetraSelLength
innerPt320 = pt30 - (tetraVerts[3] - tetraVerts[2]) * tetraSelLength
addTriStrip( tetraScale*tetraVerts[0], tetraScale*tetraVerts[2], tetraScale*pt03, tetraScale*pt23, tetraSteps, 180, 180, 180)
addTriStrip( tetraScale*tetraVerts[3], tetraScale*pt03, tetraScale*pt32, tetraScale*innerPt032, tetraSteps-1, 180, 180, 180)
addTriStrip( tetraScale*pt23, tetraScale*pt32, tetraScale*innerPt230, tetraScale*innerPt320, tetraSteps-2, 180, 180, 180)
# facet 1, 2, 3
innerPt123 = pt12 + (tetraVerts[3] - tetraVerts[1]) * tetraSelLength
innerPt321 = pt32 - (tetraVerts[3] - tetraVerts[1]) * tetraSelLength
innerPt213 = pt21 + (tetraVerts[3] - tetraVerts[2]) * tetraSelLength
addTriStrip( tetraScale*tetraVerts[2], tetraScale*tetraVerts[1], tetraScale*pt23, tetraScale*pt13, tetraSteps, 180, 180, 180)
addTriStrip( tetraScale*pt13, tetraScale*tetraVerts[3], tetraScale*innerPt123, tetraScale*pt32, tetraSteps-1, 180, 180, 180)
addTriStrip( tetraScale*pt32, tetraScale*pt23, tetraScale*innerPt321, tetraScale*innerPt213, tetraSteps-2, 180, 180, 180)
# four triangles - red, green, blue and gray for Pts 0, 1, 2, 3
addTriAngle( tetraScale*innerPt213, tetraScale*innerPt230, tetraScale*innerPt201, 180, 180, 255)
addTriAngle( tetraScale*innerPt123, tetraScale*innerPt120, tetraScale*innerPt130, 180, 255, 180)
addTriAngle( tetraScale*innerPt032, tetraScale*innerPt031, tetraScale*innerPt021, 255, 180, 180)
addTriAngle( tetraScale*innerPt301, tetraScale*innerPt320, tetraScale*innerPt321, 180, 180, 180)
# six inner quad strips between the vertices, 3 from pt0: 0-1, 0-2, 0-3, .. 3-1, 3-2, .. 1-2
addQuadStrip( tetraScale*innerPt021, tetraScale*innerPt120, tetraScale*innerPt031, tetraScale*innerPt130, tetraSteps-3, 100, 255, 189)
addQuadStrip( tetraScale*innerPt032, tetraScale*innerPt230, tetraScale*innerPt021, tetraScale*innerPt201, tetraSteps-3, 127, 255, 189)
addQuadStrip( tetraScale*innerPt031, tetraScale*innerPt301, tetraScale*innerPt032, tetraScale*innerPt320, tetraSteps-3, 127, 255, 189)
addQuadStrip( tetraScale*innerPt301, tetraScale*innerPt130, tetraScale*innerPt321, tetraScale*innerPt123, tetraSteps-3, 127, 255, 189)
addQuadStrip( tetraScale*innerPt321, tetraScale*innerPt213, tetraScale*innerPt320, tetraScale*innerPt230, tetraSteps-3, 180, 180, 255)
addQuadStrip( tetraScale*innerPt120, tetraScale*innerPt201, tetraScale*innerPt123, tetraScale*innerPt213, tetraSteps-3, 180, 255, 180)
# OK, we need two prisms to connect the tetra and dodec - so that they stick together during the 3D printing
connectorWidth = 0.1
span1End1Pt0 = (innerPt320 + innerPt301 + innerPt321) * 0.33 * tetraScale
span1End2Pt0 = dodecVerts[kpYNP] + (dodecVerts[kpYPP] - dodecVerts[kpYNP]) * 0.45
span1End2Pt0 += (dodecOffset - span1End2Pt0) * 0.05
AddTriangularPrism( span1End1Pt0, span1End2Pt0, connectorWidth, 255, 0, 0)
span2End1Pt0 = (innerPt032 + innerPt031 + innerPt021) * 0.33 * tetraScale
span2End2Pt0 = dodecVerts[kpZNN] + (dodecVerts[kCNNN] - dodecVerts[kpZNN]) * 0.45
span2End2Pt0 += (dodecOffset - span2End2Pt0) * 0.05
AddTriangularPrism( span2End1Pt0, span2End2Pt0, connectorWidth, 255, 0, 0)
saveObj = True # Obj is taken by 3D print setup SW. PLY has facet colors, which is useful for debugging
if saveObj:
plyFile = 'tedo%s.obj'%(time.strftime("%m%d", time.localtime(time.time())))
savefile = open(plyFile, 'w')
writeObjHeader( savefile, len(TriangleVertices), len(TriangleIndices), timestamp)
vertexFormat = 'v %8.3f %8.3f %8.3f\n'
else:
plyFile = 'tedo%s.ply'%(time.strftime("%m%d", time.localtime(time.time())))
savefile = open(plyFile, 'w')
writePlyHeader( savefile, len(TriangleVertices), len(TriangleIndices), timestamp)
vertexFormat = '%8.3f %8.3f %8.3f\n'
for index in range(len(TriangleVertices)):
#print '%8.3f, %8.3f, %8.3f, '%(TriangleVertices[index][0], TriangleVertices[index][1], TriangleVertices[index][2])
savefile.write(vertexFormat%(TriangleVertices[index][0], TriangleVertices[index][1], TriangleVertices[index][2]))
for index in range(len(TriangleIndices)):
if saveObj:
savefile.write('f %d %d %d\n'%(TriangleIndices[index][0]+1, TriangleIndices[index][1]+1, TriangleIndices[index][2]+1))
else:
savefile.write('3 %d %d %d %d %d %d\n'%(TriangleIndices[index][0], TriangleIndices[index][1], TriangleIndices[index][2],
TriangleIndices[index][3], TriangleIndices[index][4], TriangleIndices[index][5]))
# note = "%s -jch"%( time.strftime(" %Y-%m-%d T %H:%M:%S", time.localtime(time.time())))
savefile.close()
print "Created %s with %d vertices and %d indexed triangles "%(plyFile, len(TriangleVertices), len(TriangleIndices))
if __name__ == '__main__':
main()
2018-07-27 jch