Check if the whole plane is being on a orthographic camera render (or get a proportion of the rendered plane) Situation: On my scene, planes can be positioned anywhere and I should not move them - I move around only camera. After positioning a orthographic camera with a script, I am not sure if the whole plane is being rendered - I want to render one whole plane at a time (also areas that don't belong to a plane are not desired on a render).

My questions:


Given a camera position and the plane in 3D space (where camera is orthogonally positioned to plane), how can I check if the whole surface of a plane is being rendered using orthographic camera? 
Is it possible to calculate the proportion of a plane's surface that's going go be visible on a render?


The reason I need this is because I cant figure out proper camera positioning function. What I did until now is I: find a center of mesh and calculate the plane's normal out of it, I position the orthographic camera on plane's normal some distance away. Now I don't know how to set the orthographic camera's rotation and scale to render only the plane.

Not finished camera positioning code:

import bpy
import bpy_extras
import mathutils
from mathutils import Vector

def get_global_plane_normal(plane):
    normal_local = plane.data.vertices[0].normal.to_4d()
    normal_local.w = 0
    normal_global = (plane.matrix_world * normal_local).to_3d()
    normal_global.normalize()   
    return normal_global

def main():
    scene = bpy.context.scene
    plane = scene.objects.active

    coords = [(plane.matrix_world * v.co) for v in plane.data.vertices]

    # Calculate the center of the mesh
    local_bbox_center = 0.125 * sum((Vector(b) for b in plane.bound_box), Vector())
    global_bbox_center = plane.matrix_world * local_bbox_center

    plane_normal = get_global_plane_normal(plane)

    # Set camera to distance from a plane
    distance = 1
    camera_position_vtx = global_bbox_center + distance * plane_normal

    camera = bpy.context.scene.objects["Camera"]
    camera.location = camera_position_vtx
    bpy.context.scene.objects.active = camera
    # TODO: Rotate and set orthographic scale to the right values,
    # only the plane is visible. 
main()


Thank you for your time!
camera_fit_coords

The method camera_fit_coords does most of what you need, it returns a location and zoom factor for an ortho camera such that it fits all given coordinates.  It does not however rotate the camera, so that will have to be calculated.

Test script, make a plane active, run script and the camera will move to fit the entirety of the plane into the scene camera.


Calculate the centre of geometry of plane using bound box ( local plane coords)
Grab the local normal of the first face of plane, I've used bmesh here, could use mesh API instead.  Have to use a face normal, a vert normal on a 1 face square plane will be at 45 degrees to x, y axes. 


Find the longest boundary edge to align to the x axis of the camera.  At this step could compare the render dimensions of camera and choose to align x or y, if y swap x and y x_axis, y_axis = y_axis, x_axis
The rotation part of the new global camera matrix is created from the three orthogonal unit vectors, the plane global normal being the -z axis, the x axis aligning with global vector created from longest boundary edge. The other being z_axis.cross(x_axis) Rotation difference could have also been used here.
Call the camera_fit_coords method with a flat list of all the global coordinates of the planes bounding box, use the return values for location and orthogonal zoom.



&nbsp;

import bpy
from mathutils import Vector, Matrix
import bmesh

context = bpy.context
scene = context.scene

cam_ob = scene.camera
plane = context.object
pmw = plane.matrix_world
plane_co = sum((Vector(b) for b in plane.bound_box), Vector()) / 8
me = plane.data
bm = bmesh.new()
bm.from_mesh(me)
bm.normal_update()
bm.faces.ensure_lookup_table()
# first face, could sum them I suppose
bm.faces[0].normal_update()
plane_no = bm.faces[0].normal
# longest edge

axis = sorted(
    (e for e in bm.edges if e.is_boundary), key=lambda e: e.calc_length(), reverse=True
)[0]

# global coords
z_axis = pmw @ (plane_no) - pmw @ Vector()

x_axis = pmw @ axis.verts[1].co - pmw @ axis.verts[0].co

y_axis = z_axis.cross(x_axis)
'''
#  would need to also check scale, hmm bmesh.from_object.. doh.
render = scene.render
if render.resolution_y &lt; render.resolution_x:
    x_axis, y_axis = y_axis, x_axis
'''    
R = Matrix([v.normalized() for v in (x_axis, y_axis, z_axis)]).transposed()
cam_ob.matrix_world = R.to_4x4()
cam_ob.matrix_world.translation = pmw @ plane_co

scene.update()
coords = [t for b in plane.bound_box for t in pmw @ Vector(b)]

v, scale = cam_ob.camera_fit_coords(context.depsgraph, coords)


cam_ob.data.ortho_scale = scale
cam_ob.matrix_world.translation = v
scene.update()



There are other questions on here regarding aligning objects with a face normal, or another vector.