[API] Add improved slot locator functions (#716)

- Returns object with slot, index, and pos
- Locate-by-type returns object with slot & index
- Uses standard `undefined` return for concise chaining & validation
- Free 10x perf increase over getConnectionPos (used basic random data
to test, out of curiosity)
This commit is contained in:
filtered
2025-03-08 00:18:06 +11:00
committed by GitHub
parent 84fad5b8a5
commit 3e44f6a0c1
6 changed files with 221 additions and 69 deletions

View File

@@ -1,7 +1,8 @@
import type { ConnectingLink, Positionable } from "../interfaces"
import type { ConnectingLink, INodeInputSlot, INodeOutputSlot, ISlotType, Positionable } from "../interfaces"
import type { LinkId } from "@/LLink"
import { LGraphNode } from "@/LGraphNode"
import { type IGenericLinkOrLinks, LGraphNode } from "@/LGraphNode"
import { parseSlotTypes } from "@/strings"
/**
* Creates a flat set of all positionable items by recursively iterating through all child items.
@@ -46,3 +47,55 @@ export function isDraggingLink(linkId: LinkId, connectingLinks: ConnectingLink[]
if (linkId === connectingLink.link.id) return connectingLink
}
}
type InputOrOutput = (INodeInputSlot | INodeOutputSlot) & IGenericLinkOrLinks
type FreeSlotResult<T extends InputOrOutput> = { index: number, slot: T } | undefined
/**
* Finds the first free in/out slot with any of the comma-delimited types in {@link type}.
*
* If no slots are free, falls back in order to:
* - The first free wildcard slot
* - The first occupied slot
* - The first occupied wildcard slot
* @param slots The iterable of node slots slots to search through
* @param type The {@link ISlotType type} of slot to find
* @returns The index and slot if found, otherwise `undefined`.
*/
export function findFreeSlotOfType<T extends InputOrOutput>(
slots: T[],
type: ISlotType,
): FreeSlotResult<T> {
if (!slots?.length) return
let occupiedSlot: FreeSlotResult<T>
let wildSlot: FreeSlotResult<T>
let occupiedWildSlot: FreeSlotResult<T>
const validTypes = parseSlotTypes(type)
for (const [index, slot] of slots.entries()) {
const slotTypes = parseSlotTypes(slot.type)
for (const validType of validTypes) {
for (const slotType of slotTypes) {
if (slotType === validType) {
if (slot.link == null && !slot.links?.length) {
// Exact match - short circuit
return { index, slot }
}
// In case we can't find a free slot.
occupiedSlot ??= { index, slot }
} else if (!wildSlot && (validType === "*" || slotType === "*")) {
// Save the first free wildcard slot as a fallback
if (slot.link == null && !slot.links?.length) {
wildSlot = { index, slot }
} else {
occupiedWildSlot ??= { index, slot }
}
}
}
}
}
return wildSlot ?? occupiedSlot ?? occupiedWildSlot
}