#++++++
import bpy
import math
import mathutils

# ---Edit here
camera_cols = 5
Change_Deg = 30
camera_pitch = 0.5

startFrame = 1
FrameNum = 5
fileBaseName = "c:/blender_multi/blender_"
#fileBaseName = "/Users/Mercy_Yamada/blender_multi/blender_" #mac
# ---Edit here:end

# Object name
CamLabel = 'Camera'
CpLabel = 'ConvergenceTarget'
CamArrayLabel = 'CamArray'
CenterOfArrayLabel = 'Center_of_Array'
CpTMPLabel = 'CpTMP'
CenterOfCpLabel = 'CP_center'


camera_dim = 1  # 1 or 2
camera_rows = 1

cam_list = []
camobj_list = []

CenterOfArray = None
FrameNow = 0

CpDistance = None


def calc_inner_product(v1,v2):
    return( v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2] )


# from: http://www.sousakuba.com/Programming/gs_near_pos_on_line.html
def getCenteOfConvergencePoint():
    global CenterOfCPLabel
    global CamLabel
    global CpLabel
    global CpDistance
    #get unit vector of camera
    unit_vec = mathutils.Vector((0,0,-1))
    unit_vec.rotate(bpy.data.objects[CamLabel].matrix_world)
    
    camlocation = bpy.data.objects[CamLabel].location
    CPlocation = bpy.data.objects[CpLabel].location
    # Wow
    CPvec = CPlocation - camlocation
    CpDistance = calc_inner_product(unit_vec, CPvec)
    vec = camlocation + (unit_vec * CpDistance)
    
    bpy.ops.object.add(type='EMPTY',location=vec,rotation=(0, 0, 0))
    #print(bpy.context.scene.objects.active)
    bpy.context.scene.objects.active.name = CenterOfCpLabel



def make_Camera_Array(sp, Cdeg, Rdeg):    
    global CenterOfArray
    global CpDistance
    global CamLabel
    global cam_list
    global camobj_list
    
    print(sp,Cdeg)
    '''
    dx = CpDistance*math.cos(math.radians(deg))
    dy = CpDistance*math.sin(math.radians(deg))
    '''
    xp = -CpDistance
    yp = 0.0
    zp = 0.0
    
#    Rdeg = 90.0
    Crad = math.radians(Cdeg)
    Rrad = math.radians(Rdeg)


    dx = CpDistance*math.sin(Rrad)*math.cos(Crad) +xp
    dy = CpDistance*math.sin(Rrad)*math.sin(Crad) +yp
    dz = CpDistance*math.cos(Rrad) +zp
    '''
    dx = CpDistance*math.cos(Crad) +xp
    dy = CpDistance*math.sin(Crad) +yp
    dz = 0
    '''
    
    print(dx,dy)

    camobj = bpy.data.objects[CamLabel]
    cam_list.append(bpy.data.cameras[camobj.data.name].copy())
    camobj_list.append(bpy.data.objects.new(CamArrayLabel,cam_list[-1]))
#    camobj_list[-1].location = (dy,0,dx-CpDistance)
    camobj_list[-1].location = (dy,dz,dx)
    bpy.context.scene.objects.link(camobj_list[-1])
    camobj_list[-1].parent = CenterOfArray

    bpy.ops.object.select_all() # means deselect
    camobj_list[-1].select = True
    bpy.context.scene.objects.active = bpy.data.objects[CpTMPLabel]
    bpy.ops.object.track_set()




def make_Camera_Array0(sp, Rdeg):    
        global camera_cols
        global camera_rows

        if camera_cols%2 == 1:
           center = math.floor(camera_cols/2)
           for i in range(0,center):
               Cdeg = ((Change_Deg/2)/center) * (center - i)
               make_Camera_Array(sp, -Cdeg, Rdeg)
           make_Camera_Array(sp, 0, Rdeg)
           for i in range(center+1,camera_cols):
               Cdeg = (Change_Deg/(camera_cols-1)) * (i - center)
               make_Camera_Array(sp, Cdeg, Rdeg)
        else:
           center = math.floor(camera_cols/2)
           for i in range(0,center):
               Cdeg = (Change_Deg/(camera_cols-1))/2 * ((center - i)*2 -1)
               make_Camera_Array(sp, Cdeg, Rdeg)
               print("Cdeg:",Cdeg)
           for i in range(center,camera_cols):
               Cdeg = (Change_Deg/(camera_cols-1))/2 * ((center - i)*2 -1)
               make_Camera_Array(sp, Cdeg, Rdeg)
               print("Cdeg:",Cdeg)           





def Array_Init():
        global camera_cols
        global camera_rows
        if (camera_rows % 2):
        #    sp = -(math.floor((camera_cols/2)*camera_pitch) )
            sp = -((math.floor(camera_cols/2))*camera_pitch )
        else:
            sp = -((camera_cols/2)*camera_pitch)+ camera_pitch/2.0
        
        if (camera_dim == 1):
            sp_y = 0
            camera_rows = 1
        else:
            sp_y = sp
            camera_rows = camera_cols
        
        '''
        # Calc distance of MainCamera and ConvergencePoint
        MCx = bpy.data.objects[CamLabel].location[0]
        MCy = bpy.data.objects[CamLabel].location[1]
        MCz = bpy.data.objects[CamLabel].location[2]
        CPx = bpy.data.objects[CpLabel].location[0]
        CPy = bpy.data.objects[CpLabel].location[1]
        CPz = bpy.data.objects[CpLabel].location[2]
        global CpDistance
        CpDistance = math.sqrt(math.pow((MCx-CPx),2) + math.pow((MCy-CPy),2) + math.pow((MCz-CPz),2))
        '''
        
        getCenteOfConvergencePoint()
        global CpDistance
        print("CpDistance:",CpDistance)
        if (CpDistance <=0):
            bpy.ops.object.delete()
            return -1

        # make temporaly ConvergencePoint (to Z axis)
        bpy.ops.object.add(type='EMPTY',location=(0,0,-CpDistance),rotation=(0, 0, 0))
        bpy.context.scene.objects.active = bpy.data.objects['Empty']
        bpy.data.objects['Empty'].name = CpTMPLabel



        # Create an empty to Center and rename to 'CenterOfArray'
        bpy.ops.object.add(type='EMPTY',location=(0,0,0.),rotation=(0, 0, 0))
        bpy.context.scene.objects.active = bpy.data.objects['Empty']
        bpy.data.objects['Empty'].name = CenterOfArrayLabel
        global CenterOfArray
        CenterOfArray = bpy.context.object
        bpy.data.objects[CpTMPLabel].parent = bpy.data.objects[CenterOfArrayLabel]

        global Cam
        if camera_rows%2 == 1:
           center = math.floor(camera_rows/2)
           for i in range(0,center):
               Rdeg = ((Change_Deg/2)/center) * (center - i)
               make_Camera_Array0(sp, 90.0-Rdeg)
           make_Camera_Array0(sp, 90.0)
           for i in range(center+1,camera_rows):
               Rdeg = (Change_Deg/(camera_rows-1)) * (i - center)
               make_Camera_Array0(sp, 90.0+Rdeg)
        else:
           center = math.floor(camera_rows/2)
           for i in range(0,center):
               Rdeg = (Change_Deg/(camera_rows-1))/2 * ((center - i)*2 -1)
               make_Camera_Array0(sp, 90.0+Rdeg)
               print("Rdeg:",Rdeg)
           for i in range(center,camera_rows):
               Rdeg = (Change_Deg/(camera_rows-1))/2 * ((center - i)*2 -1)
               make_Camera_Array0(sp, 90.0+Rdeg)
               print("Rdeg:",Rdeg)           

        '''
        global Cam
        global CamLabel
        for y in range(0,camera_rows):
           for x in range(0,camera_cols):
#                cam_list.append(bpy.data.cameras.new('camtest'))
#                cam_list.append(bpy.data.cameras['Camera'].copy())
                cam_list.append(bpy.data.cameras[CamLabel].copy())
                camobj_list.append(bpy.data.objects.new('camtest_obj',cam_list[-1]))
                camobj_list[-1].location = (sp+x*camera_pitch,sp_y+y*camera_pitch,0)
                bpy.context.scene.objects.link(camobj_list[-1])
                camobj_list[-1].parent = CenterOfArray
        '''
        return 0



class MakeCameraArrayPlane(bpy.types.Operator):
    bl_idname = "object.camera_array_plane"
    bl_label = "Make plane Camera Array"
    
    def execute(self, context):
        global camera_dim
        camera_dim = 2
        ret = Array_Init()
        if ret == -1:
            print("!!! Set ",CpLabel," on front of Camera !!!")
        return {'FINISHED'}

class MakeCameraArrayVertical(bpy.types.Operator):
    bl_idname = "object.camera_array_vertical"
    bl_label = "Make Vertical Camera Array"
    
    def execute(self, context):
        global camera_dim
        camera_dim = 1
        ret = Array_Init()
        if ret == -1:
            print("!!! Set ",CpLabel," on front of Camera !!!")
        return {'FINISHED'}
    

class LocateCameraArray(bpy.types.Operator):
    bl_idname = "object.camera_array_locate"
    bl_label = "Camera Array locate on"
    
    def execute(self, context):
        global CenterOfArray
        CenterOfArray.parent = bpy.data.objects[CamLabel]
        
        bpy.ops.object.select_all() # means deselect
        bpy.context.scene.objects.active = bpy.data.objects[CamLabel]
        
        return {'FINISHED'}

class DeLocateCameraArray(bpy.types.Operator):
    bl_idname = "object.camera_array_delocate"
    bl_label = "Camera Array locate off"
    
    def execute(self, context):
        global CenterOfArray
        CenterOfArray.parent = None
        CenterOfArray.location = (0,0,0)

        return {'FINISHED'}


class RenderFrameOfCameraArray(bpy.types.Operator):
    bl_idname = "object.camera_array_renderframe"
    bl_label = "Render frame of Camera Array"
    
    def execute(self, context):
        bpy.context.area.type = 'VIEW_3D'  #<

        global camobj_list
        for y in range(0,camera_rows):
            for x in range(0,camera_cols):
                pt = camera_cols*y + x
                print("pt",pt)
#                filename = "z:\python" + str(pt).zfill(5) + ".png"
                filename = fileBaseName + str(FrameNow).zfill(5) + "_" + str(pt) + ".png"
#                filename = "c:/blender_multi/blender_" + str(FrameNow).zfill(5) + "_" + str(pt) + ".png"
                print(filename)
                camobj_list[pt].select = True
                bpy.context.scene.objects.active = camobj_list[pt]
                
#                bpy.context.area.type = 'VIEW_3D'  #<
                bpy.ops.view3d.object_as_camera()

#                bpy.context.area.type = 'TEXT_EDITOR'  #<
                bpy.ops.render.render()
                bpy.data.images['Render Result'].save_render(filepath = filename)
                
#                bpy.ops.object.delete()
        bpy.ops.object.camera_array_clear()
                
#        bpy.context.area.type = 'TEXT_EDITOR'
        return {'FINISHED'}

class ClearCameraArray(bpy.types.Operator):
    bl_idname = "object.camera_array_clear"
    bl_label = "test setting"
    
    global CamArrayLabel
    global CenterOfArrayLabel
    global CpTMPLabel
    global camobj_list
    global cam_list
    def execute(self, context):
        '''
        # clear list
        for y in range(0,camera_rows):
            for x in range(0,camera_cols):
                # cannot delete. why?
                #bpy.context.scene.objects.active = camobj_list[-1]
                ##bpy.ops.object.delete()
                camobj_list.pop()
        # bpy.context.scene.objects.active = CenterOfArray
        # bpy.ops.object.delete()
        '''

        # forece select only one
        bpy.data.objects[CenterOfCpLabel].select =True
        bpy.ops.object.select_all() # means deselect
        bpy.data.objects[CenterOfCpLabel].select =True
        
        for i in bpy.data.objects:
            n = i.name
#            print(i.name)
#            '''
            if n[:len(CamArrayLabel)] == CamArrayLabel:
                data = i.data
                bpy.data.objects[n].select = True
                bpy.ops.object.delete()
                bpy.data.cameras.remove(data)
                print("delete:",n)
#            '''
            if n[:len(CenterOfArrayLabel)] == CenterOfArrayLabel:
                bpy.data.objects[n].select = True
                bpy.ops.object.delete()
                print("delete:",n)
            if n[:len(CenterOfCpLabel)] == CenterOfCpLabel:
                bpy.data.objects[n].select = True
                bpy.ops.object.delete()
                print("delete:",n)
            if n[:len(CpTMPLabel)] == CpTMPLabel:
                bpy.data.objects[n].select = True
                bpy.ops.object.delete()
                print("delete:",n)

        # clear list
        for y in range(0,camera_rows):
            for x in range(0,camera_cols):
                # cannot delete. why?
                #bpy.context.scene.objects.active = camobj_list[-1]
                ##bpy.ops.object.delete()
                camobj_list.pop()
                cam_list.pop()

        return {'FINISHED'}


class Test_setting(bpy.types.Operator):
    bl_idname = "object.camera_array_testset"
    bl_label = "test setting"
    
    def execute(self, context):
        #bpy.ops.object.camera_array_clear()
        
        bpy.ops.mesh.primitive_cube_add()
        bpy.context.object.location = (0,0,0)
        
        bpy.data.objects[CamLabel].location = (5,5,5)
        bpy.data.objects[CamLabel].rotation_euler = mathutils.Euler((0.9344696998596191, -1.0045435374195222e-05, 2.34759259223938), 'XYZ')
        return {'FINISHED'}

class RenderAnimOfCameraArray_vertical(bpy.types.Operator):
    bl_idname = "object.camera_array_anim_vertical"
    bl_label = "render animation"
    
    def execute(self, context):
        global startFrame
        global FrameNum
        global FrameNow
        FrameNow = startFrame
        for frame in range(0, FrameNum):
            print("frame:",frame)
            bpy.ops.object.camera_array_vertical() #
            bpy.ops.object.camera_array_locate()
            bpy.ops.object.camera_array_renderframe()

            bpy.ops.screen.frame_offset(delta=1)
            FrameNow += 1

        return {'FINISHED'}

class RenderAnimOfCameraArray_plane(bpy.types.Operator):
    bl_idname = "object.camera_array_anim_plane"
    bl_label = "render animation"
    
    def execute(self, context):
        global startFrame
        global FrameNum
        global FrameNow
        FrameNow = startFrame
        for frame in range(0, FrameNum):
            print("frame:",frame)
            bpy.ops.object.camera_array_plane() #
            bpy.ops.object.camera_array_locate()
            bpy.ops.object.camera_array_renderframe()

            bpy.ops.screen.frame_offset(delta=1)
            FrameNow += 1

        return {'FINISHED'}

class PutConvergencePoint(bpy.types.Operator):
    bl_idname = "object.camera_array_putconvergencetarget"
    bl_label = "Put ConvergencePoint if not exist"
    
    def execute(self, context):
        global CpLabel
        global CamLabel

        for i in bpy.data.objects:
                n = i.name
                if n == CpLabel:
                    print("!!! ConvergencePoint is already exist !!!")
                    return {'FINISHED'}
                
        loc = bpy.data.objects[CamLabel].location          
        vec = mathutils.Vector((0,0,-3))
        vec.rotate(bpy.data.objects[CamLabel].matrix_world)
        pos = loc + vec
        
        bpy.ops.object.add(type='EMPTY',location=pos,rotation=(0, 0, 0))
        bpy.context.scene.objects.active.name = CpLabel

        return {'FINISHED'}


class SetCameraArrayVertical(bpy.types.Operator):
    bl_idname = "object.camera_array_set_vertical"
    bl_label = "render animation"
    
    def execute(self, context):
        bpy.ops.object.camera_array_vertical() #
        bpy.ops.object.camera_array_locate()
        return {'FINISHED'}

class SetCameraArrayPlane(bpy.types.Operator):
    bl_idname = "object.camera_array_set_plane"
    bl_label = "render animation"
    
    def execute(self, context):
        bpy.ops.object.camera_array_plane() #
        bpy.ops.object.camera_array_locate()
        return {'FINISHED'}

    
if __name__ == "__main__":    
    bpy.utils.register_class(MakeCameraArrayVertical)
    bpy.utils.register_class(MakeCameraArrayPlane)
    bpy.utils.register_class(LocateCameraArray)
    bpy.utils.register_class(DeLocateCameraArray)
    bpy.utils.register_class(RenderFrameOfCameraArray)
    bpy.utils.register_class(ClearCameraArray)
    bpy.utils.register_class(Test_setting)
    bpy.utils.register_class(RenderAnimOfCameraArray_vertical)    
    bpy.utils.register_class(RenderAnimOfCameraArray_plane)    
    bpy.utils.register_class(PutConvergencePoint)    
    bpy.utils.register_class(SetCameraArrayVertical)
    bpy.utils.register_class(SetCameraArrayPlane)
#++++++