Check canvas alignement

1.6.5

- feat: check canvas alignement of the Gp object compare to chosen draw axis
gpv2
Pullusb 2021-09-15 01:35:18 +02:00
parent dd68e74e2b
commit a9ed3f7e79
6 changed files with 91 additions and 16 deletions

View File

@ -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

View File

@ -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():

View File

@ -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')

View File

@ -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": "",

View File

@ -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

View File

@ -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