# Would it be possible to improve camera_fit_coords in my zoom method with a generator?

I'm trying to improve my zoom_to method for moving the camera towards an object in the scene:

def zoom_to(scene, camera, object):

co_list = []
for point_co in [point[:] for point in object.bound_box]:
for co in point_co:
co_list.append(co)

loc_new_for_cam, foo = camera.camera_fit_coords(scene, co_list)

camera.location = loc_new_for_cam


However I'm not quite understanding how camera_fit_coords works and I'm wondering if a python generator would improve the code by not having the double for-cycle build a useless list.

I tried with:

def gen_co_list(obj):
for point_co in [point[:] for point in obj.bound_box]:
for co in point_co:
yield co

def zoom_to_g(scene, camera, obj):
loc_new_for_cam, foo = camera.camera_fit_coords(scene, gen_co_list(obj))
camera.location = loc_new_for_cam


But I get:

ValueError: Object.camera_fit_coords(): error with argument 2, "coordinates" - sequence expected at dimension 1, not 'generator'

Also, if there is a way of avoiding foo completely, this could be a one-liner even. It might not be as clear, though.

EDIT: found the true improvement thanks to @batFINGER

After using the tip from @batFINGER and some other minor modification (I was initially editing gen_co_list...), the method can become a still comprehensible one-liner:

 def zoom_to_g5(scene, camera, object):
camera.location, foo = camera.camera_fit_coords(scene, [co for corner in object.bound_box for co in corner])


I wanted a faster method, so I'm using list comprehension instead of a generator. There shouldn't be a memory problem since there are only ever 8 corners in the bounding box.

The method is ~25% faster, clocking in at:

>>>print(min(timeit.Timer('cam.location=0.0,0.0,7.0; zoom_to_g5(scn, cam, obj)', setup=setup).repeat(7, 1000)))
0.007111115999578033


OLD

It seems it was a mistake in my comprehension of generators, and the code should have been:

def gen_co_list(object):
for point_co in [point[:] for point in object.bound_box]:
for co in point_co:
yield co

def zoom_to_g(scene, camera, object):
loc_new_for_cam, foo = camera.camera_fit_coords(scene, [co for co in gen_co_list(object)])
camera.location = loc_new_for_cam


That said, trying to time the code yields different results than what I expected:

setup = """
import bpy
scn = bpy.context.scene
cam = bpy.data.objects['Camera']
obj = bpy.data.objects['Cube']
def zoom_to(scene, camera, object):
co_list = []
for point_co in [point[:] for point in object.bound_box]:
for co in point_co:
co_list.append(co)
loc_new_for_cam, foo = camera.camera_fit_coords(scene, co_list)
camera.location = loc_new_for_cam

def gen_co_list(object):
for point_co in [point[:] for point in object.bound_box]:
for co in point_co:
yield co

def zoom_to_g(scene, camera, object):
loc_new_for_cam, foo = camera.camera_fit_coords(scene, [co for co in gen_co_list(object)])
camera.location = loc_new_for_cam
"""

>>>print(min(timeit.Timer('cam.location=0.0,0.0,7.0; zoom_to(scn, cam, obj)', setup=setup).repeat(7, 1000)))
0.009414574999937031
>>>print(min(timeit.Timer('cam.location=0.0,0.0,7.0; zoom_to_g(scn, cam, obj)', setup=setup).repeat(7, 1000)))
0.00963822900030209


run on blender's classic cube.

I will therefore stick with the first zoom_to version, even though I thought the contrary would happen.

• Be tempted to return a generator return (axis for p in C.object.bound_box for axis in p) and avoid the list copy point[:] – batFINGER Jul 3 '18 at 16:03
• That's what I was looking for! After 3 more iterations of code improvement I have the better method, and it is ~25% faster than the original one! – Laboratorio Cobotica Jul 4 '18 at 12:39