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:
@@ -33,6 +33,22 @@ def field_layout(nodes, parents, peers, sizes, *, gap, target_height):
|
||||
continue
|
||||
for node in members[1:]:
|
||||
roots[root(node)] = root(members[0])
|
||||
# Stack exclusive calculation chains without disturbing ordered operands.
|
||||
operands = {node for sources in peers if len(sources) > 1 for node in sources}
|
||||
chain_heights = {node: sizes[node].height for node in nodes}
|
||||
simple = {"ShaderNodeMath", "ShaderNodeVectorMath", "ShaderNodeClamp"}
|
||||
for node in nodes:
|
||||
sources = [parent for parent in parents[node] if parent in roots]
|
||||
if node.bl_idname not in simple or node in operands or len(sources) != 1:
|
||||
continue
|
||||
source = sources[0]
|
||||
if source in operands or {link.to_node for socket in source.outputs for link in socket.links} != {node}:
|
||||
continue
|
||||
first, last = root(source), root(node)
|
||||
height = chain_heights[first] + gap + chain_heights[last]
|
||||
if first != last and height <= target_height:
|
||||
roots[last] = first
|
||||
chain_heights[first] = height
|
||||
groups = defaultdict(list)
|
||||
for node in nodes:
|
||||
groups[root(node)].append(node)
|
||||
@@ -80,11 +96,12 @@ def field_layout(nodes, parents, peers, sizes, *, gap, target_height):
|
||||
layers = columns
|
||||
depth = column - 1
|
||||
links = [link for node in nodes for socket in node.inputs for link in socket.links if link.from_node in block_of]
|
||||
edges, passages = [], []
|
||||
edges, passages, vertical = [], [], []
|
||||
for link in links:
|
||||
first, last = block_of[link.from_node], block_of[link.to_node]
|
||||
if first == last:
|
||||
raise ValueError("Equal operands must not contain an internal dependency")
|
||||
vertical.append(link)
|
||||
continue
|
||||
start_offset = offsets[link.from_node] + sizes[link.from_node].sockets[link.from_socket]
|
||||
end_offset = offsets[link.to_node] + sizes[link.to_node].sockets[link.to_socket]
|
||||
previous, previous_offset, path = first, start_offset, []
|
||||
@@ -173,18 +190,18 @@ def field_layout(nodes, parents, peers, sizes, *, gap, target_height):
|
||||
raise ValueError(f"Expression layers could not be placed: {result.message}")
|
||||
centers = result.x[:count]
|
||||
positions, point_positions, right = {}, {}, 0
|
||||
vertical_blocks = {block_of[link.from_node] for link in vertical}
|
||||
for rank in range(depth + 1):
|
||||
layer = layers[rank]
|
||||
width = max(max(sizes[node].width for node in blocks[item]) if item < len(blocks) else 16 for item in layer)
|
||||
padding = gap / 2 if any(item in vertical_blocks for item in layer) else 0
|
||||
if rank:
|
||||
run = max([gap, *(abs(centers[first] + a - centers[last] - b) * 0.8
|
||||
for first, last, a, b in edges if ranks[last] == rank)])
|
||||
right += run
|
||||
right += width
|
||||
right += gap
|
||||
right += width + padding * 2
|
||||
for item in layer:
|
||||
if item < len(blocks):
|
||||
for node in blocks[item]:
|
||||
positions[node] = (right - sizes[node].width, -centers[item] - offsets[node])
|
||||
positions[node] = (right - padding - sizes[node].width, -centers[item] - offsets[node])
|
||||
else:
|
||||
point_positions[item] = (right, -centers[item])
|
||||
routes = []
|
||||
@@ -193,4 +210,12 @@ def field_layout(nodes, parents, peers, sizes, *, gap, target_height):
|
||||
points = [point for index, point in enumerate(points) if index == 0 or index == len(points)-1
|
||||
or not abs(points[index-1][1] - point[1]) < 1e-6 or not abs(points[index+1][1] - point[1]) < 1e-6]
|
||||
routes.append((link, points))
|
||||
for link in vertical:
|
||||
source, target = link.from_node, link.to_node
|
||||
x1, y1 = positions[source]
|
||||
x2, y2 = positions[target]
|
||||
middle = (y1 - sizes[source].height + y2) / 2
|
||||
routes.append((link, [(x1 + sizes[source].width + gap / 2, y1 - sizes[source].sockets[link.from_socket]),
|
||||
(x1 + sizes[source].width + gap / 2, middle),
|
||||
(x2 - gap / 2, middle), (x2 - gap / 2, y2 - sizes[target].sockets[link.to_socket])]))
|
||||
return FieldLayout(positions, right, result.x[count], routes)
|
||||
|
||||
Reference in New Issue
Block a user