Kurbo API Reference
Overview
Kurbo ("curve" in Esperanto) is a Rust 2D curves library for vector graphics and computational geometry. It provides high-accuracy curve primitives, path operations, and affine transformations.
Core Types
Basic Geometry
| Type | Description |
|---|---|
Point | 2D point (f64 coords), arithmetic with Vec2 |
Vec2 | 2D vector with dot/cross, normalize, rotate |
Rect | Rectangle (min/max), union/intersection |
Size | Width/height |
Insets | Padding/margins |
Curves
| Type | Description |
|---|---|
Line | Line segment between two points |
QuadBez | Quadratic Bézier (1 control point) |
CubicBez | Cubic Bézier (2 control points) |
Arc | Elliptical arc with SVG-style params |
Shapes
| Type | Description |
|---|---|
Circle | Center + radius |
Ellipse | Rotated ellipse |
Rect | Also a shape |
RoundedRect | Rectangle with corner radii |
Triangle | Three vertices |
Paths
| Type | Description |
|---|---|
BezPath | Vector of PathEl, multiple subpaths |
PathEl | MoveTo, LineTo, QuadTo, CurveTo, ClosePath |
PathSeg | Line, Quad, or Cubic segment |
Transforms
| Type | Description |
|---|---|
Affine | 2D affine matrix (scale, rotate, translate, skew) |
TranslateScale | Simple translate + uniform scale |
Key Traits
Shape Trait
All shapes implement this:
rust
shape.path_elements(tolerance) // Iterator, no allocation shape.to_path(tolerance) // Allocates BezPath shape.area() // Signed area shape.perimeter(tolerance) // Total length shape.winding(point) // Winding number shape.contains(point) // Point-in-shape (non-zero rule) shape.bounding_box() // Axis-aligned Rect
ParamCurve Traits
Curves (Line, QuadBez, CubicBez, Arc) implement:
rust
curve.eval(t) // Point at parameter t ∈ [0,1] curve.subsegment(t0..t1) // Extract portion curve.start() / curve.end() // Endpoints curve.arclen(accuracy) // Arc length curve.inv_arclen(s, accuracy) // Parameter at arc length s curve.nearest(point, accuracy) // Nearest point info curve.curvature(t) // Curvature at t curve.extrema() // Parameter values of extrema curve.bounding_box() // Tight bounding box
Common Patterns
Creating Shapes
rust
let circle = Circle::new((100.0, 100.0), 50.0); let rect = Rect::new(0.0, 0.0, 100.0, 50.0); let ellipse = Ellipse::new((0.0, 0.0), (100.0, 50.0), 0.0); let rounded = RoundedRect::new(0.0, 0.0, 100.0, 50.0, 10.0);
Building Paths
rust
let mut path = BezPath::new(); path.move_to((0.0, 0.0)); path.line_to((100.0, 0.0)); path.quad_to((150.0, 50.0), (100.0, 100.0)); path.curve_to((50.0, 150.0), (0.0, 150.0), (0.0, 100.0)); path.close_path();
Transformations
rust
// Chain transformations (applied left-to-right)
let affine = Affine::translate((10.0, 20.0))
.then_rotate(PI / 4.0)
.then_scale(2.0);
// Apply to geometry
let new_point = affine * point;
let new_path = affine * &path;
// Other transforms
Affine::rotate(angle)
Affine::rotate_about(angle, center)
Affine::scale(s)
Affine::scale_non_uniform(sx, sy)
Affine::skew(kx, ky)
Affine::reflect(point, direction)
Shape to Path Conversion
rust
// With allocation
let path: BezPath = circle.to_path(0.1);
// Without allocation (iterator)
for el in circle.path_elements(0.1) {
match el {
PathEl::MoveTo(p) => {},
PathEl::LineTo(p) => {},
PathEl::QuadTo(c, p) => {},
PathEl::CurveTo(c1, c2, p) => {},
PathEl::ClosePath => {},
}
}
Iterating Path Segments
rust
// As segments (Line/Quad/Cubic)
for seg in path.segments() {
match seg {
PathSeg::Line(line) => {},
PathSeg::Quad(quad) => {},
PathSeg::Cubic(cubic) => {},
}
}
Stroking
rust
use kurbo::{Stroke, StrokeOpts, Cap, Join, stroke};
let style = Stroke::new(5.0)
.with_caps(Cap::Round)
.with_join(Join::Miter)
.with_miter_limit(4.0)
.with_dashes(0.0, [10.0, 5.0]); // dash offset, pattern
let stroked = stroke(path, &style, &StrokeOpts::default(), 0.25);
SVG Interop
rust
// Path to SVG string
let svg_str = path.to_svg();
// SVG string to path
let path: BezPath = BezPath::from_svg("M0,0 L100,0 Q150,50 100,100 Z").unwrap();
Curve Operations
rust
let cubic = CubicBez::new((0,0), (30,50), (70,50), (100,0)); // Evaluate let midpoint = cubic.eval(0.5); // Split let (first_half, second_half) = cubic.subdivide(); // Arc length let len = cubic.arclen(1e-9); // Find nearest point let nearest = cubic.nearest(test_point, 1e-9); // nearest.distance_sq, nearest.t // Curvature let k = cubic.curvature(0.5);
Accuracy Parameters
Many functions take an accuracy or tolerance parameter:
- •tolerance: Maximum allowed error in path approximation (e.g., 0.1 for curves to lines)
- •accuracy: Target precision for numerical methods (e.g., 1e-9 for arc length)
Smaller values = higher accuracy = more computation.
Feature Flags
toml
[dependencies]
kurbo = { version = "0.11", features = ["serde"] }
| Feature | Description |
|---|---|
std | (default) Standard library |
libm | For no_std environments |
serde | Serialization support |
schemars | JSON schema generation |
mint | mint type conversions |
euclid | euclid type conversions |
Common Mistakes
| Mistake | Fix |
|---|---|
| Forgetting tolerance param | All path_elements/to_path calls need tolerance |
| Wrong winding for holes | Holes need opposite winding direction |
| Ignoring accuracy param | Use 1e-6 to 1e-9 for precision-critical code |
| Mutating paths during iteration | Collect changes, apply after |