UNPKG

@gravity-ui/graph

Version:

Modern graph editor component

117 lines (116 loc) 3.82 kB
/** * 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; }