From a9ed3f7e79a71e82b1fc5aa5039fd7eb43d7e8f4 Mon Sep 17 00:00:00 2001 From: Pullusb Date: Wed, 15 Sep 2021 01:35:18 +0200 Subject: [PATCH] Check canvas alignement 1.6.5 - feat: check canvas alignement of the Gp object compare to chosen draw axis --- CHANGELOG.md | 4 ++++ OP_helpers.py | 39 +++++++++++++++++++++++++++++++++++---- UI_tools.py | 3 +++ __init__.py | 2 +- functions.py | 8 +++----- utils.py | 51 +++++++++++++++++++++++++++++++++++++++++++++------ 6 files changed, 91 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 239a0a4..d20aba0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +1.6.5 + +- feat: check canvas alignement of the Gp object compare to chosen draw axis + 1.6.4 - fix: disable multi-selection for layer naming manager diff --git a/OP_helpers.py b/OP_helpers.py index 5a5d348..a21f9cf 100644 --- a/OP_helpers.py +++ b/OP_helpers.py @@ -2,7 +2,7 @@ import bpy from mathutils import Vector#, Matrix from pathlib import Path 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): bl_idname = "wm.copytext" @@ -170,7 +170,7 @@ class GPTB_OT_draw_cam(bpy.types.Operator): # dcam_col = bpy.data.collections.get(camcol_name) # if not dcam_col: - set_collection(drawcam, camcol_name) + utils.set_collection(drawcam, camcol_name) # Swap to it, unhide if necessary and hide previous context.scene.camera = maincam @@ -188,7 +188,7 @@ class GPTB_OT_draw_cam(bpy.types.Operator): if not drawcam: created=True 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': drawcam.parent = maincam @@ -390,7 +390,7 @@ class GPTB_OT_list_disabled_anims(bpy.types.Operator): else: li.append(f'{" "*len(o.name)} - {fcu.data_path} {fcu.array_index}') if li: - show_message_box(li) + utils.show_message_box(li) else: self.report({'INFO'}, f"No animation disabled on {'selection' if self.selection else 'scene'}") return {'FINISHED'} @@ -485,6 +485,36 @@ class GPTB_OT_clear_active_frame(bpy.types.Operator): 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 = ( GPTB_OT_copy_text, GPTB_OT_flipx_view, @@ -495,6 +525,7 @@ GPTB_OT_reset_cam_rot, GPTB_OT_toggle_mute_animation, GPTB_OT_list_disabled_anims, GPTB_OT_clear_active_frame, +GPTB_OT_check_canvas_alignement, ) def register(): diff --git a/UI_tools.py b/UI_tools.py index 589e3b1..8de0c41 100644 --- a/UI_tools.py +++ b/UI_tools.py @@ -167,6 +167,9 @@ class GPTB_PT_sidebar_panel(bpy.types.Panel): 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 + ## 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 ) col.operator('gp.create_empty_frames', icon='DECORATE_KEYFRAME') diff --git a/__init__.py b/__init__.py index 7e5033d..48bab4b 100755 --- a/__init__.py +++ b/__init__.py @@ -15,7 +15,7 @@ bl_info = { "name": "GP toolbox", "description": "Set of tools for Grease Pencil in animation production", "author": "Samuel Bernou, Christophe Seux", -"version": (1, 6, 4), +"version": (1, 6, 5), "blender": (2, 91, 0), "location": "Sidebar (N menu) > Gpencil > Toolbox / Gpencil properties", "warning": "", diff --git a/functions.py b/functions.py index c54155a..43f594c 100644 --- a/functions.py +++ b/functions.py @@ -6,7 +6,7 @@ from random import random as rand import numpy as np from bpy_extras.object_utils import world_to_camera_view as cam_space 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(): @@ -252,10 +252,8 @@ def set_viewport_matrix(width,height,mat): - - # get object info -def get_object_info(mesh_groups,order_list = []) : +def get_object_info(mesh_groups, order_list = []) : scene = bpy.context.scene cam = scene.camera #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 - return mesh_info,convert_table + return mesh_info, convert_table \ No newline at end of file diff --git a/utils.py b/utils.py index 6b34c2a..6afed23 100644 --- a/utils.py +++ b/utils.py @@ -253,24 +253,28 @@ def remapping(value, leftMin, leftMax, rightMin, rightMax): #### GP funcs -def get_gp_draw_plane(context): +def get_gp_draw_plane(context, obj=None): ''' return tuple with plane coordinate and normal of the curent drawing accordign to geometry''' settings = context.scene.tool_settings - orient = settings.gpencil_sculpt.lock_axis#'VIEW', 'AXIS_Y', 'AXIS_X', 'AXIS_Z', 'CURSOR' - loc = settings.gpencil_stroke_placement_view3d#'ORIGIN', 'CURSOR', 'SURFACE', 'STROKE' - mat = context.object.matrix_world if context.object else None + orient = settings.gpencil_sculpt.lock_axis #'VIEW', 'AXIS_Y', 'AXIS_X', 'AXIS_Z', 'CURSOR' + 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 + # -> placement if loc == "CURSOR": 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: plane_co = None else: plane_co = context.object.matrix_world.to_translation()# context.object.location - # -> orientation if orient == 'VIEW': #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 +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 def create_gp_palette(gp_data_block,info) : palette = gp_data_block.palettes.active