cleaned up code for onlyRenderVisibleElements

This commit is contained in:
peterkogo
2024-12-20 15:16:39 +01:00
parent 848434c419
commit 56ef230428
3 changed files with 95 additions and 91 deletions
+2 -2
View File
@@ -344,11 +344,11 @@ export function createStore(signals: StoreSignals): SvelteFlowStore {
} }
const updateConnection: UpdateConnection = (newConnection: ConnectionState) => { const updateConnection: UpdateConnection = (newConnection: ConnectionState) => {
store.rawConnection = { ...newConnection }; store._connection = { ...newConnection };
}; };
function cancelConnection() { function cancelConnection() {
store.rawConnection = initialConnection; store._connection = initialConnection;
} }
function reset() { function reset() {
@@ -53,12 +53,13 @@ import type {
IsValidConnection, IsValidConnection,
Edge, Edge,
Node, Node,
EdgeLayouted EdgeLayouted,
InternalNode
} from '$lib/types'; } from '$lib/types';
import type { StoreSignals } from './types'; import type { StoreSignals } from './types';
import { MediaQuery } from 'svelte/reactivity'; import { MediaQuery } from 'svelte/reactivity';
import { gatherLayoutedEdges, getVisibleNodes } from './visibleElements'; import { getLayoutedEdges, getVisibleNodes, type EdgeLayoutAllOptions } from './visibleElements';
export const initialNodeTypes = { export const initialNodeTypes = {
input: InputNode, input: InputNode,
@@ -75,7 +76,6 @@ export const initialEdgeTypes = {
}; };
export const getInitialStore = (signals: StoreSignals) => { export const getInitialStore = (signals: StoreSignals) => {
const previousLayoutedEdges = new Map<string, EdgeLayouted>();
// We use a class here, because Svelte adds getters & setter for us. // We use a class here, because Svelte adds getters & setter for us.
// Inline classes have some performance implications but we just call it once (max twice). // Inline classes have some performance implications but we just call it once (max twice).
class SvelteFlowStore { class SvelteFlowStore {
@@ -112,47 +112,47 @@ export const getInitialStore = (signals: StoreSignals) => {
connectionLookup: ConnectionLookup = new Map(); connectionLookup: ConnectionLookup = new Map();
edgeLookup: EdgeLookup = new Map(); edgeLookup: EdgeLookup = new Map();
_prevVisibleEdges = new Map<string, EdgeLayouted>();
visible = $derived.by(() => { visible = $derived.by(() => {
// We need to access this._nodes to trigger on changes const {
// eslint-disable-next-line @typescript-eslint/no-unused-expressions // We need to access this._nodes to trigger on changes
this._nodes; // eslint-disable-next-line @typescript-eslint/no-unused-vars
_nodes,
_edges: edges,
_prevVisibleEdges: previousEdges,
nodeLookup,
connectionMode,
onerror,
onlyRenderVisibleElements
} = this;
// We either add all or only visible nodes to visibleNodes here. let visibleNodes: Map<string, InternalNode>;
let visibleNodes = new Map(); let visibleEdges: Map<string, EdgeLayouted>;
let layoutedEdges = new Map<string, EdgeLayouted>();
// TODO: is there a more elegant solution?? const options = { edges, previousEdges, nodeLookup, connectionMode, onerror };
if (this.onlyRenderVisibleElements) {
const transform: Transform = [this.viewport.x, this.viewport.y, this.viewport.zoom]; if (onlyRenderVisibleElements) {
getVisibleNodes(this.nodeLookup, transform, this.width, this.height).forEach((node) => { // We only subscribe to viewport, width, height if onlyRenderVisibleElements is true
visibleNodes.set(node.id, node); const { viewport, width, height } = this;
}); const transform: Transform = [viewport.x, viewport.y, viewport.zoom];
layoutedEdges = gatherLayoutedEdges(
this._edges, visibleNodes = getVisibleNodes(nodeLookup, transform, width, height);
this.nodeLookup, visibleEdges = getLayoutedEdges({
previousLayoutedEdges, ...options,
this.connectionMode, onlyRenderVisible: true,
this.onerror, visibleNodes,
true,
transform, transform,
this.width, width,
this.height, height
visibleNodes });
);
} else { } else {
visibleNodes = this.nodeLookup; visibleNodes = this.nodeLookup;
layoutedEdges = gatherLayoutedEdges( visibleEdges = getLayoutedEdges(options as EdgeLayoutAllOptions);
this._edges,
this.nodeLookup,
previousLayoutedEdges,
this.connectionMode,
this.onerror,
false
);
} }
return { return {
nodes: visibleNodes, nodes: visibleNodes,
edges: layoutedEdges edges: visibleEdges
}; };
}); });
@@ -196,7 +196,8 @@ export const getInitialStore = (signals: StoreSignals) => {
nodeTypes: NodeTypes = $derived({ ...initialNodeTypes, ...signals.props.nodeTypes }); nodeTypes: NodeTypes = $derived({ ...initialNodeTypes, ...signals.props.nodeTypes });
edgeTypes: EdgeTypes = $derived({ ...initialEdgeTypes, ...signals.props.edgeTypes }); edgeTypes: EdgeTypes = $derived({ ...initialEdgeTypes, ...signals.props.edgeTypes });
// _viewport is the internal viewport. We either return signals.viewport or _viewport // _viewport is the internal viewport.
// when binding to viewport, we operate on signals.viewport instead
_viewport: Viewport = $state(signals.props.initialViewport ?? { x: 0, y: 0, zoom: 1 }); _viewport: Viewport = $state(signals.props.initialViewport ?? { x: 0, y: 0, zoom: 1 });
get viewport() { get viewport() {
return signals.viewport ?? this._viewport; return signals.viewport ?? this._viewport;
@@ -208,27 +209,29 @@ export const getInitialStore = (signals: StoreSignals) => {
this._viewport = viewport; this._viewport = viewport;
} }
connectionMode: ConnectionMode = $derived( // _connection is viewport independent and originating from XYHandle
signals.props.connectionMode ?? ConnectionMode.Strict _connection: ConnectionState = $state(initialConnection);
); // We derive a viewport dependent connection here
rawConnection: ConnectionState = $state(initialConnection);
connection: ConnectionState = $derived.by(() => { connection: ConnectionState = $derived.by(() => {
if (this.rawConnection.inProgress) { if (this._connection.inProgress) {
return { return {
...this.rawConnection, ...this._connection,
to: pointToRendererPoint(this.rawConnection.to, [ to: pointToRendererPoint(this._connection.to, [
this.viewport.x, this.viewport.x,
this.viewport.y, this.viewport.y,
this.viewport.zoom this.viewport.zoom
]) ])
}; };
} else { } else {
return { ...this.rawConnection }; return this._connection;
} }
}); });
connectionLineType: ConnectionLineType = $derived( connectionLineType: ConnectionLineType = $derived(
signals.props.connectionLineType ?? ConnectionLineType.Bezier signals.props.connectionLineType ?? ConnectionLineType.Bezier
); );
connectionMode: ConnectionMode = $derived(
signals.props.connectionMode ?? ConnectionMode.Strict
);
connectionRadius: number = $derived(signals.props.connectionRadius ?? 20); connectionRadius: number = $derived(signals.props.connectionRadius ?? 20);
isValidConnection: IsValidConnection = $derived( isValidConnection: IsValidConnection = $derived(
signals.props.isValidConnection ?? (() => true) signals.props.isValidConnection ?? (() => true)
@@ -258,14 +261,14 @@ export const getInitialStore = (signals: StoreSignals) => {
edgesInitialized: boolean = $state(false); edgesInitialized: boolean = $state(false);
viewportInitialized: boolean = $state(false); viewportInitialized: boolean = $state(false);
initialNodesLength: number = signals.nodes?.length ?? 0; _initialNodesLength: number = signals.nodes?.length ?? 0;
initialEdgesLength: number = signals.edges?.length ?? 0; _initialEdgesLength: number = signals.edges?.length ?? 0;
initialized: boolean = $derived.by(() => { initialized: boolean = $derived.by(() => {
let initialized = false; let initialized = false;
// if it hasn't been initialised check if it's now // if it hasn't been initialised check if it's now
if (this.initialNodesLength === 0) { if (this._initialNodesLength === 0) {
initialized = this.viewportInitialized; initialized = this.viewportInitialized;
} else if (this.initialEdgesLength === 0) { } else if (this._initialEdgesLength === 0) {
initialized = this.viewportInitialized && this.nodesInitialized; initialized = this.viewportInitialized && this.nodesInitialized;
} else { } else {
initialized = this.viewportInitialized && this.nodesInitialized && this.edgesInitialized; initialized = this.viewportInitialized && this.nodesInitialized && this.edgesInitialized;
@@ -273,13 +276,14 @@ export const getInitialStore = (signals: StoreSignals) => {
return initialized; return initialized;
}); });
prefersDark = new MediaQuery(
_prefersDark = new MediaQuery(
'(prefers-color-scheme: dark)', '(prefers-color-scheme: dark)',
signals.props.colorModeSSR === 'dark' signals.props.colorModeSSR === 'dark'
); );
colorMode: ColorModeClass = $derived( colorMode: ColorModeClass = $derived(
signals.props.colorMode === 'system' signals.props.colorMode === 'system'
? this.prefersDark.current ? this._prefersDark.current
? 'dark' ? 'dark'
: 'light' : 'light'
: (signals.props.colorMode ?? 'light') : (signals.props.colorMode ?? 'light')
@@ -16,42 +16,43 @@ export function getVisibleNodes(
width: number, width: number,
height: number height: number
) { ) {
return getNodesInside(nodeLookup, { x: 0, y: 0, width: width, height: height }, transform, true); const visibleNodes = new Map<string, InternalNode>();
getNodesInside(nodeLookup, { x: 0, y: 0, width: width, height: height }, transform, true).forEach(
(node) => {
visibleNodes.set(node.id, node);
}
);
return visibleNodes;
} }
// TODO: is this what we want? export interface EdgeLayoutBaseOptions {
export function gatherLayoutedEdges( edges: Edge[];
edges: Edge[], previousEdges: Map<string, EdgeLayouted>;
nodeLookup: NodeLookup, nodeLookup: NodeLookup;
previousLayoutedEdges: Map<string, EdgeLayouted>, connectionMode: ConnectionMode;
connectionMode: ConnectionMode, onerror: OnError;
onerror: OnError, }
onlyRenderVisibleElements: true,
transform: Transform, export interface EdgeLayoutAllOptions extends EdgeLayoutBaseOptions {
width: number, onlyRenderVisible: never;
height: number, visibleNodes: never;
visibleNodes: Map<string, InternalNode> transform: never;
): Map<string, EdgeLayouted>; width: never;
export function gatherLayoutedEdges( height: never;
edges: Edge[], }
nodeLookup: NodeLookup,
previousLayoutedEdges: Map<string, EdgeLayouted>, export interface EdgeLayoutOnlyVisibleOptions extends EdgeLayoutBaseOptions {
connectionMode: ConnectionMode, visibleNodes: Map<string, InternalNode>;
onerror: OnError, transform: Transform;
onlyRenderVisibleElements: false width: number;
): Map<string, EdgeLayouted>; height: number;
export function gatherLayoutedEdges( onlyRenderVisible: true;
edges: Edge[], }
nodeLookup: NodeLookup,
previousLayoutedEdges: Map<string, EdgeLayouted>, export type EdgeLayoutOptions = EdgeLayoutAllOptions | EdgeLayoutOnlyVisibleOptions;
connectionMode: ConnectionMode,
onerror: OnError, export function getLayoutedEdges(options: EdgeLayoutOptions): Map<string, EdgeLayouted> {
onlyRenderVisibleElements?: boolean, const { edges, nodeLookup, previousEdges, connectionMode, onerror, onlyRenderVisible } = options;
transform?: Transform,
width?: number,
height?: number,
visibleNodes?: Map<string, InternalNode>
): Map<string, EdgeLayouted> {
const layoutedEdges = new Map<string, EdgeLayouted>(); const layoutedEdges = new Map<string, EdgeLayouted>();
for (const edge of edges) { for (const edge of edges) {
const sourceNode = nodeLookup.get(edge.source); const sourceNode = nodeLookup.get(edge.source);
@@ -61,7 +62,8 @@ export function gatherLayoutedEdges(
continue; continue;
} }
if (onlyRenderVisibleElements) { if (onlyRenderVisible) {
const { visibleNodes, transform, width, height } = options;
if ( if (
isEdgeVisible({ isEdgeVisible({
sourceNode, sourceNode,
@@ -82,7 +84,7 @@ export function gatherLayoutedEdges(
// the current and previous edge are the same // the current and previous edge are the same
// and the source and target node are the same // and the source and target node are the same
// and references to internalNodes are the same // and references to internalNodes are the same
const previous = previousLayoutedEdges.get(edge.id); const previous = previousEdges.get(edge.id);
if ( if (
previous && previous &&
edge === previous.edge && edge === previous.edge &&
@@ -99,7 +101,7 @@ export function gatherLayoutedEdges(
targetNode, targetNode,
sourceHandle: edge.sourceHandle || null, sourceHandle: edge.sourceHandle || null,
targetHandle: edge.targetHandle || null, targetHandle: edge.targetHandle || null,
connectionMode: connectionMode, connectionMode,
onError: onerror onError: onerror
}); });
@@ -121,7 +123,5 @@ export function gatherLayoutedEdges(
} }
} }
previousLayoutedEdges = layoutedEdges;
return layoutedEdges; return layoutedEdges;
} }