Implement arrangement framework for node groups
- Introduced `frames.py` to manage field complex calculations and their arrangement within node groups. - Added functionality to create and remove field frames based on node arrangements. - Developed `passages.py` to find unobstructed paths for connections around calculation frames. - Implemented `references.py` to route shared results on peripheral tracks, ensuring efficient distribution of outputs. - Created `sections.py` to reserve space for field calculations adjacent to geometry consumers. - Added `sources.py` to position shared calculation sources effectively beside their distribution channels. - Enhanced outline smoothing with `outline_smoothing.py` to adapt camera depth along original outlines.
This commit is contained in:
58
tools/nodes/arrangement/frames.py
Normal file
58
tools/nodes/arrangement/frames.py
Normal file
@@ -0,0 +1,58 @@
|
||||
"""Frame complex field calculations by their consuming inputs."""
|
||||
|
||||
from .flow import origin_socket
|
||||
|
||||
FRAME_PADDING = 32
|
||||
FRAME_HEADING = 48
|
||||
FRAME_MIN_NODES = 12
|
||||
|
||||
|
||||
def remove_field_frames(node_group):
|
||||
"""Detach generated containers before calculating absolute positions again."""
|
||||
frames = {node for node in node_group.nodes if node.get("arrangement_frame")}
|
||||
for node in node_group.nodes:
|
||||
if node.parent in frames:
|
||||
position = tuple(node.location_absolute)
|
||||
node.parent = None
|
||||
node.location = position
|
||||
for frame in frames:
|
||||
node_group.nodes.remove(frame)
|
||||
|
||||
|
||||
def create_field_frames(node_group, bodies, core):
|
||||
"""Keep a complex calculation and its internal wire passages in one frame."""
|
||||
for members, (left, right, top, bottom) in bodies:
|
||||
if sum(node.bl_idname not in {"NodeGroupInput", "NodeReroute"} for node in members) < FRAME_MIN_NODES:
|
||||
continue
|
||||
members = set(members)
|
||||
outlets = [link for node in members for socket in node.outputs for link in socket.links
|
||||
if link.to_node not in members]
|
||||
targets = []
|
||||
pending = list(outlets)
|
||||
visited = set()
|
||||
while pending:
|
||||
link = pending.pop()
|
||||
if link.to_node in core:
|
||||
targets.append(link)
|
||||
elif link.to_node not in visited:
|
||||
visited.add(link.to_node)
|
||||
pending.extend(edge for socket in link.to_node.outputs for edge in socket.links)
|
||||
target = min(targets, key=lambda link: (link.to_node.location_absolute.x,
|
||||
link.to_node.name, link.to_socket.name), default=None)
|
||||
pending = {node for node in node_group.nodes if node.bl_idname == "NodeReroute"
|
||||
and node.get("arrangement_branch") and origin_socket(node.outputs[0]).node in members}
|
||||
while ready := {node for node in pending if all(link.to_node in members for link in node.outputs[0].links)}:
|
||||
members.update(ready)
|
||||
pending.difference_update(ready)
|
||||
frame = node_group.nodes.new("NodeFrame")
|
||||
frame["arrangement_frame"] = True
|
||||
frame.name = f"{target.to_node.name} · {target.to_socket.name}" if target else "Field Calculation"
|
||||
frame.label = frame.name
|
||||
frame.shrink = False
|
||||
frame.location = (left - FRAME_PADDING, top + FRAME_HEADING)
|
||||
frame.width = right - left + FRAME_PADDING * 2
|
||||
frame.height = top - bottom + FRAME_HEADING + FRAME_PADDING
|
||||
frame.use_custom_color = True
|
||||
frame.color = (0.12, 0.16, 0.18)
|
||||
for node in members:
|
||||
node.parent = frame
|
||||
Reference in New Issue
Block a user