Compare commits

..

No commits in common. "master" and "v3.3.2" have entirely different histories.

18 changed files with 88 additions and 253 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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