This document describes the architecture of squarified, explaining its design principles, core components, and optimization techniques.
Squarified implements a multi-layered rendering approach that logically separates different visualization concerns:
flowchart TB
Canvas[Canvas Element]
BaseLayer[Base Content Layer]
InteractionLayer[Interaction Layer]
HighlightLayer[Highlight Layer]
Canvas --> BaseLayer
Canvas --> InteractionLayer
Canvas --> HighlightLayer
Benefits:
- Selective Updates: Each layer can be updated independently, avoiding costly full redraws
- Performance Optimization: The highlight layer can animate without affecting static content
- Render Isolation: User interactions (hovering, selection) don't trigger unnecessary redraws of stable content
- Visual Separation: Clear separation between base content, interactive elements, and highlighting
Squarified uses a dedicated rendering engine called "Etoile" that abstracts Canvas API complexity:
flowchart TB
Component[Component] --> Etoile[Etoile Engine]
Etoile --> Graphics[Graphics Primitives]
Etoile --> Matrix[Matrix Transformations]
Etoile --> Schedule[Render Scheduling]
Graphics --> RoundRect[RoundRect]
Graphics --> Text[Text]
Graphics --> Box[Box Container]
Benefits:
- Abstracted Rendering: Replaces direct Canvas API calls with a maintainable object model
- Composition: Creates reusable, composable rendering units
- Transformation Management: Simplifies matrix-based transformations
- Performance Optimizations: Implements specialized rendering techniques for treemaps
- Render Batching: Groups similar operations for better performance
The core system is kept minimal and stable, with functionality added through a plugin system:
export const presetColorPlugin = definePlugin({
name: 'treemap:preset-color',
onModuleInit(modules) {
// Logic isolated in plugin
}
})Current plugins:
preset-color: Handles color assignment for treemap nodeshighlight: Manages node highlighting during interactionszoomable: Controls zoom behavior and animationsdrag: Implements drag interactionwheel: Handles scroll wheel navigation
Benefits:
- Core Stability: Plugin isolation prevents bugs in extensions from affecting the rendering core
- Logic Separation: Each functional area is encapsulated in its own plugin
- Independent Testing: Plugins can be tested in isolation
- Extensibility: New features can be added without modifying core code
- On-demand Loading: Plugins can be loaded only when needed
Instead of complex conditional logic for handling interactions, Squarified employs a robust state machine:
stateDiagram-v2
[*] --> IDLE
IDLE --> ZOOMING: zoom()
IDLE --> DRAGGING: mousedown
ZOOMING --> IDLE: animation complete
DRAGGING --> IDLE: mouseup
DRAGGING --> CANCELLED: escape key
CANCELLED --> IDLE: reset
Benefits:
- Conflict Prevention: Eliminates gesture conflicts (e.g., simultaneous drag and zoom)
- Predictable Behavior: Clearly defines allowed state transitions
- Maintainable Logic: Reduces complex conditional branches
- Debugging: State transitions are easily traceable
- Extensibility: New interaction modes can be added with minimal changes
Squarified implements multiple caching techniques to optimize performance:
Text measurement operations (particularly expensive in Canvas) are cached:
// Font measurement cache
const fontMeasureCache = new Map<string, TextMetrics>()Font size calculation uses binary search algorithms to efficiently find optimal text sizing:
function findOptimalFontSize(text, maxWidth, minSize, maxSize) {
// Binary search between minSize and maxSize
// Significantly faster than linear approaches
}Node layout calculations are memoized to avoid recomputation during animations:
flowchart TD
Input[Input Hierarchical Data] --> Preprocessor[Data Preprocessor]
Preprocessor --> Squarify[Squarify Algorithm]
Squarify --> LayoutModules[Layout Module Generation]
LayoutModules --> Plugins[Plugin Processing]
Plugins --> Renderer[Etoile Renderer]
Renderer --> Display[Canvas Display]
UserInteraction[User Interaction] --> StateMachine[State Machine]
StateMachine --> UpdateSelection[Selection Update]
UpdateSelection --> Renderer
-
Adaptive Detail Rendering:
- Small nodes are automatically combined to reduce rendering load
- Detail level adjusts based on zoom level and available space
-
Incremental Updates:
- Only changed parts of the visualization are redrawn
- The component implements diffing to identify changed nodes
-
Optimized Text Rendering:
- Text is only rendered when it will be visible and legible
- Labels are cached and reused when possible
-
Event Throttling and Debouncing:
- Intensive operations like resizing and scrolling are throttled
- Expensive recalculations are debounced during rapid interactions
- WebGL Acceleration: For extremely large datasets
- Worker-based Layout: Moving intensive calculations off the main thread
- Virtual Rendering: Only rendering visible portions of large treemaps
- Further Plugin Isolation: Reducing coupling between plugins
By implementing these architectural patterns, Squarified achieves both a maintainable codebase and high-performance visualizations for hierarchical data.