add view3d utility
2.2.1 - added: class View3D to calculate area 3d related coordinatesgpv2
parent
d1b03c804c
commit
8e223b9f3a
|
@ -1,5 +1,8 @@
|
|||
# Changelog
|
||||
|
||||
2.2.1
|
||||
|
||||
- added: class View3D to calculate area 3d related coordinates
|
||||
|
||||
2.2.0
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ from mathutils import Vector#, Matrix
|
|||
from pathlib import Path
|
||||
import math
|
||||
from math import radians
|
||||
|
||||
from .view3d_utils import View3D
|
||||
from . import utils
|
||||
|
||||
|
||||
|
@ -38,6 +38,35 @@ class GPTB_OT_flipx_view(bpy.types.Operator):
|
|||
context.scene.camera.scale.x *= -1
|
||||
return {"FINISHED"}
|
||||
|
||||
class GPTB_OT_view_camera_frame_fit(bpy.types.Operator):
|
||||
bl_idname = "view3d.view_camera_frame_fit"
|
||||
bl_label = "View Fit"
|
||||
bl_description = "Fit the camera in view (view 1:1)"
|
||||
bl_options = {"REGISTER"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.area.type == 'VIEW_3D' and \
|
||||
context.region_data.view_perspective == 'CAMERA'
|
||||
|
||||
def zoom_from_fac(self, zoomfac):
|
||||
from math import sqrt
|
||||
return (sqrt(4 * zoomfac) - sqrt(2)) * 50.0
|
||||
|
||||
def execute(self, context):
|
||||
# Calculate zoom level to fit in view considering upper and side panel (Not done by native view 1:1)
|
||||
# context.space_data.region_3d.view_camera_zoom = 0 # (value range: -30, - 600)
|
||||
view3d = View3D()
|
||||
view3d.fit_camera_view()
|
||||
|
||||
## re-center
|
||||
# context.space_data.region_3d.view_camera_offset = (0,0)
|
||||
|
||||
# With a margin
|
||||
# Calculate pan to fit view in viewport
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
class GPTB_OT_rename_data_from_obj(bpy.types.Operator):
|
||||
bl_idname = "gp.rename_data_from_obj"
|
||||
bl_label = "Rename GP From Object"
|
||||
|
@ -635,6 +664,7 @@ class GPTB_OT_open_addon_prefs(bpy.types.Operator):
|
|||
classes = (
|
||||
GPTB_OT_copy_text,
|
||||
GPTB_OT_flipx_view,
|
||||
GPTB_OT_view_camera_frame_fit,
|
||||
GPTB_OT_rename_data_from_obj,
|
||||
GPTB_OT_draw_cam,
|
||||
GPTB_OT_set_view_as_cam,
|
||||
|
|
|
@ -86,6 +86,9 @@ class GPTB_PT_sidebar_panel(Panel):
|
|||
else:
|
||||
row.prop(context.scene.camera.data, 'show_passepartout', text='', icon ='OBJECT_HIDDEN')
|
||||
row.prop(context.scene.camera.data, 'passepartout_alpha', text='')
|
||||
# row = layout.row(align=True)
|
||||
# row.operator('view3d.view_camera_frame_fit', text = 'Custom fit', icon = 'ZOOM_PREVIOUS') # FULLSCREEN_EXIT
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.operator('view3d.zoom_camera_1_to_1', text = 'Zoom 1:1', icon = 'ZOOM_PREVIOUS') # FULLSCREEN_EXIT
|
||||
row.operator('view3d.view_center_camera', text = 'Zoom fit', icon = 'FULLSCREEN_ENTER')
|
||||
|
|
|
@ -4,7 +4,7 @@ bl_info = {
|
|||
"name": "GP toolbox",
|
||||
"description": "Tool set for Grease Pencil in animation production",
|
||||
"author": "Samuel Bernou, Christophe Seux",
|
||||
"version": (2, 2, 0),
|
||||
"version": (2, 2, 1),
|
||||
"blender": (3, 0, 0),
|
||||
"location": "Sidebar (N menu) > Gpencil > Toolbox / Gpencil properties",
|
||||
"warning": "",
|
||||
|
|
|
@ -0,0 +1,237 @@
|
|||
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)
|
Loading…
Reference in New Issue