68 lines
2.3 KiB
TypeScript
68 lines
2.3 KiB
TypeScript
/**
|
||
* Graph path utilities: compute which nodes/edges are "on the path" of an update.
|
||
* Used to show connection ant trail only along the full chain (upstream → updating → downstream).
|
||
* Works with any node types; any node can signal it is updating via startConnectionPathUpdate(id).
|
||
*/
|
||
|
||
export type GraphEdge = { source: string; target: string }
|
||
|
||
/** Nodes reachable from seedIds by following edges forward (source → target). */
|
||
export function getDownstreamNodeIds(edges: GraphEdge[], seedIds: string[]): Set<string> {
|
||
const out = new Set<string>(seedIds)
|
||
let added = true
|
||
while (added) {
|
||
added = false
|
||
for (const e of edges) {
|
||
if (out.has(e.source) && !out.has(e.target)) {
|
||
out.add(e.target)
|
||
added = true
|
||
}
|
||
}
|
||
}
|
||
return out
|
||
}
|
||
|
||
/** Nodes that can reach any seed by following edges backward (target → source). */
|
||
export function getUpstreamNodeIds(edges: GraphEdge[], seedIds: string[]): Set<string> {
|
||
const out = new Set<string>(seedIds)
|
||
let added = true
|
||
while (added) {
|
||
added = false
|
||
for (const e of edges) {
|
||
if (out.has(e.target) && !out.has(e.source)) {
|
||
out.add(e.source)
|
||
added = true
|
||
}
|
||
}
|
||
}
|
||
return out
|
||
}
|
||
|
||
/**
|
||
* All node ids that lie on the path of an update.
|
||
* - If triggerNodeIds is non-empty: path = nodes that are both downstream of a trigger and
|
||
* upstream of an updating node (only the chain that actually triggered the update).
|
||
* - Otherwise: path = upstream ∪ updating ∪ downstream of updating (legacy full upstream/downstream).
|
||
* An edge should show the update animation iff both its source and target are in this set.
|
||
*/
|
||
export function getPathNodeIds(
|
||
edges: GraphEdge[],
|
||
updatingNodeIds: string[],
|
||
triggerNodeIds?: string[]
|
||
): Set<string> {
|
||
if (updatingNodeIds.length === 0) return new Set()
|
||
const upstream = getUpstreamNodeIds(edges, updatingNodeIds)
|
||
if (triggerNodeIds != null && triggerNodeIds.length > 0) {
|
||
const downstreamOfTrigger = getDownstreamNodeIds(edges, triggerNodeIds)
|
||
const path = new Set<string>()
|
||
upstream.forEach((id) => {
|
||
if (downstreamOfTrigger.has(id)) path.add(id)
|
||
})
|
||
return path
|
||
}
|
||
const downstream = getDownstreamNodeIds(edges, updatingNodeIds)
|
||
const path = new Set<string>(upstream)
|
||
downstream.forEach((id) => path.add(id))
|
||
return path
|
||
}
|