237 lines
7.4 KiB
Python
237 lines
7.4 KiB
Python
|
import bpy
|
||
|
from mathutils import Vector
|
||
|
|
||
|
class Rect:
|
||
|
def __init__(self, x, y, width, height):
|
||
|
self.x = x
|
||
|
self.y = y
|
||
|
self.width = width
|
||
|
self.height = height
|
||
|
|
||
|
@property
|
||
|
def top_left(self):
|
||
|
return Vector((self.x, self.y))
|
||
|
|
||
|
@property
|
||
|
def bottom_left(self):
|
||
|
return Vector((self.x, self.y - self.height))
|
||
|
|
||
|
@property
|
||
|
def bottom_right(self):
|
||
|
return Vector((self.x + self.width, self.y - self.height))
|
||
|
|
||
|
@property
|
||
|
def top_right(self):
|
||
|
return Vector((self.x + self.width, self.y))
|
||
|
|
||
|
def __str__(self):
|
||
|
return f'Rect(x={self.x}, y={self.y}, width={self.width}, height={self.height})'
|
||
|
|
||
|
class View3D:
|
||
|
def __init__(self, area=None):
|
||
|
if area is None:
|
||
|
area = bpy.context.area
|
||
|
if area.type != 'VIEW_3D':
|
||
|
area = next((area for area in bpy.context.screen.areas if area.type == 'VIEW_3D'), None)
|
||
|
self.area = area
|
||
|
|
||
|
@property
|
||
|
def sidebar(self):
|
||
|
return self.area.regions[3]
|
||
|
|
||
|
@property
|
||
|
def toolbar(self):
|
||
|
return self.area.regions[2]
|
||
|
|
||
|
@property
|
||
|
def tool_header(self):
|
||
|
return self.area.regions[1]
|
||
|
|
||
|
@property
|
||
|
def header(self):
|
||
|
return self.area.regions[0]
|
||
|
|
||
|
@property
|
||
|
def space(self):
|
||
|
return self.area.spaces.active
|
||
|
|
||
|
@property
|
||
|
def region(self):
|
||
|
return self.area.regions[5]
|
||
|
|
||
|
@property
|
||
|
def region_3d(self):
|
||
|
return self.space.region_3d
|
||
|
|
||
|
@property
|
||
|
def x(self):
|
||
|
return 0
|
||
|
|
||
|
@property
|
||
|
def y(self):
|
||
|
return 0
|
||
|
|
||
|
@property
|
||
|
def width(self):
|
||
|
return self.region.width
|
||
|
|
||
|
@property
|
||
|
def height(self):
|
||
|
return self.region.height
|
||
|
|
||
|
@property
|
||
|
def rect(self):
|
||
|
return Rect(self.x, self.y, self.width, self.height)
|
||
|
|
||
|
@property
|
||
|
def reduced_rect(self):
|
||
|
w, h = self.region.width, self.region.height
|
||
|
if not bpy.context.preferences.system.use_region_overlap:
|
||
|
return self.rect
|
||
|
|
||
|
## Minus tool leftbar + sidebar right
|
||
|
# top_margin = bottom_margin = 0
|
||
|
# if self.tool_header.alignment == 'TOP':
|
||
|
# top_margin += self.tool_header.height
|
||
|
# else:
|
||
|
# bottom_margin += self.tool_header.height
|
||
|
|
||
|
## Set corner values
|
||
|
# top_left = (self.toolbar.width, h - top_margin - 1)
|
||
|
# top_right = (w - self.sidebar.width, h - top_margin - 1)
|
||
|
# bottom_right = (w - self.sidebar.width, bottom_margin + 2)
|
||
|
# bottom_left = (self.toolbar.width, bottom_margin + 2)
|
||
|
|
||
|
reduced_y = 0
|
||
|
if self.tool_header.alignment == 'TOP':
|
||
|
reduced_y = self.tool_header.height
|
||
|
|
||
|
reduced_width = w - self.sidebar.width - self.toolbar.width
|
||
|
reduced_height = h - self.tool_header.height - 1
|
||
|
|
||
|
return Rect(self.toolbar.width, h - reduced_y - 1, reduced_width, reduced_height)
|
||
|
# return Rect(self.toolbar.width, h - reduced_y - 1, right_down, left_down)
|
||
|
|
||
|
def to_2d(self, coord):
|
||
|
from bpy_extras.view3d_utils import location_3d_to_region_2d
|
||
|
return location_3d_to_region_2d(self.region, self.region_3d, coord)
|
||
|
|
||
|
def to_3d(self, coord, depth_coord=None):
|
||
|
from bpy_extras.view3d_utils import region_2d_to_location_3d
|
||
|
if depth_coord is None:
|
||
|
depth_coord = bpy.context.scene.cursor.location
|
||
|
return region_2d_to_location_3d(self.region, self.region_3d, coord, depth_coord)
|
||
|
|
||
|
|
||
|
def get_camera_frame_3d(self, scene=None, camera=None):
|
||
|
if scene is None:
|
||
|
scene = bpy.context.scene
|
||
|
if camera is None:
|
||
|
camera = scene.camera
|
||
|
|
||
|
frame = camera.data.view_frame()
|
||
|
mat = camera.matrix_world
|
||
|
|
||
|
return [mat @ v for v in frame]
|
||
|
|
||
|
def get_camera_frame_2d(self, scene=None, camera=None):
|
||
|
'''View frame Top_right-CW'''
|
||
|
if scene is None:
|
||
|
scene = bpy.context.scene
|
||
|
frame_3d = self.get_camera_frame_3d(scene=scene, camera=camera)
|
||
|
|
||
|
frame_2d = [self.to_2d(v) for v in frame_3d]
|
||
|
|
||
|
rd = scene.render
|
||
|
resolution_x = rd.resolution_x * rd.pixel_aspect_x
|
||
|
resolution_y = rd.resolution_y * rd.pixel_aspect_y
|
||
|
ratio_x = min(resolution_x / resolution_y, 1.0)
|
||
|
ratio_y = min(resolution_y / resolution_x, 1.0)
|
||
|
|
||
|
## Top right - CW
|
||
|
|
||
|
frame_width = (frame_2d[1].x - frame_2d[2].x) # same size (square)
|
||
|
frame_height = (frame_2d[0].y - frame_2d[1].y) # same size (square)
|
||
|
|
||
|
cam_width = (frame_2d[1].x - frame_2d[2].x) * ratio_x
|
||
|
cam_height = (frame_2d[0].y - frame_2d[1].y) * ratio_y
|
||
|
|
||
|
cam_x = frame_2d[3].x - ((frame_width - cam_width) / 2)
|
||
|
cam_y = frame_2d[3].y - ((frame_height - cam_height) / 2)
|
||
|
|
||
|
return Rect(cam_x, cam_y, cam_width, cam_height)
|
||
|
|
||
|
|
||
|
def zoom_from_fac(self, zoomfac):
|
||
|
from math import sqrt
|
||
|
return (sqrt(4 * zoomfac) - sqrt(2)) * 50.0
|
||
|
|
||
|
def fit_camera_view(self):
|
||
|
## CENTER
|
||
|
self.region_3d.view_camera_offset = (0,0)
|
||
|
|
||
|
## ZOOM
|
||
|
|
||
|
# rect = self.reduced_rect
|
||
|
rect = self.rect
|
||
|
cam_frame = self.get_camera_frame_2d()
|
||
|
# print('width: ', rect.width)
|
||
|
# print('height: ', rect.height)
|
||
|
|
||
|
|
||
|
# xfac = rect.width / (cam_frame.width + 4)
|
||
|
# yfac = rect.height / (cam_frame.height + 4)
|
||
|
# # xfac = rect.width / (rect.width - 4)
|
||
|
# # yfac = rect.height / (rect.height - 4)
|
||
|
|
||
|
scene = bpy.context.scene
|
||
|
rd = scene.render
|
||
|
# resolution_x = rd.resolution_x * rd.pixel_aspect_x
|
||
|
# resolution_y = rd.resolution_y * rd.pixel_aspect_y
|
||
|
# xfac = min(resolution_x / resolution_y, 1.0)
|
||
|
# yfac = min(resolution_y / resolution_x, 1.0)
|
||
|
|
||
|
# xfac = rect.width / (cam_frame.width * rect.width + 4)
|
||
|
# yfac = rect.height / (cam_frame.height * rect.height + 4)
|
||
|
|
||
|
# xfac = rect.width / ((rect.width - cam_frame.width) * 2 + 4)
|
||
|
# yfac = rect.height / (rect.height - cam_frame.height + 4)
|
||
|
|
||
|
# xfac = rect.width / ((rect.width - cam_frame.width * rd.resolution_x) * 2 + 4)
|
||
|
# xfac = rect.width / ((rect.width / cam_frame.width) * rd.resolution_x)
|
||
|
# xfac = rect.width / rd.resolution_x * 2
|
||
|
# xfac = rect.width / ((cam_frame.width / rect.width) * rd.resolution_x)
|
||
|
# xfac = self.region.width / (rect.width - cam_frame.width)
|
||
|
# xfac = self.region.width / (rect.width - cam_frame.width)
|
||
|
# xfac = ((cam_frame.width - rect.width) / rd.resolution_x) * self.region.width
|
||
|
|
||
|
xfac = rect.width / cam_frame.width
|
||
|
# xfac = 0.8
|
||
|
|
||
|
# xfac = yfac = 0.8
|
||
|
# xfac = yfac = 1.0
|
||
|
# print('xfac: ', xfac) # Dbg
|
||
|
# print('yfac: ', yfac) # Dbg
|
||
|
|
||
|
|
||
|
# fac = min([xfac, yfac]) # Dbg
|
||
|
fac = xfac
|
||
|
# fac = rd.resolution_x / self.width
|
||
|
|
||
|
print('fac: ', fac)
|
||
|
print('zoom before', self.region_3d.view_camera_zoom) # Dbg
|
||
|
self.region_3d.view_camera_zoom = self.zoom_from_fac(fac)
|
||
|
print('zoom after', self.region_3d.view_camera_zoom) # Dbg
|
||
|
|
||
|
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
## construct view 3d class
|
||
|
view3d = View3D()
|
||
|
print(view3d.rect)
|
||
|
print(view3d.rect.bottom_left)
|
||
|
print(view3d.get_camera_frame_3d())
|
||
|
## construct
|
||
|
rect_frame = view3d.get_camera_frame_2d()
|
||
|
view3d.reduced_rect.bottom_left
|
||
|
bpy.context.scene.cursor.location = view3d.to_3d(view3d.reduced_rect.bottom_right)
|