mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-21 14:59:39 +00:00
Add serialisation support for floating links (#771)
Adds the serialisation code to support the new floating links impl.
This commit is contained in:
@@ -138,6 +138,14 @@ export class LGraph implements LinkNetwork, Serialisable<SerialisableGraph> {
|
||||
return
|
||||
}
|
||||
|
||||
/** Internal only. Not required for serialisation; calculated on deserialise. */
|
||||
#lastFloatingLinkId: number = 0
|
||||
|
||||
#floatingLinks: Map<LinkId, LLink> = new Map()
|
||||
get floatingLinks(): ReadonlyMap<LinkId, LLink> {
|
||||
return this.#floatingLinks
|
||||
}
|
||||
|
||||
#reroutes = new Map<RerouteId, Reroute>()
|
||||
/** All reroutes in this graph. */
|
||||
public get reroutes(): Map<RerouteId, Reroute> {
|
||||
@@ -250,6 +258,7 @@ export class LGraph implements LinkNetwork, Serialisable<SerialisableGraph> {
|
||||
|
||||
this._links.clear()
|
||||
this.reroutes.clear()
|
||||
this.#floatingLinks.clear()
|
||||
|
||||
// other scene stuff
|
||||
this._groups = []
|
||||
@@ -1369,12 +1378,12 @@ export class LGraph implements LinkNetwork, Serialisable<SerialisableGraph> {
|
||||
* Creates the object if it does not exist.
|
||||
* @param serialisedReroute See {@link SerialisableReroute}
|
||||
*/
|
||||
setReroute({ id, parentId, pos, linkIds }: OptionalProps<SerialisableReroute, "id">): Reroute {
|
||||
setReroute({ id, parentId, pos, linkIds, floating }: OptionalProps<SerialisableReroute, "id">): Reroute {
|
||||
id ??= ++this.state.lastRerouteId
|
||||
if (id > this.state.lastRerouteId) this.state.lastRerouteId = id
|
||||
|
||||
const reroute = this.reroutes.get(id) ?? new Reroute(id, this)
|
||||
reroute.update(parentId, pos, linkIds)
|
||||
reroute.update(parentId, pos, linkIds, floating)
|
||||
this.reroutes.set(id, reroute)
|
||||
return reroute
|
||||
}
|
||||
@@ -1417,7 +1426,7 @@ export class LGraph implements LinkNetwork, Serialisable<SerialisableGraph> {
|
||||
if (!reroute) return
|
||||
|
||||
// Extract reroute from the reroute chain
|
||||
const { parentId, linkIds } = reroute
|
||||
const { parentId, linkIds, floatingLinkIds } = reroute
|
||||
for (const reroute of reroutes.values()) {
|
||||
if (reroute.parentId === id) reroute.parentId = parentId
|
||||
}
|
||||
@@ -1427,6 +1436,18 @@ export class LGraph implements LinkNetwork, Serialisable<SerialisableGraph> {
|
||||
if (link && link.parentId === id) link.parentId = parentId
|
||||
}
|
||||
|
||||
for (const linkId of floatingLinkIds) {
|
||||
const link = this.floatingLinks.get(linkId)
|
||||
if (!link) {
|
||||
console.warn(`Removed reroute had floating link ID that did not exist [${linkId}]`)
|
||||
continue
|
||||
}
|
||||
|
||||
// Remove floating links with no reroutes
|
||||
if (parentId === undefined) this.#floatingLinks.delete(linkId)
|
||||
else if (link.parentId === id) link.parentId = parentId
|
||||
}
|
||||
|
||||
reroutes.delete(id)
|
||||
this.setDirtyCanvas(false, true)
|
||||
}
|
||||
@@ -1450,7 +1471,7 @@ export class LGraph implements LinkNetwork, Serialisable<SerialisableGraph> {
|
||||
* @returns value of the node
|
||||
*/
|
||||
serialize(option?: { sortNodes: boolean }): ISerialisedGraph {
|
||||
const { config, state, groups, nodes, reroutes, extra } = this.asSerialisable(option)
|
||||
const { config, state, groups, nodes, reroutes, extra, floatingLinks } = this.asSerialisable(option)
|
||||
const linkArray = [...this._links.values()]
|
||||
const links = linkArray.map(x => x.serialize())
|
||||
|
||||
@@ -1467,6 +1488,7 @@ export class LGraph implements LinkNetwork, Serialisable<SerialisableGraph> {
|
||||
last_link_id: state.lastLinkId,
|
||||
nodes,
|
||||
links,
|
||||
floatingLinks,
|
||||
groups,
|
||||
config,
|
||||
extra,
|
||||
@@ -1494,6 +1516,7 @@ export class LGraph implements LinkNetwork, Serialisable<SerialisableGraph> {
|
||||
const groups = this._groups.map(x => x.serialize())
|
||||
|
||||
const links = this._links.size ? [...this._links.values()].map(x => x.asSerialisable()) : undefined
|
||||
const floatingLinks = this.floatingLinks.size ? [...this.floatingLinks.values()].map(x => x.asSerialisable()) : undefined
|
||||
const reroutes = this.reroutes.size ? [...this.reroutes.values()].map(x => x.asSerialisable()) : undefined
|
||||
|
||||
const data: ReturnType<typeof this.asSerialisable> = {
|
||||
@@ -1503,6 +1526,7 @@ export class LGraph implements LinkNetwork, Serialisable<SerialisableGraph> {
|
||||
groups,
|
||||
nodes,
|
||||
links,
|
||||
floatingLinks,
|
||||
reroutes,
|
||||
extra,
|
||||
}
|
||||
@@ -1574,14 +1598,36 @@ export class LGraph implements LinkNetwork, Serialisable<SerialisableGraph> {
|
||||
reroutes = data.reroutes
|
||||
}
|
||||
|
||||
// Floating links
|
||||
if (Array.isArray(data.floatingLinks)) {
|
||||
for (const linkData of data.floatingLinks) {
|
||||
const floatingLink = LLink.create(linkData)
|
||||
this.#floatingLinks.set(floatingLink.id, floatingLink)
|
||||
|
||||
if (floatingLink.id > this.#lastFloatingLinkId) this.#lastFloatingLinkId = floatingLink.id
|
||||
}
|
||||
}
|
||||
|
||||
// Reroutes
|
||||
if (Array.isArray(reroutes)) {
|
||||
for (const rerouteData of reroutes) {
|
||||
const reroute = this.setReroute(rerouteData)
|
||||
this.setReroute(rerouteData)
|
||||
}
|
||||
}
|
||||
|
||||
// Drop broken links, and ignore reroutes with no valid links
|
||||
if (!reroute.validateLinks(this._links))
|
||||
this.reroutes.delete(rerouteData.id)
|
||||
// Cache floating link IDs on reroutes
|
||||
for (const floatingLink of this.floatingLinks.values()) {
|
||||
const reroutes = LLink.getReroutes(this, floatingLink)
|
||||
for (const reroute of reroutes) {
|
||||
reroute.floatingLinkIds.add(floatingLink.id)
|
||||
}
|
||||
}
|
||||
|
||||
// Drop broken reroutes
|
||||
for (const reroute of this.reroutes.values()) {
|
||||
// Drop broken links, and ignore reroutes with no valid links
|
||||
if (!reroute.validateLinks(this._links, this.floatingLinks)) {
|
||||
this.reroutes.delete(reroute.id)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1590,13 +1636,7 @@ export class LGraph implements LinkNetwork, Serialisable<SerialisableGraph> {
|
||||
// copy all stored fields
|
||||
for (const i in data) {
|
||||
// links must be accepted
|
||||
if (
|
||||
i == "nodes" ||
|
||||
i == "groups" ||
|
||||
i == "links" ||
|
||||
i === "state" ||
|
||||
i === "reroutes"
|
||||
) {
|
||||
if (["nodes", "groups", "links", "state", "reroutes", "floatingLinks"].includes(i)) {
|
||||
continue
|
||||
}
|
||||
// @ts-expect-error #574 Legacy property assignment
|
||||
|
||||
@@ -3157,10 +3157,12 @@ export class LGraphCanvas implements ConnectionColorContext {
|
||||
// Remap linkIds
|
||||
for (const reroute of reroutes.values()) {
|
||||
const ids = [...reroute.linkIds].map(x => links.get(x)?.id ?? x)
|
||||
reroute.update(reroute.parentId, undefined, ids)
|
||||
reroute.update(reroute.parentId, undefined, ids, reroute.floating)
|
||||
|
||||
// Remove any invalid items
|
||||
if (!reroute.validateLinks(graph.links)) graph.removeReroute(reroute.id)
|
||||
if (!reroute.validateLinks(graph.links, graph.floatingLinks)) {
|
||||
graph.removeReroute(reroute.id)
|
||||
}
|
||||
}
|
||||
|
||||
// Adjust positions
|
||||
|
||||
@@ -15,6 +15,16 @@ import { distance } from "./measure"
|
||||
|
||||
export type RerouteId = number
|
||||
|
||||
/** The input or output slot that an incomplete reroute link is connected to. */
|
||||
export interface FloatingRerouteSlot {
|
||||
/** The ID of the node that the slot belongs to */
|
||||
nodeId: NodeId
|
||||
/** The index of the slot on the node */
|
||||
slot: number
|
||||
/** Floating connection to an input or output */
|
||||
slotType: "input" | "output"
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an additional point on the graph that a link path will travel through. Used for visual organisation only.
|
||||
*
|
||||
@@ -42,6 +52,9 @@ export class Reroute implements Positionable, LinkSegment, Serialisable<Serialis
|
||||
this.#parentId = value
|
||||
}
|
||||
|
||||
/** Set when the reroute has no complete links but is still on the canvas. */
|
||||
floating?: FloatingRerouteSlot
|
||||
|
||||
#pos = this.#malloc.subarray(0, 2)
|
||||
/** @inheritdoc */
|
||||
get pos(): Point {
|
||||
@@ -68,6 +81,9 @@ export class Reroute implements Positionable, LinkSegment, Serialisable<Serialis
|
||||
/** The ID ({@link LLink.id}) of every link using this reroute */
|
||||
linkIds: Set<LinkId>
|
||||
|
||||
/** The ID ({@link LLink.id}) of every floating link using this reroute */
|
||||
floatingLinkIds: Set<LinkId>
|
||||
|
||||
/** Cached cos */
|
||||
cos: number = 0
|
||||
sin: number = 0
|
||||
@@ -132,10 +148,12 @@ export class Reroute implements Positionable, LinkSegment, Serialisable<Serialis
|
||||
pos?: Point,
|
||||
parentId?: RerouteId,
|
||||
linkIds?: Iterable<LinkId>,
|
||||
floatingLinkIds?: Iterable<LinkId>,
|
||||
) {
|
||||
this.#network = new WeakRef(network)
|
||||
this.update(parentId, pos, linkIds)
|
||||
this.linkIds ??= new Set()
|
||||
this.floatingLinkIds = new Set(floatingLinkIds)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -150,10 +168,12 @@ export class Reroute implements Positionable, LinkSegment, Serialisable<Serialis
|
||||
parentId: RerouteId | undefined,
|
||||
pos?: Point,
|
||||
linkIds?: Iterable<LinkId>,
|
||||
floating?: FloatingRerouteSlot,
|
||||
): void {
|
||||
this.parentId = parentId
|
||||
if (pos) this.pos = pos
|
||||
if (linkIds) this.linkIds = new Set(linkIds)
|
||||
this.floating = floating
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -161,12 +181,15 @@ export class Reroute implements Positionable, LinkSegment, Serialisable<Serialis
|
||||
* @param links Collection of valid links
|
||||
* @returns true if any links remain after validation
|
||||
*/
|
||||
validateLinks(links: Map<LinkId, LLink>): boolean {
|
||||
const { linkIds } = this
|
||||
validateLinks(links: ReadonlyMap<LinkId, LLink>, floatingLinks: ReadonlyMap<LinkId, LLink>): boolean {
|
||||
const { linkIds, floatingLinkIds } = this
|
||||
for (const linkId of linkIds) {
|
||||
if (!links.get(linkId)) linkIds.delete(linkId)
|
||||
}
|
||||
return linkIds.size > 0
|
||||
for (const linkId of floatingLinkIds) {
|
||||
if (!floatingLinks.get(linkId)) floatingLinkIds.delete(linkId)
|
||||
}
|
||||
return linkIds.size > 0 || floatingLinkIds.size > 0
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -333,11 +356,24 @@ export class Reroute implements Positionable, LinkSegment, Serialisable<Serialis
|
||||
|
||||
/** @inheritdoc */
|
||||
asSerialisable(): SerialisableReroute {
|
||||
const { id, parentId, pos, linkIds } = this
|
||||
const floating = floatingToSerialisable(this.floating)
|
||||
return {
|
||||
id: this.id,
|
||||
parentId: this.parentId,
|
||||
pos: [this.pos[0], this.pos[1]],
|
||||
linkIds: [...this.linkIds],
|
||||
id,
|
||||
parentId,
|
||||
pos: [pos[0], pos[1]],
|
||||
linkIds: [...linkIds],
|
||||
floating,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function floatingToSerialisable(floating: FloatingRerouteSlot | undefined): FloatingRerouteSlot | undefined {
|
||||
return floating
|
||||
? {
|
||||
nodeId: floating.nodeId,
|
||||
slot: floating.slot,
|
||||
slotType: floating.slotType,
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
|
||||
@@ -114,6 +114,7 @@ export interface IPinnable {
|
||||
export interface ReadonlyLinkNetwork {
|
||||
readonly links: ReadonlyMap<LinkId, LLink>
|
||||
readonly reroutes: ReadonlyMap<RerouteId, Reroute>
|
||||
readonly floatingLinks: ReadonlyMap<LinkId, LLink>
|
||||
getNodeById(id: NodeId): LGraphNode | null
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import type { IGraphGroupFlags } from "../LGraphGroup"
|
||||
import type { NodeId, NodeProperty } from "../LGraphNode"
|
||||
import type { LiteGraph } from "../litegraph"
|
||||
import type { LinkId, SerialisedLLinkArray } from "../LLink"
|
||||
import type { RerouteId } from "../Reroute"
|
||||
import type { FloatingRerouteSlot, RerouteId } from "../Reroute"
|
||||
import type { TWidgetValue } from "../types/widgets"
|
||||
import type { RenderShape } from "./globalEnums"
|
||||
|
||||
@@ -36,6 +36,7 @@ export interface SerialisableGraph {
|
||||
groups?: ISerialisedGroup[]
|
||||
nodes?: ISerialisedNode[]
|
||||
links?: SerialisableLLink[]
|
||||
floatingLinks?: SerialisableLLink[]
|
||||
reroutes?: SerialisableReroute[]
|
||||
extra?: Dictionary<unknown>
|
||||
}
|
||||
@@ -83,6 +84,7 @@ export interface ISerialisedGraph {
|
||||
last_link_id: number
|
||||
nodes: ISerialisedNode[]
|
||||
links: SerialisedLLinkArray[]
|
||||
floatingLinks?: SerialisableLLink[]
|
||||
groups: ISerialisedGroup[]
|
||||
config: LGraphConfig
|
||||
version: typeof LiteGraph.VERSION
|
||||
@@ -128,6 +130,7 @@ export interface SerialisableReroute {
|
||||
parentId?: RerouteId
|
||||
pos: Point
|
||||
linkIds: LinkId[]
|
||||
floating?: FloatingRerouteSlot
|
||||
}
|
||||
|
||||
export interface SerialisableLLink {
|
||||
|
||||
Reference in New Issue
Block a user