@graphty/layout
v1.2.9
Published
graph layout algorithms based on networkx
Maintainers
Readme
Layout
Layout is a TypeScript library for positioning nodes in graphs. It's a TypeScript port of the layout algorithms from the Python NetworkX library.
Features
The library offers various graph layout algorithms, including:
- Random Layout - Places nodes randomly in a unit square
- Circular Layout - Places nodes on a circle
- Shell Layout - Places nodes in concentric circles (shells)
- Spring Layout (Fruchterman-Reingold) - Force-directed layout with attractions and repulsions
- Spectral Layout - Uses eigenvectors of the graph's Laplacian matrix
- Spiral Layout - Places nodes along a spiral
- Bipartite Layout - Layout for bipartite graphs in two straight lines
- Multipartite Layout - Layout for multipartite graphs in levels
- BFS Layout - Layout based on breadth-first search algorithm
- Planar Layout - Planar layout without edge crossings
- Kamada-Kawai Layout - Layout based on path-length cost functions
- ForceAtlas2 Layout - Advanced force-directed algorithm
- ARF Layout - Layout with attractive and repulsive forces
Additionally, the library includes:
Graph Generators for creating common graph types:
- Complete Graph - All nodes connected to each other
- Cycle Graph - Nodes connected in a circular path
- Star Graph - Central hub connected to all other nodes
- Wheel Graph - Hub connected to nodes arranged in a rim cycle
- Grid Graph - 2D grid with nodes connected to neighbors
- Random Graph - Erdős–Rényi random graph model
- Bipartite Graph - Graph with two disjoint node sets
- Scale-Free Graph - Barabási–Albert preferential attachment model
Layout Helpers for intelligent graph analysis and layout optimization:
- groupNodes - Universal node grouping by degree, distance, k-core, or community
- detectBipartite - Automatic bipartite graph detection
- findBestRoot - Optimal root selection for tree layouts
- autoConfigureForce - Smart parameter configuration for force layouts
- layoutQuality - Layout quality measurement
- combineLayouts - Blend multiple layout algorithms
- interpolateLayouts - Smooth animation between layouts
Installation
npm install @graphty/layoutHow to Use
Import the library in your TypeScript/JavaScript project:
import {
// Layout algorithms
randomLayout,
circularLayout,
springLayout,
fruchtermanReingoldLayout,
spectralLayout,
spiralLayout,
bipartiteLayout,
multipartiteLayout,
bfsLayout,
planarLayout,
kamadaKawaiLayout,
forceatlas2Layout,
arfLayout,
rescaleLayout,
// Graph generators
completeGraph,
cycleGraph,
starGraph,
wheelGraph,
gridGraph,
randomGraph,
bipartiteGraph,
scaleFreeGraph,
// Layout helpers
groupNodes,
detectBipartite,
findBestRoot,
autoConfigureForce,
layoutQuality,
combineLayouts,
interpolateLayouts
} from '@graphty/layout'Quick Start
// Generate a graph
const graph = scaleFreeGraph(30, 2, 42)
// Auto-configure and layout
const config = autoConfigureForce(graph)
const positions = springLayout(graph, config.k, null, null, config.iterations)
// Or use specialized layouts
const bipartite = detectBipartite(graph)
if (bipartite) {
const positions = bipartiteLayout(graph, bipartite.setA)
}
// Or use shell layout with automatic grouping
const shells = groupNodes(graph, 'degree', 3)
const positions = shellLayout(graph, shells)Graph Structure
The module accepts graphs in two formats:
1. Graph Object with methods (preferred)
const graph = {
nodes: () => [0, 1, 2, 3],
edges: () => [
[0, 1],
[1, 2],
[2, 3],
[3, 0]
],
getEdgeData: (source, target, attr) => number // optional for edge weights
}2. Simple array of nodes
const nodes = [0, 1, 2, 3]Graph Generation
The library includes utilities to generate common graph types for testing and demonstration:
Complete Graph
Creates a complete graph with all possible edges between nodes.
const graph = completeGraph(5)
// Creates a graph with 5 nodes (0-4) and 10 edges (all pairs connected)Cycle Graph
Creates a cycle graph where nodes form a closed loop.
const graph = cycleGraph(6)
// Creates a graph with 6 nodes (0-5) connected in a cycle: 0-1-2-3-4-5-0Star Graph
Creates a star graph with one central hub connected to all other nodes.
const graph = starGraph(7)
// Creates a graph with 7 nodes where node 0 is connected to all others (1-6)Wheel Graph
Creates a wheel graph - a hub connected to all nodes of a rim cycle.
const graph = wheelGraph(6)
// Creates a graph with 6 nodes: hub (0) connected to rim cycle (1-2-3-4-5-1)Grid Graph
Creates a 2D grid graph with specified rows and columns.
const graph = gridGraph(3, 4)
// Creates a 3x4 grid with nodes named "row,col" (e.g., "0,0", "0,1", etc.)
// Nodes are connected to their horizontal and vertical neighborsRandom Graph
Creates a random graph with specified edge probability.
const graph = randomGraph(10, 0.3, 42)
// Creates a graph with 10 nodes (0-9)
// Each possible edge has 30% chance of existing
// Seed 42 ensures reproducible resultsBipartite Graph
Creates a bipartite graph with two sets of nodes.
const graph = bipartiteGraph(3, 4, 0.5, 123)
// Creates two sets: A0,A1,A2 and B0,B1,B2,B3
// Each edge between sets has 50% chance of existing
// Returns graph with additional setA and setB propertiesScale-Free Graph
Creates a scale-free graph using the Barabási-Albert preferential attachment model.
const graph = scaleFreeGraph(20, 2, 456)
// Creates a graph with 20 nodes
// Each new node connects to 2 existing nodes (preferential attachment)
// Results in a power-law degree distribution with some high-degree hubsUsing Generated Graphs with Layouts
All generated graphs work seamlessly with the layout algorithms:
// Generate a complete graph and apply circular layout
const graph = completeGraph(8)
const positions = circularLayout(graph)
// Generate a grid with auto-configured spring layout
const grid = gridGraph(5, 5)
const config = autoConfigureForce(grid)
const gridPositions = springLayout(
grid,
config.k,
null,
null,
config.iterations
)
// Generate a scale-free network with optimized ForceAtlas2
const network = scaleFreeGraph(50, 3, 42)
const networkConfig = autoConfigureForce(network)
const networkPositions = forceatlas2Layout(
network,
null,
networkConfig.iterations,
1.0,
networkConfig.scalingRatio,
networkConfig.gravity
)
// Use bipartite graph with automatic detection
const bipartite = bipartiteGraph(5, 7, 0.4, 123)
const bipartitePositions = bipartiteLayout(bipartite, bipartite.setA)Layout Helpers
The library includes helper functions to simplify working with complex layouts:
groupNodes() - Universal Node Grouping
Groups nodes for shell, multipartite, or custom layouts based on various metrics:
// Group by degree (connectivity) - great for shell layouts
const shells = groupNodes(graph, 'degree', 3)
const positions = shellLayout(graph, shells)
// Group by distance from root - perfect for hierarchical layouts
const layers = groupNodes(graph, 'bfs', 0, { root: 'A' })
const positions = multipartiteLayout(graph, layers)
// Group by k-core (dense subgraphs) - ideal for social networks
const cores = groupNodes(graph, 'k-core')
const positions = shellLayout(graph, cores)
// Group by community detection - useful for modular networks
const communities = groupNodes(graph, 'community', 5)detectBipartite() - Automatic Bipartite Detection
Automatically detects if a graph is bipartite and finds the two sets:
const result = detectBipartite(graph)
if (result) {
// Graph is bipartite! Use specialized layout
const positions = bipartiteLayout(graph, result.setA)
} else {
// Not bipartite, use general layout
const positions = springLayout(graph)
}findBestRoot() - Optimal Root Node Selection
Finds the best starting node for tree-like layouts (BFS, hierarchical):
const root = findBestRoot(graph)
const positions = bfsLayout(graph, root)autoConfigureForce() - Smart Force Layout Configuration
Automatically configures parameters based on graph properties:
const config = autoConfigureForce(graph)
// Use with Fruchterman-Reingold
const positions = springLayout(graph, config.k, null, null, config.iterations)
// Use with ForceAtlas2
const positions = forceatlas2Layout(
graph,
null,
config.iterations,
1.0,
config.scalingRatio,
config.gravity
)layoutQuality() - Layout Quality Metrics
Measure and compare layout quality:
const circular = circularLayout(graph)
const spring = springLayout(graph)
const metricsC = layoutQuality(graph, circular)
const metricsS = layoutQuality(graph, spring)
console.log('Circular layout - avg edge length:', metricsC.avgEdgeLength)
console.log('Spring layout - avg edge length:', metricsS.avgEdgeLength)
console.log('Spring layout - min node distance:', metricsS.minNodeDistance)combineLayouts() - Blend Multiple Layouts
Create hybrid layouts by combining different algorithms:
const circular = circularLayout(graph)
const spring = springLayout(graph)
// 30% circular structure, 70% force-directed
const hybrid = combineLayouts([circular, spring], [0.3, 0.7])interpolateLayouts() - Smooth Layout Transitions
Create animation frames between different layouts:
const startLayout = circularLayout(graph)
const endLayout = springLayout(graph)
// Generate 30 frames for smooth animation
const frames = interpolateLayouts(startLayout, endLayout, 30)
// Use frames[0] through frames[30] for animationHelper Usage Patterns
Smart Shell Layout
// Automatically choose best grouping method based on graph density
const n = graph.nodes().length
const m = graph.edges().length
const density = (2 * m) / (n * (n - 1))
const method = density < 0.1 ? 'bfs' : density > 0.5 ? 'k-core' : 'degree'
const shells = groupNodes(graph, method)
const positions = shellLayout(graph, shells)Adaptive Layout Selection
// Choose layout based on graph properties
let positions
if (detectBipartite(graph)) {
const { setA } = detectBipartite(graph)
positions = bipartiteLayout(graph, setA)
} else if (graph.nodes().length > 100) {
// Large graph - use fast layout
positions = circularLayout(graph)
} else {
// Default to auto-configured force layout
const config = autoConfigureForce(graph)
positions = springLayout(graph, config.k, null, null, config.iterations)
}Progressive Layout Refinement
// Start with fast layout, progressively refine
const initial = circularLayout(graph)
const refined = springLayout(graph, null, initial, null, 50)
const final = kamadaKawaiLayout(graph, null, refined)Layout Helper Quick Reference
| Helper Function | Purpose | Best Use Case |
| -------------------------------- | ------------------------------- | ------------------------------------- |
| groupNodes(graph, 'degree') | Group by connectivity | Shell layouts for scale-free networks |
| groupNodes(graph, 'bfs') | Group by distance from root | Hierarchical/tree layouts |
| groupNodes(graph, 'k-core') | Group by subgraph density | Social network analysis |
| groupNodes(graph, 'community') | Group by detected communities | Modular network visualization |
| detectBipartite(graph) | Check if graph is bipartite | Matching problems, assignments |
| findBestRoot(graph) | Find optimal tree root | BFS layout, hierarchical layout |
| autoConfigureForce(graph) | Auto-configure force parameters | Any force-directed layout |
| layoutQuality(graph, pos) | Measure layout quality | Comparing different layouts |
| combineLayouts([...], [...]) | Blend multiple layouts | Custom hybrid visualizations |
| interpolateLayouts(from, to) | Create animation frames | Interactive transitions |
Usage Examples
Circular Layout
// Use our graph generator instead of manual construction
const graph = cycleGraph(8)
const positions = circularLayout(graph)
// Nodes arranged in a perfect circleSpring Layout (Fruchterman-Reingold)
// Generate a grid and apply force-directed layout with auto-configured parameters
const graph = gridGraph(5, 5)
const config = autoConfigureForce(graph)
const positions = springLayout(
graph,
config.k, // optimal distance
null, // initial positions
null, // fixed nodes
config.iterations // iterations
)
// Or use fruchtermanReingoldLayout (same function)
const positions2 = fruchtermanReingoldLayout(graph, config.k)Bipartite graph layout
// Generate a bipartite graph and detect sets automatically
const graph = bipartiteGraph(4, 6, 0.5, 42)
// Option 1: Use the built-in sets
const positions = bipartiteLayout(graph, graph.setA, 'vertical')
// Option 2: Auto-detect bipartite structure
const detected = detectBipartite(graph)
if (detected) {
const positions2 = bipartiteLayout(graph, detected.setA, 'horizontal')
}3D Support
All layout algorithms support 3D positioning by setting the dim parameter to 3:
// Any layout algorithm in 3D
const positions3D = springLayout(graph, null, null, null, 50, 1, [0, 0, 0], 3)
// Returns: { node1: [x, y, z], node2: [x, y, z], ... }
// ForceAtlas2 in 3D
const fa3D = forceatlas2Layout(graph, null, 100, 1, 2, 1, false, false, null, null, null, false, false, 42, 3)
// Circular layout in 3D (creates a sphere)
const circular3D = circularLayout(graph, 1, [0, 0, 0], 3)When using 3D layouts, positions will have three coordinates [x, y, z] instead of two.
Common Parameters
Most layout functions share these parameters:
- scale (number): Scale factor for positions (default: 1)
- center (number[]): Center coordinates around which to center the layout (default: [0, 0] for 2D, [0, 0, 0] for 3D)
- dim (number): Layout dimension - 2D or 3D (default: 2)
- seed (number): Seed for random generation (for reproducible layouts)
TypeScript Types
type Node = string | number
type Edge = [Node, Node]
type PositionMap = Record<Node, number[]>
interface Graph {
nodes?: () => Node[]
edges?: () => Edge[]
getEdgeData?: (source: Node, target: Node, attr: string) => any
}Utilities
Layout Rescaling
import { rescaleLayout } from './layout.js'
// Rescale existing positions
const scaledPositions = rescaleLayout(positions, 2.0, [10, 10])Available Algorithms
Force-Directed Layouts
springLayout()/fruchtermanReingoldLayout()- Classic force-directed algorithmforceatlas2Layout()- Advanced algorithm with many configuration optionsarfLayout()- Attractive and repulsive forceskamadaKawaiLayout()- Based on shortest-path distances
Geometric Layouts
randomLayout()- Random placementcircularLayout()- Circular arrangementshellLayout()- Concentric circlesspiralLayout()- Spiral arrangement
Specialized Layouts
spectralLayout()- Based on eigenvectors of the Laplacian matrixbipartiteLayout()- For bipartite graphsmultipartiteLayout()- For multi-level graphsbfsLayout()- Based on breadth-first searchplanarLayout()- For planar graphs without crossings
Utilities
rescaleLayout()- Rescale and recenter positionsrescaleLayoutDict()- Rescale a dictionary of positions
Detailed Examples for each Algorithm
Random Layout
// Generate any graph and apply random layout
const graph = completeGraph(10)
const positions = randomLayout(graph, [0, 0], 2, 42)
// Nodes randomly placed in unit square with seed 42Circular Layout
// Perfect for cyclic or complete graphs
const graph = cycleGraph(12)
const positions = circularLayout(graph)
// 12 nodes evenly spaced on a circleShell Layout
// Use automatic node grouping for shell layout
const graph = scaleFreeGraph(30, 2, 42)
// Group nodes by degree (hubs in center)
const shells = groupNodes(graph, 'degree', 3)
const positions = shellLayout(graph, shells)
// Or group by k-core for social networks
const kCoreShells = groupNodes(graph, 'k-core')
const positions2 = shellLayout(graph, kCoreShells)Spring Layout (Fruchterman-Reingold)
// Auto-configure parameters based on graph size
const graph = randomGraph(20, 0.2, 42)
const config = autoConfigureForce(graph)
const positions = springLayout(
graph,
config.k, // optimal distance
null, // initial positions
null, // fixed nodes
config.iterations // iterations
)Spectral Layout
// Great for revealing graph structure
const graph = gridGraph(6, 6)
const positions = spectralLayout(graph)
// Grid structure preserved in spectral embeddingSpiral Layout
// Perfect for sequential or time-based data
const graph = cycleGraph(50)
const positions = spiralLayout(
graph,
1, // scale
[0, 0], // center
2, // dim
0.35, // resolution
true // equidistant points
)Bipartite Layout
// Generate bipartite graph and layout automatically
const graph = bipartiteGraph(5, 7, 0.4, 42)
const positions = bipartiteLayout(
graph,
graph.setA, // first group nodes (auto-generated)
'vertical', // align: 'vertical' or 'horizontal'
1, // scale
[0, 0], // center
4 / 3 // aspectRatio
)Multipartite Layout
// Use automatic layer detection with groupNodes
const graph = scaleFreeGraph(20, 2, 42)
const layers = groupNodes(graph, 'bfs', 0, { root: findBestRoot(graph) })
// Convert to multipartite format
const layerMap = {}
layers.forEach((nodes, i) => {
layerMap[i] = nodes
})
const positions = multipartiteLayout(
graph,
layerMap, // subsetKey: layer mapping
'vertical', // align
1, // scale
[0, 0] // center
)BFS Layout
// Use automatic root detection for tree-like graphs
const graph = starGraph(10)
const root = findBestRoot(graph) // Automatically finds node 0 (hub)
const positions = bfsLayout(
graph,
root, // start: best root node
'vertical', // align
1, // scale
[0, 0] // center
)Planar Layout
// Create a planar graph (grid is always planar)
const graph = gridGraph(4, 4)
const positions = planarLayout(graph, 1, [0, 0], 2)
// Note: throws error if graph is not planar
// For unknown graphs, check planarity first
if (isPlanar(graph)) {
const positions = planarLayout(graph)
} else {
// Fall back to non-planar layout
const positions = springLayout(graph)
}Kamada-Kawai Layout
// Great for small to medium graphs
const graph = wheelGraph(8)
const positions = kamadaKawaiLayout(
graph,
null, // dist: distance matrix (auto)
null, // pos: initial positions (auto)
'weight', // weight: edge weight attribute
1, // scale
[0, 0], // center
2 // dim
)ForceAtlas2 Layout
// Auto-configure for your graph type
const graph = scaleFreeGraph(50, 3, 42)
const config = autoConfigureForce(graph)
const positions = forceatlas2Layout(
graph,
null, // pos: initial positions
config.iterations, // maxIter: auto-configured
1.0, // jitterTolerance
config.scalingRatio, // scalingRatio: auto-configured
config.gravity, // gravity: auto-configured
false, // distributedAction
false, // strongGravity
null, // nodeMass: node masses
null, // nodeSize: node sizes
null, // weight: weight attribute
true, // dissuadeHubs: good for scale-free
false, // linlog: logarithmic attraction
42, // seed
2 // dim: 2 for 2D, 3 for 3D
)
// 3D layout example
const positions3D = forceatlas2Layout(
graph,
null,
100,
1.0,
2.0,
1.0,
false,
false,
null,
null,
null,
false,
false,
42,
3 // 3D mode - returns [x, y, z] coordinates
)ARF Layout
// Layout with attractive and repulsive forces
const graph = completeGraph(10)
const positions = arfLayout(
graph,
null, // pos: initial positions
1, // scaling
1.1, // a: spring force (must be > 1)
1000, // maxIter
42 // seed
)Advanced Examples: Combining Generators and Helpers
Example 1: Community-Based Visualization
// Generate a scale-free network (hubs and communities)
const graph = scaleFreeGraph(100, 3, 42)
// Detect communities and use them for shell layout
const communities = groupNodes(graph, 'community', 4)
const positions = shellLayout(graph, communities)
// Or use communities for coloring in your visualization
const communityMap = new Map()
communities.forEach((nodes, idx) => {
nodes.forEach(node => communityMap.set(node, idx))
})Example 2: Adaptive Layout Selection
function chooseOptimalLayout(graph) {
const n = graph.nodes().length
const m = graph.edges().length
const density = (2 * m) / (n * (n - 1))
// Check for special graph types
const bipartite = detectBipartite(graph)
if (bipartite) {
return bipartiteLayout(graph, bipartite.setA)
}
// Choose based on graph properties
if (n > 100) {
// Large graph - use fast circular layout
return circularLayout(graph)
} else if (density < 0.1) {
// Sparse graph - use spring layout
const config = autoConfigureForce(graph)
return springLayout(graph, config.k, null, null, config.iterations)
} else {
// Dense graph - use spectral or kamada-kawai
return n < 50 ? kamadaKawaiLayout(graph) : spectralLayout(graph)
}
}
// Usage
const graph = randomGraph(30, 0.3, 42)
const positions = chooseOptimalLayout(graph)Example 3: Animated Layout Transitions
// Start with circular layout
const graph = completeGraph(15)
const startLayout = circularLayout(graph)
// Optimize with force-directed
const config = autoConfigureForce(graph)
const endLayout = springLayout(graph, config.k, null, null, config.iterations)
// Create smooth animation frames
const frames = interpolateLayouts(startLayout, endLayout, 60)
// Use frames[0] through frames[60] for animation
function animate(frameIndex) {
const positions = frames[frameIndex]
// Update your visualization with these positions
}Example 4: Hierarchical Network Analysis
// Generate a preferential attachment network
const graph = scaleFreeGraph(50, 2, 42)
// Find natural hierarchy using k-core decomposition
const kCores = groupNodes(graph, 'k-core')
// Layout with most connected nodes in center
const positions = shellLayout(graph, kCores)
// Or create a tree-like view
const root = findBestRoot(graph)
const bfsPositions = bfsLayout(graph, root)Example 5: Quality-Driven Layout
// Try multiple layouts and pick the best
function findBestLayout(graph) {
const candidates = [
{ name: 'circular', positions: circularLayout(graph) },
{ name: 'spectral', positions: spectralLayout(graph) },
{ name: 'spring', positions: springLayout(graph) }
]
let best = candidates[0]
let bestScore = Infinity
candidates.forEach(candidate => {
const quality = layoutQuality(graph, candidate.positions)
const score = quality.edgeLengthStdDev / quality.avgEdgeLength
if (score < bestScore) {
bestScore = score
best = candidate
}
})
console.log(`Best layout: ${best.name} (score: ${bestScore.toFixed(3)})`)
return best.positions
}Example 6: Hybrid Layouts
// Create a graph with clear structure
const graph = gridGraph(6, 6)
// Get geometric and force-based layouts
const grid = circularLayout(graph)
const force = springLayout(graph)
// Blend them: 40% geometric structure, 60% force optimization
const hybrid = combineLayouts([grid, force], [0.4, 0.6])
// The result preserves some grid structure while optimizing edge lengthsError Handling
try {
const positions = planarLayout(nonPlanarGraph)
} catch (error) {
console.error('Graph is not planar:', error.message)
}
try {
const positions = arfLayout(graph, null, 1, 0.5) // a <= 1
} catch (error) {
console.error('Invalid parameter a:', error.message)
}Installation
npm install @graphty/layoutBuilding from Source
If you want to build the TypeScript module from source:
Clone the repository:
git clone https://github.com/graphty-org/layout.git cd layoutInstall dependencies:
npm installCompile TypeScript to JavaScript:
npm run buildThis will compile the
layout.tsfile to JavaScript and generate type declarations in thedist/directory.For development with automatic compilation:
npm run devThis will watch for changes and automatically recompile the TypeScript files.
Development Server
The project includes a Vite development server for testing and viewing examples.
Starting the Development Server
npm run serveThis will:
- Start a development server on port 3000
- Automatically open your browser to
/examples/ - Provide hot module reloading for development
Configuring the Development Server
You can customize the server configuration using environment variables:
Create a
.envfile (copy from.env.example):cp .env.example .envConfigure server options in
.env:# Server host (defaults to true for network exposure) HOST=localhost # For local-only access HOST=0.0.0.0 # For network access HOST=my.server.com # For custom domain # Server port (defaults to 3000) PORT=3000 PORT=8080 # Custom portStart the server with your configuration:
npm run serve
Alternative: Build and Serve
To build the project and then serve the examples:
npm run examplesThis command:
- Builds the TypeScript files to JavaScript
- Starts the Vite development server
Note: The compiled JavaScript files will be available in the dist/ directory. You can import from the compiled JavaScript files or directly use the TypeScript source files in a TypeScript project.
Implementation
The module includes complete implementations of:
- Random Number Generator with seed support for reproducible results
- Mathematical utilities similar to NumPy for multidimensional array operations
- Force-directed algorithms with L-BFGS optimization for Kamada-Kawai
- Planarity algorithms including Left-Right test for planar graphs
- Auto-scaling system to automatically normalize positions
Performance Tips
Large Graphs (>1000 nodes):
// Use fast layouts first const initial = circularLayout(graph) // Then refine with limited iterations const refined = springLayout(graph, null, initial, null, 50)Dense Graphs:
// Use spectral layout for dense graphs const density = (2 * m) / (n * (n - 1)) if (density > 0.5) { const positions = spectralLayout(graph) }Real-time Updates:
// Pre-calculate layout quality const quality = layoutQuality(graph, positions) // Use interpolation for smooth updates const frames = interpolateLayouts(oldPositions, newPositions, 30)Memory Optimization:
// For very large graphs, use generators function* layoutInChunks(graph, chunkSize = 100) { const nodes = graph.nodes() for (let i = 0; i < nodes.length; i += chunkSize) { const chunk = nodes.slice(i, i + chunkSize) // Process chunk... yield chunk } }
Development
Building the Project
The project uses a unified build system:
# Build TypeScript to JavaScript
npm run build
# Build the bundled ES module (dist/layout.js)
npm run build:bundle
# Build everything
npm run build:allRunning Examples Locally
# Start development server with examples
npm run examples
# or
npm run serveThis will:
- Build the bundled
dist/layout.js - Start a Vite dev server at http://localhost:3000
- Automatically redirect imports to use the bundled version
Building for GitHub Pages
To build a static site for GitHub Pages deployment:
# Build static site in gh-pages/ directory
npm run build:gh-pagesThis creates a gh-pages/ directory with:
- All example HTML files
- The bundled
layout.js - All necessary assets
To deploy to GitHub Pages, see gh-pages/DEPLOY.md after building.
Development Workflow
- Make changes to TypeScript source files in
src/ - Test locally with
npm run examples - Run tests with
npm test - Build for production with
npm run build:all - Deploy examples with
npm run build:gh-pages
Recent Updates
Version 1.2.7 (Latest)
- Fixed: ForceAtlas2 now correctly returns numeric Z coordinates in 3D mode (previously returned NaN)
- Fixed: NPM package now uses the correct bundled entry point (
dist/layout.js) - Added: Full 3D support documentation and examples
Contributing
This project is a TypeScript port of the NetworkX Python library. For contributions and issues, visit the GitHub repository.
License
MIT License - see LICENSE file for details.
