A Rust port of libavoid - Fast, object-avoiding connector routing for interactive diagram editors. Includes WebAssembly bindings for JavaScript/TypeScript.
libavoid is a cross-platform library providing fast, object-avoiding connector routing for use in interactive diagram editors. This is a Rust implementation of the original C++ library by Michael Wybrow from Monash University.
- Fast incremental routing - Efficient updates when shapes move
- Multiple routing modes:
- Polyline routing (direct diagonal paths)
- Orthogonal routing (rectilinear/Manhattan routing)
- Object avoidance - Automatically routes around obstacles
- Transaction support - Batch multiple operations for performance
- Connection pins - Attach connectors to specific points on shapes
- Configurable parameters - Shape buffer distance, nudging, penalties
- WebAssembly support - Use in browsers and Node.js
Add to your Cargo.toml:
[dependencies]
libavoid = "0.1.0"Build the WASM module:
# Install wasm-pack if you haven't already
cargo install wasm-pack
# Build for web
wasm-pack build --target web --features wasm
# Or build for Node.js
wasm-pack build --target nodejs --features wasmuse libavoid::{Router, Point, Rectangle, ConnRef, ConnEnd, ConnType, PolygonInterface};
fn main() {
// Create a router with orthogonal routing
let mut router = Router::new(ConnType::Orthogonal as u32);
router.set_transaction_use(true);
// Add an obstacle
let obstacle = Rectangle::new(Point::new(100.0, 100.0), 80.0, 60.0);
router.add_shape(obstacle.into(), 1);
// Create a connector
let src = ConnEnd::new(Point::new(50.0, 100.0));
let dst = ConnEnd::new(Point::new(200.0, 100.0));
let mut conn = ConnRef::with_endpoints(1, src, dst);
conn.set_routing_type(ConnType::Orthogonal);
router.add_connector(conn);
// Process routing
router.process_transaction();
// Get the route
if let Some(conn) = router.get_connector(1) {
if let Some(route) = conn.display_route() {
for i in 0..route.size() {
let pt = route.at(i);
println!("Point: ({}, {})", pt.x, pt.y);
}
}
}
}import init, { Router, Point, Rectangle, ConnRef, ConnEnd, ShapeRef } from './pkg/libavoid.js';
const ORTHOGONAL_ROUTING = 2;
async function main() {
await init();
// Create router
const router = new Router(ORTHOGONAL_ROUTING);
// Add obstacle
const rect = new Rectangle(new Point(100, 100), 80, 60);
const shape = new ShapeRef(router, rect.toPolygon());
router.addShape(shape);
// Create connector
const srcEnd = new ConnEnd(new Point(50, 100));
const dstEnd = new ConnEnd(new Point(200, 100));
const conn = ConnRef.createWithEndpoints(router, srcEnd, dstEnd);
conn.setRoutingType(ORTHOGONAL_ROUTING);
router.addConnector(conn);
// Process and get route
router.processTransaction();
const route = router.getConnectorRoute(conn.id());
for (let i = 0; i < route.size(); i++) {
const pt = route.at(i);
console.log(`Point: (${pt.x}, ${pt.y})`);
}
}# Rust example
cargo run --example simple_routing
# Web examples (start server from examples directory)
cd examples
python3 -m http.server 8080
# Then open http://localhost:8080/web/gallery.html| Example | Description | Location |
|---|---|---|
| Simple Routing | Basic Rust connector routing | examples/simple_routing.rs |
| Web Gallery | Interactive demo of all routing features | examples/web/gallery.html |
| Zombie Chase | Game demo using pathfinding | examples/zombies/index.html |
| Node.js | Server-side routing example | examples/node/main.mjs |
The web gallery (examples/web/gallery.html) demonstrates:
- Basic Polyline Routing - Direct paths around obstacles
- Orthogonal Routing - Manhattan-style H/V only paths
- Multiple Obstacles - Routing through complex layouts
- Shape Operations - Add, move, delete shapes dynamically
- Transaction Batching - Performance comparison
- Multiple Connectors - Many simultaneous routes
- Crossing Detection - Visualize route intersections
- Interactive Demo - Click to add shapes and connectors
- Route Nudging - Automatic separation of overlapping routes
- Options Comparison - Side-by-side parameter effects
A playable game demonstrating real-time pathfinding:
- Zombies chase the player using libavoid routing
- Multiple rounds with increasing difficulty
- Shows polyline routing for direct pursuit paths
- Toggle debug view to see connector paths
Configure routing behavior:
use libavoid::RoutingParameter;
// Distance to keep between routes and obstacles
router.set_routing_parameter(RoutingParameter::ShapeBufferDistance, 10.0);
// Distance between parallel route segments (nudging)
router.set_routing_parameter(RoutingParameter::IdealNudgingDistance, 10.0);
// Penalty for route segments (affects path complexity)
router.set_routing_parameter(RoutingParameter::SegmentPenalty, 10.0);
// Penalty for route crossings
router.set_routing_parameter(RoutingParameter::CrossingPenalty, 200.0);Enable/disable routing features:
use libavoid::RoutingOption;
// Enable nudging (separates overlapping orthogonal routes)
router.set_routing_option(RoutingOption::NudgeOrthogonalRoutes, true);
// Penalize shared path segments
router.set_routing_option(RoutingOption::PenaliseOrthogonalSharedPaths, true);# Run Rust tests
cargo test
# Run JavaScript tests
cd js-tests
npm install
npm run build:wasm # Build WASM for tests
npm testThe JS test suite includes:
- Unit tests for individual classes
- API compatibility tests with libavoid-js
- Behavioral parity tests
- Integration tests
cargo build --release# For web (ES modules)
wasm-pack build --target web --features wasm
# For Node.js
wasm-pack build --target nodejs --features wasm
# For bundlers (webpack, etc.)
wasm-pack build --target bundler --features wasmcargo benchThe library is organized into modules:
| Module | Description |
|---|---|
geometry |
Core types: Point, Polygon, Rectangle, Box |
router |
Main routing engine and API |
connector |
Connector definitions and routing |
shape |
Shape/obstacle representation |
visibility |
Visibility graph computation |
orthogonal |
Orthogonal routing with visibility graphs |
vpsc |
Variable Placement with Separation Constraints (nudging) |
wasm |
WebAssembly bindings |
Uses visibility graphs to find shortest paths that avoid obstacles. Paths can have any angle.
Generates rectilinear (horizontal/vertical only) paths using:
- Orthogonal visibility graph generation
- A* pathfinding on the visibility graph
- VPSC-based nudging to separate overlapping segments
This is a port of the original libavoid C++ library:
- Author: Michael Wybrow, Monash University
- Repository: https://github.com/mjwybrow/adaptagrams
- License: LGPL 2.1
- Research: Based on peer-reviewed graph drawing research
LGPL-2.1 (same as the original library)
Core functionality implemented:
- Basic geometry types
- Router core
- Polyline routing
- Orthogonal routing
- Visibility graph
- A* pathfinding
- Shape management
- Connector management
- Transaction support
- Connection pins (basic)
- Route nudging (VPSC)
- WebAssembly bindings
- Hyperedge routing
- Junction support
- Cluster support
Contributions welcome! Areas of interest:
- Performance optimizations
- Missing features from C++ version
- Additional examples and documentation
- Test coverage improvements
- Michael Wybrow for the original C++ libavoid library
- Monash University's Adaptive Diagrams and Documents lab