#! /usr/bin/env python
# File: sgf2pov.py

##   Written by Ulrich Goertz (u@g0ertz.de)

##   sgf2pov is a program which takes a go board position or an SGF file and
##   creates source code which can be fed into the ray-tracing program
##   povray, to produce a high quality 3d image of the position.

import sys
from random import randint

epsilon = 0.0001

BOARDSIZE = 15.0
BOARDHEIGHT = 1.0

LINEWIDTH = 0.05

step = BOARDSIZE / 20
smallstep = 0.01

def placeWhiteStone(line_number, i, irreg = (0,0)):
    outfile.write('sphere { 0 %f \n' % (BOARDSIZE/40))
    outfile.write('  texture{ \n')
    if randint(0,2) == 0:
        outfile.write('    pigment { \n')
        outfile.write('      marble // gradient x\n')
        outfile.write('      color_map {\n')
        outfile.write('        [0.0 color White]\n')
        outfile.write('        [0.2 color White]\n')
        outfile.write('        [0.45 color Gray95]\n')
        outfile.write('        [0.55 color Gray95]\n')
        outfile.write('        [0.75 color White]\n')
        outfile.write('        [1.0 color White]\n')
        outfile.write('      } \n')
        outfile.write('      frequency %f \n' % (6.5 + .2 * randint(0,5)))
        outfile.write('      turbulence %f \n' % (.08 + .008 * randint(0,5)))
        outfile.write('    }\n')
        outfile.write('    finish { Shiny } // ambient .4} // specular .5 roughness .05 }\n')
    else:
        outfile.write('    pigment { \n')
        outfile.write('      marble // gradient x\n')
        outfile.write('      color_map {\n')
        outfile.write('        [0.0 color White]\n')
        outfile.write('        [0.2 color White]\n')
        outfile.write('        [0.45 color Gray85]\n')
        outfile.write('        [0.55 color Gray85]\n')
        outfile.write('        [0.75 color White]\n')
        outfile.write('        [1.0 color White]\n')
        outfile.write('      } \n')
        outfile.write('      frequency %f \n' % (6.5 + .2 * randint(0,5)))
        outfile.write('      turbulence %f \n' % (.08 + .008 * randint(0,5)))
        outfile.write('    }\n')
        outfile.write('    finish { Shiny } // specular .5 roughness .05 }\n')
    outfile.write('  }\n')
    outfile.write('  scale < 1,0.4,1>\n')
    outfile.write('  rotate %f * y \n' % randint(0,359))
    outfile.write('translate <%f, %f, %f > }\n' % ((line_number-9)*step + smallstep*irreg[0],
                                                   0.2*BOARDSIZE/20-0.05, (i-9)*step + smallstep*irreg[1]))

def placeBlackStone(line_number, i, irreg=(0,0)):
    outfile.write('sphere { 0 %f \n' % (BOARDSIZE/40))
    outfile.write('  texture{ pigment { Black } finish { specular .2 roughness .05 } } \n')
    outfile.write('  scale < 1,0.4,1> \n')
    outfile.write('  translate <%f, %f, %f > ' % ((line_number-9)*step + smallstep*irreg[0],
                                                     0.2*BOARDSIZE/20-0.05, (i-9)*step + smallstep*irreg[1]))
    outfile.write('}\n')


def putBoard():
    outfile.write('plane { y, -1 texture{ pigment{ White } finish{ Shiny } } }\n')

    outfile.write('box {\n')
    outfile.write('  < %f, %f, %f >, \n' % (-BOARDSIZE/2,-BOARDHEIGHT,-BOARDSIZE/2))
    outfile.write('  < %f, %f, %f >\n' % (BOARDSIZE/2, -10*epsilon, BOARDSIZE/2))
    outfile.write('  texture {\n')
    outfile.write('    T_Wood10\n')
    outfile.write('    scale 2\n')
    # outfile.write('    normal { dents 4 scale 4 }\n')
    outfile.write('  }\n')
    outfile.write('}\n')
    

    # draw lines on the board

    for i in range(19):
        outfile.write('box {\n')
        outfile.write('  <%f, %f, %f>,\n' % (-BOARDSIZE/2 + (i+1)*step - LINEWIDTH/2, -9*epsilon, -BOARDSIZE/2+step))
        outfile.write('  <%f, %f, %f>\n' % (-BOARDSIZE/2 + (i+1)*step + LINEWIDTH/2, -8*epsilon, +BOARDSIZE/2-step))
        outfile.write('  texture{ pigment{ Black } finish {specular .02 ambient 0 } }\n')
        outfile.write('}\n')
        
        outfile.write('box {\n')
        outfile.write('  <%f, %f, %f>,\n' % (-BOARDSIZE/2 + step, -7*epsilon, -BOARDSIZE/2+(i+1)*step - LINEWIDTH/2))
        outfile.write('  <%f, %f, %f>\n' % (+BOARDSIZE/2 - step, -6*epsilon, -BOARDSIZE/2+(i+1)*step + LINEWIDTH/2))
        outfile.write('  texture{ pigment{ Black } finish {specular .02 ambient 0 } }\n')
        outfile.write('}\n')

    # hoshi points

    for i in [4, 10, 16]:
        for j in [4, 10, 16]:
            outfile.write('cylinder { < %f, %f, %f >, <%f, %f, %f >, %f \n' % \
                          (-BOARDSIZE/2+(i)*step, 5*epsilon, -BOARDSIZE/2+(j)*step,
                           -BOARDSIZE/2+(i)*step, 4*epsilon, -BOARDSIZE/2+(j)*step,
                           2*LINEWIDTH))
            outfile.write('  texture{ pigment{ Black } finish {specular .02 ambient 0 } }\n')
            outfile.write('}\n')


def drawPosition(pos, irreg = None):
    if irreg:
        for line_number in range(19):
            for i in range(19):
                if pos[(line_number,i)] == 'O':
                    placeWhiteStone(line_number, i, irreg[(line_number,i)])
                elif pos[(line_number,i)] == 'X':
                    placeBlackStone(line_number, i, irreg[(line_number,i)])
        
    else:
        for line_number in range(19):
            for i in range(19):
                if pos[(line_number,i)] == 'O':
                    placeWhiteStone(line_number, i)
                elif pos[(line_number,i)] == 'X':
                    placeBlackStone(line_number, i)



def getIrreg(pos):
    irreg = {}
    
    for i in range(19):
        for j in range(19):
            irreg[(i,j)] = [0,0]
            
    for i in range(19):

        foundBeginning = -1
        
        for j in range(19):
            if pos[(i,j)] == '.':
                if foundBeginning == -1: continue

                # string completed

                if j-foundBeginning > 15:
                    irreg[(i,foundBeginning)][1] = randint(-2,0)
                elif j-foundBeginning > 8:
                    irreg[(i,foundBeginning)][1] = randint(-2,1)
                else:
                    irreg[(i,foundBeginning)][1] = randint(-2,2)
                    
                for k in range(foundBeginning+1, j):

                    if j - k > 15:
                        limit = 0
                    elif j - k > 8:
                        limit = 1
                    else:
                        limit = 2
                    
                    irreg[(i,k)][1] = irreg[(i,k-1)][1] + randint(0, limit-irreg[(i,k-1)][1])

                foundBeginning = -1

            elif pos[(i,j)] in ['X', 'O'] and foundBeginning == -1:
                foundBeginning = j

    for j in range(19):

        foundBeginning = -1
        
        for i in range(18,-1):
            if pos[(i,j)] == '.':
                if foundBeginning == -1: continue

                # string completed

                if i-foundBeginning > 15:
                    irreg[(foundBeginning,j)][1] = randint(-2,0)
                elif i-foundBeginning > 8:
                    irreg[(foundBeginning,j)][1] = randint(-2,1)
                else:
                    irreg[(foundBeginning,j)][1] = randint(-2,2)
                    
                for k in range(foundBeginning+1, i):

                    if i - k > 15:
                        limit = 0
                    elif i - k > 8:
                        limit = 1
                    else:
                        limit = 2
                    
                    irreg[(k,j)][1] = irreg[(k-1,j)][1] + randint(0, limit-irreg[(k-1,j)][1])

                foundBeginning = -1

            elif pos[(i,j)] in ['X', 'O'] and foundBeginning == -1:
                foundBeginning = i


    return irreg
        

def initPOV():
    outfile.write('#include "shapes.inc"\n')
    outfile.write('#include "colors.inc"\n')
    outfile.write('#include "textures.inc"\n')
    outfile.write('#include "woods.inc"\n')
    outfile.write('#include "stones.inc"\n')


def putCamera():
    outfile.write('camera {\n')
    outfile.write('  location  <0.5, 12.0, -12>\n')
    # outfile.write('  location  <0,.3,0>\n')
    outfile.write('  up        <0.0, 1.0,  0.0>\n')
    outfile.write('  right     <4/3, 0.0,  0.0>\n')
    outfile.write('  look_at   <.0, .0,  .0>\n')
    outfile.write('}\n')


def putLightSource():
    # outfile.write('light_source { <-1,2,0> color White }\n')
    outfile.write('light_source { <5, 15, 0> color White }\n')

if len(sys.argv) < 2:
    print 'Usage: sgf2pov.py FILENAME'
    print 'This will read a position in SL format from FILENAME,'
    print 'and write a file which can be processed by Povray to'
    print 'FILENAME.pov.'
    sys.exit()

outfile = open(sys.argv[1]+'.pov', 'w')

initPOV()
putCamera()
putLightSource()

putBoard()

try:
    posfile = open(sys.argv[1])
    lines = posfile.readlines()
    posfile.close()
except:
    print 'Unable to open file', sys.argv[1], '.'
    sys.exit()

pos = {}
line_number = 0
for line in lines:
    if not line.startswith('$$ | '): continue
    l = line[5:].split(' ')
    for i in range(19):
        pos[(line_number,i)] = l[i]
    line_number += 1

irreg = getIrreg(pos)
drawPosition(pos, irreg)

outfile.close()


