Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPullusb <bernou.samuel@gmail.com>2021-01-31 23:44:03 +0300
committerPullusb <bernou.samuel@gmail.com>2021-01-31 23:44:03 +0300
commit7bc0fa6bf2d8fb564759a4b2c776eda098f61bd9 (patch)
treeab248ed28b48e6825ee53a8baa6b33b9520a377a /greasepencil_tools
parent9d30d2bccb154f2c1a4b665bab7d6c02ff5c51b1 (diff)
GPencil Tools: Canvas rotation improvements
- Added operators to store/restore camera rotation (handy to quickly get back to a prefered angle) - Use a modifier key to step the rotation by a user-chosen angle - Addon preferences UI cleanup
Diffstat (limited to 'greasepencil_tools')
-rw-r--r--greasepencil_tools/__init__.py2
-rw-r--r--greasepencil_tools/prefs.py185
-rw-r--r--greasepencil_tools/rotate_canvas.py93
-rw-r--r--greasepencil_tools/ui_panels.py3
4 files changed, 193 insertions, 90 deletions
diff --git a/greasepencil_tools/__init__.py b/greasepencil_tools/__init__.py
index 9ac882ba..adcc7aa7 100644
--- a/greasepencil_tools/__init__.py
+++ b/greasepencil_tools/__init__.py
@@ -21,7 +21,7 @@ bl_info = {
"name": "Grease Pencil Tools",
"description": "Extra tools for Grease Pencil",
"author": "Samuel Bernou, Antonio Vazquez, Daniel Martinez Lara, Matias Mendiola",
-"version": (1, 2, 2),
+"version": (1, 3, 0),
"blender": (2, 91, 0),
"location": "Sidebar > Grease Pencil > Grease Pencil Tools",
"warning": "",
diff --git a/greasepencil_tools/prefs.py b/greasepencil_tools/prefs.py
index fff0f18c..533f5b99 100644
--- a/greasepencil_tools/prefs.py
+++ b/greasepencil_tools/prefs.py
@@ -23,6 +23,7 @@ from bpy.props import (
EnumProperty,
StringProperty,
PointerProperty,
+ FloatProperty,
# IntProperty,
)
@@ -62,13 +63,6 @@ class GreasePencilAddonPrefs(bpy.types.AddonPreferences):
default="Grease Pencil",
update=update_panel)
- pref_tabs : EnumProperty(
- items=(('PREF', "Preferences", "Preferences properties of GP"),
- ('TUTO', "Tutorial", "How to use the tool"),
- # ('KEYMAP', "Keymap", "customise the default keymap"),
- ),
- default='PREF')
-
# --- props
use_clic_drag : BoolProperty(
name='Use click drag directly on points',
@@ -131,95 +125,124 @@ class GreasePencilAddonPrefs(bpy.types.AddonPreferences):
default = True,
update=auto_rebind)
+ rc_angle_step: FloatProperty(
+ name="Angle Steps",
+ description="Step the rotation using this angle when using rotate canvas step modifier",
+ default=0.0872664600610733, # 5
+ min=0.01745329238474369, # 1
+ max=3.1415927410125732, # # 180
+ soft_min=0.01745329238474369, # 1
+ soft_max=1.5707963705062866, # 90
+ step=10, precision=1, subtype='ANGLE', unit='ROTATION')
+
def draw(self, context):
prefs = get_addon_prefs()
layout = self.layout
# layout.use_property_split = True
row= layout.row(align=True)
- row.prop(self, "pref_tabs", expand=True)
- if self.pref_tabs == 'PREF':
+ ## TAB CATEGORY
+ box = layout.box()
+ row = box.row(align=True)
+ row.label(text="Panel Category:")
+ row.prop(self, "category", text="")
+
+ ## BOX DEFORM
+ box = layout.box()
+ row = box.row(align=True)
+ row.label(text='Box Deform:')
+ row.operator("wm.call_menu", text="", icon='QUESTION').name = "GPT_MT_box_deform_doc"
+ box.prop(self, "use_clic_drag")
+ # box.separator()
+ box.prop(self, "default_deform_type")
+ box.label(text="Deformer type can be changed during modal with 'M' key, this is for default behavior", icon='INFO')
+
+ box.prop(self, "auto_swap_deform_type")
+ box.label(text="Once 'M' is hit, auto swap is desactivated to stay in your chosen mode", icon='INFO')
+
+ ## ROTATE CANVAS
+ box = layout.box()
+ box.label(text='Rotate canvas:')
+
+ box.prop(self, "canvas_use_shortcut", text='Bind Shortcuts')
+
+ if self.canvas_use_shortcut:
+
+ row = box.row()
+ row.label(text="(Auto rebind when changing shortcut)")#icon=""
+ # row.operator("prefs.rebind_shortcut", text='Bind/Rebind shortcuts', icon='FILE_REFRESH')#EVENT_SPACEKEY
+ row = box.row(align = True)
+ row.prop(self, "use_ctrl", text='Ctrl')#, expand=True
+ row.prop(self, "use_alt", text='Alt')#, expand=True
+ row.prop(self, "use_shift", text='Shift')#, expand=True
+ row.prop(self, "mouse_click",text='')#expand=True
+
+ if not self.use_ctrl and not self.use_alt and not self.use_shift:
+ box.label(text="Choose at least one modifier to combine with click (default: Ctrl+Alt)", icon="ERROR")# INFO
+
+ if not all((self.use_ctrl, self.use_alt, self.use_shift)):
+ row = box.row(align = True)
+ snap_key_list = []
+ if not self.use_ctrl:
+ snap_key_list.append('Ctrl')
+ if not self.use_shift:
+ snap_key_list.append('Shift')
+ if not self.use_alt:
+ snap_key_list.append('Alt')
- ## TAB CATEGORY
- box = layout.box()
- row = box.row(align=True)
- row.label(text="Panel Category:")
- row.prop(self, "category", text="")
+ row.label(text=f"Step rotation with: {' or '.join(snap_key_list)}", icon='DRIVER_ROTATIONAL_DIFFERENCE')
+ row.prop(self, "rc_angle_step", text='Angle Steps')
- ## BOX DEFORM
- box = layout.box()
- box.label(text='Box Deform:')
- box.prop(self, "use_clic_drag")
- # box.separator()
- box.prop(self, "default_deform_type")
- box.label(text="Deformer type can be changed during modal with 'M' key, this is for default behavior", icon='INFO')
- box.prop(self, "auto_swap_deform_type")
- box.label(text="Once 'M' is hit, auto swap is desactivated to stay in your chosen mode", icon='INFO')
+ else:
+ box.label(text="No hotkey has been set automatically. Following operators needs to be set manually:", icon="ERROR")
+ box.label(text="view3d.rotate_canvas")
+ box.prop(self, 'canvas_use_hud')
- ## ROTATE CANVAS
- box = layout.box()
- box.label(text='Rotate canvas:')
+ ## SCRUB TIMELINE
+ box = layout.box()
+ draw_ts_pref(prefs.ts, box)
- box.prop(self, "canvas_use_shortcut", text='Bind Shortcuts')
- if self.canvas_use_shortcut:
-
- row = box.row()
- row.label(text="(Auto rebind when changing shortcut)")#icon=""
- # row.operator("prefs.rebind_shortcut", text='Bind/Rebind shortcuts', icon='FILE_REFRESH')#EVENT_SPACEKEY
- row = box.row(align = True)
- row.prop(self, "use_ctrl", text='Ctrl')#, expand=True
- row.prop(self, "use_alt", text='Alt')#, expand=True
- row.prop(self, "use_shift", text='Shift')#, expand=True
- row.prop(self, "mouse_click",text='')#expand=True
-
- if not self.use_ctrl and not self.use_alt and not self.use_shift:
- box.label(text="Choose at least one modifier to combine with click (default: Ctrl+Alt)", icon="ERROR")# INFO
-
- else:
- box.label(text="No hotkey has been set automatically. Following operators needs to be set manually:", icon="ERROR")
- box.label(text="view3d.rotate_canvas")
- box.prop(self, 'canvas_use_hud')
-
- ## SCRUB TIMELINE
- box = layout.box()
- draw_ts_pref(prefs.ts, box)
-
- if self.pref_tabs == 'TUTO':
-
- #**Behavior from context mode**
- col = layout.column()
- col.label(text='Box Deform Tool')
- col.label(text="Usage:", icon='MOD_LATTICE')
- col.label(text="Use the shortcut 'Ctrl+T' in available modes (listed below)")
- col.label(text="The lattice box is generated facing your view (be sure to face canvas if you want to stay on it)")
- col.label(text="Use shortcuts below to deform (a help will be displayed in the topbar)")
-
- col.separator()
- col.label(text="Shortcuts:", icon='HAND')
- col.label(text="Spacebar / Enter : Confirm")
- col.label(text="Delete / Backspace / Tab(twice) / Ctrl+T : Cancel")
- col.label(text="M : Toggle between Linear and Spline mode at any moment")
- col.label(text="1-9 top row number : Subdivide the box")
- col.label(text="Ctrl + arrows-keys : Subdivide the box incrementally in individual X/Y axis")
-
- col.separator()
- col.label(text="Modes and deformation target:", icon='PIVOT_BOUNDBOX')
- col.label(text="- Object mode : The whole GP object is deformed (including all frames)")
- col.label(text="- GPencil Edit mode : Deform Selected points")
- col.label(text="- Gpencil Paint : Deform last Strokes")
- # col.label(text="- Lattice edit : Revive the modal after a ctrl+Z")
-
- col.separator()
- col.label(text="Notes:", icon='TEXT')
- col.label(text="- If you return in box deform after applying (with a ctrl+Z), you need to hit 'Ctrl+T' again to revive the modal.")
- col.label(text="- A cancel warning will be displayed the first time you hit Tab")
+# def box_deform_tuto(layout):
+class GPT_MT_box_deform_doc(bpy.types.Menu):
+ # bl_idname = "OBJECT_MT_custom_menu"
+ bl_label = "Box Deform Infos Sheet"
+ def draw(self, context):
+ layout = self.layout
+ # call another menu
+ #layout.operator("wm.call_menu", text="Unwrap").name = "VIEW3D_MT_uv_map"
+ #**Behavior from context mode**
+ col = layout.column()
+ col.label(text='Box Deform Tool')
+ col.label(text="Usage:", icon='MOD_LATTICE')
+ col.label(text="Use the shortcut 'Ctrl+T' in available modes (listed below)")
+ col.label(text="The lattice box is generated facing your view (be sure to face canvas if you want to stay on it)")
+ col.label(text="Use shortcuts below to deform (a help will be displayed in the topbar)")
+
+ col.separator()
+ col.label(text="Shortcuts:", icon='HAND')
+ col.label(text="Spacebar / Enter : Confirm")
+ col.label(text="Delete / Backspace / Tab(twice) / Ctrl+T : Cancel")
+ col.label(text="M : Toggle between Linear and Spline mode at any moment")
+ col.label(text="1-9 top row number : Subdivide the box")
+ col.label(text="Ctrl + arrows-keys : Subdivide the box incrementally in individual X/Y axis")
+
+ col.separator()
+ col.label(text="Modes and deformation target:", icon='PIVOT_BOUNDBOX')
+ col.label(text="- Object mode : The whole GP object is deformed (including all frames)")
+ col.label(text="- GPencil Edit mode : Deform Selected points")
+ col.label(text="- Gpencil Paint : Deform last Strokes")
+ # col.label(text="- Lattice edit : Revive the modal after a ctrl+Z")
+
+ col.separator()
+ col.label(text="Notes:", icon='TEXT')
+ col.label(text="- If you return in box deform after applying (with a ctrl+Z), you need to hit 'Ctrl+T' again to revive the modal.")
+ col.label(text="- A cancel warning will be displayed the first time you hit Tab")
### rotate canvas keymap
-
addon_keymaps = []
def register_keymaps():
pref = get_addon_prefs()
@@ -242,11 +265,11 @@ def unregister_keymaps():
addon_keymaps.clear()
-
### REGISTER ---
classes = (
GPTS_timeline_settings,
+ GPT_MT_box_deform_doc,
GreasePencilAddonPrefs,
)
diff --git a/greasepencil_tools/rotate_canvas.py b/greasepencil_tools/rotate_canvas.py
index e2ada531..dcafc2fb 100644
--- a/greasepencil_tools/rotate_canvas.py
+++ b/greasepencil_tools/rotate_canvas.py
@@ -12,6 +12,16 @@ import blf
from gpu_extras.batch import batch_for_shader
from gpu_extras.presets import draw_circle_2d
+def step_value(value, step):
+ '''return the step closer to the passed value'''
+ abs_angle = abs(value)
+ diff = abs_angle % step
+ lower_step = abs_angle - diff
+ higher_step = lower_step + step
+ if abs_angle - lower_step < higher_step - abs_angle:
+ return math.copysign(lower_step, value)
+ else:
+ return math.copysign(higher_step, value)
def draw_callback_px(self, context):
# 50% alpha, 2 pixel width line
@@ -70,8 +80,9 @@ class RC_OT_RotateCanvas(bpy.types.Operator):
self.cam.rotation_mode = self.org_rotation_mode
return {'FINISHED'}
+
def modal(self, context, event):
- if event.type in {'MOUSEMOVE','INBETWEEN_MOUSEMOVE'}:
+ if event.type in {'MOUSEMOVE'}:#,'INBETWEEN_MOUSEMOVE'
# Get current mouse coordination (region)
self.pos_current = mathutils.Vector((event.mouse_region_x, event.mouse_region_y))
# Get current vector
@@ -79,6 +90,19 @@ class RC_OT_RotateCanvas(bpy.types.Operator):
# Calculates the angle between initial and current vectors
self.angle = self.vector_initial.angle_signed(self.vector_current)#radian
# print (math.degrees(self.angle), self.vector_initial, self.vector_current)
+
+
+ ## handle snap key
+ snap = False
+ if self.snap_ctrl and event.ctrl:
+ snap = True
+ if self.snap_shift and event.shift:
+ snap = True
+ if self.snap_alt and event.alt:
+ snap = True
+ ## Snapping to specific degrees angle
+ if snap:
+ self.angle = step_value(self.angle, self.snap_step)
if self.in_cam:
self.cam.matrix_world = self.cam_matrix
@@ -119,7 +143,8 @@ class RC_OT_RotateCanvas(bpy.types.Operator):
return {'RUNNING_MODAL'}
def invoke(self, context, event):
- self.hud = get_addon_prefs().canvas_use_hud
+ prefs = get_addon_prefs()
+ self.hud = prefs.canvas_use_hud
self.angle = 0.0
self.in_cam = context.region_data.view_perspective == 'CAMERA'
@@ -158,6 +183,13 @@ class RC_OT_RotateCanvas(bpy.types.Operator):
# Initializes the current vector with the same initial vector.
self.vector_current = self.vector_initial.copy()
+ #Snap keys
+ self.snap_ctrl = not prefs.use_ctrl
+ self.snap_shift = not prefs.use_shift
+ self.snap_alt = not prefs.use_alt
+ # round to closer degree and convert back to radians
+ self.snap_step = math.radians(round(math.degrees(prefs.rc_angle_step)))
+
args = (self, context)
if self.hud:
self._handle = bpy.types.SpaceView3D.draw_handler_add(draw_callback_px, args, 'WINDOW', 'POST_PIXEL')
@@ -165,14 +197,59 @@ class RC_OT_RotateCanvas(bpy.types.Operator):
return {'RUNNING_MODAL'}
+## -- Set / Reset rotation buttons
+
+class RC_OT_Set_rotation(bpy.types.Operator):
+ bl_idname = 'view3d.rotate_canvas_set'
+ bl_label = 'Save Rotation'
+ bl_description = 'Save active camera rotation (per camera property)'
+ bl_options = {"REGISTER"}
+
+ @classmethod
+ def poll(cls, context):
+ return context.space_data.region_3d.view_perspective == 'CAMERA'
+
+ def execute(self, context):
+ cam_ob = context.scene.camera
+ cam_ob['stored_rotation'] = cam_ob.rotation_euler
+ if not cam_ob.get('_RNA_UI'):
+ cam_ob['_RNA_UI'] = {}
+ cam_ob['_RNA_UI']["stored_rotation"] = {
+ "description":"Stored camera rotation (Gpencil tools > rotate canvas operator)",
+ "subtype":'EULER',
+ # "is_overridable_library":0,
+ }
+
+ return {'FINISHED'}
+
+class RC_OT_Reset_rotation(bpy.types.Operator):
+ bl_idname = 'view3d.rotate_canvas_reset'
+ bl_label = 'Restore Rotation'
+ bl_description = 'Restore active camera rotation from previously saved state'
+ bl_options = {"REGISTER", "UNDO"}
+
+ @classmethod
+ def poll(cls, context):
+ return context.space_data.region_3d.view_perspective == 'CAMERA' and context.scene.camera.get('stored_rotation')
+
+ def execute(self, context):
+ cam_ob = context.scene.camera
+ cam_ob.rotation_euler = cam_ob['stored_rotation']
+ return {'FINISHED'}
+
+
### --- REGISTER
-def register():
- bpy.utils.register_class(RC_OT_RotateCanvas)
+classes = (
+ RC_OT_RotateCanvas,
+ RC_OT_Set_rotation,
+ RC_OT_Reset_rotation,
+)
+def register():
+ for cls in classes:
+ bpy.utils.register_class(cls)
def unregister():
- bpy.utils.unregister_class(RC_OT_RotateCanvas)
-
-# if __name__ == "__main__":
-# register()
+ for cls in reversed(classes):
+ bpy.utils.unregister_class(cls) \ No newline at end of file
diff --git a/greasepencil_tools/ui_panels.py b/greasepencil_tools/ui_panels.py
index 292d0e5a..06ad6cb2 100644
--- a/greasepencil_tools/ui_panels.py
+++ b/greasepencil_tools/ui_panels.py
@@ -41,6 +41,9 @@ class GP_PT_sidebarPanel(bpy.types.Panel):
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')
+ row = layout.row(align=True)
+ row.operator('view3d.rotate_canvas_reset', text = 'Reset Rotation', icon = 'FILE_REFRESH')
+ row.operator('view3d.rotate_canvas_set', text = 'Save Rotation', icon = 'DRIVER_ROTATIONAL_DIFFERENCE')
def menu_boxdeform_entry(self, context):