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
from bpy.types import Operator
from . import utils
def get_layer_list(self, context):
@ -65,27 +66,25 @@ class GPTB_OT_duplicate_send_to_layer(Operator) :
## Remove overlapping frames
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
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)
## Delete original frames as an option
if self.delete_source:
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:
mess += f' ({replaced} replaced)'
mod = context.mode
bpy.ops.gpencil.editmode_toggle()
bpy.ops.object.mode_set(mode=mod)
self.report({'INFO'}, mess)
return {'FINISHED'}

101
utils.py
View File

@ -5,6 +5,7 @@ import mathutils
import math
import subprocess
from time import time
from math import sqrt
from mathutils import Vector
from sys import platform
@ -589,6 +590,106 @@ def copy_stroke_to_frame(s, frame, select=True):
ns.points.update()
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
# -----------------