- Implemented the "O Image Relief Plane" Geometry Nodes group to create a fixed-base image relief using depth images and various parameters. - Created the "O Shadeless" shader node group to display color without scene lighting while preserving shadows and reflections. - Introduced a preview feature for node groups, allowing for interactive visualization of node arrangements in an HTML format. - Added a template HTML file for rendering node previews with customizable views and controls.
64 lines
2.5 KiB
Python
64 lines
2.5 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 == "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 == "GeometryNodeGroup"
|
|
) 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", "capture_items", "enum_items"):
|
|
controls += len(getattr(node, collection, ()))
|
|
if node.bl_idname == "GeometryNodeGroup" 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)
|