Force Smith
A Rust toolkit for implementing and developing force-directed layout algorithms with built-in debugging and visualization capabilities.
Features
- 🔧 Builder API: Ergonomic API for composing force-directed layout algorithms from modular components
- 🎨 Interactive Visualizer: Real-time visualization with debug mode for step-by-step algorithm execution
- ⚡ High Performance: Built on Bevy ECS for efficient graph processing
- 🔍 Debug Tools: Inspect forces, visualize node movements, and tune parameters in real-time
- 📦 Modular Design: Mix and match force applicators, constraints, and layout strategies
Quick Start
Installation
Add Force Smith to your Cargo.toml:
[]
= "1.0.1"
# For full features
= { = "1.0.1", = ["full"] }
Basic Usage
use *;
// Build a layout algorithm
let layout = new
.add_force
.add_force
.with_parameter
.with_parameter
.with_parameter
.build;
// Run with visualization
visualize_dbg;
Features
Core Features (always enabled)
builder: Layout builder API for composing algorithmsutils: Utilities including force applicators and helpers
Optional Features
visualizer: Interactive visualization and debugging tools (requires Bevy)
Examples
Force Smith includes three comprehensive examples demonstrating different use cases:
1. Fruchterman-Reingold Algorithm
A complete implementation of the classic Fruchterman-Reingold force-directed layout algorithm.
Features:
- Attractive forces: f_a(d) = d² / k
- Repulsive forces: f_r(d) = -k² / d
- Temperature-based annealing
- Prints iteration progress and final positions
2. Fruchterman-Reingold with Visualizer
Interactive version with real-time parameter tuning.
Features:
- Adjust spring constant (k), temperature, and cooling rate in real-time
- Switch between Normal and Debug modes
- Visualize force vectors in debug mode
- Load custom graphs or generate random ones
3. Parameterized Derive Macro
Comprehensive demonstration of the #[derive(Parameterized)] macro.
Features:
- Shows all supported parameter types:
f32,i32,bool - Demonstrates real-time parameter tuning in the UI
- Includes gravity toggle and iteration control
- 8 different tunable parameters
- Custom spring-based physics model
What you'll learn:
- How to use the Parameterized derive macro
- How to name parameters for the UI
- How different parameter types appear in the UI (sliders vs checkboxes)
- How to handle boolean flags that affect behavior
Example Graphs
The graphs/ directory contains sample graph files:
simple_graph.json- 5-node pentagon (good starter)line_graph.json- 10-node chaintree_graph.json- 7-node hierarchical structurestar_graph.json- 8-node hub structuredense_graph.json- 6-node dense graph
You can load these in the visualizer examples through the Graph Configuration UI.
Visualizer Usage
Loading a Graph
From File:
- Select "From File" in the Graph Configuration window
- Choose a graph from the
graphs/directory - Click "Apply"
Generate Random:
- Select "From Config" in the Graph Configuration window
- Set number of vertices and edges
- Check "Connected" to ensure graph connectivity
- Click "Apply"
Layout Controls
Normal Mode (Continuous):
- Click "▶ Run" to start continuous layout computation
- Click "⏹ Stop" to halt execution
- Nodes move smoothly toward their destination positions
Debug Mode (Step-by-Step):
- Switch to "Debug Mode"
- Click "⏭ Step" next to "Compute Forces"
- Red arrows show individual force vectors
- Green arrow shows the destination position
- Click "⏭ Step" next to "Update Positions" to apply forces
- Repeat to step through algorithm iterations
Parameter Configuration
Adjust these parameters in real-time while the layout runs:
- Temperature: Controls node movement speed (higher = faster)
- Ideal Edge Length: Target distance between connected nodes
- Cooling Rate: How fast the system stabilizes (higher = faster convergence)
UI Controls
- Smooth Movement: Toggle smooth interpolation vs instant node positioning
- Hover over UI elements for tooltips with detailed information
Camera Controls
Keyboard (Vim-style):
h- Move leftj- Move downk- Move upl- Move right- Arrow keys also work as an alternative
+or=- Zoom in-- Zoom out
Mouse/Touchpad:
- Scroll wheel - Zoom in/out
- Left-click + drag - Pan around the scene
The camera movement speed scales with the current zoom level for smooth navigation at any scale.
Graph File Format
Create custom graphs as JSON files:
vertices: Number of nodes in the graphedges: Array of connections (indices are 0-based)
Architecture
Force Smith is organized into several modules:
builder: Fluent API for constructing layout algorithmsengine: Core layout engine and iteration logicgraph: Graph data structures and utilitiesutils: Utility functions and helpersforces: Force applicators (attraction, repulsion, etc.)
visualizer: Bevy-based interactive visualization (optional)camera: Camera controls and scene setupinterface: UI panels and user controlsrendering: Visual representation of graphssimulation: Layout computation and execution modes
API Overview
Implementing Custom Algorithms
The builder pattern makes it easy to create custom force-directed algorithms:
use *;
// Define your algorithm's configuration
// Build your layout algorithm
let mut layout = build
.
.with_graph_loading_fn
.with_position_update_fn
// Add attractive forces between connected nodes
.with_force
// Add repulsive forces between all nodes
.with_force
.to_layout;
// Use your algorithm
let graph = Graph ;
layout.load_graph;
for _ in 0..100
let positions = layout.get_positions;
Force Applicators
Force applicators determine how forces are applied to nodes:
linear_attraction_applicator: Apply forces only between connected nodes (edges)linear_repulsion_applicator: Apply forces between all pairs of nodes
You can also create custom applicators for specific layouts (e.g., hierarchical, radial).
LayoutBuilder API
build
. // Set configuration type
.with_graph_loading_fn // Load and initialize graph
.with_position_update_fn // Update positions
.with_force // Add force calculations
.to_layout // Build the algorithm
Visualizer Configuration
Customize the visualizer appearance and behavior:
use *;
let config = VisualizerConfiguration ;
visualize_dbg;
Available colors:
- Primary:
Red,Green,Blue(soft, muted tones) - Accents:
Orange,Purple,Cyan,Pink,Yellow - Neutrals:
White,LightGrey,Grey,DarkGrey,Black
All colors are carefully tuned for good contrast on dark/grey backgrounds.
Using the Parameterized Derive Macro
For visualizer integration, use the #[derive(Parameterized)] macro to automatically expose your configuration parameters in the UI:
use *;
What the macro does:
- Automatically implements the
Parameterizedtrait - Generates
get_parameters()method to export current values to the UI - Generates
update_parameters()method to apply UI changes back to your config - No manual serialization/deserialization needed!
UI Representation:
f32→ Slider with decimal precisioni32→ Slider with integer stepsbool→ Checkbox
Supported types: f32, i32, bool
The parameters will appear in the "Config" panel of the visualizer, allowing you to tune your algorithm interactively without recompiling.
See it in action:
This example shows 8 different parameters (f32, i32, and bool) and how they appear in the UI.
Contributing
Contributions are welcome! Please feel free to submit issues or pull requests.
License
MIT License - see LICENSE file for details.