#!BPY

"""
Name: 'Blender Go Cubic'
Blender: 248
Group: 'Render'
Tooltip: 'Make Camera spin and render 6 cubic images!'
"""

__author__ = "Stefano <S68> Selleri, macouno"
__url__ = ("http://wiki.blender.org/index.php/Extensions:Py/Scripts/Manual/Render/Panorama_Maker_BGC")
__version__ = "0.8"
__bpydoc__ = """\

this script Renders 6 imagesfor use iin creating Cubic Panoramas.

These Images can then be re-used to create uv mapped Panoramic cubes.

The images can also be imported into other applications such as Flash Or Quicktime "GoCubic" Program,

for interactive 3d displays.

"""

#############################################################
#                                                           #
# Blender Go Cubic                !                         #
#                                                           #
# (C) February 2005 Stefano <S68> Selleri                   #
# Released under the Blender Artistic Licence (BAL)         #
# See www.blender.org                                       #
# Changed to GPL to replace disused (BAL)                                                          #
# Adapted by macouno for full functionality                 #
#                                                           #
#############################################################
# History                                                   #
# V: 0.0.0 - 13-11-04 - Macouno published his clever tut    #
#                       on Panoramas, but I'm too lazy to   #
#                       set cameras by hand!                #
#    0.0.2 - 13-11-04 - A GUI!                              #
#    0.0.3 - 27-02-05 - Made the renderer reset all changes #
#                       it renames the files after render   #
#    0.0.4 - 04-03-05 - Included cylindrical render         #
#                       And made it get correct path data   #
#    0.0.5 - 05-03-05 - New UI functionalities, partial     #
#                       cubic renderings, Multiple pano     #
#                       settings.                           #
#    0.0.6 - 28-04-05 - ObjectMode                          #
#    0.0.7 - 14-05-05 - Limits to Object Mode, bugs         #
#    0.0.8 - 19-03-07 - Ported to 2.43 API                  #
#############################################################
# ***** BEGIN GPL LICENSE BLOCK *****
#
# (C) February 2005 Stefano <S68> Selleri 
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, maximum  02111-1307, USA.
#
# ***** END GPL LICENCE BLOCK *****

import Blender
from Blender import *
import os
import re
import math

#############################################################
# GLOBALS                                                   #
#############################################################
VERSION         ='0.0.7'

EVENTHASH = {
    'NOEVENT': 1,
    'REDRAW': 2,
    'EXIT': 3,
    'DO_CUB': 4,
    'DO_CYL': 5,
    'DO_OBJ': 6,

    'CYL_STRONG': 101,
    'CYL_WEAK' : 102,

    'CUB_ALL': 201,
    'CUB_NONE': 202,
    }


#############################################################
# Populate the menu                                         #
#############################################################

## Get scene data
cur = Scene.GetCurrent()

## Get the current image render context
cntx = cur.getRenderingContext()

## Get the current frame
fra = cntx.currentFrame()

## Get the current file directory
base = Blender.Get('filename')
filename = base
dirx = Blender.sys.dirname(filename)

## Get the render path
pth1 = cntx.getRenderPath()

try:
    rel = re.search(r'\/\/',pth1).start()
except:
    rel = -1

if rel != -1:
    pthx = pth1[2:]    
    pthx = Blender.sys.join(dirx,pthx)
else:
    pthx = pth1

DATAHASH = {
    'SIZE': Draw.Create(500),
    'SIZEXC': Draw.Create(25),
    'SIZEXT': Draw.Create(1000),
    'SIZEYC': Draw.Create(500),
    'FRAME': Draw.Create(fra),
    'NAME': Draw.Create(pthx),
    'TYPE': Draw.Create(2),
    'PARTS': Draw.Create(40),

    'CF1': Draw.Create(1),
    'CF2': Draw.Create(1),
    'CF3': Draw.Create(1),
    'CF4': Draw.Create(1),
    'CF5': Draw.Create(1),
    'CF6': Draw.Create(1),

    'SIZEX': Draw.Create(320),
    'SIZEY': Draw.Create(200),
    'NTHETA': Draw.Create(5),
    'NPHI': Draw.Create(8),
    'TH0' : Draw.Create(0.0),
    'TH1' : Draw.Create(180.0),
    'PH0' : Draw.Create(0.0),
    'PH1' : Draw.Create(360.0),
    'R': Draw.Create(10.0)
}

#############################################################
# Store Settings For later re-use                           #
#############################################################
def GetSettings(cntx):

    State = {}
    cntx = cur.getRenderingContext()
    
    State['Rpath'] = cntx.getRenderPath()

    State['SizeX'] = cntx.imageSizeX()
    State['SizeY'] = cntx.imageSizeY()
    State['aspectRatioX'] = cntx.aspectRatioX()
    State['aspectRatioY'] = cntx.aspectRatioY()
    State['partsX'] = cntx.partsX()
    State['partsY'] = cntx.partsY()

    State['Frame'] = cntx.currentFrame()
    State['startFrame'] = cntx.startFrame()
    State['endFrame'] = cntx.endFrame()

    State['CameraObj'] = cur.getCurrentCamera()
    State['CameraIpo'] = State['CameraObj'].getIpo()
    State['CameraDat'] = State['CameraObj'].getData()
    State['CameraLen'] = State['CameraDat'].getLens()
    State['CameraEul'] = State['CameraObj'].getEuler()

    return (State)

#############################################################
# Restore Settings                                          #
#############################################################
def SetSettings(cntx,State):

    cntx.setRenderPath(State['Rpath'])

    cntx.imageSizeX(State['SizeX'])
    cntx.imageSizeY(State['SizeY'])
    cntx.aspectRatioX(State['aspectRatioX'])
    cntx.aspectRatioY(State['aspectRatioY'])
    cntx.partsX(State['partsX'])
    cntx.partsY(State['partsY'])

    cntx.currentFrame(State['Frame'])
    cntx.startFrame(State['startFrame'])
    cntx.endFrame(State['endFrame'])

    cur.setCurrentCamera(State['CameraObj'])
    State['CameraObj'].setEuler(State['CameraEul'])
    try:
        State['CameraObj'].setIpo(State['CameraIpo'])
    except:
        State['CameraObj'].clearIpo()
    State['CameraDat'].setLens(State['CameraLen'])

    return ()

#############################################################
# set image stuff                                           #
#############################################################
def SetImage(cntx):
    global DATAHASH

    if DATAHASH['TYPE'].val == 1:
        cntx.setImageType(Scene.Render.JPEG)
        ext = '.jpg'
    elif DATAHASH['TYPE'].val == 3:
        cntx.setImageType(Scene.Render.TARGA)
        ext = '.tga'
    else:
        cntx.setImageType(Scene.Render.PNG)
        ext= '.png'

    ## enable extensions
    cntx.enableExtensions(1)

    return (ext)

#############################################################
# SET FOR RENDER   Cylindrical                              #
#############################################################
def RenderCyl():

    ## Get the current image render context
    cntx = cur.getRenderingContext()

    ############################## GET THE CURRENT DATA
    Current = GetSettings(cntx)

    ############################## SET THE DATA
    ## Enable pano rendering
    cntx.enablePanorama(1)

    ## Set the nr of parts
    cntx.partsY(1)
    cntx.partsX(DATAHASH['PARTS'].val)

    ## Set image extension
    ext = SetImage(cntx)

    ## Set the image render size
    cntx.imageSizeX(DATAHASH['SIZEXT'].val)
    cntx.imageSizeY(DATAHASH['SIZEYC'].val)

    ## Set the aspect ratio
    cntx.aspectRatioX(1)
    cntx.aspectRatioY(1)

    ## Set the start and end frames
    cntx.startFrame(DATAHASH['FRAME'].val)
    cntx.endFrame(DATAHASH['FRAME'].val)

    ## Calculate lense setting
    setx = float(DATAHASH['SIZEXC'].val)
    sety = float(DATAHASH['SIZEYC'].val)
    setp = DATAHASH['PARTS'].val
    #if setx < sety:
    # New as for 2.42 Panoramic style
    lenz = 5.1 #(setx/sety) * (16/math.tan(math.pi/setp))
    #else:
    #    lenz = (16/math.tan(math.pi/setp))

    Current['CameraDat'].setLens(lenz)

    ## Set the Render path
    cntx.setRenderPath(DATAHASH['NAME'].val)

    ########################## RENDER
    print "Starting Rendering Process *CYLINDER*"
    cntx.renderAnim()
    #cntx.render()
    print "Rendered Cylindrical panorama"

    #############################################################
    # RESET AFTER RENDER                                        #
    #############################################################
    SetSettings(cntx,Current)
    cntx.enablePanorama(0)

#############################################################
# Perform single Render                                     #
#############################################################
def DoCubeFace (i,Ex,Ey,Ez,pthx,ext,frchk,Current):

    ## Set the render path
    pth2 = (pthx + str(i))
    cntx.setRenderPath(pth2)

    ## Set the camera angle
    Current['CameraObj'].setEuler([Ex,Ey,Ez])
    cntx.renderAnim()

    ## Rename the rendered file
    newname = (pth2 + ext);
    oldname = (pth2 + frchk);
    if os.path.isfile(newname):
        os.remove(newname)
    os.rename(oldname, newname)
    print "Renamed "+oldname+" to "+newname

#############################################################
# SET FOR RENDER   CUBE                                     #
#############################################################

def RenderCub():

    ## Get the current image render context
    cntx = cur.getRenderingContext()

    ############################## GET THE CURRENT DATA
    Current = GetSettings(cntx)

    ########################## SET THE DATA
    ## Clear the Ipo
    Current['CameraObj'].clearIpo()

    ## Set render path
    pthx = DATAHASH['NAME'].val

    ## Set image extension
    ext = SetImage(cntx)

    ## Set frame numbers for file correction
    frchk = `DATAHASH['FRAME'].val`
    frnmbr = DATAHASH['FRAME'].val

    if frnmbr < 10:
        frchk = ('000' + frchk)
    elif frnmbr < 100:
        frchk = ('00' + frchk)
    elif frnmbr < 1000:
        frchk = ('0' + frchk)

    ## disable extensions
    cntx.enableExtensions(0)

    ## Set the aspect ratio
    cntx.aspectRatioX(1)
    cntx.aspectRatioY(1)

    ## Set the image render size
    cntx.imageSizeX(DATAHASH['SIZE'].val)
    cntx.imageSizeY(DATAHASH['SIZE'].val)

    ## Set the lens to 16.0
    Current['CameraDat'].setLens(16.0)

    ## Set the start and end frames
    cntx.startFrame(DATAHASH['FRAME'].val)
    cntx.endFrame(DATAHASH['FRAME'].val)

    ########################## first anim
    print "Starting Rendering Process *CUBIC*"

    if (DATAHASH['CF1'].val==1):
        DoCubeFace (1,math.pi/2.0,0.0,0.0,pthx,ext,frchk,Current)

    if (DATAHASH['CF2'].val==1):
        DoCubeFace (2,math.pi/2.0,0.0,-math.pi/2.0,pthx,ext,frchk,Current)

    if (DATAHASH['CF3'].val==1):
        DoCubeFace (3,math.pi/2.0,0.0,-math.pi,pthx,ext,frchk,Current)

    if (DATAHASH['CF4'].val==1):
        DoCubeFace (4,math.pi/2.0,0.0,-3.0*math.pi/2.0,pthx,ext,frchk,Current)

    if (DATAHASH['CF5'].val==1):
        DoCubeFace (5,math.pi,0.0,0.0,pthx,ext,frchk,Current)

    if (DATAHASH['CF6'].val==1):
        DoCubeFace (6,0.0,0.0,0.0,pthx,ext,frchk,Current)

    print "Rendered Cubic Panorama"

    #############################################################
    # RESET AFTER RENDER                                        #
    #############################################################
    SetSettings(cntx,Current)
    cntx.enableExtensions(1)

#############################################################
# Perform single Render                                     #
#############################################################
def DoObjShot (i,j,M,pthx,ext,frchk,Current):

    ## Set the render path
    rr = str(i+1)
    if(len(rr)==1):
        rr = '0'+rr
    ss = str(j+1)
    if(len(ss)==1):
        ss = '0'+ss
        
    pth2 = (pthx + 'Row' + rr + 'shot' + ss)
    cntx.setRenderPath(pth2)

    ## Set the camera angle
    Current['CameraObj'].setMatrix(M)
    cntx.renderAnim()

    ## Rename the rendered file
    newname = (pth2 + ext);
    oldname = (pth2 + frchk);
    if os.path.isfile(newname):
        os.remove(newname)
    os.rename(oldname, newname)
    print "Created "+newname

#############################################################
# SET FOR RENDER  OBJ                                       #
#############################################################

def RenderObj():

    ## Get the current image render context
    cntx = cur.getRenderingContext()

    ############################## GET THE CURRENT DATA
    Current = GetSettings(cntx)

    ########################## SET THE DATA
    ## Clear the Ipo
    Current['CameraObj'].clearIpo()

    ## Set render path
    pthx = DATAHASH['NAME'].val

    ## Set image extension
    ext = SetImage(cntx)

    ## Set frame numbers for file correction
    frchk = `DATAHASH['FRAME'].val`
    frnmbr = DATAHASH['FRAME'].val

    if frnmbr < 10:
        frchk = ('000' + frchk)
    elif frnmbr < 100:
        frchk = ('00' + frchk)
    elif frnmbr < 1000:
        frchk = ('0' + frchk)

    ## disable extensions
    cntx.enableExtensions(0)

    ## Set the aspect ratio
    cntx.aspectRatioX(1)
    cntx.aspectRatioY(1)

    ## Set the image render size
    cntx.imageSizeX(DATAHASH['SIZEX'].val)
    cntx.imageSizeY(DATAHASH['SIZEY'].val)

    ## Leave lenses alone
    #Current['CameraDat'].setLens(16.0)
    Current['CameraObj'].setEuler([0.,0.,0.])

    ## Set the start and end frames
    cntx.startFrame(DATAHASH['FRAME'].val)
    cntx.endFrame(DATAHASH['FRAME'].val)

    ########################## first anim
    print "Starting Rendering Process *OBJ*"

    for i in range(DATAHASH['NTHETA'].val):
        for j in range(DATAHASH['NPHI'].val):
            th = math.pi*( (DATAHASH['TH1'].val - DATAHASH['TH0'].val)*i/(DATAHASH['NTHETA'].val-1) + DATAHASH['TH0'].val)/180.0
            ph = math.pi*( (DATAHASH['PH1'].val - DATAHASH['PH0'].val)*j/(DATAHASH['NPHI'].val) + DATAHASH['PH0'].val)/180.0
            print th,ph
            r  = DATAHASH['R'].val

            xx  = -math.sin(ph)
            yx  = math.cos(ph)
            zx  = 0.0 

            xy  = -math.cos(th)*math.cos(ph)
            yy  = -math.cos(th)*math.sin(ph)
            zy  = math.sin(th) 

            xz  = math.sin(th)*math.cos(ph)
            yz  = math.sin(th)*math.sin(ph)
            zz  = math.cos(th)

            M   = Mathutils.Matrix([xx, yx, zx, 0.0],
                                   [xy, yy, zy, 0.0],
                                   [xz, yz, zz, 0.0],
                                   [r*xz, r*yz, r*zz, 1.0])

            DoObjShot (i,j,M,pthx,ext,frchk,Current)
            
    print "Rendered Object Turntable"

    #############################################################
    # RESET AFTER RENDER                                        #
    #############################################################
    SetSettings(cntx,Current)
    cntx.enableExtensions(1)
#############################################################
# MAIN WINDOW                                               #
#############################################################
def MainWindow():
    global EVENTHASH,VERSION
    global DATA


    #############################################################
    # Backgrounds                                               #
    #############################################################

    BGL.glClearColor(0.5, 0.5, 0.5, 0.0)
    BGL.glClear(BGL.GL_COLOR_BUFFER_BIT)

    BGL.glColor3f(0, 0, 0) 			# Black
    BGL.glRectf(2, 2, 380, 480)

    BGL.glColor3f(1, 0.9, 0.7) 			# Light blue
    BGL.glRectf(3, 3, 379, 479)

    BGL.glColor3f(0.3, 0.27, 0.35) 		# Dark purple 0
    BGL.glRectf(4, 4, 378, 95)

    BGL.glColor3f(0, 0, 0) 			# Black 0
    BGL.glRectf(4, 96, 378, 130)

    BGL.glColor3f(0.3, 0.27, 0.35) 		# Dark purple 1
    BGL.glRectf(4, 131, 378, 201)

    BGL.glColor3f(0, 0, 0) 			# Black 1
    BGL.glRectf(4, 202, 378, 236)

    BGL.glColor3f(0.3, 0.27, 0.35) 		# Dark purple 2
    BGL.glRectf(4, 237, 378, 292)

    BGL.glColor3f(0, 0, 0) 			# Black 2
    BGL.glRectf(4, 293, 378, 327)

    BGL.glColor3f(0.3, 0.27, 0.35) 		# Dark purple 3
    BGL.glRectf(4, 328, 378, 378)

    BGL.glColor3f(0, 0, 0) 			# Black 3
    BGL.glRectf(4, 379, 378, 413)

    BGL.glColor3f(0.3, 0.27, 0.35) 		# Dark purple 3
    BGL.glRectf(4, 414, 378, 443)

    BGL.glColor3f(0, 0, 0) 			# Black 4
    BGL.glRectf(4, 444, 378, 478)

    #############################################################
    # Common Header                                             #
    #############################################################

    BGL.glColor3f(1, 0.9, 0.7)
    BGL.glRasterPos2d(15, 457)
    Draw.Text("BGC - Blender Go Cube v. "+VERSION)
    BGL.glRasterPos2d(15, 425)
    Draw.Text("(C) 2005 Stefano Selleri <a.k.a. S68> and macouno")
    Draw.Button("EXIT", EVENTHASH['EXIT'], 319, 449, 50, 24, "Exit BGC")
    Draw.Button("GO", EVENTHASH['DO_CYL'], 319, 298, 50, 25, "Render cylindrical panorama")
    Draw.Button("GO", EVENTHASH['DO_CUB'], 319, 208, 50, 25, "Render cubic panorama")
    Draw.Button("GO", EVENTHASH['DO_OBJ'], 319, 102, 50, 25, "Render Object Turntable")


    #############################################################
    # General Screen                                            #
    #############################################################
    BGL.glColor3f(1, 0.9, 0.7)
    BGL.glRasterPos2d(15, 392)
    Draw.Text("General settings: Set File Path First!")

    DATAHASH['FRAME'] =  Draw.Slider(
                        "Frame: ", EVENTHASH['NOEVENT'], 15, 354, 175, 18,
                        DATAHASH['FRAME'].val, 1, 18000, 0,
                        "The frame to render. Current frame is default")

    types = "Image type %t|JPG %x1|PNG %x2|Targa %x3"

    DATAHASH['TYPE'] = Draw.Menu(types, EVENTHASH['NOEVENT'],
                                 190, 354, 175, 18, DATAHASH['TYPE'].val,
                                 "The image type. Type will remain set after the script is closed.")

    DATAHASH['NAME'] =  Draw.String(
                        "Path: ", EVENTHASH['NOEVENT'], 15, 334, 350, 18,
                        DATAHASH['NAME'].val, 128,
                        "Complete path and an image name without extension")




    #############################################################
    # Cylindrical Screen                                        #
    #############################################################
    BGL.glColor3f(1, 0.9, 0.7)
    BGL.glRasterPos2d(15, 306)
    Draw.Text("Cylindrical panorama settings:")


    DATAHASH['PARTS'] = Draw.Slider(
                        "X Parts: ", EVENTHASH['CYL_STRONG'], 15, 265, 175, 18,
                        DATAHASH['PARTS'].val, 4, 512, 0,
                        "The nr of slices the cylinder will render in. The more slices the less distortion.")

    #DATAHASH['SIZEXC'] =Draw.Slider(
    #                    "Size Xp: ", EVENTHASH['CYL_STRONG'], 190, 265, 175, 18,
    #                    DATAHASH['SIZEXC'].val, 4, 10000, 0,
    #                    "The width of each X part. Will be multiplied by the nr of parts.")

    DATAHASH['SIZEXT'] =Draw.Slider(
                        "Size X: ", EVENTHASH['CYL_WEAK'], 15, 245, 175, 18,
                        DATAHASH['SIZEXT'].val, 4, 100000, 0,
                        "The width of the whole panorama.")

    DATAHASH['SIZEYC'] =Draw.Slider(
                        "Size Y: ", EVENTHASH['NOEVENT'], 190, 245, 175, 18,
                        DATAHASH['SIZEYC'].val, 4, 10000, 0,
                        "The total height of the cylindrical image")



    #############################################################
    # Cubic Screen                                              #
    #############################################################
    BGL.glColor3f(1, 0.9, 0.7)
    BGL.glRasterPos2d(15, 215)
    Draw.Text("Cubic panorama settings:")
    BGL.glColor3f(1, 1, 1)

    DATAHASH['SIZE'] =  Draw.Slider(
                        "Size: ", EVENTHASH['NOEVENT'], 15, 158, 240, 18,
                        DATAHASH['SIZE'].val, 4, 10000, 0,
                        "The image will be a square")

    ##### Toggles to switch on/off determined faces
    DATAHASH['CF1'] =   Draw.Toggle(
                        "1", EVENTHASH['NOEVENT'], 257, 158, 18, 18,
                        DATAHASH['CF1'].val,
                        "Render Face 1 (Front) Image ?")
    DATAHASH['CF2'] =   Draw.Toggle(
                        "2", EVENTHASH['NOEVENT'], 275, 158, 18, 18,
                        DATAHASH['CF2'].val,
                        "Render Face 2 (Right) Image ?")
    DATAHASH['CF3'] =   Draw.Toggle(
                        "3", EVENTHASH['NOEVENT'], 293, 158, 18, 18,
                        DATAHASH['CF3'].val,
                        "Render Face 3 (Back) Image ?")
    DATAHASH['CF4'] =   Draw.Toggle(
                        "4", EVENTHASH['NOEVENT'], 311, 158, 18, 18,
                        DATAHASH['CF4'].val,
                        "Render Face 4 (Left) Image ?")
    DATAHASH['CF5'] =   Draw.Toggle(
                        "5", EVENTHASH['NOEVENT'], 293, 176, 18, 18,
                        DATAHASH['CF5'].val,
                        "Render Face 5 (Top) Image ?")
    DATAHASH['CF6'] =   Draw.Toggle(
                        "6", EVENTHASH['NOEVENT'], 293, 140, 18, 18,
                        DATAHASH['CF6'].val,
                        "Render Face 6 (Bottom) Image ?")

    Draw.Button("All", EVENTHASH['CUB_ALL'], 257, 176, 36, 18,
                "Set all six renders to ON (Default)")

    Draw.Button("None", EVENTHASH['CUB_NONE'], 257, 140, 36, 18,
                "Set all six renders to OFF")

    #############################################################
    # Object Screen                                             #
    #############################################################
    BGL.glColor3f(1, 0.9, 0.7)
    BGL.glRasterPos2d(15, 109)
    Draw.Text("Object Turntable settings:")
    BGL.glColor3f(1, 1, 1)

    DATAHASH['SIZEX'] = Draw.Slider(
                        "Size X: ", EVENTHASH['NOEVENT'], 15, 70, 150, 18,
                        DATAHASH['SIZEX'].val, 4, 10000, 0,
                        "Image width")
    DATAHASH['SIZEY'] = Draw.Slider(
                        "Size Y: ", EVENTHASH['NOEVENT'], 165, 70, 150, 18,
                        DATAHASH['SIZEY'].val, 4, 10000, 0,
                        "Image height")

    DATAHASH['NTHETA']= Draw.Slider(
                        "N Th.: ", EVENTHASH['NOEVENT'], 15, 50, 150, 18,
                        DATAHASH['NTHETA'].val, 3, 19, 0,
                        "Total Images on a Theta scan")
    DATAHASH['TH0']= Draw.Number(
                        "Th0: ", EVENTHASH['REDRAW'], 165, 50, 100, 18,
                        DATAHASH['TH0'].val, 0, DATAHASH['TH1'].val, 
                        "Beginning Theta (0=top)")
    DATAHASH['TH1']= Draw.Number(
                        "Th1: ", EVENTHASH['REDRAW'], 265, 50, 100, 18,
                        DATAHASH['TH1'].val, DATAHASH['TH0'].val, 180, 
                        "Beginning Theta (180=bottom)")

    DATAHASH['NPHI']  = Draw.Slider(
                        "N Ph.: ", EVENTHASH['NOEVENT'], 15, 30, 150, 18,
                        DATAHASH['NPHI'].val, 4, 36, 0,
                        "Total Images on a Phi scan")
    DATAHASH['PH0']= Draw.Number(
                        "Ph0: ", EVENTHASH['REDRAW'], 165, 30, 100, 18,
                        DATAHASH['PH0'].val, 0, DATAHASH['PH1'].val, 
                        "Beginning Phi (0=front)")
    DATAHASH['PH1']= Draw.Number(
                        "Ph1: ", EVENTHASH['REDRAW'], 265, 30, 100, 18,
                        DATAHASH['PH1'].val, DATAHASH['PH0'].val, 360, 
                        "Beginning Phi (360=front again)")

    DATAHASH['R']     = Draw.Slider(
                        "Distance: ", EVENTHASH['NOEVENT'], 15, 10, 350, 18,
                        DATAHASH['R'].val, 1., 1000., 0,
                        "Camera distance from origin")

#############################################################
# Graphics                                                  #
#############################################################

def draw():
    MainWindow()

#############################################################
# Event Handler                                             #
#############################################################

def event(evt, val):
	if (evt== Draw.QKEY and not val):
		Draw.Exit()

def bevent(evt):
    global EVENTHASH,DATAHASH

    if   (evt == EVENTHASH['EXIT']):
        print 'EXIT'
        Draw.Exit()
    elif (evt == EVENTHASH['REDRAW']):
        Draw.Redraw()
    elif (evt == EVENTHASH['DO_CUB']):
        RenderCub()
        Draw.Redraw()
    elif (evt == EVENTHASH['DO_CYL']):
        RenderCyl()
        Draw.Redraw()
    elif (evt == EVENTHASH['DO_OBJ']):
        RenderObj()
        Draw.Redraw()
    #### Cylindrical UI Updates
    #elif (evt == EVENTHASH['CYL_STRONG']):
    #    ## PartsX or SizeX changed, we only need to update TotalSize X
    #    DATAHASH['SIZEXT'].val = DATAHASH['PARTS'].val*DATAHASH['SIZEXC'].val
    #    Draw.Redraw()
    #elif (evt == EVENTHASH['CYL_WEAK']):
    #    ## TotalSizeX changed, we need to update BOTH SizeX and TotalSize X
    #    DATAHASH['SIZEXC'].val = int(DATAHASH['SIZEXT'].val/DATAHASH['PARTS'].val)
    #    DATAHASH['SIZEXT'].val = DATAHASH['PARTS'].val*DATAHASH['SIZEXC'].val
    #    Draw.Redraw()
    #### Cubic UI Updates
    elif (evt == EVENTHASH['CUB_ALL']):
        DATAHASH['CF1'].val = 1
        DATAHASH['CF2'].val = 1
        DATAHASH['CF3'].val = 1
        DATAHASH['CF4'].val = 1
        DATAHASH['CF5'].val = 1
        DATAHASH['CF6'].val = 1
        Draw.Redraw()
    elif (evt == EVENTHASH['CUB_NONE']):
        DATAHASH['CF1'].val = 0
        DATAHASH['CF2'].val = 0
        DATAHASH['CF3'].val = 0
        DATAHASH['CF4'].val = 0
        DATAHASH['CF5'].val = 0
        DATAHASH['CF6'].val = 0
        Draw.Redraw()
        
#############################################################
# Registering all functions                                 #
#############################################################

Draw.Register(draw, event, bevent)