This commit is contained in:
65
tools/nodes/arrangement/dimensions.py
Normal file
65
tools/nodes/arrangement/dimensions.py
Normal file
@@ -0,0 +1,65 @@
|
||||
"""Estimate node bounds and socket offsets in unscaled editor units."""
|
||||
|
||||
import json
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
RULES = json.loads(Path(__file__).with_suffix(".json").read_text(encoding="utf-8"))
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class NodeDimensions:
|
||||
width: float
|
||||
height: float
|
||||
sockets: dict
|
||||
|
||||
|
||||
def visible_socket(socket):
|
||||
"""Include only sockets drawn for the node's current operation."""
|
||||
return socket.enabled and not socket.is_unavailable and not socket.hide
|
||||
|
||||
|
||||
def estimate_node_dimensions(node):
|
||||
"""Reserve socket rows, expanded vector values, and node-specific controls."""
|
||||
if node.bl_idname == "NodeFrame":
|
||||
return NodeDimensions(node.width, node.height, {})
|
||||
if node.bl_idname == "NodeReroute":
|
||||
return NodeDimensions(16, 16, {socket: 8 for socket in (*node.inputs, *node.outputs)})
|
||||
inputs = [s for s in node.inputs if visible_socket(s)]
|
||||
outputs = [s for s in node.outputs if visible_socket(s)]
|
||||
width = RULES["wide_width"] if (
|
||||
"Attribute" in node.bl_idname or node.bl_idname in {"GeometryNodeGroup", "ShaderNodeGroup", "ShaderNodeFloatCurve"}
|
||||
) else RULES["width"]
|
||||
if node.hide:
|
||||
height = max(RULES["collapsed_height"], 16 + 12 * max(len(inputs), len(outputs)))
|
||||
sockets = {
|
||||
socket: (index + 1) * height / (len(side) + 1)
|
||||
for side in (inputs, outputs)
|
||||
for index, socket in enumerate(side)
|
||||
}
|
||||
return NodeDimensions(width, height, sockets)
|
||||
|
||||
# Conservative reserves include spacing between UI sections.
|
||||
controls = RULES["controls"][node.bl_idname]
|
||||
for collection in ("repeat_items", "state_items", "input_items", "main_items", "generation_items", "enum_items"):
|
||||
controls += len(getattr(node, collection, ()))
|
||||
if node.bl_idname in {"GeometryNodeGroup", "ShaderNodeGroup"} and node.node_tree:
|
||||
controls += sum(item.item_type == "PANEL" for item in node.node_tree.interface.items_tree)
|
||||
cursor = RULES["header"] + RULES["padding"]
|
||||
sockets = {}
|
||||
for socket in outputs:
|
||||
sockets[socket] = cursor + RULES["row"] / 2
|
||||
cursor += RULES["row"]
|
||||
cursor += controls * RULES["row"] + RULES["padding"]
|
||||
for socket in inputs:
|
||||
rows = 1
|
||||
if not socket.is_linked and not socket.hide_value:
|
||||
if socket.type in {"VECTOR", "ROTATION"}:
|
||||
rows = 4
|
||||
elif socket.type == "MATRIX":
|
||||
rows = 5
|
||||
extra = max(0, len(socket.links) - 1) * 8 if socket.is_multi_input else 0
|
||||
sockets[socket] = cursor + RULES["row"] / 2 + extra / 2
|
||||
cursor += rows * RULES["row"] + extra
|
||||
return NodeDimensions(width, cursor + RULES["padding"], sockets)
|
||||
Reference in New Issue
Block a user