""" Author: Kirby Urner, 4D Solutions, Sept. 15 2005 Last modified: Sept. 17 2005 Version #: 1.3 Developed for a Saturday Academy class (PSU, Portland)[1] In a curriculum context, some parts are left unfinished, plus even this version is suggestive of possible enhancements, e.g. the Camera and Header APIs could be far more developed. [1] this is during a time after SA had moved to PSU from OGI """ class Vector(object): color = 'Red' def __init__(self, xyz): self.xyz = xyz def write(self): basic = "cylinder {<0,0,0>, <%s,%s,%s>, 0.1" % self.xyz return "%s %s" % (basic, "pigment { color %s } no_shadow }" % self.color) def __repr__(self): return "Vector <%s, %s, %s>" % self.xyz def __mul__(self, scalar): xyz = tuple([scalar * i for i in self.xyz]) return Vector(xyz) def __add__(self, other): xyz = tuple([ i+j for i,j in zip(self.xyz, other.xyz)]) return Vector(xyz) def _get_xyz(self): return '<%s, %s, %s>' % self.xyz def _get_length(self): return pow(sum([i**2 for i in xyz]), 0.5) coords = property(_get_xyz) length = property(_get_length) class Edge(object): color = 'Red' def __init__(self, v0, v1): self.v0 = v0 self.v1 = v1 def __repr__(self): return "Edge %s, %s" % (self.v0.coords, self.v1.coords) def write(self): basic = "cylinder {%s, %s, 0.1" % (self.v0.coords, self.v1.coords) return "%s %s" % (basic, "pigment { color %s } no_shadow }" % self.color) class Vertex(object): color = 'Red' radius = 0.1 def __init__(self, v0): self.v0 = v0 def __repr__(self): return "Sphere %s, radius = %s" % (self.v0.coords, self.radius) def write(self): basic = "sphere {%s, %s" % (self.v0.coords, self.radius) return "%s %s" % (basic, "pigment { color %s } no_shadow}" % self.color) class Polyhedron(object): color = 'Red' def __init__(self, vertsdict, faces): self.verts = vertsdict self.faces = faces self.edges = self.__get_edges() def __repr__(self): return "Polyhedron: %s vertices, %s edges, %s faces" \ % (len(self.verts), len(self.edges), len(self.faces)) def __get_edges(self): """ take a list of face-tuples and distill all the unique edges, e.g. ((1,2,3)) => ((1,2),(2,3),(3,1)) e.g. icosahedron has 20 faces and 30 unique edges ( = cubocta 24 + tetra's 6 edges to squares per jitterbug) """ uniqueset = set() for f in self.faces: edgetries = zip(f, f[1:]+ (f[0],)) for e in edgetries: e = tuple(sorted(e)) uniqueset.add(e) return tuple(uniqueset) def write(self, name): """ generate the edges and vertices in POV-Ray scene description language """ lines = ['#declare %s = union {' % name] for e in self.edges: edge = Edge( self.verts[e[0]], self.verts[e[1]] ) edge.color = self.color lines.append(edge.write()) for v in self.verts.values(): sphere = Vertex(v) sphere.color = self.color lines.append(sphere.write()) lines.append("}") lines.append("object {%s}\n" % name) return '\n'.join(lines) def __mul__(self, scalar): newvectors = {} for v in self.verts: newvectors[v] = self.verts[v] * scalar return Polyhedron(newvectors, self.faces) class Header (object): # defaults bgcolor = "color Gray50" lightloc0 = "<300, 300, -1000>" lightloc1 = "<-300, 300, -1000>" lightcolor = "White" @staticmethod def write(): return """ #include "colors.inc" background { %(bgcolor)s } light_source{ %(lightloc0)s %(lightcolor)s } light_source{ %(lightloc1)s %(lightcolor)s } """ % Header.__dict__ class Camera (object): # defaults look_at = 0 location = '<0, .1, -25>' @staticmethod def write(): return """ camera { location %(location)s look_at %(look_at)s } """ % Camera.__dict__ def buildicosa(): """ Build an icosahedron from three mutually perpendicular golden rectangles """ phi = (1 + pow(5,0.5))/2.0 verts = {# 2*phi x 2 rectangle in XZ 1:Vector((-phi, 0, -1)), 2:Vector((-phi, 0, 1)), 3:Vector(( phi, 0, 1)), 4:Vector(( phi, 0, -1)), # 2*phi x 2 rectange in XY 5:Vector(( -1, phi, 0)), 6:Vector(( 1, phi, 0)), 7:Vector(( 1, -phi, 0)), 8:Vector(( -1, -phi, 0)), # 2*phi x 2 rectange in YZ 9:Vector(( 0, 1, phi)), 10:Vector(( 0, -1, phi)), 11:Vector(( 0, -1, -phi)), 12:Vector(( 0, 1, -phi))} faces = ((5,6,9),(5,9,2),(5,2,1),(5,1,12),(5,12,6), (1,11,12),(11,12,4),(12,4,6),(4,6,3),(6,3,9), (3,9,10),(9,10,2),(10,2,8),(2,8,1),(8,1,11), (7,11,4),(7,4,3),(7,3,10),(7,10,8),(7,8,11)) return verts, faces def main(): icosa = Polyhedron(*buildicosa()) # overwriting defaults: # Halloweeny skin (or Princetonian as the case may be) icosa.color = 'Orange' Header.bgcolor = 'Black' Header.lightloc1 = '<0, 0, 0>' Camera.location = '<2, 4, -3>' f = file('icosa.pov','w') print >> f, Header.write() print >> f, Camera.write() print >> f, icosa.write("bigicosa") # shapes need a name # added a lush green interior icosa, why not? # smaller is edgewise 80% of original, # 0.512 original volume icosasmall = icosa * 0.8 icosasmall.color = 'Green' print >> f, icosasmall.write("smallicosa") f.close() # just to be neat & tidy if __name__ == '__main__': main()