2025-07-01
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
import bpy
|
||||
import mathutils
|
||||
|
||||
from .utils import arrange_relax, global_loc, calc_collision_y, calc_node
|
||||
|
||||
|
||||
def step(step_num, iter_num, nodes, props, root_center, iter_func):
|
||||
iter_cnt = 0
|
||||
for i in range(iter_num):
|
||||
new_center = mathutils.Vector((0, 0))
|
||||
node_cnt = 0
|
||||
changed = False
|
||||
for node in nodes:
|
||||
if node.type == 'FRAME':
|
||||
continue
|
||||
if props.ArrangeOnlySelected and not node.select:
|
||||
continue
|
||||
t = i / iter_num
|
||||
if iter_func(node, t):
|
||||
changed = True
|
||||
new_center += global_loc(node)
|
||||
node_cnt += 1
|
||||
|
||||
if not changed and props.AdaptiveIters:
|
||||
break
|
||||
if not props.ArrangeOnlySelected:
|
||||
new_center /= node_cnt
|
||||
slide = root_center - new_center # Keep Center
|
||||
for node in nodes:
|
||||
if node.type == 'FRAME':
|
||||
continue
|
||||
node.location += slide
|
||||
iter_cnt += 1
|
||||
if iter_cnt > props.BackgroundIterations:
|
||||
iter_cnt = 0
|
||||
props.ArrangeState = str(i) + "/" + str(iter_num) + " " + str(step_num) + "/4"
|
||||
yield 1
|
||||
|
||||
|
||||
class NodeRelaxArrange(bpy.types.Operator):
|
||||
"""Arrange Nodes"""
|
||||
bl_idname = "node_relax.arrange"
|
||||
bl_label = "Arrange Nodes"
|
||||
|
||||
bl_options = {"UNDO", "REGISTER"}
|
||||
|
||||
_timer = None
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
space = context.space_data
|
||||
if space.type == 'NODE_EDITOR' and space.node_tree is not None:
|
||||
return True
|
||||
return False
|
||||
|
||||
def main_routine(self, context):
|
||||
yield 1
|
||||
nodes = self.tree.nodes
|
||||
props = context.scene.NodeRelax_props
|
||||
root_center = mathutils.Vector((0, 0)) # Original Center
|
||||
|
||||
if not props.ArrangeOnlySelected:
|
||||
node_cnt = 0
|
||||
for node in nodes:
|
||||
if node.type == 'FRAME':
|
||||
continue
|
||||
root_center += global_loc(node)
|
||||
node_cnt += 1
|
||||
root_center /= node_cnt
|
||||
|
||||
yield from step(1, props.Iterations_S1, nodes, props, root_center,
|
||||
lambda curr_node, e: arrange_relax(curr_node, 1, 1, props.Distance, False))
|
||||
|
||||
yield from step(2, props.Iterations_S2, nodes, props, root_center,
|
||||
lambda curr_node, e: arrange_relax(curr_node, 1, 1, props.Distance, True))
|
||||
|
||||
dist = mathutils.Vector((0, props.Distance))
|
||||
yield from step(3, props.Iterations_S3, nodes, props, root_center,
|
||||
lambda curr_node, e: calc_collision_y(curr_node, nodes, e, dist))
|
||||
|
||||
dist = mathutils.Vector((props.Distance, props.Distance))
|
||||
zero_vec = mathutils.Vector((0, 0))
|
||||
yield from step(4, props.Iterations_S4, nodes, props, root_center,
|
||||
lambda curr_node, e: calc_node(curr_node, nodes, min(1, e * 2), zero_vec, 0.2, 1, dist, True))
|
||||
|
||||
yield 0
|
||||
|
||||
def finish(self, context):
|
||||
wm = context.window_manager
|
||||
wm.event_timer_remove(self._timer)
|
||||
props = context.scene.NodeRelax_props
|
||||
props.ArrangeState = ""
|
||||
|
||||
def modal(self, context, event):
|
||||
if event.type == 'TIMER':
|
||||
state = next(self.main_coroutine)
|
||||
if state == 0:
|
||||
self.finish(context)
|
||||
return {'FINISHED'}
|
||||
|
||||
if event.type in {'ESC'}:
|
||||
self.finish(context)
|
||||
return {'FINISHED'}
|
||||
|
||||
return {'RUNNING_MODAL'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
self.tree = context.space_data.edit_tree
|
||||
|
||||
wm = context.window_manager
|
||||
self.main_coroutine = self.main_routine(context)
|
||||
self._timer = wm.event_timer_add(0.01, window=context.window)
|
||||
wm.modal_handler_add(self)
|
||||
return {'RUNNING_MODAL'}
|
||||
Reference in New Issue
Block a user