Add floating reroutes (#773)

### Floating reroutes

Native reroutes can now be kept in a disconnected state.

Link chains may be kept provided they are connected to _either_ an input
or an output. By design, reroutes will be automatically removed if both
sides are disconnected.
This commit is contained in:
filtered
2025-03-14 06:56:57 +11:00
committed by GitHub
parent d0e1998415
commit e6a914117b
7 changed files with 312 additions and 84 deletions

View File

@@ -104,12 +104,12 @@ export class LLink implements LinkSegment, Serialisable<SerialisableLLink> {
}
/**
* Gets all reroutes from the output slot to this segment. If this segment is a reroute, it will be the last element.
* Gets all reroutes from the output slot to this segment. If this segment is a reroute, it will not be included.
* @returns An ordered array of all reroutes from the node output to
* this reroute or the reroute before it. Otherwise, an empty array.
*/
static getReroutes(
network: ReadonlyLinkNetwork,
network: Pick<ReadonlyLinkNetwork, "reroutes">,
linkSegment: LinkSegment,
): Reroute[] {
if (!linkSegment.parentId) return []
@@ -119,7 +119,7 @@ export class LLink implements LinkSegment, Serialisable<SerialisableLLink> {
}
static getFirstReroute(
network: LinkNetwork,
network: Pick<ReadonlyLinkNetwork, "reroutes">,
linkSegment: LinkSegment,
): Reroute | undefined {
return LLink.getReroutes(network, linkSegment).at(0)
@@ -166,15 +166,44 @@ export class LLink implements LinkSegment, Serialisable<SerialisableLLink> {
/**
* Disconnects a link and removes it from the graph, cleaning up any reroutes that are no longer used
* @param network The container (LGraph) where reroutes should be updated
* @param keepReroutes If `true`, reroutes will not be garbage collected.
* @param keepReroutes If `undefined`, reroutes will be automatically removed if no links remain.
* If `input` or `output`, reroutes will not be automatically removed, and retain a connection to the input or output, respectively.
*/
disconnect(network: LinkNetwork, keepReroutes?: boolean): void {
disconnect(network: LinkNetwork, keepReroutes?: "input" | "output"): void {
const reroutes = LLink.getReroutes(network, this)
const lastReroute = reroutes.at(-1)
// When floating from output, 1-to-1 ratio of floating link to final reroute (tree-like)
const outputFloating = keepReroutes === "output" &&
lastReroute?.linkIds.size === 1 &&
lastReroute.floatingLinkIds.size === 0
// When floating from inputs, the final (input side) reroute may have many floating links
if (outputFloating || (keepReroutes === "input" && lastReroute)) {
const newLink = LLink.create(this)
newLink.id = -1
if (keepReroutes === "input") {
newLink.origin_id = -1
newLink.origin_slot = -1
lastReroute.floating = { slotType: "input" }
} else {
newLink.target_id = -1
newLink.target_slot = -1
lastReroute.floating = { slotType: "output" }
}
network.addFloatingLink(newLink)
}
for (const reroute of reroutes) {
reroute.linkIds.delete(this.id)
if (!keepReroutes && !reroute.linkIds.size)
if (!keepReroutes && !reroute.linkIds.size && !reroute.floatingLinkIds.size) {
network.reroutes.delete(reroute.id)
}
}
network.links.delete(this.id)
}