Compare commits
No commits in common. "master" and "v3.3.2" have entirely different histories.
17
CHANGELOG.md
17
CHANGELOG.md
@ -1,22 +1,5 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
4.1.3
|
|
||||||
|
|
||||||
- fixed: error in "remove stroke duplication" using file checker
|
|
||||||
- added: `Remove Redundant GP Stroke` Operator (Not exposed, avaialbe in search with developer extras enabled). Allow to use directly without going using file checker
|
|
||||||
|
|
||||||
4.1.2
|
|
||||||
|
|
||||||
- fixed: Error in Draw cam overlay breaking after random undo step `Ctrl + Z`
|
|
||||||
|
|
||||||
4.1.1
|
|
||||||
|
|
||||||
- added: option in copy stroke world space to not bake move
|
|
||||||
|
|
||||||
4.1.0
|
|
||||||
|
|
||||||
- added: operator `list collection visibility conflicts` (not exposed, called from search menu with dev extras enabled in prefs)
|
|
||||||
|
|
||||||
4.0.3
|
4.0.3
|
||||||
|
|
||||||
changed: File checker doest not fix directly when clicked (also removed choice in preference):
|
changed: File checker doest not fix directly when clicked (also removed choice in preference):
|
||||||
|
@ -6,7 +6,6 @@ from bpy.props import (FloatProperty,
|
|||||||
StringProperty,
|
StringProperty,
|
||||||
IntProperty)
|
IntProperty)
|
||||||
from .. import utils
|
from .. import utils
|
||||||
from ..utils import is_hidden
|
|
||||||
|
|
||||||
## copied from OP_key_duplicate_send
|
## copied from OP_key_duplicate_send
|
||||||
def get_layer_list(self, context):
|
def get_layer_list(self, context):
|
||||||
@ -143,7 +142,7 @@ class GP_OT_create_empty_frames(bpy.types.Operator):
|
|||||||
tgt_layers = [l for i, l in enumerate(gpl) if i == active_index - 1]
|
tgt_layers = [l for i, l in enumerate(gpl) if i == active_index - 1]
|
||||||
|
|
||||||
elif self.targeted_layers == 'ALL_VISIBLE':
|
elif self.targeted_layers == 'ALL_VISIBLE':
|
||||||
tgt_layers = [l for l in gpl if not is_hidden(l) and l != gpl.active]
|
tgt_layers = [l for l in gpl if not l.hide and l != gpl.active]
|
||||||
|
|
||||||
elif self.targeted_layers == 'CHOSEN':
|
elif self.targeted_layers == 'CHOSEN':
|
||||||
if not self.layers_enum:
|
if not self.layers_enum:
|
||||||
|
@ -5,14 +5,11 @@ from ..utils import (location_to_region,
|
|||||||
vector_length,
|
vector_length,
|
||||||
draw_gp_stroke,
|
draw_gp_stroke,
|
||||||
extrapolate_points_by_length,
|
extrapolate_points_by_length,
|
||||||
simple_draw_gp_stroke,
|
simple_draw_gp_stroke)
|
||||||
is_hidden,
|
|
||||||
is_locked)
|
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
from math import degrees
|
from math import degrees
|
||||||
from mathutils import Vector
|
from mathutils import Vector
|
||||||
|
|
||||||
# from os.path import join, basename, exists, dirname, abspath, splitext
|
# from os.path import join, basename, exists, dirname, abspath, splitext
|
||||||
|
|
||||||
# iterate over selected layer and all/selected frame and close gaps between line extermities with a tolerance level
|
# iterate over selected layer and all/selected frame and close gaps between line extermities with a tolerance level
|
||||||
@ -279,9 +276,9 @@ class GPSTK_OT_extend_lines(bpy.types.Operator):
|
|||||||
if self.layer_tgt == 'ACTIVE':
|
if self.layer_tgt == 'ACTIVE':
|
||||||
lays = [ob.data.layers.active]
|
lays = [ob.data.layers.active]
|
||||||
elif self.layer_tgt == 'SELECTED':
|
elif self.layer_tgt == 'SELECTED':
|
||||||
lays = [l for l in ob.data.layers if l.select and not is_hidden(l)]
|
lays = [l for l in ob.data.layers if l.select and not l.hide]
|
||||||
elif self.layer_tgt == 'ALL_VISIBLE':
|
elif self.layer_tgt == 'ALL_VISIBLE':
|
||||||
lays = [l for l in ob.data.layers if not is_hidden(l)]
|
lays = [l for l in ob.data.layers if not l.hide]
|
||||||
else:
|
else:
|
||||||
lays = [l for l in ob.data.layers if not any(x in l.name for x in ('spot', 'colo'))]
|
lays = [l for l in ob.data.layers if not any(x in l.name for x in ('spot', 'colo'))]
|
||||||
|
|
||||||
@ -340,9 +337,9 @@ class GPSTK_OT_change_closeline_length(bpy.types.Operator):
|
|||||||
if self.layer_tgt == 'ACTIVE':
|
if self.layer_tgt == 'ACTIVE':
|
||||||
lays = [ob.data.layers.active]
|
lays = [ob.data.layers.active]
|
||||||
elif self.layer_tgt == 'SELECTED':
|
elif self.layer_tgt == 'SELECTED':
|
||||||
lays = [l for l in ob.data.layers if l.select and not is_hidden(l)]
|
lays = [l for l in ob.data.layers if l.select and not l.hide]
|
||||||
elif self.layer_tgt == 'ALL_VISIBLE':
|
elif self.layer_tgt == 'ALL_VISIBLE':
|
||||||
lays = [l for l in ob.data.layers if not is_hidden(l)]
|
lays = [l for l in ob.data.layers if not l.hide]
|
||||||
else:
|
else:
|
||||||
lays = [l for l in ob.data.layers if not any(x in l.name for x in ('spot', 'colo'))]
|
lays = [l for l in ob.data.layers if not any(x in l.name for x in ('spot', 'colo'))]
|
||||||
|
|
||||||
@ -378,7 +375,7 @@ class GPSTK_OT_comma_finder(bpy.types.Operator):
|
|||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
ct = 0
|
ct = 0
|
||||||
ob = context.object
|
ob = context.object
|
||||||
lays = [l for l in ob.data.layers if not is_hidden(l) and not is_locked(l)]
|
lays = [l for l in ob.data.layers if not l.hide and not l.lock]
|
||||||
for l in lays:
|
for l in lays:
|
||||||
if not l.current_frame():continue
|
if not l.current_frame():continue
|
||||||
for s in l.current_frame().drawing.strokes:
|
for s in l.current_frame().drawing.strokes:
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
## GP clipboard : Copy/Cut/Paste Grease Pencil strokes to/from OS clipboard across layers and blends
|
## GP clipboard : Copy/Cut/Paste Grease Pencil strokes to/from OS clipboard across layers and blends
|
||||||
## View3D > Toolbar > Gpencil > GP clipboard
|
## View3D > Toolbar > Gpencil > GP clipboard
|
||||||
## in 4.2, existed in standalone scripts: https://github.com/Pullusb/GP_clipboard
|
## in 4.2- existed in standalone scripts: https://github.com/Pullusb/GP_clipboard
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
import mathutils
|
import mathutils
|
||||||
@ -9,7 +9,6 @@ import json
|
|||||||
from time import time
|
from time import time
|
||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
from itertools import groupby
|
from itertools import groupby
|
||||||
from .utils import is_locked, is_hidden
|
|
||||||
|
|
||||||
def convertAttr(Attr):
|
def convertAttr(Attr):
|
||||||
'''Convert given value to a Json serializable format'''
|
'''Convert given value to a Json serializable format'''
|
||||||
@ -140,7 +139,7 @@ def copycut_strokes(layers=None, copy=True, keep_empty=True):
|
|||||||
# color = gp.palettes.active.colors.active.name
|
# color = gp.palettes.active.colors.active.name
|
||||||
if not layers:
|
if not layers:
|
||||||
# by default all visible layers
|
# by default all visible layers
|
||||||
layers = [l for l in gpl if not is_hidden(l) and not is_locked(l)] # []
|
layers = [l for l in gpl if not l.hide and not l.lock] # []
|
||||||
if not isinstance(layers, list):
|
if not isinstance(layers, list):
|
||||||
# if a single layer object is send put in a list
|
# if a single layer object is send put in a list
|
||||||
layers = [layers]
|
layers = [layers]
|
||||||
@ -236,7 +235,7 @@ def copy_all_strokes(layers=None):
|
|||||||
|
|
||||||
if not layers:
|
if not layers:
|
||||||
# by default all visible layers
|
# by default all visible layers
|
||||||
layers = [l for l in gpl if not is_hidden(l) and not is_locked(l)]# include locked ?
|
layers = [l for l in gpl if not l.hide and not l.lock]# include locked ?
|
||||||
if not isinstance(layers, list):
|
if not isinstance(layers, list):
|
||||||
# if a single layer object is send put in a list
|
# if a single layer object is send put in a list
|
||||||
layers = [layers]
|
layers = [layers]
|
||||||
@ -276,7 +275,7 @@ def copy_all_strokes_in_frame(frame=None, layers=None, obj=None,
|
|||||||
|
|
||||||
if not layers:
|
if not layers:
|
||||||
# by default all visible layers
|
# by default all visible layers
|
||||||
layers = [l for l in gpl if not is_hidden(l) and not is_locked(l)] # include locked ?
|
layers = [l for l in gpl if not l.hide and not l.lock] # include locked ?
|
||||||
if not isinstance(layers, list):
|
if not isinstance(layers, list):
|
||||||
# if a single layer object is send put in a list
|
# if a single layer object is send put in a list
|
||||||
layers = [layers]
|
layers = [layers]
|
||||||
@ -478,9 +477,6 @@ class GPCLIP_OT_copy_multi_strokes(bpy.types.Operator):
|
|||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
return context.object and context.object.type == 'GREASEPENCIL'
|
return context.object and context.object.type == 'GREASEPENCIL'
|
||||||
|
|
||||||
bake_moves : bpy.props.BoolProperty(name='Bake Move', default=True,
|
|
||||||
description='Copy every frame where object has moved, else copy only existing frames')
|
|
||||||
|
|
||||||
radius : bpy.props.BoolProperty(name='radius', default=True,
|
radius : bpy.props.BoolProperty(name='radius', default=True,
|
||||||
description='Dump point radius attribute (already skipped if at default value)')
|
description='Dump point radius attribute (already skipped if at default value)')
|
||||||
opacity : bpy.props.BoolProperty(name='opacity', default=True,
|
opacity : bpy.props.BoolProperty(name='opacity', default=True,
|
||||||
@ -505,7 +501,6 @@ class GPCLIP_OT_copy_multi_strokes(bpy.types.Operator):
|
|||||||
layout=self.layout
|
layout=self.layout
|
||||||
layout.use_property_split = True
|
layout.use_property_split = True
|
||||||
col = layout.column()
|
col = layout.column()
|
||||||
col.prop(self, 'bake_moves')
|
|
||||||
col.label(text='Keep following point attributes:')
|
col.label(text='Keep following point attributes:')
|
||||||
col.prop(self, 'radius')
|
col.prop(self, 'radius')
|
||||||
col.prop(self, 'opacity')
|
col.prop(self, 'opacity')
|
||||||
@ -516,6 +511,7 @@ class GPCLIP_OT_copy_multi_strokes(bpy.types.Operator):
|
|||||||
return
|
return
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
|
bake_moves = True
|
||||||
skip_empty_frame = False
|
skip_empty_frame = False
|
||||||
|
|
||||||
org_frame = context.scene.frame_current
|
org_frame = context.scene.frame_current
|
||||||
@ -525,12 +521,12 @@ class GPCLIP_OT_copy_multi_strokes(bpy.types.Operator):
|
|||||||
#ct = check_radius()
|
#ct = check_radius()
|
||||||
layerdic = {}
|
layerdic = {}
|
||||||
|
|
||||||
layerpool = [l for l in gpl if not is_hidden(l) and l.select] # and not is_locked(l)
|
layerpool = [l for l in gpl if not l.hide and l.select] # and not l.lock
|
||||||
if not layerpool:
|
if not layerpool:
|
||||||
self.report({'ERROR'}, 'No layers selected in GP dopesheet (needs to be visible and selected to be copied)\nHint: Changing active layer reset selection to active only')
|
self.report({'ERROR'}, 'No layers selected in GP dopesheet (needs to be visible and selected to be copied)\nHint: Changing active layer reset selection to active only')
|
||||||
return {"CANCELLED"}
|
return {"CANCELLED"}
|
||||||
|
|
||||||
if not self.bake_moves: # copy only drawed frames as is.
|
if not bake_moves: # copy only drawed frames as is.
|
||||||
for l in layerpool:
|
for l in layerpool:
|
||||||
if not l.frames:
|
if not l.frames:
|
||||||
continue# skip empty layers
|
continue# skip empty layers
|
||||||
|
@ -11,7 +11,6 @@ from bpy_extras.view3d_utils import region_2d_to_location_3d, region_2d_to_vecto
|
|||||||
location_3d_to_region_2d, region_2d_to_origin_3d, region_2d_to_location_3d
|
location_3d_to_region_2d, region_2d_to_origin_3d, region_2d_to_location_3d
|
||||||
from time import time
|
from time import time
|
||||||
from math import pi, cos, sin
|
from math import pi, cos, sin
|
||||||
from .utils import is_locked, is_hidden
|
|
||||||
|
|
||||||
|
|
||||||
def get_gp_mat(gp, name, set_active=False):
|
def get_gp_mat(gp, name, set_active=False):
|
||||||
@ -441,7 +440,7 @@ class GPTB_OT_eraser(Operator):
|
|||||||
|
|
||||||
t0 = time()
|
t0 = time()
|
||||||
gp_mats = gp.data.materials
|
gp_mats = gp.data.materials
|
||||||
gp_layers = [l for l in gp.data.layers if not is_locked(l) or is_hidden(l)]
|
gp_layers = [l for l in gp.data.layers if not l.lock or l.hide]
|
||||||
self.gp_frames = [l.current_frame() for l in gp_layers]
|
self.gp_frames = [l.current_frame() for l in gp_layers]
|
||||||
'''
|
'''
|
||||||
points_data = [(s, f, gp_mats[s.material_index]) for f in gp_frames for s in f.drawing.strokes]
|
points_data = [(s, f, gp_mats[s.material_index]) for f in gp_frames for s in f.drawing.strokes]
|
||||||
|
@ -2,7 +2,6 @@ import bpy
|
|||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from . import utils
|
from . import utils
|
||||||
|
|
||||||
from bpy.props import (BoolProperty,
|
from bpy.props import (BoolProperty,
|
||||||
@ -10,85 +9,30 @@ from bpy.props import (BoolProperty,
|
|||||||
CollectionProperty,
|
CollectionProperty,
|
||||||
StringProperty)
|
StringProperty)
|
||||||
|
|
||||||
def remove_stroke_exact_duplications(apply=True, verbose=True, select=False):
|
def remove_stroke_exact_duplications(apply=True):
|
||||||
'''Remove accidental stroke duplication (points exactly in the same place)
|
'''Remove accidental stroke duplication (points exactly in the same place)
|
||||||
:apply: Remove the duplication instead of just listing dupes
|
:apply: Remove the duplication instead of just listing dupes
|
||||||
:select: Select the duplicated strokes instead of removing them (disabled by apply)
|
|
||||||
return number of duplication found/deleted
|
return number of duplication found/deleted
|
||||||
'''
|
'''
|
||||||
# TODO: Add additional check of material (even if unlikely to happen, better to avoid false positive)
|
# TODO: add additional check of material (even if unlikely to happen)
|
||||||
ct = 0
|
ct = 0
|
||||||
if verbose:
|
gp_datas = [gp for gp in bpy.data.grease_pencils]
|
||||||
print('\nRemove redundant strokes in GP frames...')
|
|
||||||
gp_datas = [gp for gp in bpy.data.grease_pencils_v3]
|
|
||||||
for gp in gp_datas:
|
for gp in gp_datas:
|
||||||
for l in gp.layers:
|
for l in gp.layers:
|
||||||
for f in l.frames:
|
for f in l.frames:
|
||||||
stroke_list = []
|
stroke_list = []
|
||||||
idx_to_delete = []
|
for s in reversed(f.drawing.strokes):
|
||||||
|
|
||||||
|
point_list = [p.position for p in s.points]
|
||||||
|
|
||||||
for idx, s in enumerate(f.drawing.strokes):
|
|
||||||
point_list = [p.position.copy() for p in s.points]
|
|
||||||
if point_list in stroke_list:
|
if point_list in stroke_list:
|
||||||
ct += 1
|
ct += 1
|
||||||
idx_to_delete.append(idx)
|
if apply:
|
||||||
if not apply and select:
|
# Remove redundancy
|
||||||
s.select = True
|
f.drawing.strokes.remove(s)
|
||||||
else:
|
else:
|
||||||
stroke_list.append(point_list)
|
stroke_list.append(point_list)
|
||||||
if not apply and select:
|
|
||||||
s.select = False
|
|
||||||
|
|
||||||
if idx_to_delete:
|
|
||||||
if verbose:
|
|
||||||
print(f"{gp.name} > {l.name} > frame {f.frame_number}: {len(idx_to_delete)} strokes")
|
|
||||||
if apply:
|
|
||||||
# Remove redundancy (carefull, passing an empty list delete all strokes)
|
|
||||||
f.drawing.remove_strokes(indices=idx_to_delete)
|
|
||||||
return ct
|
return ct
|
||||||
|
|
||||||
class GPTB_OT_remove_stroke_duplication(bpy.types.Operator):
|
|
||||||
bl_idname = "gp.remove_stroke_duplication"
|
|
||||||
bl_label = "Remove Redundant GP Stroke"
|
|
||||||
bl_description = "Within every frame, remove every strokes that are exactly superposed with a previous one"
|
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
|
||||||
|
|
||||||
apply : bpy.props.BoolProperty(name="Remove Stroke", default=True,
|
|
||||||
description="Remove the duplication, else list only",
|
|
||||||
options={'SKIP_SAVE'})
|
|
||||||
|
|
||||||
select : bpy.props.BoolProperty(name="Select Duplicated Strokes", default=False)
|
|
||||||
|
|
||||||
def invoke(self, context, event):
|
|
||||||
# self.file_dump = event.ctrl
|
|
||||||
return context.window_manager.invoke_props_dialog(self) # , width=400
|
|
||||||
# return self.execute(context)
|
|
||||||
|
|
||||||
def draw(self, context):
|
|
||||||
layout=self.layout
|
|
||||||
layout.use_property_split = True
|
|
||||||
col = layout.column()
|
|
||||||
col.prop(self, 'apply')
|
|
||||||
|
|
||||||
row=col.row(align=True)
|
|
||||||
row.prop(self, 'select')
|
|
||||||
row.enabled = not self.apply
|
|
||||||
if not self.apply and not self.select:
|
|
||||||
col.label(text="Only list in console and report number of duplications", icon='INFO')
|
|
||||||
|
|
||||||
def execute(self, context):
|
|
||||||
ct = remove_stroke_exact_duplications(apply=self.apply, select=self.select, verbose=True)
|
|
||||||
if ct > 0:
|
|
||||||
if self.apply:
|
|
||||||
self.report({'INFO'}, f'Removed {ct} strokes duplications')
|
|
||||||
else:
|
|
||||||
self.report({'INFO'}, f'Found {ct} strokes duplications')
|
|
||||||
else:
|
|
||||||
self.report({'INFO'}, 'No stroke duplication found')
|
|
||||||
|
|
||||||
return {'FINISHED'}
|
|
||||||
|
|
||||||
|
|
||||||
class GPTB_OT_file_checker(bpy.types.Operator):
|
class GPTB_OT_file_checker(bpy.types.Operator):
|
||||||
bl_idname = "gp.file_checker"
|
bl_idname = "gp.file_checker"
|
||||||
bl_label = "Check File"
|
bl_label = "Check File"
|
||||||
@ -322,7 +266,6 @@ class GPTB_OT_file_checker(bpy.types.Operator):
|
|||||||
bpy.context.scene.tool_settings.lock_object_mode = False
|
bpy.context.scene.tool_settings.lock_object_mode = False
|
||||||
|
|
||||||
if fix.remove_redundant_strokes:
|
if fix.remove_redundant_strokes:
|
||||||
print('removing redundant strokes')
|
|
||||||
ct = remove_stroke_exact_duplications(apply=apply)
|
ct = remove_stroke_exact_duplications(apply=apply)
|
||||||
if ct > 0:
|
if ct > 0:
|
||||||
mess = f'Removed {ct} strokes duplications' if apply else f'Found {ct} strokes duplications'
|
mess = f'Removed {ct} strokes duplications' if apply else f'Found {ct} strokes duplications'
|
||||||
@ -330,7 +273,7 @@ class GPTB_OT_file_checker(bpy.types.Operator):
|
|||||||
|
|
||||||
# ## Set onion skin filter to 'All type'
|
# ## Set onion skin filter to 'All type'
|
||||||
# fix_kf_type = 0
|
# fix_kf_type = 0
|
||||||
# for gp in bpy.data.grease_pencils_v3:#from data
|
# for gp in bpy.data.grease_pencils:#from data
|
||||||
# if not gp.is_annotation:
|
# if not gp.is_annotation:
|
||||||
# if gp.onion_keyframe_type != 'ALL':
|
# if gp.onion_keyframe_type != 'ALL':
|
||||||
# gp.onion_keyframe_type = 'ALL'
|
# gp.onion_keyframe_type = 'ALL'
|
||||||
@ -706,98 +649,6 @@ class GPTB_OT_list_object_visibility_conflicts(bpy.types.Operator):
|
|||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
## -- List collection visibility conflicts
|
|
||||||
|
|
||||||
# class GPTB_PG_collection_visibility(bpy.types.PropertyGroup):
|
|
||||||
# """Property group to handle collection visibility"""
|
|
||||||
# is_hidden: BoolProperty(
|
|
||||||
# name="Hide in Viewport",
|
|
||||||
# description="Toggle collection visibility in viewport",
|
|
||||||
# get=lambda self: self.get("is_hidden", False),
|
|
||||||
# set=lambda self, value: self.set_visibility(value)
|
|
||||||
# )
|
|
||||||
|
|
||||||
# collection_name: StringProperty(name="Collection Name")
|
|
||||||
|
|
||||||
def get_collection_children_recursive(col, cols=None) -> list:
|
|
||||||
'''return a list of all the child collections
|
|
||||||
and their subcollections in the passed collection'''
|
|
||||||
|
|
||||||
cols = cols or []
|
|
||||||
for sub in col.children:
|
|
||||||
if sub not in cols:
|
|
||||||
cols.append(sub)
|
|
||||||
if len(sub.children):
|
|
||||||
cols = get_collection_children_recursive(sub, cols)
|
|
||||||
return cols
|
|
||||||
|
|
||||||
class GPTB_OT_list_collection_visibility_conflicts(bpy.types.Operator):
|
|
||||||
bl_idname = "gp.list_collection_visibility_conflicts"
|
|
||||||
bl_label = "List Collection Visibility Conflicts"
|
|
||||||
bl_description = "List collection visibility conflicts, when viewport and render have different values"
|
|
||||||
bl_options = {"REGISTER"}
|
|
||||||
|
|
||||||
# visibility_items: CollectionProperty(type=GPTB_PG_collection_visibility)
|
|
||||||
show_filter : bpy.props.EnumProperty(
|
|
||||||
name="View Filter",
|
|
||||||
description="Filter collections based on their exclusion status",
|
|
||||||
items=(
|
|
||||||
('ALL', "All", "Show all collections", 0),
|
|
||||||
('NOT_EXCLUDED', "Not Excluded", "Show collections that are not excluded", 1),
|
|
||||||
('EXCLUDED', "Excluded", "Show collections that are excluded", 2)
|
|
||||||
),
|
|
||||||
default='NOT_EXCLUDED')
|
|
||||||
|
|
||||||
def invoke(self, context, event):
|
|
||||||
## get all viewlayer collections
|
|
||||||
vcols = get_collection_children_recursive(context.view_layer.layer_collection)
|
|
||||||
vcols = list(set(vcols)) # ensure no duplicates
|
|
||||||
|
|
||||||
## Store collection with conflicts
|
|
||||||
# layer_collection.is_visible against render visibility ?
|
|
||||||
## Do not list currently excluded collections
|
|
||||||
self.conflict_collections = [vc for vc in vcols if not (vc.hide_viewport == vc.collection.hide_viewport == vc.collection.hide_render)]
|
|
||||||
self.included_collection = [vc for vc in self.conflict_collections if not vc.exclude]
|
|
||||||
self.excluded_collection = [vc for vc in self.conflict_collections if vc.exclude]
|
|
||||||
|
|
||||||
return context.window_manager.invoke_props_dialog(self, width=250)
|
|
||||||
|
|
||||||
def draw(self, context):
|
|
||||||
layout = self.layout
|
|
||||||
layout.prop(self, 'show_filter', expand=True)
|
|
||||||
|
|
||||||
# Add sync buttons at the top
|
|
||||||
row = layout.row(align=False)
|
|
||||||
# TODO: Add "set all from" ops on collection (optionnal)
|
|
||||||
# row.label(text="Sync All Visibility From:")
|
|
||||||
# row.operator("gp.sync_visibility_from_viewlayer", text="", icon='HIDE_OFF')
|
|
||||||
# row.operator("gp.sync_visibility_from_viewport", text="", icon='RESTRICT_VIEW_OFF')
|
|
||||||
# row.operator("gp.sync_visibility_from_render", text="", icon='RESTRICT_RENDER_OFF')
|
|
||||||
layout.separator()
|
|
||||||
|
|
||||||
if self.show_filter == 'ALL':
|
|
||||||
vl_collections = self.conflict_collections
|
|
||||||
elif self.show_filter == 'EXCLUDED':
|
|
||||||
vl_collections = self.excluded_collection
|
|
||||||
elif self.show_filter == 'NOT_EXCLUDED':
|
|
||||||
vl_collections = self.included_collection
|
|
||||||
|
|
||||||
col = layout.column()
|
|
||||||
for vlcol in vl_collections:
|
|
||||||
row = col.row(align=False)
|
|
||||||
row.label(text=vlcol.name)
|
|
||||||
|
|
||||||
# Viewlayer collection settings
|
|
||||||
row.prop(vlcol, "exclude", text="", emboss=False)
|
|
||||||
row.prop(vlcol, "hide_viewport", text="", emboss=False)
|
|
||||||
|
|
||||||
# Direct collection properties
|
|
||||||
row.prop(vlcol.collection, 'hide_viewport', text='', emboss=False)
|
|
||||||
row.prop(vlcol.collection, 'hide_render', text='', emboss=False)
|
|
||||||
|
|
||||||
def execute(self, context):
|
|
||||||
return {'FINISHED'}
|
|
||||||
|
|
||||||
## not exposed in UI, Check is performed in Check file (can be called in popped menu)
|
## not exposed in UI, Check is performed in Check file (can be called in popped menu)
|
||||||
class GPTB_OT_list_modifier_visibility(bpy.types.Operator):
|
class GPTB_OT_list_modifier_visibility(bpy.types.Operator):
|
||||||
bl_idname = "gp.list_modifier_visibility"
|
bl_idname = "gp.list_modifier_visibility"
|
||||||
@ -845,13 +696,11 @@ GPTB_OT_sync_visibility_from_render,
|
|||||||
GPTB_OT_sync_visibible_to_render,
|
GPTB_OT_sync_visibible_to_render,
|
||||||
GPTB_PG_object_visibility,
|
GPTB_PG_object_visibility,
|
||||||
GPTB_OT_list_object_visibility_conflicts,
|
GPTB_OT_list_object_visibility_conflicts,
|
||||||
GPTB_OT_list_collection_visibility_conflicts,
|
|
||||||
GPTB_OT_list_modifier_visibility,
|
GPTB_OT_list_modifier_visibility,
|
||||||
GPTB_OT_copy_string_to_clipboard,
|
GPTB_OT_copy_string_to_clipboard,
|
||||||
GPTB_OT_copy_multipath_clipboard,
|
GPTB_OT_copy_multipath_clipboard,
|
||||||
GPTB_OT_file_checker,
|
GPTB_OT_file_checker,
|
||||||
GPTB_OT_links_checker,
|
GPTB_OT_links_checker,
|
||||||
GPTB_OT_remove_stroke_duplication,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
|
@ -12,7 +12,7 @@ from .utils import (location_to_region, region_to_location)
|
|||||||
## Do not work on multiple object
|
## Do not work on multiple object
|
||||||
def batch_flat_reproject(obj, proj_type='VIEW', all_strokes=True, restore_frame=False):
|
def batch_flat_reproject(obj, proj_type='VIEW', all_strokes=True, restore_frame=False):
|
||||||
'''Reproject
|
'''Reproject
|
||||||
:all_stroke: affect hidden, locked layers
|
:all_stroke: affect hided, locked layers
|
||||||
'''
|
'''
|
||||||
|
|
||||||
if restore_frame:
|
if restore_frame:
|
||||||
|
@ -2,6 +2,7 @@ import bpy
|
|||||||
from mathutils import Vector
|
from mathutils import Vector
|
||||||
from . import utils
|
from . import utils
|
||||||
|
|
||||||
|
|
||||||
class GPTB_OT_create_follow_path_curve(bpy.types.Operator):
|
class GPTB_OT_create_follow_path_curve(bpy.types.Operator):
|
||||||
bl_idname = "object.create_follow_path_curve"
|
bl_idname = "object.create_follow_path_curve"
|
||||||
bl_label = "Create Follow Path Curve"
|
bl_label = "Create Follow Path Curve"
|
||||||
|
@ -212,14 +212,9 @@ class GPTB_OT_draw_cam(Operator):
|
|||||||
# 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
|
||||||
|
|
||||||
## Hide cam object
|
## hide cam object
|
||||||
|
drawcam.hide_viewport = True
|
||||||
# Ensure at lviewport viz is active to ensure visibility and refresh state
|
|
||||||
maincam.hide_viewport = False
|
maincam.hide_viewport = False
|
||||||
# drawcam.hide_viewport = False # Not absolutely needed
|
|
||||||
|
|
||||||
drawcam.hide_set(True)
|
|
||||||
maincam.hide_set(False)
|
|
||||||
|
|
||||||
## if in main camera GO to drawcam
|
## if in main camera GO to drawcam
|
||||||
elif context.scene.camera.name not in ('draw_cam', 'obj_cam'):
|
elif context.scene.camera.name not in ('draw_cam', 'obj_cam'):
|
||||||
@ -272,12 +267,8 @@ class GPTB_OT_draw_cam(Operator):
|
|||||||
|
|
||||||
## hide cam object
|
## hide cam object
|
||||||
context.scene.camera = drawcam
|
context.scene.camera = drawcam
|
||||||
# Ensure viewport viz is active to ensure visibility and refresh state
|
|
||||||
drawcam.hide_viewport = False
|
drawcam.hide_viewport = False
|
||||||
maincam.hide_viewport = False
|
maincam.hide_viewport = True
|
||||||
## Set viewlayer visibility
|
|
||||||
drawcam.hide_set(False)
|
|
||||||
maincam.hide_set(True)
|
|
||||||
|
|
||||||
if created and drawcam.name == 'obj_cam': # Go in camera view
|
if created and drawcam.name == 'obj_cam': # Go in camera view
|
||||||
context.region_data.view_perspective = 'CAMERA'
|
context.region_data.view_perspective = 'CAMERA'
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import bpy
|
import bpy
|
||||||
from .utils import get_addon_prefs, is_locked, is_hidden
|
from .utils import get_addon_prefs
|
||||||
from bpy.props import BoolProperty ,EnumProperty ,StringProperty
|
from bpy.props import BoolProperty ,EnumProperty ,StringProperty
|
||||||
|
|
||||||
class GPTB_OT_jump_gp_keyframe(bpy.types.Operator):
|
class GPTB_OT_jump_gp_keyframe(bpy.types.Operator):
|
||||||
@ -45,15 +45,15 @@ class GPTB_OT_jump_gp_keyframe(bpy.types.Operator):
|
|||||||
return {"CANCELLED"}
|
return {"CANCELLED"}
|
||||||
|
|
||||||
if self.target == 'ACTIVE':
|
if self.target == 'ACTIVE':
|
||||||
gpl = [l for l in context.object.data.layers if l.select and not is_hidden(l)]
|
gpl = [l for l in context.object.data.layers if l.select and not l.hide]
|
||||||
if not context.object.data.layers.active in gpl:
|
if not context.object.data.layers.active in gpl:
|
||||||
gpl.append(context.object.data.layers.active)
|
gpl.append(context.object.data.layers.active)
|
||||||
|
|
||||||
elif self.target == 'VISIBLE':
|
elif self.target == 'VISIBLE':
|
||||||
gpl = [l for l in context.object.data.layers if not is_hidden(l)]
|
gpl = [l for l in context.object.data.layers if not l.hide]
|
||||||
|
|
||||||
elif self.target == 'ACCESSIBLE':
|
elif self.target == 'ACCESSIBLE':
|
||||||
gpl = [l for l in context.object.data.layers if not is_hidden(l) and not is_locked(l)]
|
gpl = [l for l in context.object.data.layers if not l.hide and not l.lock]
|
||||||
|
|
||||||
if self.keyframe_type != 'NONE':
|
if self.keyframe_type != 'NONE':
|
||||||
# use shortcut choice override
|
# use shortcut choice override
|
||||||
|
@ -4,7 +4,7 @@ import mathutils
|
|||||||
from mathutils import Vector, Matrix, geometry
|
from mathutils import Vector, Matrix, geometry
|
||||||
from bpy_extras import view3d_utils
|
from bpy_extras import view3d_utils
|
||||||
from time import time
|
from time import time
|
||||||
from .utils import get_gp_draw_plane, location_to_region, region_to_location, is_locked, is_hidden
|
from .utils import get_gp_draw_plane, location_to_region, region_to_location
|
||||||
|
|
||||||
class GP_OT_pick_closest_layer(Operator):
|
class GP_OT_pick_closest_layer(Operator):
|
||||||
bl_idname = "gp.pick_closest_layer"
|
bl_idname = "gp.pick_closest_layer"
|
||||||
@ -76,7 +76,7 @@ class GP_OT_pick_closest_layer(Operator):
|
|||||||
self.point_pair = []
|
self.point_pair = []
|
||||||
if context.scene.tool_settings.use_grease_pencil_multi_frame_editing:
|
if context.scene.tool_settings.use_grease_pencil_multi_frame_editing:
|
||||||
for layer in gp.layers:
|
for layer in gp.layers:
|
||||||
if is_hidden(layer):
|
if layer.hide:
|
||||||
continue
|
continue
|
||||||
for f in layer.frames:
|
for f in layer.frames:
|
||||||
if not f.select:
|
if not f.select:
|
||||||
@ -89,9 +89,9 @@ class GP_OT_pick_closest_layer(Operator):
|
|||||||
self.point_pair += [(Vector((*location_to_region(mat @ p.position), 0)), layer) for p in s.points]
|
self.point_pair += [(Vector((*location_to_region(mat @ p.position), 0)), layer) for p in s.points]
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# [s for l in gp.layers if not is_locked(l) and not is_hidden(l) for s in l.current_frame().stokes]
|
# [s for l in gp.layers if not l.lock and not l.hide for s in l.current_frame().stokes]
|
||||||
for layer in gp.layers:
|
for layer in gp.layers:
|
||||||
if is_hidden(layer) or not layer.current_frame():
|
if layer.hide or not layer.current_frame():
|
||||||
continue
|
continue
|
||||||
for s in layer.current_frame().drawing.strokes:
|
for s in layer.current_frame().drawing.strokes:
|
||||||
if self.stroke_filter == 'STROKE' and not self.ob.data.materials[s.material_index].grease_pencil.show_stroke:
|
if self.stroke_filter == 'STROKE' and not self.ob.data.materials[s.material_index].grease_pencil.show_stroke:
|
||||||
|
@ -4,12 +4,7 @@ import mathutils
|
|||||||
from mathutils import Vector, Matrix, geometry
|
from mathutils import Vector, Matrix, geometry
|
||||||
from bpy_extras import view3d_utils
|
from bpy_extras import view3d_utils
|
||||||
from time import time
|
from time import time
|
||||||
from .utils import (get_gp_draw_plane,
|
from .utils import get_gp_draw_plane, location_to_region, region_to_location
|
||||||
location_to_region,
|
|
||||||
region_to_location,
|
|
||||||
is_locked,
|
|
||||||
is_hidden)
|
|
||||||
|
|
||||||
|
|
||||||
### passing by 2D projection
|
### passing by 2D projection
|
||||||
def get_3d_coord_on_drawing_plane_from_2d(context, co):
|
def get_3d_coord_on_drawing_plane_from_2d(context, co):
|
||||||
@ -84,7 +79,7 @@ class GP_OT_pick_closest_material(Operator):
|
|||||||
|
|
||||||
if context.scene.tool_settings.use_grease_pencil_multi_frame_editing:
|
if context.scene.tool_settings.use_grease_pencil_multi_frame_editing:
|
||||||
for l in self.gp.layers:
|
for l in self.gp.layers:
|
||||||
if is_hidden(l):# is_locked(l) or
|
if l.hide:# l.lock or
|
||||||
continue
|
continue
|
||||||
for f in l.frames:
|
for f in l.frames:
|
||||||
if not f.select:
|
if not f.select:
|
||||||
@ -93,9 +88,9 @@ class GP_OT_pick_closest_material(Operator):
|
|||||||
self.stroke_list.append(s)
|
self.stroke_list.append(s)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# [s for l in self.gp.layers if not is_locked(l) and not is_hidden(l) for s in l.current_frame().stokes]
|
# [s for l in self.gp.layers if not l.lock and not l.hide for s in l.current_frame().stokes]
|
||||||
for l in self.gp.layers:
|
for l in self.gp.layers:
|
||||||
if is_hidden(l) or not l.current_frame():# is_locked(l) or
|
if l.hide or not l.current_frame():# l.lock or
|
||||||
continue
|
continue
|
||||||
for s in l.current_frame().drawing.strokes:
|
for s in l.current_frame().drawing.strokes:
|
||||||
self.stroke_list.append(s)
|
self.stroke_list.append(s)
|
||||||
@ -240,7 +235,7 @@ class GP_OT_pick_closest_material(Operator):
|
|||||||
|
|
||||||
if context.scene.tool_settings.use_grease_pencil_multi_frame_editing:
|
if context.scene.tool_settings.use_grease_pencil_multi_frame_editing:
|
||||||
for l in gp.layers:
|
for l in gp.layers:
|
||||||
if is_hidden(l):# is_locked(l) or
|
if l.hide:# l.lock or
|
||||||
continue
|
continue
|
||||||
for f in l.frames:
|
for f in l.frames:
|
||||||
if not f.select:
|
if not f.select:
|
||||||
@ -249,9 +244,9 @@ class GP_OT_pick_closest_material(Operator):
|
|||||||
self.stroke_list.append(s)
|
self.stroke_list.append(s)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# [s for l in gp.layers if not is_locked(l) and not is_hidden(l) for s in l.current_frame().stokes]
|
# [s for l in gp.layers if not l.lock and not l.hide for s in l.current_frame().stokes]
|
||||||
for l in gp.layers:
|
for l in gp.layers:
|
||||||
if is_hidden(l) or not l.current_frame():# is_locked(l) or
|
if l.hide or not l.current_frame():# l.lock or
|
||||||
continue
|
continue
|
||||||
for s in l.current_frame().drawing.strokes:
|
for s in l.current_frame().drawing.strokes:
|
||||||
self.stroke_list.append(s)
|
self.stroke_list.append(s)
|
||||||
|
@ -166,7 +166,7 @@ class GPTB_OT_import_obj_palette(Operator):
|
|||||||
|
|
||||||
# unlink objects and their gp data
|
# unlink objects and their gp data
|
||||||
for src_ob in linked_objs:
|
for src_ob in linked_objs:
|
||||||
bpy.data.grease_pencils_v3.remove(src_ob.data)
|
bpy.data.grease_pencils.remove(src_ob.data)
|
||||||
|
|
||||||
|
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
import bpy
|
import bpy
|
||||||
import mathutils
|
import mathutils
|
||||||
import numpy as np
|
|
||||||
|
|
||||||
from mathutils import Matrix, Vector
|
from mathutils import Matrix, Vector
|
||||||
from math import pi
|
from math import pi
|
||||||
|
import numpy as np
|
||||||
from time import time
|
from time import time
|
||||||
from mathutils.geometry import intersect_line_plane
|
|
||||||
from . import utils
|
from . import utils
|
||||||
from .utils import is_hidden, is_locked
|
from mathutils.geometry import intersect_line_plane
|
||||||
|
|
||||||
def get_scale_matrix(scale):
|
def get_scale_matrix(scale):
|
||||||
# recreate a neutral mat scale
|
# recreate a neutral mat scale
|
||||||
@ -17,6 +15,35 @@ def get_scale_matrix(scale):
|
|||||||
matscale = matscale_x @ matscale_y @ matscale_z
|
matscale = matscale_x @ matscale_y @ matscale_z
|
||||||
return matscale
|
return matscale
|
||||||
|
|
||||||
|
'''
|
||||||
|
## Old reproject method using Operators:
|
||||||
|
omode = bpy.context.mode
|
||||||
|
|
||||||
|
if all_strokes:
|
||||||
|
layers_state = [[l, l.hide, l.lock, l.lock_frame] for l in obj.data.layers]
|
||||||
|
for l in obj.data.layers:
|
||||||
|
l.hide = False
|
||||||
|
l.lock = False
|
||||||
|
l.lock_frame = False
|
||||||
|
bpy.ops.object.mode_set(mode='EDIT')
|
||||||
|
|
||||||
|
|
||||||
|
for fnum in frame_list:
|
||||||
|
bpy.context.scene.frame_current = fnum
|
||||||
|
bpy.ops.gpencil.select_all(action='SELECT')
|
||||||
|
bpy.ops.gpencil.reproject(type=proj_type) # 'INVOKE_DEFAULT'
|
||||||
|
bpy.ops.gpencil.select_all(action='DESELECT')
|
||||||
|
|
||||||
|
# restore
|
||||||
|
if all_strokes:
|
||||||
|
for layer, hide, lock, lock_frame in layers_state:
|
||||||
|
layer.hide = hide
|
||||||
|
layer.lock = lock
|
||||||
|
layer.lock_frame = lock_frame
|
||||||
|
|
||||||
|
bpy.ops.object.mode_set(mode=omode)
|
||||||
|
'''
|
||||||
|
|
||||||
def batch_reproject(obj, proj_type='VIEW', all_strokes=True, restore_frame=False):
|
def batch_reproject(obj, proj_type='VIEW', all_strokes=True, restore_frame=False):
|
||||||
'''Reproject - ops method
|
'''Reproject - ops method
|
||||||
:all_stroke: affect hidden, locked layers
|
:all_stroke: affect hidden, locked layers
|
||||||
@ -46,7 +73,7 @@ def batch_reproject(obj, proj_type='VIEW', all_strokes=True, restore_frame=False
|
|||||||
if not all_strokes:
|
if not all_strokes:
|
||||||
if not layer.select:
|
if not layer.select:
|
||||||
continue
|
continue
|
||||||
if is_hidden(layer) or is_locked(layer):
|
if layer.hide or layer.lock:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
frame = next((f for f in layer.frames if f.frame_number == i), None)
|
frame = next((f for f in layer.frames if f.frame_number == i), None)
|
||||||
|
@ -6,7 +6,7 @@ Blender addon - Various tool to help with grease pencil in animation productions
|
|||||||
|
|
||||||
**[Download latest](https://git.autourdeminuit.com/autour_de_minuit/gp_toolbox/archive/master.zip)**
|
**[Download latest](https://git.autourdeminuit.com/autour_de_minuit/gp_toolbox/archive/master.zip)**
|
||||||
|
|
||||||
**[Download for Blender 4.2 and below from release page](https://git.autourdeminuit.com/autour_de_minuit/gp_toolbox/releases)**
|
**[Download for Blender 4.2 and below](https://git.autourdeminuit.com/autour_de_minuit/gp_toolbox/archive/v3.3.0.zip)**
|
||||||
|
|
||||||
**[Demo video](https://www.youtube.com/watch?v=Htgao_uPWNs)**
|
**[Demo video](https://www.youtube.com/watch?v=Htgao_uPWNs)**
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ Blender addon - Boîte à outils de grease pencil pour la production d'animation
|
|||||||
|
|
||||||
**[Télécharger la dernière version](https://git.autourdeminuit.com/autour_de_minuit/gp_toolbox/archive/master.zip)**
|
**[Télécharger la dernière version](https://git.autourdeminuit.com/autour_de_minuit/gp_toolbox/archive/master.zip)**
|
||||||
|
|
||||||
**[Téléchargement pour Blender 4.2 ou inférieur depuis la page des releases](https://git.autourdeminuit.com/autour_de_minuit/gp_toolbox/releases)**
|
**[Télécharger pour Blender 4.2 ou inférieure](https://git.autourdeminuit.com/autour_de_minuit/gp_toolbox/archive/v3.3.0.zip)**
|
||||||
|
|
||||||
**[Demo video](https://www.youtube.com/watch?v=Htgao_uPWNs)**
|
**[Demo video](https://www.youtube.com/watch?v=Htgao_uPWNs)**
|
||||||
|
|
||||||
|
@ -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": (4, 1, 3),
|
"version": (4, 0, 3),
|
||||||
"blender": (4, 3, 0),
|
"blender": (4, 3, 0),
|
||||||
"location": "Sidebar (N menu) > Gpencil > Toolbox / Gpencil properties",
|
"location": "Sidebar (N menu) > Gpencil > Toolbox / Gpencil properties",
|
||||||
"warning": "",
|
"warning": "",
|
||||||
|
@ -11,12 +11,10 @@ from bpy.props import (
|
|||||||
from .OP_cursor_snap_canvas import cursor_follow_update
|
from .OP_cursor_snap_canvas import cursor_follow_update
|
||||||
from .OP_layer_manager import layer_name_build
|
from .OP_layer_manager import layer_name_build
|
||||||
|
|
||||||
|
def change_edit_lines_opacity(self, context):
|
||||||
## Obsolete: Gpv3 has no edit line color anymore
|
for gp in bpy.data.grease_pencils:
|
||||||
# def change_edit_lines_opacity(self, context):
|
if not gp.is_annotation:
|
||||||
# for gp in bpy.data.grease_pencils:
|
gp.edit_line_color[3]=self.edit_lines_opacity
|
||||||
# if not gp.is_annotation:
|
|
||||||
# gp.edit_line_color[3]=self.edit_lines_opacity
|
|
||||||
|
|
||||||
|
|
||||||
def update_layer_name(self, context):
|
def update_layer_name(self, context):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user