66 lines
2.7 KiB
Python
66 lines
2.7 KiB
Python
"""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)
|