Fix duplicate-send to to layer

master
pullusb 2024-11-21 15:11:06 +01:00
parent d3e4072564
commit 6dbf666ee3
2 changed files with 110 additions and 10 deletions

View File

@ -1,5 +1,6 @@
import bpy import bpy
from bpy.types import Operator from bpy.types import Operator
from . import utils
def get_layer_list(self, context): def get_layer_list(self, context):
@ -65,26 +66,24 @@ class GPTB_OT_duplicate_send_to_layer(Operator) :
## Remove overlapping frames ## Remove overlapping frames
for f in reversed(to_replace): for f in reversed(to_replace):
target_layer.frames.remove(f) target_layer.frames.remove(f.frame_number)
# FIXME : need to createa a dedicated function to copy a frame
## Copy original frames ## Copy original frames
for f in selected_frames: for f in selected_frames:
target_layer.frames.copy(f) utils.copy_frame_at(f, target_layer, f.frame_number)
# target_layer.frames.copy(f) # GPv2
sent = len(selected_frames) sent = len(selected_frames)
## Delete original frames as an option ## Delete original frames as an option
if self.delete_source: if self.delete_source:
for f in reversed(selected_frames): for f in reversed(selected_frames):
act_layer.frames.remove(f) act_layer.frames.remove(f.frame_number)
mess = f'{sent} keys moved'
else:
mess = f'{sent} keys copied'
mess = f'{sent} keys copied'
if replaced: if replaced:
mess += f' ({replaced} replaced)' mess += f' ({replaced} replaced)'
mod = context.mode
bpy.ops.gpencil.editmode_toggle()
bpy.ops.object.mode_set(mode=mod)
self.report({'INFO'}, mess) self.report({'INFO'}, mess)
return {'FINISHED'} return {'FINISHED'}

101
utils.py
View File

@ -5,6 +5,7 @@ import mathutils
import math import math
import subprocess import subprocess
from time import time
from math import sqrt from math import sqrt
from mathutils import Vector from mathutils import Vector
from sys import platform from sys import platform
@ -589,6 +590,106 @@ def copy_stroke_to_frame(s, frame, select=True):
ns.points.update() ns.points.update()
return ns return ns
def bulk_copy_attributes(source_attr, target_attr):
# Get the data as flat numpy array based on attribute type and domain
if source_attr.data_type == 'INT':
data = np.empty(len(source_attr.data), dtype=np.int32)
source_attr.data.foreach_get('value', data)
target_attr.data.foreach_set('value', data)
elif source_attr.data_type == 'INT8':
data = np.empty(len(source_attr.data), dtype=np.int8)
source_attr.data.foreach_get('value', data)
target_attr.data.foreach_set('value', data)
elif source_attr.data_type == 'FLOAT':
data = np.empty(len(source_attr.data), dtype=np.float32)
source_attr.data.foreach_get('value', data)
target_attr.data.foreach_set('value', data)
elif source_attr.data_type == 'FLOAT_VECTOR':
data = np.empty(len(source_attr.data) * 3, dtype=np.float32)
source_attr.data.foreach_get('vector', data)
target_attr.data.foreach_set('vector', data)
elif source_attr.data_type == 'FLOAT_COLOR':
data = np.empty(len(source_attr.data) * 4, dtype=np.float32)
source_attr.data.foreach_get('color', data)
target_attr.data.foreach_set('color', data)
elif source_attr.data_type == 'BOOLEAN':
data = np.empty(len(source_attr.data), dtype=bool)
source_attr.data.foreach_get('value', data)
target_attr.data.foreach_set('value', data)
## Filter by domain ? (not needed it seem, dtypes are the same)
'''
if source_attr.domain == 'POINT':
if source_attr.data_type == 'FLOAT':
data = np.empty(len(source_attr.data), dtype=np.float32)
source_attr.data.foreach_get('value', data)
target_attr.data.foreach_set('value', data)
elif source_attr.data_type == 'FLOAT_VECTOR':
data = np.empty(len(source_attr.data) * 3, dtype=np.float32)
source_attr.data.foreach_get('vector', data)
target_attr.data.foreach_set('vector', data)
elif source_attr.data_type == 'FLOAT_COLOR':
data = np.empty(len(source_attr.data) * 4, dtype=np.float32)
source_attr.data.foreach_get('color', data)
target_attr.data.foreach_set('color', data)
## No Boolean by defaut on points domain
elif source_attr.domain == 'CURVE':
if source_attr.data_type == 'INT':
data = np.empty(len(source_attr.data), dtype=np.int32)
source_attr.data.foreach_get('value', data)
target_attr.data.foreach_set('value', data)
elif source_attr.data_type == 'INT8':
data = np.empty(len(source_attr.data), dtype=np.int8)
source_attr.data.foreach_get('value', data)
target_attr.data.foreach_set('value', data)
elif source_attr.data_type == 'FLOAT_COLOR':
data = np.empty(len(source_attr.data) * 4, dtype=np.float32)
source_attr.data.foreach_get('color', data)
target_attr.data.foreach_set('color', data)
elif source_attr.data_type == 'BOOLEAN':
data = np.empty(len(source_attr.data), dtype=bool)
source_attr.data.foreach_get('value', data)
target_attr.data.foreach_set('value', data)
'''
def copy_attribute_values(src_dr, dst_dr, source_attr, dest_attr):
## Zip method to copy one by one
## One liner
# val_type = {'FLOAT_COLOR': 'color','FLOAT_VECTOR': 'vector'}.get(source_attr.data_type, 'value')
if source_attr.data_type == 'FLOAT_COLOR':
val_type = 'color'
elif source_attr.data_type == 'FLOAT_VECTOR':
val_type = 'vector'
else:
val_type = 'value'
## /!\ DOESN'T WORK. index is wrong
for src_idx, dest_idx in zip(
range(src_dr.curve_offsets[0].value, src_dr.curve_offsets[-1].value),
range(dst_dr.curve_offsets[0].value, dst_dr.curve_offsets[-1].value)):
setattr(dest_attr.data[dest_idx], val_type, getattr(source_attr.data[src_idx], val_type))
def copy_frame_at(source_frame, layer, frame_number):
'''Copy a frame (source_frame) to a layer at given frame_number'''
source_drawing = source_frame.drawing
frame = layer.frames.new(frame_number)
dr = frame.drawing
dr.add_strokes([len(s.points) for s in source_drawing.strokes])
for attr_name in source_drawing.attributes.keys():
source_attr = source_drawing.attributes[attr_name]
if attr_name not in dr.attributes:
dr.attributes.new(
name=attr_name, type=source_attr.data_type, domain=source_attr.domain)
target_attr = dr.attributes[attr_name]
bulk_copy_attributes(source_attr, target_attr)
## Broken
# start_time = time()
# copy_attribute_values(source_drawing, dr, source_attr, target_attr)
# end_time = time()
# print(f"copy_attribute_values execution time: {end_time - start_time} seconds")
# ----------------- # -----------------
### Vector utils 3d ### Vector utils 3d
# ----------------- # -----------------