gp_toolbox/view3d_utils.py

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)