"""
Kirby Urner
4D Solutions
First published: Apr 29 2007

Suitable for spatial geometry and/or synergetics students.

Update May 10:  I'd forgotten 8 of the 20 Icosahedron triangles!  Added.
Update May 13:  Added Octahedron, Mite, Coupler

"""

from stickworks import Vector, Edge
from visual import color
from math import sqrt

phi = (sqrt(5) + 1)/2.0
        
class Polyhedron (object):

    # defaults may be overridden

    showfaces      = True
    showedges      = True
    showvertices   = True
    # default POV-Ray textures

    face_texture   = 'T_Stone14'   # from stones.inc

    vertex_texture = 'T_Silver_1A' # from metals.inc

    edge_texture   = 'T_Copper_4A' # from metals.inc

    
    def scale(self, scalefactor):
        newverts = {}
        for v in self.vertices:
            newverts[v] = self.vertices[v] * scalefactor
        return self.__class__(newverts)

    __mul__ = __rmul__ = scale

    def translate(self, vector):
        newverts = {}
        for v in self.vertices:
            newverts[v] = self.vertices[v] + vector
        return self.__class__(newverts)

    __add__ = __radd__ = translate
    
    def _distill(self):

        edges = []
        unique = set()
        
        for f in self.faces:
            for pair in zip(f , f[1:] + (f[0],)):
                unique.add( tuple(sorted(pair)) )

        for edge in unique:
            edges.append( Edge(self.vertices[edge[0]],self.vertices[edge[1]]) )

        return edges            

    def draw(self):
        # VPython wireframe view, native to stickworks.py

        for e in self.edges:
            e.draw()
            

class Amodule (Polyhedron) :
    pass

class Bmodule (Polyhedron) :
    pass

class Mite (Polyhedron) :

    def __init__(self,
                 verts  = dict(j = Vector(( 0,  1, 0)),
                               o = Vector(( 0,  0, 0)),
                               r = Vector(( 1,  0, 1)),
                               s = Vector(( 1,  0,-1)))):
                 
        # 4 vertices

        self.vertices = verts

        # 4 faces

        self.faces = (('j','o','r'),('j','r','s'),('j','s','o'),('o','r','s'))

        self.edges = self._distill()
        
class Smite (Polyhedron) :
    pass

class Coupler (Polyhedron) :
    
    def __init__(self,
                 verts  = dict(j = Vector(( 0,  1, 0)),
                               l = Vector(( 0, -1, 0)),
                               q = Vector((-1,  0, 1)),
                               r = Vector(( 1,  0, 1)),
                               s = Vector(( 1,  0,-1)),
                               t = Vector((-1,  0,-1)))):

        # 6 vertices

        self.vertices = verts

        # 8 faces

        self.faces = (('j','q','r'),('j','r','s'),('j','s','t'),('j','t','q'),
                      ('l','q','r'),('l','r','s'),('l','s','t'),('l','t','q'))

        self.edges = self._distill()

class Tetrahedron (Polyhedron) :

    def __init__(self,
                 verts  = dict(a = Vector((-1, -1, 1)),
                               b = Vector((-1,  1, -1)),
                               c = Vector((1, 1, 1)),
                               d = Vector((1, -1, -1)))):
        """
        Imagine a cube centered at the origin and with
        a positive octant vertex at (1,1,1).  Inscribe
        a regular tetrahedron as six face diagonals therein.
        """
        # 4 vertices

        self.vertices = verts

        # 4 faces

        self.faces = (('a','b','c'),('a','c','d'),
                      ('a','d','b'),('b','d','c'))

        self.edges = self._distill()

class Cube (Polyhedron):

    def __init__(self, verts = dict( a = Vector((-1, -1, 1)),
                                     b = Vector((-1,  1, -1)),
                                     c = Vector((1, 1, 1)),
                                     d = Vector((1, -1, -1)),
                                     e = Vector((1,  1, -1)),
                                     f = Vector((1, -1,  1)),
                                     g = Vector((-1, -1, -1)),
                                     h = Vector((-1, 1, 1)))):

        # 8 vertices

        self.vertices = verts

        # 6 faces

        self.faces = (('a','f','c','h'),('h','c','e','b'),
                      ('b','e','d','g'),('g','d','f','a'),
                      ('c','f','d','e'),('a','h','b','g'))

        self.edges = self._distill()

class Octahedron (Polyhedron):

    def __init__(self, verts = dict( i = Vector(( 0, 0, 1)),
                                     j = Vector(( 0, 1, 0)),
                                     k = Vector(( 0, 0,-1)),
                                     l = Vector(( 0,-1, 0)),
                                     m = Vector(( 1, 0, 0)),
                                     n = Vector((-1, 0, 0)))):

        # 6 vertices

        self.vertices = verts

        # 8 faces

        self.faces = (('i','l','m'),('i','m','j'),('i','j','n'),('i','n','l'),                      
                      ('k','l','m'),('k','m','j'),('k','j','n'),('k','n','l'))

        self.edges = self._distill()
                                     
class Dodecahedron (Polyhedron):
    pass

class Icosahedron (Polyhedron):

    def __init__(self, verts = dict(
            # 12 vertices at the corners of 3 mutually

            # orthogonal golden rectangles

            xya=Vector(( phi/2, 0.5, 0.0)), # phi rectangle in xy

            xyb=Vector(( phi/2,-0.5, 0.0)),
            xyc=Vector((-phi/2,-0.5, 0.0)),
            xyd=Vector((-phi/2, 0.5, 0.0)),
            #-----------------------------

            xza=Vector((-0.5, 0.0, phi/2)), # Phi rectangle in xz 

            xzb=Vector(( 0.5, 0.0, phi/2)),
            xzc=Vector(( 0.5, 0.0,-phi/2)),
            xzd=Vector((-0.5, 0.0,-phi/2)),
            #-----------------------------

            yza=Vector(( 0.0, phi/2, 0.5)), # Phi rectangle in yz 

            yzb=Vector(( 0.0, phi/2,-0.5)),
            yzc=Vector(( 0.0,-phi/2,-0.5)),
            yzd=Vector(( 0.0,-phi/2, 0.5)),
            )):

        # 12 vertices

        self.vertices = verts

        # 20 equiangular triangles

        self.faces = (
            ('xza','xzb','yzd'),
            ('yzd','xzb','xyb'),
            ('xyb','xzb','xya'),
            ('xya','yza','xzb'),
            ('xzb','yza','xza'),

            ('xzd','xzc','yzb'),
            ('yzb','xzd','xyd'),
            ('xyd','xzd','xyc'),
            ('xyc','xzd','yzc'),
            ('yzc','xzd','xzc'),

            ('xyd','yzb','yza'),
            ('yza','yzb','xya'),
            ('xya','yzb','xzc'),
            ('xzc','xya','xyb'),
            ('xyb','xzc','yzc'),
            ('yzc','xyb','yzd'),
            ('yzd','yzc','xyc'),
            ('xyc','yzd','xza'),
            ('xza','xyc','xyd'),
            ('xyd','xza','yza')
            )
           

        self.edges = self._distill()
        
        self.rectangles = (
            ('xya','xyb','xyc','xyd'),
            ('xza','xzb','xzc','xzd'),
            ('yza','yzb','yzc','yzd'))    

    def goldrects(self):
        Edge.color = green
        for r in self.rectangles:
            c0,c1,c2,c3 = [self.vertices[i] for i in r]
            Edge(c0,c1).draw()
            Edge(c1,c2).draw()
            Edge(c2,c3).draw()
            Edge(c3,c0).draw()

class Cuboctahedron (Polyhedron):
    pass

def test():
    """
    The Concentric Hierarchy by R. Buckminster Fuller
    """
    Edge.color = color.orange
    tetra = Tetrahedron() * 0.5
    tetra.draw()
    
    Edge.color = color.green
    cube = Cube() * 0.5
    cube.draw()

    Edge.color = color.red
    cube = Octahedron()
    cube.draw()

    Edge.color = color.cyan
    ico = Icosahedron() * sqrt(2)
    ico.draw()

def test2():
    """
    Coupler in a Cube (canonical volumes 1 and 3 respectively)
    """

    Edge.color = color.orange
    tetra = Tetrahedron()
    tetra.draw()
    
    Edge.color = color.blue
    coupler = Mite()
    coupler.draw()

    #Edge.color = color.blue

    #coupler = Coupler()

    #coupler.draw()

    
    Edge.color = color.green
    cube = Cube()
    cube.draw()    

if __name__ == '__main__':
    test()
    # test2()

# code highlighted using py2html.py version 0.8