2021-09-07 23:11:42 +02:00
|
|
|
import bpy
|
|
|
|
import re
|
|
|
|
from mathutils import Vector
|
|
|
|
from pathlib import Path
|
2021-09-17 18:36:15 +02:00
|
|
|
from math import isclose
|
2021-09-07 23:11:42 +02:00
|
|
|
from collections import defaultdict
|
|
|
|
|
|
|
|
|
|
|
|
def create_node(type, tree=None, **kargs):
|
|
|
|
'''Get a type, a tree to add in, and optionnaly multiple attribute to set
|
|
|
|
return created node
|
|
|
|
'''
|
|
|
|
tree = tree or bpy.context.scene.node_tree
|
|
|
|
|
|
|
|
node = tree.nodes.new(type)
|
|
|
|
for k,v in kargs.items():
|
|
|
|
setattr(node, k, v)
|
|
|
|
|
|
|
|
return node
|
|
|
|
|
|
|
|
def new_aa_node(tree):
|
|
|
|
'''create AA node'''
|
|
|
|
aa = create_node('CompositorNodeAntiAliasing', tree) # type = ANTIALIASING
|
2021-09-17 18:36:15 +02:00
|
|
|
aa.threshold = 0.5 # 1.0
|
|
|
|
aa.contrast_limit = 0.5 # 0.25
|
2021-09-07 23:11:42 +02:00
|
|
|
aa.corner_rounding = 0.25
|
|
|
|
aa.hide = True
|
|
|
|
return aa
|
|
|
|
|
|
|
|
|
2021-09-15 18:28:16 +02:00
|
|
|
def copy_settings(obj_a, obj_b):
|
|
|
|
exclusion = ['bl_rna', 'id_data', 'identifier','name_property','rna_type','properties', 'stamp_note_text','use_stamp_note',
|
|
|
|
'settingsFilePath', 'settingsStamp', 'select', 'matrix_local', 'matrix_parent_inverse',
|
|
|
|
'matrix_basis','location','rotation_euler', 'rotation_quaternion', 'rotation_axis_angle', 'scale']
|
|
|
|
|
|
|
|
for attr in dir(obj_a):
|
|
|
|
if attr.startswith('__'):
|
|
|
|
continue
|
|
|
|
if attr in exclusion:
|
|
|
|
continue
|
|
|
|
# print('attr: ', attr)
|
|
|
|
# if obj_a.is_property_readonly(attr): # block when things aren't attribute
|
|
|
|
# continue
|
|
|
|
try:
|
|
|
|
val = getattr(obj_a, attr)
|
|
|
|
except AttributeError:
|
2021-09-15 19:36:06 +02:00
|
|
|
# print(f'cant get {attr}')
|
|
|
|
pass
|
2021-09-15 18:28:16 +02:00
|
|
|
|
|
|
|
try:
|
|
|
|
setattr(obj_b, attr, val)
|
|
|
|
except:
|
2021-09-15 19:36:06 +02:00
|
|
|
# print(f"can't set {attr}")
|
|
|
|
pass
|
2021-09-15 18:28:16 +02:00
|
|
|
|
|
|
|
|
2021-09-07 23:11:42 +02:00
|
|
|
def set_settings(scene=None):
|
|
|
|
if not scene:
|
|
|
|
scene = bpy.context.scene
|
|
|
|
# specify scene settings for these kind of render
|
|
|
|
scene.eevee.taa_render_samples = 1
|
|
|
|
scene.grease_pencil_settings.antialias_threshold = 0
|
2021-09-10 18:32:50 +02:00
|
|
|
scene.render.film_transparent = True
|
|
|
|
scene.view_settings.view_transform = 'Standard'
|
|
|
|
|
2021-09-07 23:11:42 +02:00
|
|
|
|
|
|
|
def get_render_scene():
|
|
|
|
'''Get / Create a scene named Render'''
|
|
|
|
render = bpy.data.scenes.get('Render')
|
|
|
|
if not render:
|
2021-09-15 18:28:16 +02:00
|
|
|
current = bpy.context.scene
|
2021-09-07 23:11:42 +02:00
|
|
|
render = bpy.data.scenes.new('Render')
|
2021-09-15 18:28:16 +02:00
|
|
|
## copy original settings over to new scene
|
|
|
|
# copy_settings(current, render) # BAD
|
2021-09-17 18:36:15 +02:00
|
|
|
for attr in ['frame_start', 'frame_end', 'frame_current', 'camera', 'world']:
|
2021-09-15 18:28:16 +02:00
|
|
|
setattr(render, attr, getattr(current, attr))
|
|
|
|
copy_settings(current.render, render.render)
|
2021-09-08 18:29:10 +02:00
|
|
|
|
|
|
|
## link cameras (and lights ?)
|
|
|
|
for ob in bpy.context.scene.objects:
|
|
|
|
if ob.type in ('CAMERA', 'LIGHT'):
|
|
|
|
render.collection.objects.link(ob)
|
2021-09-15 18:28:16 +02:00
|
|
|
|
2021-09-15 19:36:06 +02:00
|
|
|
# use same cam and world
|
2021-09-17 18:36:15 +02:00
|
|
|
# render.camera = current.camera
|
|
|
|
# render.world = current.world
|
2021-09-08 18:29:10 +02:00
|
|
|
|
|
|
|
# set adapted render settings (no AA)
|
|
|
|
set_settings(render)
|
2021-09-07 23:11:42 +02:00
|
|
|
render.use_nodes = True
|
|
|
|
return render
|
|
|
|
|
|
|
|
def get_view_layer(name, scene=None):
|
|
|
|
'''get viewlayer name
|
|
|
|
return existing/created viewlayer
|
|
|
|
'''
|
|
|
|
if not scene:
|
|
|
|
scene = get_render_scene()
|
|
|
|
### pass double letter prefix as suffix
|
|
|
|
## pass_name = re.sub(r'^([A-Z]{2})(_)(.*)', r'\3\2\1', 'name')
|
|
|
|
## pass_name = f'{name}_{passe}'
|
|
|
|
pass_vl = scene.view_layers.get(name)
|
|
|
|
if not pass_vl:
|
|
|
|
pass_vl = scene.view_layers.new(name)
|
|
|
|
return pass_vl
|
|
|
|
|
|
|
|
### node location tweaks
|
|
|
|
|
|
|
|
def real_loc(n):
|
|
|
|
if not n.parent:
|
|
|
|
return n.location
|
|
|
|
return n.location + real_loc(n.parent)
|
|
|
|
|
|
|
|
def get_frame_transform(f, node_tree=None):
|
|
|
|
'''Return real transform location of a frame node
|
|
|
|
only works with one level of nesting (not recursive)
|
|
|
|
'''
|
|
|
|
if not node_tree:
|
|
|
|
node_tree = f.id_data
|
|
|
|
if f.type != 'FRAME':
|
|
|
|
return
|
|
|
|
# return real_loc(f), f.dimensions
|
|
|
|
|
|
|
|
childs = [n for n in node_tree.nodes if n.parent == f]
|
|
|
|
# real_locs = [f.location + n.location for n in childs]
|
|
|
|
|
|
|
|
xs = [n.location.x for n in childs] + [n.location.x + n.dimensions.x for n in childs]
|
|
|
|
ys = [n.location.y for n in childs] + [n.location.y - n.dimensions.y for n in childs]
|
|
|
|
xs.sort(key=lambda loc: loc) # x val : ascending
|
|
|
|
ys.sort(key=lambda loc: loc) # ascending # , reversed=True) # y val : descending
|
|
|
|
|
|
|
|
loc = Vector((min(xs), max(ys)))
|
|
|
|
dim = Vector((max(xs) - min(xs) + 60, max(ys) - min(ys) + 60))
|
|
|
|
|
|
|
|
return loc, dim
|
|
|
|
|
|
|
|
|
|
|
|
## get all frames with their real transform.
|
|
|
|
|
|
|
|
def bbox(f, frames):
|
|
|
|
xs=[]
|
|
|
|
ys=[]
|
|
|
|
for n in frames[f]: # nodes of passed frame
|
|
|
|
# Better as Vectors ?
|
|
|
|
if n.type == 'FRAME':
|
|
|
|
if n not in frames.keys():
|
|
|
|
# print(f'frame {n.name} not in frame list')
|
|
|
|
continue
|
|
|
|
all_xs, all_ys = bbox(n, frames) # frames[n]
|
|
|
|
xs += all_xs
|
|
|
|
ys += all_ys
|
|
|
|
|
|
|
|
else:
|
|
|
|
loc = real_loc(n)
|
|
|
|
xs += [loc.x, loc.x + n.dimensions.x] # + (n.dimensions.x/get_dpi_factor())
|
|
|
|
ys += [loc.y, loc.y - n.dimensions.y] # - (n.dimensions.y/get_dpi_factor())
|
|
|
|
|
|
|
|
|
|
|
|
# margin ~= 30
|
|
|
|
# return xs and ys
|
|
|
|
return [min(xs)-30, max(xs)+30], [min(ys)-30, max(ys)+30]
|
|
|
|
|
|
|
|
def get_frames_bbox(node_tree):
|
|
|
|
'''Return a dic with all frames
|
|
|
|
ex: {frame_node: (location, dimension), ...}
|
|
|
|
'''
|
|
|
|
|
|
|
|
# create dic of frame object with his direct child nodes nodes
|
|
|
|
frames = defaultdict(list)
|
|
|
|
frames_bbox = {}
|
|
|
|
for n in node_tree.nodes:
|
|
|
|
if not n.parent:
|
|
|
|
continue
|
|
|
|
# also contains frames
|
|
|
|
frames[n.parent].append(n)
|
|
|
|
|
|
|
|
# Dic for bbox coord
|
|
|
|
for f, nodes in frames.items():
|
|
|
|
if f.parent:
|
|
|
|
continue
|
|
|
|
|
|
|
|
xs, ys = bbox(f, frames)
|
|
|
|
# xs, ys = bbox(nodes, frames)
|
|
|
|
|
|
|
|
## returning: list of corner coords
|
|
|
|
# coords = [
|
|
|
|
# Vector((xs[0], ys[1])),
|
|
|
|
# Vector((xs[1], ys[1])),
|
|
|
|
# Vector((xs[1], ys[0])),
|
|
|
|
# Vector((xs[0], ys[0])),
|
|
|
|
# ]
|
|
|
|
# frames_bbox[f] = coords
|
|
|
|
|
|
|
|
## returning: (loc vector, dimensions vector)
|
|
|
|
frames_bbox[f] = Vector((xs[0], ys[1])), Vector((xs[1] - xs[0], ys[1] - ys[0]))
|
|
|
|
|
|
|
|
return frames_bbox
|
|
|
|
|
|
|
|
|
|
|
|
## nodes helper functions
|
|
|
|
|
|
|
|
def clear_nodegroup(name, full_clear=False):
|
|
|
|
'''remove duplication of a nodegroup (.???)
|
|
|
|
also remove the base one if full_clear True
|
|
|
|
'''
|
|
|
|
|
|
|
|
for ng in reversed(bpy.data.node_groups):
|
|
|
|
pattern = name + r'\.\d{3}'
|
2021-09-10 18:32:50 +02:00
|
|
|
|
|
|
|
if not full_clear and ng.users:
|
|
|
|
continue
|
|
|
|
|
2021-09-07 23:11:42 +02:00
|
|
|
if re.search(pattern, ng.name):
|
|
|
|
bpy.data.node_groups.remove(ng)
|
|
|
|
|
|
|
|
if full_clear and ng.name == name:
|
|
|
|
# if full clear
|
|
|
|
bpy.data.node_groups.remove(ng)
|
|
|
|
|
|
|
|
|
|
|
|
def rearrange_frames(node_tree):
|
|
|
|
frame_d = get_frames_bbox(node_tree) # dic : {frame_node:(loc vector, dimensions vector), ...}
|
|
|
|
if not frame_d:
|
|
|
|
print('no frame found')
|
|
|
|
return
|
|
|
|
|
2021-09-15 19:36:06 +02:00
|
|
|
# print([f.name for f in frame_d.keys()])
|
|
|
|
|
2021-09-07 23:11:42 +02:00
|
|
|
## order the dict by frame.y location
|
|
|
|
frame_d = {key: value for key, value in sorted(frame_d.items(), key=lambda pair: pair[1][0].y - pair[1][1].y, reverse=True)}
|
|
|
|
frames = [[f, v[0], v[1].y] for f, v in frame_d.items()] # [frame_node, real_loc, real dimensions]
|
2021-09-16 00:19:57 +02:00
|
|
|
|
2021-09-07 23:11:42 +02:00
|
|
|
top = frames[0][1].y # upper node location.y
|
2021-09-16 00:19:57 +02:00
|
|
|
# top = 0 #always start a 0
|
2021-09-07 23:11:42 +02:00
|
|
|
offset = 0
|
|
|
|
for f in frames:
|
|
|
|
## f[1] : real loc Vector
|
|
|
|
## f[0] : frame
|
|
|
|
|
|
|
|
## move frame by offset needed (delta between real_loc and "fake" loc , minus offset)
|
2021-09-16 00:19:57 +02:00
|
|
|
f[0].location.y = (f[1].y - f[0].location.y) - offset # avoid offset when recalculating from 0 top
|
2021-09-07 23:11:42 +02:00
|
|
|
# f[0].location.y = f[1].y - top - offset
|
2021-09-16 00:19:57 +02:00
|
|
|
offset += f[2] + 200 # gap
|
2021-09-07 23:11:42 +02:00
|
|
|
|
|
|
|
f[0].update()
|
|
|
|
|
|
|
|
def reorder_inputs(ng):
|
|
|
|
rl_nodes = [s.links[0].from_node for s in ng.inputs if s.is_linked and s.links and s.links[0].from_node.type == 'R_LAYERS']
|
|
|
|
rl_nodes.sort(key=lambda x: x.location.y, reverse=True)
|
|
|
|
names = [n.layer for n in rl_nodes]
|
|
|
|
inputs_names = [s.name for s in ng.inputs]
|
|
|
|
filtered_names = [n for n in names if n in inputs_names]
|
|
|
|
|
|
|
|
for dest, name in enumerate(filtered_names):
|
|
|
|
## rebuild list at each iteration so index are good
|
|
|
|
inputs_names = [s.name for s in ng.inputs]
|
|
|
|
src = inputs_names.index(name)
|
|
|
|
# reorder on node_tree not directly on node!
|
|
|
|
ng.node_tree.inputs.move(src, dest)
|
|
|
|
|
|
|
|
def reorder_outputs(ng):
|
|
|
|
ordered_out_name = [nis.name for nis in ng.inputs if nis.name in [o.name for o in ng.outputs]]
|
|
|
|
for s_name in ordered_out_name:
|
|
|
|
all_outnames = [o.name for o in ng.outputs]
|
|
|
|
# reorder on nodetree, not on node !
|
|
|
|
ng.node_tree.outputs.move(all_outnames.index(s_name), ordered_out_name.index(s_name))
|
|
|
|
|
|
|
|
def clear_disconnected(fo):
|
|
|
|
for inp in reversed(fo.inputs):
|
|
|
|
if not inp.is_linked:
|
|
|
|
print(f'Deleting unlinked fileout slot: {inp.name}')
|
|
|
|
fo.inputs.remove(inp)
|
|
|
|
|
|
|
|
def reorder_fileout(fo, ng=None):
|
|
|
|
if not ng: # get connected nodegroup
|
|
|
|
for s in fo.inputs:
|
|
|
|
if s.is_linked and s.links and s.links[0].from_node.type == 'GROUP':
|
|
|
|
ng = s.links[0].from_node
|
|
|
|
break
|
|
|
|
if not ng:
|
|
|
|
print(f'No nodegroup to refer to filter {fo.name}')
|
|
|
|
return
|
|
|
|
ordered = [o.links[0].to_socket.name for o in ng.outputs if o.is_linked and o.is_linked and o.links[0].to_node == fo]
|
|
|
|
for s_name in ordered:
|
|
|
|
all_outnames = [s.name for s in fo.inputs] # same as [fs.path for fs in fo.file_slots]
|
|
|
|
fo.inputs.move(all_outnames.index(s_name), ordered.index(s_name))
|
|
|
|
|
2021-09-10 18:32:50 +02:00
|
|
|
def reorganise_NG_nodegroup(ng):
|
|
|
|
'''refit node content to avoid overlap'''
|
|
|
|
ngroup = ng.node_tree
|
|
|
|
ng_in = ngroup.nodes.get('Group Input')
|
|
|
|
offset = 35
|
|
|
|
y = 0
|
|
|
|
for s in ng_in.outputs:
|
|
|
|
if s.is_linked:
|
|
|
|
s.links[0].to_node.location.y = y
|
|
|
|
y -= offset
|
|
|
|
|
2021-09-07 23:11:42 +02:00
|
|
|
def connect_to_group_output(n):
|
|
|
|
for o in n.outputs:
|
|
|
|
if o.is_linked:
|
|
|
|
if o.links[0].to_node.type == 'GROUP_OUTPUT':
|
|
|
|
return o.links[0].to_socket
|
|
|
|
val = connect_to_group_output(o.links[0].to_node)
|
|
|
|
if val:
|
|
|
|
return val
|
|
|
|
return False
|
|
|
|
|
|
|
|
def connect_to_group_input(n):
|
|
|
|
for i in n.inputs:
|
|
|
|
if i.is_linked:
|
|
|
|
if i.links[0].from_node.type == 'GROUP_INPUT':
|
|
|
|
return i.links[0].from_socket
|
|
|
|
val = connect_to_group_input(i.links[0].from_node)
|
|
|
|
if val:
|
|
|
|
return val
|
2021-09-08 18:29:10 +02:00
|
|
|
return False
|
|
|
|
|
2021-09-10 18:32:50 +02:00
|
|
|
def clear_nodegroup_content_if_disconnected(ngroup):
|
|
|
|
'''Get a nodegroup.node_tree
|
|
|
|
delete orphan nodes that are not connected from group input node
|
|
|
|
'''
|
|
|
|
for n in reversed(ngroup.nodes):
|
|
|
|
if n.type in ('GROUP_INPUT', 'GROUP_OUTPUT'):
|
|
|
|
continue
|
|
|
|
if not connect_to_group_input(n) and not connect_to_group_output(n): # is disconnected from both side
|
|
|
|
ngroup.nodes.remove(n)
|
|
|
|
|
2021-09-15 18:28:16 +02:00
|
|
|
def clean_nodegroup_inputs(ng, skip_existing_pass=True):
|
2021-09-10 18:32:50 +02:00
|
|
|
'''Clear inputs to output of passed nodegroup if not connected'''
|
|
|
|
ngroup = ng.node_tree
|
2021-09-15 18:28:16 +02:00
|
|
|
rl_nodes = [n.layer for n in ng.id_data.nodes if n.type == 'R_LAYERS']
|
2021-09-10 18:32:50 +02:00
|
|
|
for i in range(len(ng.inputs))[::-1]:
|
2021-09-15 18:28:16 +02:00
|
|
|
if not ng.inputs[i].is_linked:
|
|
|
|
if skip_existing_pass and any(ng.inputs[i].name == x for x in rl_nodes):
|
|
|
|
# a render layer of this name still exists
|
|
|
|
continue
|
2021-09-10 18:32:50 +02:00
|
|
|
ngroup.inputs.remove(ngroup.inputs[i])
|
|
|
|
|
|
|
|
# clear_nodegroup_content_if_disconnected(ngroup)
|
|
|
|
|
2021-09-08 18:29:10 +02:00
|
|
|
def random_color(alpha=False):
|
|
|
|
import random
|
|
|
|
if alpha:
|
|
|
|
return (random.uniform(0,1), random.uniform(0,1), random.uniform(0,1), 1)
|
2021-09-10 18:32:50 +02:00
|
|
|
return (random.uniform(0,1), random.uniform(0,1), random.uniform(0,1))
|
|
|
|
|
2021-09-10 22:59:52 +02:00
|
|
|
def nodegroup_merge_inputs(ngroup):
|
|
|
|
'''Get a nodegroup
|
|
|
|
merge every group inputs with alpha over
|
|
|
|
then connect to antialias and a new output
|
|
|
|
'''
|
|
|
|
|
|
|
|
ng_in = ngroup.nodes.get('Group Input')
|
|
|
|
ng_out = ngroup.nodes.get('Group Output')
|
|
|
|
|
|
|
|
x, y = ng_in.location.x + 200, 0
|
|
|
|
|
|
|
|
offset_x, offset_y = 150, -100
|
|
|
|
|
|
|
|
# merge all inputs in alphaover nodes
|
|
|
|
prev = None
|
|
|
|
for i in range(len(ng_in.outputs)-1): # skip waiting point
|
|
|
|
inp = ng_in.outputs[i]
|
|
|
|
if not prev:
|
|
|
|
prev = ng_in
|
|
|
|
continue
|
|
|
|
|
|
|
|
# live connect
|
|
|
|
ao = create_node('CompositorNodeAlphaOver', tree=ngroup, location=(x,y), hide=True)
|
|
|
|
ngroup.links.new(prev.outputs[0], ao.inputs[1])
|
|
|
|
ngroup.links.new(inp, ao.inputs[2])
|
|
|
|
|
|
|
|
x += offset_x
|
|
|
|
y += offset_y
|
|
|
|
prev = ao
|
|
|
|
|
|
|
|
## create a merged name as output ??
|
|
|
|
aa = new_aa_node(ngroup)
|
|
|
|
aa.location = (ao.location.x + 200, ao.location.y)
|
|
|
|
ngroup.links.new(ao.outputs[0], aa.inputs[0]) # node_tree
|
|
|
|
|
|
|
|
# create one input and link
|
|
|
|
out = ngroup.outputs.new('NodeSocketColor', ngroup.inputs[0].name)
|
|
|
|
ngroup.links.new(aa.outputs[0], ng_out.inputs[0])
|
|
|
|
|
|
|
|
## --- renumbering funcs ---
|
2021-09-10 18:32:50 +02:00
|
|
|
|
|
|
|
def get_numbered_output(out, slot_name):
|
|
|
|
'''Return output slot name without looking for numbering ???_
|
|
|
|
'''
|
|
|
|
pattern = r'^(?:\d{3}_)?' # optional non capture group of 3 digits + _
|
|
|
|
pattern = f'{pattern}{slot_name}'
|
|
|
|
for inp in out.inputs:
|
|
|
|
if re.match(pattern, inp.name):
|
|
|
|
return inp
|
|
|
|
|
|
|
|
|
|
|
|
def add_fileslot_number(fs, number):
|
|
|
|
elems = fs.path.split('/')
|
|
|
|
for i, e in enumerate(elems):
|
|
|
|
if re.match(r'^\d{3}_', e):
|
|
|
|
elems[i] = re.sub(r'^(\d{3})', lambda x: str(number).zfill(3), e)
|
|
|
|
else:
|
|
|
|
elems[i] = f'{str(number).zfill(3)}_{e}'
|
|
|
|
new = '/'.join(elems)
|
|
|
|
fs.path = new
|
|
|
|
return new
|
|
|
|
|
|
|
|
def renumber(fo, offset=10):
|
|
|
|
'''Force renumber all the slots with a 3'''
|
|
|
|
if fo.type != 'OUTPUT_FILE': return
|
2021-09-10 22:59:52 +02:00
|
|
|
ct = 10 # start at 10
|
2021-09-10 18:32:50 +02:00
|
|
|
for fs in fo.file_slots:
|
|
|
|
add_fileslot_number(fs, ct)
|
|
|
|
ct += offset
|
|
|
|
|
2021-09-10 22:59:52 +02:00
|
|
|
def get_num(string) -> int:
|
|
|
|
'''get a tring or a file_slot object
|
|
|
|
return leading number or None
|
|
|
|
'''
|
|
|
|
if not isinstance(string, str):
|
|
|
|
string = string.path
|
2021-09-10 18:32:50 +02:00
|
|
|
num = re.search(r'^(\d{3})_', string)
|
|
|
|
if num:
|
|
|
|
return int(num.group(1))
|
2021-09-10 22:59:52 +02:00
|
|
|
|
|
|
|
def delete_numbering(fo): # padding=3
|
|
|
|
'''Delete prefix numbering on all slots on passed file output'''
|
2021-09-10 18:32:50 +02:00
|
|
|
|
|
|
|
if fo.type != 'OUTPUT_FILE': return
|
2021-09-10 22:59:52 +02:00
|
|
|
for fs in fo.file_slots:
|
|
|
|
elems = fs.path.split('/')
|
|
|
|
for i, e in enumerate(elems):
|
|
|
|
elems[i] = re.sub(r'^\d{3}_', '', e)
|
|
|
|
|
|
|
|
new = '/'.join(elems)
|
|
|
|
fs.path = new
|
2021-09-10 18:32:50 +02:00
|
|
|
|
2021-09-10 22:59:52 +02:00
|
|
|
def renumber_keep_existing(fo, offset=10):
|
|
|
|
'''Renumber by keeping existing numbers and inserting new one whenever possible
|
|
|
|
Big and ugly function that do the trick nonetheless...
|
|
|
|
'''
|
|
|
|
|
|
|
|
if fo.type != 'OUTPUT_FILE': return
|
|
|
|
ct = 10
|
|
|
|
|
|
|
|
fsl = fo.file_slots
|
2021-09-10 18:32:50 +02:00
|
|
|
|
2021-09-10 22:59:52 +02:00
|
|
|
last_idx = len(fsl) - 1
|
2021-09-10 18:32:50 +02:00
|
|
|
prev = None
|
|
|
|
prev_num = None
|
2021-09-10 22:59:52 +02:00
|
|
|
for idx, fs in enumerate(fsl):
|
2021-09-15 19:36:06 +02:00
|
|
|
# print('-->', idx, fs.path)
|
2021-09-10 22:59:52 +02:00
|
|
|
|
2021-09-10 18:32:50 +02:00
|
|
|
if idx == last_idx: # handle last
|
|
|
|
if idx > 0:
|
2021-09-10 22:59:52 +02:00
|
|
|
prev = fsl[idx-1]
|
|
|
|
num = get_num(prev)
|
|
|
|
if num is not None:
|
2021-09-15 10:34:35 +02:00
|
|
|
add_fileslot_number(fs, num + offset)
|
2021-09-10 18:32:50 +02:00
|
|
|
else:
|
2021-09-10 22:59:52 +02:00
|
|
|
add_fileslot_number(fs, ct)
|
2021-09-10 18:32:50 +02:00
|
|
|
else:
|
2021-09-10 22:59:52 +02:00
|
|
|
add_fileslot_number(fs, 10) # there is only one slot (maybe don't number ?)
|
2021-09-10 18:32:50 +02:00
|
|
|
break
|
|
|
|
|
|
|
|
# update the ct with the current taken number if any
|
2021-09-10 22:59:52 +02:00
|
|
|
number = get_num(fs)
|
|
|
|
if number is not None:
|
2021-09-10 18:32:50 +02:00
|
|
|
prev = fs
|
|
|
|
ct = number + offset
|
|
|
|
continue # skip already numbered
|
|
|
|
|
|
|
|
# analyse all next slots until there is numbered
|
2021-09-10 22:59:52 +02:00
|
|
|
divider = 0
|
2021-09-15 19:36:06 +02:00
|
|
|
# print(f'range(1, {len(fsl) - idx}')
|
2021-09-10 22:59:52 +02:00
|
|
|
for i in range(1, len(fsl) - idx):
|
|
|
|
next_num = get_num(fsl[idx + i])
|
|
|
|
if next_num is not None:
|
|
|
|
divider = i+1
|
2021-09-10 18:32:50 +02:00
|
|
|
break
|
|
|
|
|
2021-09-10 22:59:52 +02:00
|
|
|
if idx == 0: # handle first
|
2021-09-10 18:32:50 +02:00
|
|
|
prev_num = 0
|
|
|
|
prev = None
|
2021-09-10 22:59:52 +02:00
|
|
|
if next_num is None:
|
|
|
|
add_fileslot_number(fs, 0)
|
|
|
|
elif next_num == 0:
|
|
|
|
print(f'Cannot insert value before 0 to {fsl.path}')
|
|
|
|
continue
|
|
|
|
else:
|
|
|
|
add_fileslot_number(fs, int(next_num / 2))
|
2021-09-10 18:32:50 +02:00
|
|
|
else:
|
2021-09-10 22:59:52 +02:00
|
|
|
prev = fsl[idx-1]
|
|
|
|
test_prev = get_num(prev)
|
|
|
|
if test_prev is not None:
|
|
|
|
prev_num = test_prev
|
|
|
|
|
|
|
|
if not divider:
|
|
|
|
if prev_num is not None:
|
|
|
|
add_fileslot_number(fs, prev_num + offset)
|
|
|
|
else:
|
|
|
|
add_fileslot_number(fs, ct)
|
2021-09-10 18:32:50 +02:00
|
|
|
|
2021-09-10 22:59:52 +02:00
|
|
|
else:
|
|
|
|
if prev_num is not None:
|
|
|
|
# iterate rename
|
|
|
|
gap_inc = int((next_num - prev_num) / divider)
|
|
|
|
if gap_inc < 1: # same values !
|
|
|
|
print(f'cannot insert a median value at {fs.path} between {prev_num} and {next_num}')
|
|
|
|
continue
|
|
|
|
|
|
|
|
ct = prev_num
|
|
|
|
for temp_id in range(idx, idx+i):
|
|
|
|
ct += gap_inc
|
|
|
|
add_fileslot_number(fsl[temp_id], ct)
|
|
|
|
else:
|
|
|
|
print("what's going on ?\n")
|
2021-09-10 18:32:50 +02:00
|
|
|
|
|
|
|
# first check if it has a number (if not bas)
|
|
|
|
prev = fs
|
2021-09-17 16:31:26 +02:00
|
|
|
ct += offset
|
|
|
|
|
|
|
|
|
2021-09-17 18:36:15 +02:00
|
|
|
def has_channel_color(layer):
|
|
|
|
'''Return True if gp_layer.channel_color is different than the default (0.2, 0.2, 0.2) '''
|
|
|
|
if not any(isclose(i, 0.2, abs_tol=0.001) for i in layer.channel_color):
|
|
|
|
return True
|
|
|
|
|
2021-09-17 16:31:26 +02:00
|
|
|
## confirm pop-up message:
|
|
|
|
def show_message_box(_message = "", _title = "Message Box", _icon = 'INFO'):
|
|
|
|
def draw(self, context):
|
|
|
|
for l in _message:
|
|
|
|
if isinstance(l, str):
|
|
|
|
self.layout.label(text=l)
|
|
|
|
else:
|
|
|
|
self.layout.label(text=l[0], icon=l[1])
|
|
|
|
|
|
|
|
if isinstance(_message, str):
|
|
|
|
_message = [_message]
|
|
|
|
bpy.context.window_manager.popup_menu(draw, title = _title, icon = _icon)
|