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