@gravity-ui/graph
Version:
Modern graph editor component
117 lines (116 loc) • 3.82 kB
TypeScript
/**
* Fast tracker for calculating graph boundaries (usableRect) with smart optimization.
*
* ## What it does:
* Keeps track of the overall bounding box of all graph elements (blocks, connections, etc.)
* without having to recalculate everything when elements move.
*
* ## Why it's fast:
*
* ### The Problem:
* Normally, when you move ANY element in a graph with 1000 blocks,
* you'd need to check ALL 1000 blocks to find new boundaries.
* This is slow and causes lag during drag operations.
*
* ### Our Solution:
* We keep track of which elements are at each "wall" of the graph:
* - Left wall (minX)
* - Right wall (maxX)
* - Top wall (maxY)
* - Bottom wall (minY)
*
* ### The Magic:
* - **95% of moves**: Element is NOT at any wall → Check in O(1), super fast! ⚡
* - **5% of moves**: Element IS at a wall → May need O(n) recalculation, but rare
* - **Result**: Smooth 120fps+ interactions even with complex graphs
*
* ### Real Example:
* ```
* Graph with many blocks:
* ┌─────────────────────────────────────┐
* │ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ │
* │ │A│ │B│ │C│ │D│ │E│ │F│ │G│ ... │
* │ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ │
* └─────────────────────────────────────┘
* ```
*
* Moving block C (middle): Only check "does C touch any walls?" → NO → Done!
* Moving block A (edge): Check "is A the only one on left wall?" → Handle accordingly
*
* ## How to use:
* ```typescript
* const tracker = new IncrementalBoundingBoxTracker();
* tracker.add(element); // Add element to tracking
* tracker.update(element, newPos); // Update element position
* tracker.remove(element); // Remove element
* const bounds = tracker.toJSON(); // Get current boundaries (always fast!)
* ```
*
* This enables responsive camera positioning, smooth zoom/pan operations,
* and lag-free dragging even with hundreds of graph elements.
*/
export declare class IncrementalBoundingBoxTracker<T extends {
minX: number;
minY: number;
maxX: number;
maxY: number;
}> {
private items;
private bounds;
private boundaryElements;
has(item: T): boolean;
/**
* Add element for tracking - O(1)
* @param item Element to track
* @returns void
*/
add(item: T): void;
/**
* Remove element from tracking - O(1) or O(n) if boundary element
* @param item Element to remove
* @returns void
*/
remove(item: T): void;
/**
* Update element - O(1) in most cases
* @param item Element to update
* @param newBounds New element bounds
* @returns void
*/
update(item: T, newBounds: Partial<T>): void;
/**
* Clear all elements - O(1)
*
* @returns void
*/
clear(): void;
/**
* Load array of elements - O(n)
* @param items Array of elements to load
* @returns void
*/
load(items: T[]): void;
/**
* Get bounding box - O(1)
* @returns Object with element count and bounding box
*/
toJSON(): {
length: number;
minX: number;
minY: number;
maxX: number;
maxY: number;
};
private updateBoundsIncremental;
private updateBoundsOptimized;
private needsRecalculation;
private isOnBoundary;
private removeFromBoundaryTracking;
private recalculateBounds;
/**
* Check if item has valid bounds (no undefined, NaN, or non-finite values)
* @param item Element to check
* @returns true if bounds are valid
*/
private isValidBounds;
}