Check canvas alignement
1.6.5 - feat: check canvas alignement of the Gp object compare to chosen draw axisgpv2
parent
dd68e74e2b
commit
a9ed3f7e79
|
@ -1,5 +1,9 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
1.6.5
|
||||||
|
|
||||||
|
- feat: check canvas alignement of the Gp object compare to chosen draw axis
|
||||||
|
|
||||||
1.6.4
|
1.6.4
|
||||||
|
|
||||||
- fix: disable multi-selection for layer naming manager
|
- fix: disable multi-selection for layer naming manager
|
||||||
|
|
|
@ -2,7 +2,7 @@ import bpy
|
||||||
from mathutils import Vector#, Matrix
|
from mathutils import Vector#, Matrix
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from math import radians
|
from math import radians
|
||||||
from .utils import get_gp_objects, set_collection, show_message_box
|
from . import utils
|
||||||
|
|
||||||
class GPTB_OT_copy_text(bpy.types.Operator):
|
class GPTB_OT_copy_text(bpy.types.Operator):
|
||||||
bl_idname = "wm.copytext"
|
bl_idname = "wm.copytext"
|
||||||
|
@ -170,7 +170,7 @@ class GPTB_OT_draw_cam(bpy.types.Operator):
|
||||||
|
|
||||||
# dcam_col = bpy.data.collections.get(camcol_name)
|
# dcam_col = bpy.data.collections.get(camcol_name)
|
||||||
# if not dcam_col:
|
# if not dcam_col:
|
||||||
set_collection(drawcam, camcol_name)
|
utils.set_collection(drawcam, camcol_name)
|
||||||
|
|
||||||
# Swap to it, unhide if necessary and hide previous
|
# Swap to it, unhide if necessary and hide previous
|
||||||
context.scene.camera = maincam
|
context.scene.camera = maincam
|
||||||
|
@ -188,7 +188,7 @@ class GPTB_OT_draw_cam(bpy.types.Operator):
|
||||||
if not drawcam:
|
if not drawcam:
|
||||||
created=True
|
created=True
|
||||||
drawcam = bpy.data.objects.new(dcam_name, context.scene.camera.data)
|
drawcam = bpy.data.objects.new(dcam_name, context.scene.camera.data)
|
||||||
set_collection(drawcam, 'manip_cams')
|
utils.set_collection(drawcam, 'manip_cams')
|
||||||
|
|
||||||
if dcam_name == 'draw_cam':
|
if dcam_name == 'draw_cam':
|
||||||
drawcam.parent = maincam
|
drawcam.parent = maincam
|
||||||
|
@ -390,7 +390,7 @@ class GPTB_OT_list_disabled_anims(bpy.types.Operator):
|
||||||
else:
|
else:
|
||||||
li.append(f'{" "*len(o.name)} - {fcu.data_path} {fcu.array_index}')
|
li.append(f'{" "*len(o.name)} - {fcu.data_path} {fcu.array_index}')
|
||||||
if li:
|
if li:
|
||||||
show_message_box(li)
|
utils.show_message_box(li)
|
||||||
else:
|
else:
|
||||||
self.report({'INFO'}, f"No animation disabled on {'selection' if self.selection else 'scene'}")
|
self.report({'INFO'}, f"No animation disabled on {'selection' if self.selection else 'scene'}")
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
@ -485,6 +485,36 @@ class GPTB_OT_clear_active_frame(bpy.types.Operator):
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
class GPTB_OT_check_canvas_alignement(bpy.types.Operator):
|
||||||
|
bl_idname = "gp.check_canvas_alignement"
|
||||||
|
bl_label = "Check Canvas Alignement"
|
||||||
|
bl_description = "Check if view is aligned to canvas\nWarn if the drawing angle to surface is too high\nThere can be some error margin"
|
||||||
|
bl_options = {"REGISTER"}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
# if lock_axis is 'VIEW' then the draw axis is always aligned
|
||||||
|
return context.object and context.object.type == 'GPENCIL'# and context.scene.tool_settings.gpencil_sculpt.lock_axis != 'VIEW'
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
if context.scene.tool_settings.gpencil_sculpt.lock_axis == 'VIEW':
|
||||||
|
self.report({'INFO'}, 'Drawing plane use "View" (always aligned)')
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
_angle, ret, message = utils.check_angle_from_view(obj=context.object, context=context)
|
||||||
|
if not ret or not message:
|
||||||
|
self.report({'ERROR'}, 'Could not get view angle infos')
|
||||||
|
return {'CANCELLED'}
|
||||||
|
|
||||||
|
title = 'Aligned \o/' if ret == 'INFO' else "Not aligned !"
|
||||||
|
|
||||||
|
if context.region_data.view_perspective != 'CAMERA':
|
||||||
|
title = title + ' ( not in camera view)'
|
||||||
|
|
||||||
|
utils.show_message_box(_message=message, _title=title, _icon=ret)
|
||||||
|
# self.report({ret}, message)
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
classes = (
|
classes = (
|
||||||
GPTB_OT_copy_text,
|
GPTB_OT_copy_text,
|
||||||
GPTB_OT_flipx_view,
|
GPTB_OT_flipx_view,
|
||||||
|
@ -495,6 +525,7 @@ GPTB_OT_reset_cam_rot,
|
||||||
GPTB_OT_toggle_mute_animation,
|
GPTB_OT_toggle_mute_animation,
|
||||||
GPTB_OT_list_disabled_anims,
|
GPTB_OT_list_disabled_anims,
|
||||||
GPTB_OT_clear_active_frame,
|
GPTB_OT_clear_active_frame,
|
||||||
|
GPTB_OT_check_canvas_alignement,
|
||||||
)
|
)
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
|
|
|
@ -167,6 +167,9 @@ class GPTB_PT_sidebar_panel(bpy.types.Panel):
|
||||||
row.operator('object.depth_proportional_move', text='Depth move', icon='TRANSFORM_ORIGINS')
|
row.operator('object.depth_proportional_move', text='Depth move', icon='TRANSFORM_ORIGINS')
|
||||||
|
|
||||||
## col.operator('gp.batch_reproject_all_frames') # text=Batch Reproject # added to context menu
|
## col.operator('gp.batch_reproject_all_frames') # text=Batch Reproject # added to context menu
|
||||||
|
## check drawing alignement
|
||||||
|
col.operator('gp.check_canvas_alignement', icon='DRIVER_ROTATIONAL_DIFFERENCE')
|
||||||
|
|
||||||
## Create empty frame on layer (ops stored under GP_colorize... might be best to separate in another panel )
|
## Create empty frame on layer (ops stored under GP_colorize... might be best to separate in another panel )
|
||||||
col.operator('gp.create_empty_frames', icon='DECORATE_KEYFRAME')
|
col.operator('gp.create_empty_frames', icon='DECORATE_KEYFRAME')
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ bl_info = {
|
||||||
"name": "GP toolbox",
|
"name": "GP toolbox",
|
||||||
"description": "Set of tools for Grease Pencil in animation production",
|
"description": "Set of tools for Grease Pencil in animation production",
|
||||||
"author": "Samuel Bernou, Christophe Seux",
|
"author": "Samuel Bernou, Christophe Seux",
|
||||||
"version": (1, 6, 4),
|
"version": (1, 6, 5),
|
||||||
"blender": (2, 91, 0),
|
"blender": (2, 91, 0),
|
||||||
"location": "Sidebar (N menu) > Gpencil > Toolbox / Gpencil properties",
|
"location": "Sidebar (N menu) > Gpencil > Toolbox / Gpencil properties",
|
||||||
"warning": "",
|
"warning": "",
|
||||||
|
|
|
@ -6,7 +6,7 @@ from random import random as rand
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from bpy_extras.object_utils import world_to_camera_view as cam_space
|
from bpy_extras.object_utils import world_to_camera_view as cam_space
|
||||||
import bmesh
|
import bmesh
|
||||||
from .utils import link_vert,gp_stroke_to_bmesh,draw_gp_stroke,remapping
|
from .utils import get_gp_draw_plane, link_vert,gp_stroke_to_bmesh,draw_gp_stroke,remapping
|
||||||
|
|
||||||
|
|
||||||
def get_view_origin_position():
|
def get_view_origin_position():
|
||||||
|
@ -252,10 +252,8 @@ def set_viewport_matrix(width,height,mat):
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# get object info
|
# get object info
|
||||||
def get_object_info(mesh_groups,order_list = []) :
|
def get_object_info(mesh_groups, order_list = []) :
|
||||||
scene = bpy.context.scene
|
scene = bpy.context.scene
|
||||||
cam = scene.camera
|
cam = scene.camera
|
||||||
#scale = scene.render.resolution_percentage / 100.0
|
#scale = scene.render.resolution_percentage / 100.0
|
||||||
|
@ -378,4 +376,4 @@ def get_object_info(mesh_groups,order_list = []) :
|
||||||
scene.render.resolution_y = res_y
|
scene.render.resolution_y = res_y
|
||||||
|
|
||||||
|
|
||||||
return mesh_info,convert_table
|
return mesh_info, convert_table
|
49
utils.py
49
utils.py
|
@ -253,24 +253,28 @@ def remapping(value, leftMin, leftMax, rightMin, rightMax):
|
||||||
|
|
||||||
#### GP funcs
|
#### GP funcs
|
||||||
|
|
||||||
def get_gp_draw_plane(context):
|
def get_gp_draw_plane(context, obj=None):
|
||||||
''' return tuple with plane coordinate and normal
|
''' return tuple with plane coordinate and normal
|
||||||
of the curent drawing accordign to geometry'''
|
of the curent drawing accordign to geometry'''
|
||||||
|
|
||||||
settings = context.scene.tool_settings
|
settings = context.scene.tool_settings
|
||||||
orient = settings.gpencil_sculpt.lock_axis#'VIEW', 'AXIS_Y', 'AXIS_X', 'AXIS_Z', 'CURSOR'
|
orient = settings.gpencil_sculpt.lock_axis #'VIEW', 'AXIS_Y', 'AXIS_X', 'AXIS_Z', 'CURSOR'
|
||||||
loc = settings.gpencil_stroke_placement_view3d#'ORIGIN', 'CURSOR', 'SURFACE', 'STROKE'
|
loc = settings.gpencil_stroke_placement_view3d #'ORIGIN', 'CURSOR', 'SURFACE', 'STROKE'
|
||||||
|
if obj:
|
||||||
|
mat = obj.matrix_world
|
||||||
|
else:
|
||||||
mat = context.object.matrix_world if context.object else None
|
mat = context.object.matrix_world if context.object else None
|
||||||
|
|
||||||
# -> placement
|
# -> placement
|
||||||
if loc == "CURSOR":
|
if loc == "CURSOR":
|
||||||
plane_co = context.scene.cursor.location
|
plane_co = context.scene.cursor.location
|
||||||
else:#ORIGIN (also on origin if set to 'SURFACE', 'STROKE')
|
|
||||||
|
else: # ORIGIN (also on origin if set to 'SURFACE', 'STROKE')
|
||||||
if not context.object:
|
if not context.object:
|
||||||
plane_co = None
|
plane_co = None
|
||||||
else:
|
else:
|
||||||
plane_co = context.object.matrix_world.to_translation()# context.object.location
|
plane_co = context.object.matrix_world.to_translation()# context.object.location
|
||||||
|
|
||||||
|
|
||||||
# -> orientation
|
# -> orientation
|
||||||
if orient == 'VIEW':
|
if orient == 'VIEW':
|
||||||
#only depth is important, no need to get view vector
|
#only depth is important, no need to get view vector
|
||||||
|
@ -294,6 +298,41 @@ def get_gp_draw_plane(context):
|
||||||
|
|
||||||
return plane_co, plane_no
|
return plane_co, plane_no
|
||||||
|
|
||||||
|
def check_angle_from_view(obj=None, plane_no=None, context=None):
|
||||||
|
'''Return angle to obj according to chosen drawing axis'''
|
||||||
|
import math
|
||||||
|
from bpy_extras import view3d_utils
|
||||||
|
|
||||||
|
if not context:
|
||||||
|
context = bpy.context
|
||||||
|
|
||||||
|
if not plane_no:
|
||||||
|
_plane_co, plane_no = get_gp_draw_plane(context, obj=obj)
|
||||||
|
view_direction = view3d_utils.region_2d_to_vector_3d(context.region, context.region_data, (context.region.width/2.0, context.region.height/2.0))
|
||||||
|
|
||||||
|
angle = math.degrees(view_direction.angle(plane_no))
|
||||||
|
|
||||||
|
# correct angle value when painting from other side (seems a bit off...)
|
||||||
|
if angle > 90:
|
||||||
|
angle = abs(90 - (angle - 90))
|
||||||
|
|
||||||
|
# over angle warning
|
||||||
|
if angle > 75:
|
||||||
|
return angle, 'ERROR', f"Canvas not aligned at all! (angle: {angle:.2f}°), use Realign GP or change draw axis"
|
||||||
|
|
||||||
|
if angle > 6:
|
||||||
|
return angle, 'ERROR', f'Canvas not aligned! angle: {angle:.2f}°, use Realign GP'
|
||||||
|
|
||||||
|
if angle > 3: # below 3 is reasonable (camera seem to view induce error !)
|
||||||
|
return angle, 'INFO', f'Canvas almost aligned (angle: {angle:.2f}°)'
|
||||||
|
|
||||||
|
if angle == 0:
|
||||||
|
return angle, 'INFO', f'Canvas perfectly aligned (angle: {angle:.2f}°)'
|
||||||
|
|
||||||
|
# below 1 consider aligned
|
||||||
|
return angle, 'INFO', f'Canvas alignement seem ok (angle: {angle:.2f}°)'
|
||||||
|
|
||||||
|
|
||||||
## need big update
|
## need big update
|
||||||
def create_gp_palette(gp_data_block,info) :
|
def create_gp_palette(gp_data_block,info) :
|
||||||
palette = gp_data_block.palettes.active
|
palette = gp_data_block.palettes.active
|
||||||
|
|
Loading…
Reference in New Issue