Expand description
§linkage-blaze
3D turtle graphics for animated jointed mechanisms and figures. Describe a figure with moves,
turns, branches, links, joints, disks, and spheres, then animate parameters to bring it to life. Runs both no_std and std.
§Project Links
- Live demo gallery - Preview and run the interactive browser demos.
- Crate on crates.io - Releases, installation information, and package metadata.
- Documentation on docs.rs
- GitHub repository - Source, issues, and development history.
- Release checklist - Crate, demo-version, GitHub Pages, and publication procedure.
§What is Linkage Blaze?
Linkage Blaze is a Rust-based domain-specific language (DSL) for making animated jointed drawings. It works like 3D turtle graphics: move forward, turn, branch, draw links, place joints, and add simple shapes such as disks and spheres. Animate a few parameters, and the drawing moves.
The demos include robot arms, clocks, and motion-controlled skeletons. The workspace targets microcontrollers through Device Envoy’s CYD (Cheap Yellow Display) APIs (ESP32 , RP, WASM).
The default crate configuration is no_std and allocation-free, so figures
live in flash and animate on small microcontrollers. An opt-in alloc feature
adds heap-based conveniences where an allocator is available.
§Gallery
The live gallery is the main showcase: carlkcarlk.github.io/linkage-blaze/demos/
It shows preview images of each demo and links to the live, interactive WASM versions.
§Articles
- Nine Rules for Compile-Time Work with Rust
const fn(Part 1), published in Level Up Coding, includes examples from Linkage Blaze and Device Envoy. - Nine Rules for Compile-Time Work with Rust
const fn(Part 2) uses Linkage Blaze to demonstrate a compile-time DSL and examines the practical limits ofconst fn.
§Usage
linkage-blaze can run on embedded systems:
- It does not require the Rust standard library (
no_std). - It does not use heap allocation.
- ESP and RP applications can use the
LinkageFixedaccess generated bylinkage_file!.
It can also run, with more features, in std runs:
allocenables owned parsing.bvhenables host-side APIs for reading Biovision Hierarchy (BVH) motion-capture files.
[dependencies]
linkage-blaze = "<latest version>"Replace <latest version> with the current release shown on
crates.io.
Install the Biovision Hierarchy (BVH) converter with:
cargo install linkage-blaze --features bvh --bin bvh-to-lb§Platform Examples
- Raspberry Pi Pico / RP - Pico 1 and Pico 2 examples, including Pico W variants.
- ESP32 - Cheap Yellow Display examples across supported ESP32 families and boards.
- Browser / WASM - Browser builds behind the live gallery.
The platform examples and browser adapter use the workspace version but are not
separately published to crates.io. WASM applications provide their own
cdylib and may depend on linkage-blaze with the alloc feature.
§Quick Start
The core workflow is: construct one or more linkages, combine them, obtain a
borrowed LinkageView,
and evaluate that view for geometry or a final pose.
§1. Construct a linkage from steps
LinkageFixed
stores an allocation-free linkage in fixed-capacity arrays. Start at the origin,
optionally define normalized parameters, and append movement and drawing steps
with the fluent methods:
const ARM: LinkageFixed<1, 1, 8> = LinkageFixed::start()
.define_param("shoulder", 0.5)
.yaw_param("shoulder", -90.0, 90.0)
.forward(3.0)
.mark("hand");The const generic arguments are the parameter count, mark-slot count, and step capacity. Unused step capacity is allowed.
§2. Combine linkages
LinkageFixed::combine
appends another linkage without replaying its initial Start step:
const BASE: LinkageFixed<0, 0, 2> = LinkageFixed::start().forward(2.0);
const TIP: LinkageFixed<0, 0, 2> = LinkageFixed::start().left(1.0);
const FIGURE: LinkageFixed<0, 0, 4> = BASE.combine(TIP.view());The output type states the combined parameter, mark, and step capacities.
§3. Evaluate the final pose
Call LinkageFixed::view
to borrow a linkage, then pass one normalized value per parameter to
LinkageView::final_pose:
const LINKAGE: LinkageFixed<1, 0, 4> = LinkageFixed::start()
.define_param("reach", 0.5)
.forward_param("reach", 1.0, 5.0);
let pose = LINKAGE.view().final_pose(&[0.5])?;
assert!(pose.position().is_close_to(&Vec3::from([3.0, 0.0, 0.0]), 1e-5));§4. Evaluate for rendering
LinkageView::draw_items_3d
evaluates strokes and shapes as an iterator of
render::Item3d
values. A platform renderer can project and draw each item without allocating:
const LINKAGE: LinkageFixed<0, 0, 3> = LinkageFixed::start()
.forward(2.0)
.left(1.0);
let stroke_count = LINKAGE
.view()
.draw_items_3d(&[])?
.filter(|item| matches!(item, Item3d::Stroke(_)))
.count();
assert_eq!(stroke_count, 2);See the complete ESP32, RP, and WASM examples for display integration.
§5. Save and import a large linkage
Put a long fluent expression in a .lb.rs asset file. The file contains one
linkage![...] expression with leading-dot methods:
linkage![
.define_param("reach", 0.5)
.forward_param("reach", 1.0, 5.0)
.mark("tip")
];Import it with linkage_file!.
The macro measures the asset at compile time and creates a module containing
its exact fixed type, value, and borrowed view. Choose view() for a
borrowed, allocation-free handle, fixed() when you need the compile-time
owner, or buf() under alloc when the linkage must be growable:
use linkage_blaze::linkage_file;
linkage_file! {
figure {
file: "assets/figure.lb.rs",
}
}
type FigureFixed = figure::Fixed;
type FigureView = figure::View;
const FIGURE: FigureFixed = figure::fixed();
const FIGURE_VIEW: FigureView = figure::view();
#[cfg(feature = "alloc")]
let figure_buf: figure::Buf = figure::buf();The import is a text excerpt because rustdoc cannot provide the external asset file at the macro call site. The repository’s linkage_file integration test compile-checks the complete external-file import path.
§6. Edit a large linkage
Open the interactive Linkage Blaze editor
to edit and preview .lb.rs assets. Open an existing file or paste an
expression, adjust its generated parameter controls while watching the 3D
preview, and use Save or Save As to write the edited .lb.rs file.
Editor output uses the same syntax as the allocator-backed parser, so it can be checked before use:
use linkage_blaze::LinkageBuf;
let edited_source = r#"
linkage![
.define_param("reach", 0.5)
.forward_param("reach", 1.0, 5.0)
.mark("tip")
]
"#;
let linkage = LinkageBuf::<1, 1>::from_lb_rs(edited_source)?;
assert_eq!(linkage.view().dof(), 1);For motion-capture input, see the
bvh module
and its Biovision Hierarchy conversion APIs.
§Policy on AI-assisted development and contributions
The use of AI tools is permitted for development and contributions to this repository. AI may be used as a productivity aid for drafting, exploration, and refactoring.
All code and documentation contributed to this repository must be reviewed, edited, and validated by a human contributor. AI tools are not a substitute for design judgment, testing, or responsibility for correctness.
AGENTS.md contains the general instructions and constraints given to AI tools used during development of this repository.
§License
Licensed under either:
at your option.
§Coordinate System
Model-space axes:
- +X = forward / along the link
- +Y = left
- +Z = up
Rotations:
- yaw = rotate about local +Z
- pitch = rotate about local +Y
- roll = rotate about local +X
Modules§
- bvh
- Support for the Biovision Hierarchy (BVH) motion-capture file format.
- examples
- Platform-neutral example logic for the Linkage Blaze display examples.
- render
- Evaluated three-dimensional drawing geometry and projection helpers.
Macros§
- linkage
- Define a linkage expression inside a
.lb.rsasset file. - linkage_
file
Structs§
- Draw
Item3d Iter - Iterator over
Item3ds produced by evaluating a linkage. - Linkage
Buf - An allocator-backed, growable linkage buffer for runtime construction or parsing.
- Linkage
Fixed - An allocation-free linkage with compile-time-fixed capacities.
- Linkage
View - A borrowed linkage used to evaluate, compose, and render a linkage.
- Mat3
- Local-frame orientation matrix stored row-major:
mat[row][col]. - Param
- A named linkage parameter with a normalized default value.
- Param
Arg - A linkage parameter reference and the operation-value range it controls.
- Pose
- A 3D position and local-frame orientation after evaluating a linkage step.
- Rgb888
- RGB color used by pen and shape operations in a linkage.
- Styled
Pose - A
Poseplus the pen state, color, and width active after a linkage step. - Vec3
- 3D position or vector
[x, y, z].
Enums§
- Error
- Error returned by linkage evaluation and named-mark lookup.
- PenState
- Whether movement currently emits strokes.
- Step
- One movement, drawing, shape, mark, or restore operation in a linkage.
- StepArg
- A fixed operation value or a value interpolated from a normalized linkage parameter.
Traits§
- RgbColor
- Channel access and basic color constants for
Rgb888. See the color and rendering example. RGB color. - WebColors
- CSS named colors available as
Rgb888::CSS_*constants in linkage assets. See the color and rendering example. Named colors as defined in the CSS specification.
