212 lines
5.4 KiB
JavaScript
212 lines
5.4 KiB
JavaScript
import {
|
|
COMBINE_PRIMITIVES_NODE_CLASS,
|
|
END_FOREACH_NODE_CLASS,
|
|
FOREACH_NODE_CLASS,
|
|
isPrimitivesLinkType,
|
|
PRIMITIVE_SLOT_TYPE,
|
|
SPLIT_PRIMITIVES_NODE_CLASS,
|
|
isRelayNodeClass,
|
|
} from "./constants.js"
|
|
import { getNodeGraph } from "./graph-context.js"
|
|
import { getGraphLink } from "./graph-links.js"
|
|
|
|
export function getNodeClassName(node) {
|
|
return node?.comfyClass || node?.type || ""
|
|
}
|
|
|
|
export function isNodeClass(node, className) {
|
|
return getNodeClassName(node) === className
|
|
}
|
|
|
|
export function isPrimitivePipelineNode(node) {
|
|
const cls = getNodeClassName(node)
|
|
return cls === FOREACH_NODE_CLASS || cls === END_FOREACH_NODE_CLASS
|
|
}
|
|
|
|
export function isPrimitiveRelayNode(node) {
|
|
if (!node) {
|
|
return false
|
|
}
|
|
const cls = getNodeClassName(node)
|
|
if (cls.includes("Reroute") || node.type === "Reroute") {
|
|
return true
|
|
}
|
|
if (isRelayNodeClass(cls)) {
|
|
return true
|
|
}
|
|
const slots = [...(node.inputs || []), ...(node.outputs || [])]
|
|
if (!slots.length) {
|
|
return false
|
|
}
|
|
return slots.every(
|
|
(slot) =>
|
|
!slot.type || slot.type === "*" || isPrimitivesLinkType(slot.type) || slot.type === PRIMITIVE_SLOT_TYPE,
|
|
)
|
|
}
|
|
|
|
/** @returns {Array<object>} */
|
|
function upstreamInputsToFollow(node) {
|
|
if (isNodeClass(node, FOREACH_NODE_CLASS)) {
|
|
const inp = node.inputs?.find((i) => i.name === "combined")
|
|
return inp?.link ? [inp] : []
|
|
}
|
|
if (isNodeClass(node, END_FOREACH_NODE_CLASS)) {
|
|
const inp = node.inputs?.find((i) => i.name === "value")
|
|
return inp?.link ? [inp] : []
|
|
}
|
|
if (isPrimitiveRelayNode(node) || isPrimitivePipelineNode(node)) {
|
|
return (node.inputs ?? []).filter((i) => i?.link)
|
|
}
|
|
return (node.inputs ?? []).filter((i) => i?.link)
|
|
}
|
|
|
|
export function walkUpstreamForCombineNode(node, combineNodeClass, visited, graph) {
|
|
if (!node || visited.has(node.id)) {
|
|
return null
|
|
}
|
|
visited.add(node.id)
|
|
|
|
if (isNodeClass(node, combineNodeClass)) {
|
|
return node
|
|
}
|
|
|
|
const resolvedGraph = graph ?? getNodeGraph(node)
|
|
if (!resolvedGraph) {
|
|
return null
|
|
}
|
|
|
|
for (const input of upstreamInputsToFollow(node)) {
|
|
const link = getGraphLink(resolvedGraph, input.link)
|
|
if (!link) {
|
|
continue
|
|
}
|
|
const upstream = resolvedGraph.getNodeById(link.origin_id)
|
|
const found = walkUpstreamForCombineNode(upstream, combineNodeClass, visited, resolvedGraph)
|
|
if (found) {
|
|
return found
|
|
}
|
|
}
|
|
return null
|
|
}
|
|
|
|
/**
|
|
* combined / value 入力から Combine を上流へ辿る。
|
|
*
|
|
* @param {object} node
|
|
* @param {string} combineNodeClass
|
|
* @param {string} [inputName="combined"]
|
|
* @returns {object|null}
|
|
*/
|
|
export function findCombineUpstream(node, combineNodeClass, inputName = "combined") {
|
|
const input = node.inputs?.find((inp) => inp.name === inputName)
|
|
if (!input?.link) {
|
|
return null
|
|
}
|
|
const graph = getNodeGraph(node)
|
|
if (!graph) {
|
|
return null
|
|
}
|
|
const link = getGraphLink(graph, input.link)
|
|
if (!link) {
|
|
return null
|
|
}
|
|
const origin = graph.getNodeById(link.origin_id)
|
|
if (!origin) {
|
|
return null
|
|
}
|
|
if (isNodeClass(origin, combineNodeClass)) {
|
|
return origin
|
|
}
|
|
return walkUpstreamForCombineNode(origin, combineNodeClass, new Set([node.id]), graph)
|
|
}
|
|
|
|
export function findDownstreamSplitNodes(node, splitNodeClass, visited, graph) {
|
|
if (!node || visited.has(node.id)) {
|
|
return []
|
|
}
|
|
visited.add(node.id)
|
|
|
|
if (isNodeClass(node, splitNodeClass)) {
|
|
return [node]
|
|
}
|
|
if (!isPrimitiveRelayNode(node) && !isPrimitivePipelineNode(node)) {
|
|
return []
|
|
}
|
|
|
|
const resolvedGraph = graph ?? getNodeGraph(node)
|
|
if (!resolvedGraph) {
|
|
return []
|
|
}
|
|
|
|
const splits = []
|
|
for (const output of node.outputs || []) {
|
|
if (!output?.links?.length) {
|
|
continue
|
|
}
|
|
for (const linkId of output.links) {
|
|
const link = getGraphLink(resolvedGraph, linkId)
|
|
if (!link) {
|
|
continue
|
|
}
|
|
const target = resolvedGraph.getNodeById(link.target_id)
|
|
splits.push(...findDownstreamSplitNodes(target, splitNodeClass, visited, resolvedGraph))
|
|
}
|
|
}
|
|
return splits
|
|
}
|
|
|
|
export function notifyDownstreamSplitNodes(combineNode, splitNodeClass) {
|
|
const combinedOutput = combineNode.outputs?.find((o) => o.name === "combined")
|
|
if (!combinedOutput?.links?.length) {
|
|
return
|
|
}
|
|
|
|
const graph = getNodeGraph(combineNode)
|
|
if (!graph) {
|
|
return
|
|
}
|
|
|
|
const visited = new Set([combineNode.id])
|
|
const notified = new Set()
|
|
|
|
for (const linkId of combinedOutput.links) {
|
|
const link = getGraphLink(graph, linkId)
|
|
if (!link) {
|
|
continue
|
|
}
|
|
const target = graph.getNodeById(link.target_id)
|
|
for (const split of findDownstreamSplitNodes(target, splitNodeClass, visited, graph)) {
|
|
if (notified.has(split.id)) {
|
|
continue
|
|
}
|
|
notified.add(split.id)
|
|
split.scheduleStabilize?.()
|
|
}
|
|
}
|
|
}
|
|
|
|
export function propagatePrimitiveSplitSync(
|
|
fromNode,
|
|
combineNodeClass = COMBINE_PRIMITIVES_NODE_CLASS,
|
|
splitNodeClass = SPLIT_PRIMITIVES_NODE_CLASS,
|
|
) {
|
|
if (!fromNode) {
|
|
return
|
|
}
|
|
|
|
const graph = getNodeGraph(fromNode)
|
|
const visitedDown = new Set()
|
|
for (const split of findDownstreamSplitNodes(fromNode, splitNodeClass, visitedDown, graph)) {
|
|
split.scheduleStabilize?.()
|
|
}
|
|
|
|
const combine = walkUpstreamForCombineNode(fromNode, combineNodeClass, new Set(), graph)
|
|
if (combine) {
|
|
notifyDownstreamSplitNodes(combine, splitNodeClass)
|
|
}
|
|
}
|
|
|
|
export function findLinkedCombineNode(splitNode, combineNodeClass) {
|
|
return findCombineUpstream(splitNode, combineNodeClass, "combined")
|
|
}
|