From b3d030e36b533f2500b44e92c84a64396cc9b6eb Mon Sep 17 00:00:00 2001 From: filtered <176114999+webfiltered@users.noreply.github.com> Date: Sun, 27 Apr 2025 06:51:41 +1000 Subject: [PATCH] [API] Add LLink.resolve static / instance methods (#974) - `LLink.resolve`: Resolves all IDs on the link to their respective objects (node IDs, slot indexes) - `LinkNetwork.getLink`: a more concise pattern to resolve links --- src/LGraph.ts | 11 +++++++++ src/LLink.ts | 58 ++++++++++++++++++++++++++++++++++++++++++++++- src/interfaces.ts | 2 ++ 3 files changed, 70 insertions(+), 1 deletion(-) diff --git a/src/LGraph.ts b/src/LGraph.ts index 7bef76e64..c9a1fd703 100644 --- a/src/LGraph.ts +++ b/src/LGraph.ts @@ -1174,6 +1174,17 @@ export class LGraph implements LinkNetwork, Serialisable { } } + /** + * Finds the link with the provided ID. + * @param id ID of link to find + * @returns The link with the provided {@link id}, otherwise `undefined`. Always returns `undefined` if `id` is nullish. + */ + getLink(id: null | undefined): undefined + getLink(id: LinkId | null | undefined): LLink | undefined + getLink(id: LinkId | null | undefined): LLink | undefined { + return id == null ? undefined : this._links.get(id) + } + /** * Returns a reroute by ID, or `undefined` if the ID is `null` or `undefined`. * @param id ID of reroute to return diff --git a/src/LLink.ts b/src/LLink.ts index ed40f670b..99876bf6e 100644 --- a/src/LLink.ts +++ b/src/LLink.ts @@ -1,5 +1,7 @@ import type { CanvasColour, + INodeInputSlot, + INodeOutputSlot, ISlotType, LinkNetwork, LinkSegment, @@ -20,7 +22,15 @@ export type SerialisedLLinkArray = [ type: ISlotType, ] -type BasicReadonlyNetwork = Pick +interface ResolvedConnection { + inputNode: LGraphNode | undefined + outputNode: LGraphNode | undefined + input: INodeInputSlot | undefined + output: INodeOutputSlot | undefined + link: LLink +} + +type BasicReadonlyNetwork = Pick // this is the class in charge of storing link information export class LLink implements LinkSegment, Serialisable { @@ -180,6 +190,52 @@ export class LLink implements LinkSegment, Serialisable { return network.getNodeById(id) ?? undefined } + /** + * Resolves a link ID to the link, node, and slot objects. + * @param linkId The {@link id} of the link to resolve + * @param network The link network to search + * @returns An object containing the input and output nodes, as well as the input and output slots. + * @remarks This method is heavier than others; it will always resolve all objects. + * Whilst the performance difference should in most cases be negligible, + * it is recommended to use simpler methods where appropriate. + */ + static resolve(linkId: LinkId | null | undefined, network: BasicReadonlyNetwork): ResolvedConnection | undefined { + return network.getLink(linkId)?.resolve(network) + } + + /** + * Resolves a list of link IDs to the link, node, and slot objects. + * Discards invalid link IDs. + * @param linkIds An iterable of link {@link id}s to resolve + * @param network The link network to search + * @returns An array of resolved connections. If a link is not found, it is not included in the array. + * @see {@link LLink.resolve} + */ + static resolveMany(linkIds: Iterable, network: BasicReadonlyNetwork): ResolvedConnection[] { + const resolved: ResolvedConnection[] = [] + for (const id of linkIds) { + const r = network.getLink(id)?.resolve(network) + if (r) resolved.push(r) + } + return resolved + } + + /** + * Resolves the primitive ID values stored in the link to the referenced objects. + * @param network The link network to search + * @returns An object containing the input and output nodes, as well as the input and output slots. + * @remarks This method is heavier than others; it will always resolve all objects. + * Whilst the performance difference should in most cases be negligible, + * it is recommended to use simpler methods where appropriate. + */ + resolve(network: BasicReadonlyNetwork): ResolvedConnection { + const inputNode = this.target_id === -1 ? undefined : network.getNodeById(this.target_id) ?? undefined + const outputNode = this.origin_id === -1 ? undefined : network.getNodeById(this.origin_id) ?? undefined + const input = inputNode?.inputs[this.target_slot] + const output = outputNode?.outputs[this.origin_slot] + return { inputNode, outputNode, input, output, link: this } + } + configure(o: LLink | SerialisedLLinkArray) { if (Array.isArray(o)) { this.id = o[0] diff --git a/src/interfaces.ts b/src/interfaces.ts index cc53b3c8b..cb5db0f51 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -127,6 +127,8 @@ export interface ReadonlyLinkNetwork { readonly reroutes: ReadonlyMap readonly floatingLinks: ReadonlyMap getNodeById(id: NodeId | null | undefined): LGraphNode | null + getLink(id: null | undefined): undefined + getLink(id: LinkId | null | undefined): LLink | undefined getReroute(parentId: null | undefined): undefined getReroute(parentId: RerouteId | null | undefined): Reroute | undefined }