How DrawDB Automatically Arranges Tables in Database Diagrams
DrawDB uses the Dagre graph layout engine via the autoArrange utility in src/utils/autoArrange.js to compute optimal positions for tables based on their relational topology, locked status, and dynamic visual dimensions.
When working with complex database schemas in DrawDB, manually positioning numerous tables becomes impractical. The open-source diagramming tool solves this through a sophisticated automatic layout system that analyzes entity relationships and visual constraints to produce clean, readable diagrams without manual intervention.
The Auto-Arrange Algorithm Architecture
The core implementation resides in src/utils/autoArrange.js, which exports a function that orchestrates a multi-stage layout pipeline. This utility receives three critical inputs: an array of table objects representing diagram entities, an array of relationship objects defining connections, and a settings object containing visual parameters like tableWidth, showComments, and separation distances.
Preserving User Intent with Locked Tables
Before calculating positions, the algorithm respects manual adjustments by filtering out locked tables. Only entities with !table.locked are considered for repositioning, ensuring that deliberately placed tables remain stationary while the algorithm reorganizes surrounding elements.
const movable = tables.filter((table) => !table.locked);
Step-by-Step Layout Execution in src/utils/autoArrange.js
1. Connected Component Detection
The system first distinguishes between tables participating in relationships and isolated entities. It constructs a connectedIds set by iterating through the relationships array, identifying tables where both endpoints are movable. This separation allows the algorithm to apply graph-based layout to connected schemas while handling standalone tables separately.
2. Dynamic Size Calculation
Visual dimensions are computed using the sizeOf helper function (lines 24-32), which invokes getTableHeight from src/utils/utils.js. This calculation accounts for:
- Field count and heights defined in
src/data/constants.js - Comment visibility states (
showComments) - Header dimensions (
tableHeaderHeight) - Relationship indicator spacing
3. Graph Construction with Dagre
For connected tables, the algorithm builds a directed graph using the @dagrejs/dagre library. Each table becomes a node with pre-calculated dimensions, while relationships become directed edges. The graph configuration specifies left-to-right orientation:
const graph = new dagre.graphlib.Graph();
graph.setGraph({ rankdir: "LR", ranksep: rankSep, nodesep: nodeSep });
4. Layout Computation
Dagre's layout(graph) method executes the actual positioning algorithm (line 54), computing optimal x and y coordinates that minimize edge crossings and respect the specified separation constraints. The algorithm then centers these coordinates on each node's top-left corner for proper alignment with DrawDB's coordinate system.
5. Isolated Table Grid Placement
Tables without relationships are arranged in a grid pattern beneath the connected component. The layout algorithm calculates row positions using the isolatedGap parameter and the overall width of the connected graph, preventing visual overlap while maintaining uniform spacing.
// Simplified logic for isolated table placement
const positions = [];
let currentX = 0;
let currentY = connectedHeight + isolatedGap;
Fallback Manual Layout
When users prefer simpler distributions or when the auto-arrange feature is bypassed, DrawDB falls back to src/utils/arrangeTables.js. This deterministic algorithm divides tables into two horizontal rows with fixed gaps, providing a quick organizational option that lacks the relationship awareness of the Dagre-based approach but executes faster for simple schemas.
Practical Implementation Example
To programmatically trigger layout calculation within a component or custom script:
import { autoArrange } from '@/utils/autoArrange';
// Assuming current diagram state
const { tables, relationships, settings } = diagram;
// Calculate new positions
const newPositions = autoArrange(tables, relationships, settings);
// Apply coordinates to state
newPositions.forEach(({ id, x, y }) => {
const table = tables.find(t => t.id === id);
if (table) {
table.x = x;
table.y = y;
}
});
The function returns an array of { id, x, y } objects that integrate directly with the diagram's state management, triggering a re-render with optimized positions.
Summary
- DrawDB automatically arrange tables using the Dagre directed graph library implemented in
src/utils/autoArrange.js - The algorithm filters locked tables, calculates dynamic heights using constants from
src/data/constants.js, and builds a relational graph - Connected components use hierarchical left-to-right layout while isolated tables utilize a grid placement algorithm
- Visual spacing is controlled via
nodeSep,rankSep, andisolatedGapconfiguration parameters - A deterministic fallback exists in
src/utils/arrangeTables.jsfor simple row-based arrangements
Frequently Asked Questions
What graph layout library does DrawDB use for auto-arranging tables?
DrawDB uses Dagre (@dagrejs/dagre), a directed graph layout engine specifically designed for DAGs (Directed Acyclic Graphs). Dagre computes optimal node positions while minimizing edge crossings and respecting user-defined separation constraints between ranks and nodes.
How does DrawDB prevent manually positioned tables from moving during auto-arrangment?
The autoArrange function checks the locked boolean property on each table object at lines 12-22. Tables with locked: true are excluded from the movable array, ensuring they remain at their current coordinates while the algorithm repositions only unlocked tables around them.
Can I customize the spacing between auto-arranged tables?
Yes, the algorithm respects three key spacing parameters passed through the settings object: nodeSep controls horizontal gaps between adjacent nodes, rankSep determines vertical distance between hierarchical levels, and isolatedGap sets the spacing for the grid layout applied to unconnected tables.
What happens to tables that have no relationships to other entities?
Isolated tables are detected during the connected component analysis and routed to a separate layout path (lines 68-84). These tables are arranged in a simple grid pattern beneath the connected component, calculated using the isolatedGap parameter and the bounding width of the main graph to ensure visual balance.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →