Fix duplicate-send to to layer
parent
d3e4072564
commit
6dbf666ee3
|
@ -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
101
utils.py
|
@ -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
|
||||||
# -----------------
|
# -----------------
|
||||||
|
|
Loading…
Reference in New Issue