add view3d utility
2.2.1 - added: class View3D to calculate area 3d related coordinatesgpv2
parent
d1b03c804c
commit
8e223b9f3a
|
@ -1,5 +1,8 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
2.2.1
|
||||||
|
|
||||||
|
- added: class View3D to calculate area 3d related coordinates
|
||||||
|
|
||||||
2.2.0
|
2.2.0
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ from mathutils import Vector#, Matrix
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import math
|
import math
|
||||||
from math import radians
|
from math import radians
|
||||||
|
from .view3d_utils import View3D
|
||||||
from . import utils
|
from . import utils
|
||||||
|
|
||||||
|
|
||||||
|
@ -38,6 +38,35 @@ class GPTB_OT_flipx_view(bpy.types.Operator):
|
||||||
context.scene.camera.scale.x *= -1
|
context.scene.camera.scale.x *= -1
|
||||||
return {"FINISHED"}
|
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):
|
class GPTB_OT_rename_data_from_obj(bpy.types.Operator):
|
||||||
bl_idname = "gp.rename_data_from_obj"
|
bl_idname = "gp.rename_data_from_obj"
|
||||||
bl_label = "Rename GP From Object"
|
bl_label = "Rename GP From Object"
|
||||||
|
@ -635,6 +664,7 @@ class GPTB_OT_open_addon_prefs(bpy.types.Operator):
|
||||||
classes = (
|
classes = (
|
||||||
GPTB_OT_copy_text,
|
GPTB_OT_copy_text,
|
||||||
GPTB_OT_flipx_view,
|
GPTB_OT_flipx_view,
|
||||||
|
GPTB_OT_view_camera_frame_fit,
|
||||||
GPTB_OT_rename_data_from_obj,
|
GPTB_OT_rename_data_from_obj,
|
||||||
GPTB_OT_draw_cam,
|
GPTB_OT_draw_cam,
|
||||||
GPTB_OT_set_view_as_cam,
|
GPTB_OT_set_view_as_cam,
|
||||||
|
|
|
@ -86,6 +86,9 @@ class GPTB_PT_sidebar_panel(Panel):
|
||||||
else:
|
else:
|
||||||
row.prop(context.scene.camera.data, 'show_passepartout', text='', icon ='OBJECT_HIDDEN')
|
row.prop(context.scene.camera.data, 'show_passepartout', text='', icon ='OBJECT_HIDDEN')
|
||||||
row.prop(context.scene.camera.data, 'passepartout_alpha', text='')
|
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 = layout.row(align=True)
|
||||||
row.operator('view3d.zoom_camera_1_to_1', text = 'Zoom 1:1', icon = 'ZOOM_PREVIOUS') # FULLSCREEN_EXIT
|
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')
|
row.operator('view3d.view_center_camera', text = 'Zoom fit', icon = 'FULLSCREEN_ENTER')
|
||||||
|
|
|
@ -4,7 +4,7 @@ bl_info = {
|
||||||
"name": "GP toolbox",
|
"name": "GP toolbox",
|
||||||
"description": "Tool set for Grease Pencil in animation production",
|
"description": "Tool set for Grease Pencil in animation production",
|
||||||
"author": "Samuel Bernou, Christophe Seux",
|
"author": "Samuel Bernou, Christophe Seux",
|
||||||
"version": (2, 2, 0),
|
"version": (2, 2, 1),
|
||||||
"blender": (3, 0, 0),
|
"blender": (3, 0, 0),
|
||||||
"location": "Sidebar (N menu) > Gpencil > Toolbox / Gpencil properties",
|
"location": "Sidebar (N menu) > Gpencil > Toolbox / Gpencil properties",
|
||||||
"warning": "",
|
"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