Files
ComfyUI_frontend/src/renderer/extensions/minimap/data/AbstractMinimapDataSource.ts
Johnpaul Chiwetelu 8ffe63f54e Layoutstore Minimap calculation (#5547)
This pull request refactors the minimap rendering system to use a
unified, extensible data source abstraction for all minimap operations.
By introducing a data source interface and factory, the minimap can now
seamlessly support multiple sources of node layout (such as the
`LayoutStore` or the underlying `LiteGraph`), improving maintainability
and future extensibility. Rendering logic and change detection
throughout the minimap have been updated to use this new abstraction,
resulting in cleaner code and easier support for new data models.

**Core architecture improvements:**

* Introduced a new `IMinimapDataSource` interface and related data types
(`MinimapNodeData`, `MinimapLinkData`, `MinimapGroupData`) to
standardize node, link, and group data for minimap rendering.
* Added an abstract base class `AbstractMinimapDataSource` that provides
shared logic for bounds and group/link extraction, and implemented two
concrete data sources: `LiteGraphDataSource` (for classic graph data)
and `LayoutStoreDataSource` (for layout store data).
[[1]](diffhunk://#diff-ea46218fc9ffced84168a5ff975e4a30e43f7bf134ee8f02ed2eae66efbb729dR1-R95)
[[2]](diffhunk://#diff-9a6b7c6be25b4dbeb358fea18f3a21e78797058ccc86c818ed1e5f69c7355273R1-R30)
[[3]](diffhunk://#diff-f200ba9495a03157198abff808ed6c3761746071404a52adbad98f6a9d01249bR1-R42)
* Created a `MinimapDataSourceFactory` that selects the appropriate data
source based on the presence of layout store data, enabling seamless
switching between data models.

**Minimap rendering and logic refactoring:**

* Updated all minimap rendering functions (`renderGroups`,
`renderNodes`, `renderConnections`) and the main `renderMinimapToCanvas`
entry point to use the unified data source interface, significantly
simplifying the rendering code and decoupling it from the underlying
graph structure.
[[1]](diffhunk://#diff-3670f99330b2e24aca3cffeeac6600adf8abadd6dd585f596d60fde1dd093121L1-R11)
[[2]](diffhunk://#diff-3670f99330b2e24aca3cffeeac6600adf8abadd6dd585f596d60fde1dd093121R33-R75)
[[3]](diffhunk://#diff-3670f99330b2e24aca3cffeeac6600adf8abadd6dd585f596d60fde1dd093121L66-R124)
[[4]](diffhunk://#diff-3670f99330b2e24aca3cffeeac6600adf8abadd6dd585f596d60fde1dd093121L134-R161)
[[5]](diffhunk://#diff-3670f99330b2e24aca3cffeeac6600adf8abadd6dd585f596d60fde1dd093121L153-R187)
[[6]](diffhunk://#diff-3670f99330b2e24aca3cffeeac6600adf8abadd6dd585f596d60fde1dd093121L187-L188)
[[7]](diffhunk://#diff-3670f99330b2e24aca3cffeeac6600adf8abadd6dd585f596d60fde1dd093121R227-R231)
[[8]](diffhunk://#diff-3670f99330b2e24aca3cffeeac6600adf8abadd6dd585f596d60fde1dd093121L230-R248)
* Refactored minimap viewport and graph change detection logic to use
the data source abstraction for bounds, node, and link change detection,
and to respond to layout store version changes.
[[1]](diffhunk://#diff-d92e448dee5e30782a66b9e66d8c8b05626dffd0b2ff1032f2612b9a9b9c51f6L2-R10)
[[2]](diffhunk://#diff-d92e448dee5e30782a66b9e66d8c8b05626dffd0b2ff1032f2612b9a9b9c51f6R33-R35)
[[3]](diffhunk://#diff-d92e448dee5e30782a66b9e66d8c8b05626dffd0b2ff1032f2612b9a9b9c51f6L99-R141)
[[4]](diffhunk://#diff-d92e448dee5e30782a66b9e66d8c8b05626dffd0b2ff1032f2612b9a9b9c51f6R157-R160)
[[5]](diffhunk://#diff-338d14c67dabffaf6f68fbf09b16e8d67bead2b9df340e46601b2fbd57331521L8-R11)
[[6]](diffhunk://#diff-338d14c67dabffaf6f68fbf09b16e8d67bead2b9df340e46601b2fbd57331521L56-R64)

These changes make the minimap codebase more modular and robust, and lay
the groundwork for supporting additional node layout strategies in the
future.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5547-Layoutstore-Minimap-calculation-26e6d73d3650813e9457c051dff41ca1)
by [Unito](https://www.unito.io)
2025-09-19 13:52:57 -07:00

96 lines
2.6 KiB
TypeScript

import type { LGraph } from '@/lib/litegraph/src/litegraph'
import { calculateNodeBounds } from '@/renderer/core/spatial/boundsCalculator'
import type {
IMinimapDataSource,
MinimapBounds,
MinimapGroupData,
MinimapLinkData,
MinimapNodeData
} from '../types'
/**
* Abstract base class for minimap data sources
* Provides common functionality and shared implementation
*/
export abstract class AbstractMinimapDataSource implements IMinimapDataSource {
constructor(protected graph: LGraph | null) {}
// Abstract methods that must be implemented by subclasses
abstract getNodes(): MinimapNodeData[]
abstract getNodeCount(): number
abstract hasData(): boolean
// Shared implementation using calculateNodeBounds
getBounds(): MinimapBounds {
const nodes = this.getNodes()
if (nodes.length === 0) {
return { minX: 0, minY: 0, maxX: 100, maxY: 100, width: 100, height: 100 }
}
// Convert MinimapNodeData to the format expected by calculateNodeBounds
const compatibleNodes = nodes.map((node) => ({
pos: [node.x, node.y],
size: [node.width, node.height]
}))
const bounds = calculateNodeBounds(compatibleNodes)
if (!bounds) {
return { minX: 0, minY: 0, maxX: 100, maxY: 100, width: 100, height: 100 }
}
return bounds
}
// Shared implementation for groups
getGroups(): MinimapGroupData[] {
if (!this.graph?._groups) return []
return this.graph._groups.map((group) => ({
x: group.pos[0],
y: group.pos[1],
width: group.size[0],
height: group.size[1],
color: group.color
}))
}
// TODO: update when Layoutstore supports links
getLinks(): MinimapLinkData[] {
if (!this.graph) return []
return this.extractLinksFromGraph(this.graph)
}
protected extractLinksFromGraph(graph: LGraph): MinimapLinkData[] {
const links: MinimapLinkData[] = []
const nodeMap = new Map(this.getNodes().map((n) => [n.id, n]))
for (const node of graph._nodes) {
if (!node.outputs) continue
const sourceNodeData = nodeMap.get(String(node.id))
if (!sourceNodeData) continue
for (const output of node.outputs) {
if (!output.links) continue
for (const linkId of output.links) {
const link = graph.links[linkId]
if (!link) continue
const targetNodeData = nodeMap.get(String(link.target_id))
if (!targetNodeData) continue
links.push({
sourceNode: sourceNodeData,
targetNode: targetNodeData,
sourceSlot: link.origin_slot,
targetSlot: link.target_slot
})
}
}
}
return links
}
}